Make wolfcrypt-py work with FIPS ready and other improvements.
- Detect ECC timing resistance and call wc_ecc_set_rng where appropriate. - Detect FIPS version and use that information to figure out how to map hash enum values (see _TYPE_SHA and friends). - Don't call wc_HmacSetKey in the _Hmac constructor if the key passed to _init is length 0. This can happen, for example, when the _Hmac object is being copied. The copy operation copies over the raw memory from the underlying C object, so it's not important that we call wc_HmacSetKey in this case. - Removed a unit test that expected importing an ECC public key from a private key to fail. This does fail in the default wolfSSL version for wolfcrypt-py, v4.1.0-stable, but we added the feature to be able to import public from private with wolfSSL PR #2916. As a result, this test fails with v4.8.1-stable. We should upgrade wolfcrypt-py's default wolfSSL version (and the wolfcrypt-py version itself) in the near future. - The array slicing in test_key_encoding was wrong in many places. This likely stemmed from the author thinking slices were inclusive, but that's only true for the first element of the slice (e.g. [0:31] is elements 0-30 inclusive, not elements 0-31 inclusive). This was uncovered by testing with FIPS ready, which adds -DWOLFSSL_VALIDATE_ECC_IMPORT, causing us to check ECC keys with wc_ecc_check_key. wc_ecc_check_key kept saying, "hey, that point's not on the curve." The array slicing problem was the culprit. - Fixed tests that were doing HMAC with a key less than HMAC_FIPS_MIN_KEY.pull/24/head
parent
8ed0316993
commit
b79527f876
|
@ -20,6 +20,7 @@
|
|||
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
from distutils.util import get_platform
|
||||
from cffi import FFI
|
||||
from wolfcrypt import __wolfssl_version__ as version
|
||||
|
@ -58,12 +59,15 @@ AES_ENABLED = 1
|
|||
HMAC_ENABLED = 1
|
||||
RSA_ENABLED = 1
|
||||
RSA_BLINDING_ENABLED = 1
|
||||
ECC_TIMING_RESISTANCE_ENABLED = 1
|
||||
ECC_ENABLED = 1
|
||||
ED25519_ENABLED = 1
|
||||
KEYGEN_ENABLED = 1
|
||||
CHACHA_ENABLED = 1
|
||||
PWDBASED_ENABLED = 0
|
||||
FIPS_ENABLED = 0
|
||||
FIPS_VERSION = 0
|
||||
ERROR_STRINGS_ENABLED = 1
|
||||
|
||||
# detect native features based on options.h defines
|
||||
if featureDetection:
|
||||
|
@ -78,12 +82,19 @@ if featureDetection:
|
|||
CHACHA_ENABLED = 1 if '#define HAVE_CHACHA' in optionsHeaderStr else 0
|
||||
HMAC_ENABLED = 0 if '#define NO_HMAC' in optionsHeaderStr else 1
|
||||
RSA_ENABLED = 0 if '#define NO_RSA' in optionsHeaderStr else 1
|
||||
ECC_TIMING_RESISTANCE_ENABLED = 1 if '#define ECC_TIMING_RESISTANT' in optionsHeaderStr else 0
|
||||
RSA_BLINDING_ENABLED = 1 if '#define WC_RSA_BLINDING' in optionsHeaderStr else 0
|
||||
ECC_ENABLED = 1 if '#define HAVE_ECC' in optionsHeaderStr else 0
|
||||
ED25519_ENABLED = 1 if '#define HAVE_ED25519' in optionsHeaderStr else 0
|
||||
KEYGEN_ENABLED = 1 if '#define WOLFSSL_KEY_GEN' in optionsHeaderStr else 0
|
||||
PWDBASED_ENABLED = 0 if '#define NO_PWDBASED' in optionsHeaderStr else 1
|
||||
FIPS_ENABLED = 1 if '#define HAVE_FIPS' in optionsHeaderStr else 0
|
||||
ERROR_STRINGS_ENABLED = 0 if '#define NO_ERROR_STRINGS' in optionsHeaderStr else 1
|
||||
|
||||
if '#define HAVE_FIPS' in optionsHeaderStr:
|
||||
FIPS_ENABLED = 1
|
||||
version_match = re.search(r'#define HAVE_FIPS_VERSION\s+(\d+)', optionsHeaderStr)
|
||||
if version_match is not None:
|
||||
FIPS_VERSION = int(version_match.group(1))
|
||||
|
||||
if RSA_BLINDING_ENABLED and FIPS_ENABLED:
|
||||
# These settings can't coexist. See settings.h.
|
||||
|
@ -131,11 +142,13 @@ ffibuilder.set_source(
|
|||
int HMAC_ENABLED = """ + str(HMAC_ENABLED) + """;
|
||||
int RSA_ENABLED = """ + str(RSA_ENABLED) + """;
|
||||
int RSA_BLINDING_ENABLED = """ + str(RSA_BLINDING_ENABLED) + """;
|
||||
int ECC_TIMING_RESISTANCE_ENABLED = """ + str(ECC_TIMING_RESISTANCE_ENABLED) + """;
|
||||
int ECC_ENABLED = """ + str(ECC_ENABLED) + """;
|
||||
int ED25519_ENABLED = """ + str(ED25519_ENABLED) + """;
|
||||
int KEYGEN_ENABLED = """ + str(KEYGEN_ENABLED) + """;
|
||||
int PWDBASED_ENABLED = """ + str(PWDBASED_ENABLED) + """;
|
||||
int FIPS_ENABLED = """ + str(FIPS_ENABLED) + """;
|
||||
int FIPS_VERSION = """ + str(FIPS_VERSION) + """;
|
||||
""",
|
||||
include_dirs=[wolfssl_inc_path()],
|
||||
library_dirs=[wolfssl_lib_path()],
|
||||
|
@ -155,11 +168,13 @@ _cdef = """
|
|||
extern int HMAC_ENABLED;
|
||||
extern int RSA_ENABLED;
|
||||
extern int RSA_BLINDING_ENABLED;
|
||||
extern int ECC_TIMING_RESISTANCE_ENABLED;
|
||||
extern int ECC_ENABLED;
|
||||
extern int ED25519_ENABLED;
|
||||
extern int KEYGEN_ENABLED;
|
||||
extern int PWDBASED_ENABLED;
|
||||
extern int FIPS_ENABLED;
|
||||
extern int FIPS_VERSION;
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned int word32;
|
||||
|
@ -339,14 +354,19 @@ if ECC_ENABLED:
|
|||
int* stat, ecc_key* key);
|
||||
"""
|
||||
|
||||
if ECC_ENABLED and MPAPI_ENABLED:
|
||||
_cdef += """
|
||||
int wc_ecc_sign_hash_ex(const byte* in, word32 inlen, WC_RNG* rng,
|
||||
ecc_key* key, mp_int *r, mp_int *s);
|
||||
if MPAPI_ENABLED:
|
||||
_cdef += """
|
||||
int wc_ecc_sign_hash_ex(const byte* in, word32 inlen, WC_RNG* rng,
|
||||
ecc_key* key, mp_int *r, mp_int *s);
|
||||
int wc_ecc_verify_hash_ex(mp_int *r, mp_int *s, const byte* hash,
|
||||
word32 hashlen, int* res, ecc_key* key);
|
||||
"""
|
||||
|
||||
if ECC_TIMING_RESISTANCE_ENABLED:
|
||||
_cdef += """
|
||||
int wc_ecc_set_rng(ecc_key* key, WC_RNG* rng);
|
||||
"""
|
||||
|
||||
int wc_ecc_verify_hash_ex(mp_int *r, mp_int *s, const byte* hash,
|
||||
word32 hashlen, int* res, ecc_key* key);
|
||||
"""
|
||||
|
||||
if ED25519_ENABLED:
|
||||
_cdef += """
|
||||
|
|
|
@ -712,6 +712,11 @@ if _lib.ECC_ENABLED:
|
|||
if ret < 0:
|
||||
raise WolfCryptError("Key generation error (%d)" % ret)
|
||||
|
||||
if _lib.ECC_TIMING_RESISTANCE_ENABLED:
|
||||
ret = _lib.wc_ecc_set_rng(ecc.native_object, rng.native_object)
|
||||
if ret < 0:
|
||||
raise WolfCryptError("Error setting ECC RNG (%d)" % ret)
|
||||
|
||||
return ecc
|
||||
|
||||
def decode_key(self, key):
|
||||
|
|
|
@ -260,7 +260,7 @@ if _lib.SHA3_ENABLED:
|
|||
|
||||
# Hmac types
|
||||
|
||||
if _lib.FIPS_ENABLED:
|
||||
if _lib.FIPS_ENABLED and _lib.FIPS_VERSION <= 2:
|
||||
_TYPE_SHA = 1
|
||||
_TYPE_SHA256 = 2
|
||||
_TYPE_SHA384 = 5
|
||||
|
@ -309,9 +309,19 @@ if _lib.HMAC_ENABLED:
|
|||
def _init(self, hmac, key):
|
||||
if _lib.wc_HmacInit(self._native_object, _ffi.NULL, -2) != 0:
|
||||
raise WolfCryptError("wc_HmacInit error")
|
||||
ret = _lib.wc_HmacSetKey(self._native_object, hmac, key, len(key))
|
||||
if ret < 0:
|
||||
raise WolfCryptError("wc_HmacSetKey returned %d" % ret)
|
||||
# If the key isn't set, don't call wc_HmacSetKey. This can happen,
|
||||
# for example, when the HMAC object is being copied. See the copy
|
||||
# function of _Hash.
|
||||
ret = 0
|
||||
if len(key) > 0:
|
||||
ret = _lib.wc_HmacSetKey(self._native_object, hmac, key, len(key))
|
||||
if ret < 0:
|
||||
err_str = "no error description found"
|
||||
try:
|
||||
err_str = _ffi.string(_lib.wc_GetErrorString(ret)).decode()
|
||||
except:
|
||||
pass
|
||||
raise WolfCryptError("wc_HmacSetKey returned {}: {}".format(ret, err_str))
|
||||
return ret
|
||||
|
||||
def _update(self, data):
|
||||
|
|
|
@ -408,9 +408,6 @@ if _lib.ECC_ENABLED:
|
|||
with pytest.raises(WolfCryptError):
|
||||
EccPrivate(vectors[EccPublic].key) # invalid key type
|
||||
|
||||
with pytest.raises(WolfCryptError):
|
||||
EccPublic(vectors[EccPrivate].key) # invalid key type
|
||||
|
||||
with pytest.raises(WolfCryptError): # invalid key size
|
||||
EccPrivate.make_key(1024)
|
||||
|
||||
|
@ -430,27 +427,27 @@ if _lib.ECC_ENABLED:
|
|||
|
||||
# Test EccPrivate.encode_key_raw/decode_key_raw
|
||||
key = vectors[EccPrivate].raw_key
|
||||
raw_priv.decode_key_raw(key[0:31], key[32:63], key[64:-1])
|
||||
raw_priv.decode_key_raw(key[0:32], key[32:64], key[64:96])
|
||||
qx, qy, d = raw_priv.encode_key_raw()
|
||||
assert qx[0:31] == vectors[EccPrivate].raw_key[0:31]
|
||||
assert qy[0:31] == vectors[EccPrivate].raw_key[32:63]
|
||||
assert d[0:31] == vectors[EccPrivate].raw_key[64:-1]
|
||||
assert qx[0:32] == vectors[EccPrivate].raw_key[0:32]
|
||||
assert qy[0:32] == vectors[EccPrivate].raw_key[32:64]
|
||||
assert d[0:32] == vectors[EccPrivate].raw_key[64:96]
|
||||
# Verify ECC key is the same as the raw key
|
||||
qx, qy, d = priv.encode_key_raw()
|
||||
assert qx[0:31] == vectors[EccPrivate].raw_key[0:31]
|
||||
assert qy[0:31] == vectors[EccPrivate].raw_key[32:63]
|
||||
assert d[0:31] == vectors[EccPrivate].raw_key[64:-1]
|
||||
assert qx[0:32] == vectors[EccPrivate].raw_key[0:32]
|
||||
assert qy[0:32] == vectors[EccPrivate].raw_key[32:64]
|
||||
assert d[0:32] == vectors[EccPrivate].raw_key[64:96]
|
||||
|
||||
# Test EccPublic.encode_key_raw/decode_key_raw
|
||||
key = vectors[EccPublic].raw_key
|
||||
raw_pub.decode_key_raw(key[0:31], key[32:-1])
|
||||
raw_pub.decode_key_raw(key[0:32], key[32:64])
|
||||
qx, qy = raw_pub.encode_key_raw()
|
||||
assert qx[0:31] == vectors[EccPublic].raw_key[0:31]
|
||||
assert qy[0:31] == vectors[EccPublic].raw_key[32:63]
|
||||
assert qx[0:32] == vectors[EccPublic].raw_key[0:32]
|
||||
assert qy[0:32] == vectors[EccPublic].raw_key[32:64]
|
||||
# Verify ECC public key is the same as the raw key
|
||||
qx, qy = pub.encode_key_raw()
|
||||
assert qx[0:31] == vectors[EccPublic].raw_key[0:31]
|
||||
assert qy[0:31] == vectors[EccPublic].raw_key[32:63]
|
||||
assert qx[0:32] == vectors[EccPublic].raw_key[0:32]
|
||||
assert qy[0:32] == vectors[EccPublic].raw_key[32:64]
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -93,24 +93,23 @@ def vectors():
|
|||
if _lib.HMAC_ENABLED:
|
||||
if _lib.SHA_ENABLED:
|
||||
vectorArray[HmacSha]=TestVector(
|
||||
digest=t2b("5dfabcfb3a25540824867cd21f065f52f73491e0")
|
||||
digest=t2b("7ab9aca2c87c7c45ba2ffa52f719fdbd8fbff62d")
|
||||
)
|
||||
if _lib.SHA256_ENABLED:
|
||||
vectorArray[HmacSha256]=TestVector(
|
||||
digest=t2b("4b641d721493d80f019d9447830ebfee" +
|
||||
"89234a7d594378b89f8bb73873576bf6")
|
||||
digest=t2b("9041ac8c66fc350a1a0d5f4fff9d8ef74721d5a43ec8893a2" +
|
||||
"875cf69576c45c2")
|
||||
)
|
||||
if _lib.SHA384_ENABLED:
|
||||
vectorArray[HmacSha384]=TestVector(
|
||||
digest=t2b("e72c72070c9c5c78e3286593068a510c1740cdf9dc34b512" +
|
||||
"ccec97320295db1fe673216b46fe72e81f399a9ec04780ab")
|
||||
digest=t2b("f8c589ddf5489404f85c3c718a8345f207fb1ed6c6f5ecb09" +
|
||||
"8e8be8aeb1aaa9f0c6dd84c141410b29a47a1a2b3a85ae0")
|
||||
)
|
||||
if _lib.SHA512_ENABLED:
|
||||
vectorArray[HmacSha512]=TestVector(
|
||||
digest=t2b("c7f48db79314fc2b5be9a93fd58601a1" +
|
||||
"bf42f397ec7f66dba034d44503890e6b" +
|
||||
"5708242dcd71a248a78162d815c685f6" +
|
||||
"038a4ac8cb34b8bf18986dbd300c9b41")
|
||||
digest=t2b("7708a12ca110cd81a334bd4e8bddc4314acd3ed218bbff7c6" +
|
||||
"486e149fc145e9f5c05f05e919f7c2bc027266e986679984c" +
|
||||
"3ade1a14084ad7627a65c3671a2d05")
|
||||
)
|
||||
|
||||
return vectorArray
|
||||
|
@ -146,8 +145,12 @@ def hash_cls(request):
|
|||
|
||||
def hash_new(cls, data=None):
|
||||
if cls in hash_params:
|
||||
# If it's a non-HMAC hash algo, we don't need a key. Call the
|
||||
# constructor that doesn't take a key.
|
||||
return cls(data)
|
||||
return cls("python", data)
|
||||
# HMAC requires a key (first parameter to constructor below). Do not shorten
|
||||
# the length of this key below the FIPS requirement. See HMAC_FIPS_MIN_KEY.
|
||||
return cls("wolfCrypt is the best crypto around", data)
|
||||
|
||||
|
||||
def test_hash(hash_cls, vectors):
|
||||
|
|
|
@ -19,8 +19,11 @@ def pbkdf2_vectors():
|
|||
vectors = []
|
||||
|
||||
if _lib.PWDBASED_ENABLED and _lib.SHA_ENABLED and _lib.HMAC_ENABLED:
|
||||
# HMAC requires a key, which in this case is the password. Do not
|
||||
# shorten the length of the password below the FIPS requirement.
|
||||
# See HMAC_FIPS_MIN_KEY.
|
||||
vectors.append(TestVector(
|
||||
password="pass1234",
|
||||
password="wolfcrypt is the best crypto around",
|
||||
salt="salt1234",
|
||||
iterations=1000,
|
||||
key_length=Sha.digest_size,
|
||||
|
|
Loading…
Reference in New Issue