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 os
|
||||||
import sys
|
import sys
|
||||||
|
import re
|
||||||
from distutils.util import get_platform
|
from distutils.util import get_platform
|
||||||
from cffi import FFI
|
from cffi import FFI
|
||||||
from wolfcrypt import __wolfssl_version__ as version
|
from wolfcrypt import __wolfssl_version__ as version
|
||||||
|
@ -58,12 +59,15 @@ AES_ENABLED = 1
|
||||||
HMAC_ENABLED = 1
|
HMAC_ENABLED = 1
|
||||||
RSA_ENABLED = 1
|
RSA_ENABLED = 1
|
||||||
RSA_BLINDING_ENABLED = 1
|
RSA_BLINDING_ENABLED = 1
|
||||||
|
ECC_TIMING_RESISTANCE_ENABLED = 1
|
||||||
ECC_ENABLED = 1
|
ECC_ENABLED = 1
|
||||||
ED25519_ENABLED = 1
|
ED25519_ENABLED = 1
|
||||||
KEYGEN_ENABLED = 1
|
KEYGEN_ENABLED = 1
|
||||||
CHACHA_ENABLED = 1
|
CHACHA_ENABLED = 1
|
||||||
PWDBASED_ENABLED = 0
|
PWDBASED_ENABLED = 0
|
||||||
FIPS_ENABLED = 0
|
FIPS_ENABLED = 0
|
||||||
|
FIPS_VERSION = 0
|
||||||
|
ERROR_STRINGS_ENABLED = 1
|
||||||
|
|
||||||
# detect native features based on options.h defines
|
# detect native features based on options.h defines
|
||||||
if featureDetection:
|
if featureDetection:
|
||||||
|
@ -78,12 +82,19 @@ if featureDetection:
|
||||||
CHACHA_ENABLED = 1 if '#define HAVE_CHACHA' in optionsHeaderStr else 0
|
CHACHA_ENABLED = 1 if '#define HAVE_CHACHA' in optionsHeaderStr else 0
|
||||||
HMAC_ENABLED = 0 if '#define NO_HMAC' in optionsHeaderStr else 1
|
HMAC_ENABLED = 0 if '#define NO_HMAC' in optionsHeaderStr else 1
|
||||||
RSA_ENABLED = 0 if '#define NO_RSA' 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
|
RSA_BLINDING_ENABLED = 1 if '#define WC_RSA_BLINDING' in optionsHeaderStr else 0
|
||||||
ECC_ENABLED = 1 if '#define HAVE_ECC' 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
|
ED25519_ENABLED = 1 if '#define HAVE_ED25519' in optionsHeaderStr else 0
|
||||||
KEYGEN_ENABLED = 1 if '#define WOLFSSL_KEY_GEN' 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
|
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:
|
if RSA_BLINDING_ENABLED and FIPS_ENABLED:
|
||||||
# These settings can't coexist. See settings.h.
|
# These settings can't coexist. See settings.h.
|
||||||
|
@ -131,11 +142,13 @@ ffibuilder.set_source(
|
||||||
int HMAC_ENABLED = """ + str(HMAC_ENABLED) + """;
|
int HMAC_ENABLED = """ + str(HMAC_ENABLED) + """;
|
||||||
int RSA_ENABLED = """ + str(RSA_ENABLED) + """;
|
int RSA_ENABLED = """ + str(RSA_ENABLED) + """;
|
||||||
int RSA_BLINDING_ENABLED = """ + str(RSA_BLINDING_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 ECC_ENABLED = """ + str(ECC_ENABLED) + """;
|
||||||
int ED25519_ENABLED = """ + str(ED25519_ENABLED) + """;
|
int ED25519_ENABLED = """ + str(ED25519_ENABLED) + """;
|
||||||
int KEYGEN_ENABLED = """ + str(KEYGEN_ENABLED) + """;
|
int KEYGEN_ENABLED = """ + str(KEYGEN_ENABLED) + """;
|
||||||
int PWDBASED_ENABLED = """ + str(PWDBASED_ENABLED) + """;
|
int PWDBASED_ENABLED = """ + str(PWDBASED_ENABLED) + """;
|
||||||
int FIPS_ENABLED = """ + str(FIPS_ENABLED) + """;
|
int FIPS_ENABLED = """ + str(FIPS_ENABLED) + """;
|
||||||
|
int FIPS_VERSION = """ + str(FIPS_VERSION) + """;
|
||||||
""",
|
""",
|
||||||
include_dirs=[wolfssl_inc_path()],
|
include_dirs=[wolfssl_inc_path()],
|
||||||
library_dirs=[wolfssl_lib_path()],
|
library_dirs=[wolfssl_lib_path()],
|
||||||
|
@ -155,11 +168,13 @@ _cdef = """
|
||||||
extern int HMAC_ENABLED;
|
extern int HMAC_ENABLED;
|
||||||
extern int RSA_ENABLED;
|
extern int RSA_ENABLED;
|
||||||
extern int RSA_BLINDING_ENABLED;
|
extern int RSA_BLINDING_ENABLED;
|
||||||
|
extern int ECC_TIMING_RESISTANCE_ENABLED;
|
||||||
extern int ECC_ENABLED;
|
extern int ECC_ENABLED;
|
||||||
extern int ED25519_ENABLED;
|
extern int ED25519_ENABLED;
|
||||||
extern int KEYGEN_ENABLED;
|
extern int KEYGEN_ENABLED;
|
||||||
extern int PWDBASED_ENABLED;
|
extern int PWDBASED_ENABLED;
|
||||||
extern int FIPS_ENABLED;
|
extern int FIPS_ENABLED;
|
||||||
|
extern int FIPS_VERSION;
|
||||||
|
|
||||||
typedef unsigned char byte;
|
typedef unsigned char byte;
|
||||||
typedef unsigned int word32;
|
typedef unsigned int word32;
|
||||||
|
@ -339,14 +354,19 @@ if ECC_ENABLED:
|
||||||
int* stat, ecc_key* key);
|
int* stat, ecc_key* key);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if ECC_ENABLED and MPAPI_ENABLED:
|
if MPAPI_ENABLED:
|
||||||
_cdef += """
|
_cdef += """
|
||||||
int wc_ecc_sign_hash_ex(const byte* in, word32 inlen, WC_RNG* rng,
|
int wc_ecc_sign_hash_ex(const byte* in, word32 inlen, WC_RNG* rng,
|
||||||
ecc_key* key, mp_int *r, mp_int *s);
|
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:
|
if ED25519_ENABLED:
|
||||||
_cdef += """
|
_cdef += """
|
||||||
|
|
|
@ -712,6 +712,11 @@ if _lib.ECC_ENABLED:
|
||||||
if ret < 0:
|
if ret < 0:
|
||||||
raise WolfCryptError("Key generation error (%d)" % ret)
|
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
|
return ecc
|
||||||
|
|
||||||
def decode_key(self, key):
|
def decode_key(self, key):
|
||||||
|
|
|
@ -260,7 +260,7 @@ if _lib.SHA3_ENABLED:
|
||||||
|
|
||||||
# Hmac types
|
# Hmac types
|
||||||
|
|
||||||
if _lib.FIPS_ENABLED:
|
if _lib.FIPS_ENABLED and _lib.FIPS_VERSION <= 2:
|
||||||
_TYPE_SHA = 1
|
_TYPE_SHA = 1
|
||||||
_TYPE_SHA256 = 2
|
_TYPE_SHA256 = 2
|
||||||
_TYPE_SHA384 = 5
|
_TYPE_SHA384 = 5
|
||||||
|
@ -309,9 +309,19 @@ if _lib.HMAC_ENABLED:
|
||||||
def _init(self, hmac, key):
|
def _init(self, hmac, key):
|
||||||
if _lib.wc_HmacInit(self._native_object, _ffi.NULL, -2) != 0:
|
if _lib.wc_HmacInit(self._native_object, _ffi.NULL, -2) != 0:
|
||||||
raise WolfCryptError("wc_HmacInit error")
|
raise WolfCryptError("wc_HmacInit error")
|
||||||
ret = _lib.wc_HmacSetKey(self._native_object, hmac, key, len(key))
|
# If the key isn't set, don't call wc_HmacSetKey. This can happen,
|
||||||
if ret < 0:
|
# for example, when the HMAC object is being copied. See the copy
|
||||||
raise WolfCryptError("wc_HmacSetKey returned %d" % ret)
|
# 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
|
return ret
|
||||||
|
|
||||||
def _update(self, data):
|
def _update(self, data):
|
||||||
|
|
|
@ -408,9 +408,6 @@ if _lib.ECC_ENABLED:
|
||||||
with pytest.raises(WolfCryptError):
|
with pytest.raises(WolfCryptError):
|
||||||
EccPrivate(vectors[EccPublic].key) # invalid key type
|
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
|
with pytest.raises(WolfCryptError): # invalid key size
|
||||||
EccPrivate.make_key(1024)
|
EccPrivate.make_key(1024)
|
||||||
|
|
||||||
|
@ -430,27 +427,27 @@ if _lib.ECC_ENABLED:
|
||||||
|
|
||||||
# Test EccPrivate.encode_key_raw/decode_key_raw
|
# Test EccPrivate.encode_key_raw/decode_key_raw
|
||||||
key = vectors[EccPrivate].raw_key
|
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()
|
qx, qy, d = raw_priv.encode_key_raw()
|
||||||
assert qx[0:31] == vectors[EccPrivate].raw_key[0:31]
|
assert qx[0:32] == vectors[EccPrivate].raw_key[0:32]
|
||||||
assert qy[0:31] == vectors[EccPrivate].raw_key[32:63]
|
assert qy[0:32] == vectors[EccPrivate].raw_key[32:64]
|
||||||
assert d[0:31] == vectors[EccPrivate].raw_key[64:-1]
|
assert d[0:32] == vectors[EccPrivate].raw_key[64:96]
|
||||||
# Verify ECC key is the same as the raw key
|
# Verify ECC key is the same as the raw key
|
||||||
qx, qy, d = priv.encode_key_raw()
|
qx, qy, d = priv.encode_key_raw()
|
||||||
assert qx[0:31] == vectors[EccPrivate].raw_key[0:31]
|
assert qx[0:32] == vectors[EccPrivate].raw_key[0:32]
|
||||||
assert qy[0:31] == vectors[EccPrivate].raw_key[32:63]
|
assert qy[0:32] == vectors[EccPrivate].raw_key[32:64]
|
||||||
assert d[0:31] == vectors[EccPrivate].raw_key[64:-1]
|
assert d[0:32] == vectors[EccPrivate].raw_key[64:96]
|
||||||
|
|
||||||
# Test EccPublic.encode_key_raw/decode_key_raw
|
# Test EccPublic.encode_key_raw/decode_key_raw
|
||||||
key = vectors[EccPublic].raw_key
|
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()
|
qx, qy = raw_pub.encode_key_raw()
|
||||||
assert qx[0:31] == vectors[EccPublic].raw_key[0:31]
|
assert qx[0:32] == vectors[EccPublic].raw_key[0:32]
|
||||||
assert qy[0:31] == vectors[EccPublic].raw_key[32:63]
|
assert qy[0:32] == vectors[EccPublic].raw_key[32:64]
|
||||||
# Verify ECC public key is the same as the raw key
|
# Verify ECC public key is the same as the raw key
|
||||||
qx, qy = pub.encode_key_raw()
|
qx, qy = pub.encode_key_raw()
|
||||||
assert qx[0:31] == vectors[EccPublic].raw_key[0:31]
|
assert qx[0:32] == vectors[EccPublic].raw_key[0:32]
|
||||||
assert qy[0:31] == vectors[EccPublic].raw_key[32:63]
|
assert qy[0:32] == vectors[EccPublic].raw_key[32:64]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -93,24 +93,23 @@ def vectors():
|
||||||
if _lib.HMAC_ENABLED:
|
if _lib.HMAC_ENABLED:
|
||||||
if _lib.SHA_ENABLED:
|
if _lib.SHA_ENABLED:
|
||||||
vectorArray[HmacSha]=TestVector(
|
vectorArray[HmacSha]=TestVector(
|
||||||
digest=t2b("5dfabcfb3a25540824867cd21f065f52f73491e0")
|
digest=t2b("7ab9aca2c87c7c45ba2ffa52f719fdbd8fbff62d")
|
||||||
)
|
)
|
||||||
if _lib.SHA256_ENABLED:
|
if _lib.SHA256_ENABLED:
|
||||||
vectorArray[HmacSha256]=TestVector(
|
vectorArray[HmacSha256]=TestVector(
|
||||||
digest=t2b("4b641d721493d80f019d9447830ebfee" +
|
digest=t2b("9041ac8c66fc350a1a0d5f4fff9d8ef74721d5a43ec8893a2" +
|
||||||
"89234a7d594378b89f8bb73873576bf6")
|
"875cf69576c45c2")
|
||||||
)
|
)
|
||||||
if _lib.SHA384_ENABLED:
|
if _lib.SHA384_ENABLED:
|
||||||
vectorArray[HmacSha384]=TestVector(
|
vectorArray[HmacSha384]=TestVector(
|
||||||
digest=t2b("e72c72070c9c5c78e3286593068a510c1740cdf9dc34b512" +
|
digest=t2b("f8c589ddf5489404f85c3c718a8345f207fb1ed6c6f5ecb09" +
|
||||||
"ccec97320295db1fe673216b46fe72e81f399a9ec04780ab")
|
"8e8be8aeb1aaa9f0c6dd84c141410b29a47a1a2b3a85ae0")
|
||||||
)
|
)
|
||||||
if _lib.SHA512_ENABLED:
|
if _lib.SHA512_ENABLED:
|
||||||
vectorArray[HmacSha512]=TestVector(
|
vectorArray[HmacSha512]=TestVector(
|
||||||
digest=t2b("c7f48db79314fc2b5be9a93fd58601a1" +
|
digest=t2b("7708a12ca110cd81a334bd4e8bddc4314acd3ed218bbff7c6" +
|
||||||
"bf42f397ec7f66dba034d44503890e6b" +
|
"486e149fc145e9f5c05f05e919f7c2bc027266e986679984c" +
|
||||||
"5708242dcd71a248a78162d815c685f6" +
|
"3ade1a14084ad7627a65c3671a2d05")
|
||||||
"038a4ac8cb34b8bf18986dbd300c9b41")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return vectorArray
|
return vectorArray
|
||||||
|
@ -146,8 +145,12 @@ def hash_cls(request):
|
||||||
|
|
||||||
def hash_new(cls, data=None):
|
def hash_new(cls, data=None):
|
||||||
if cls in hash_params:
|
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(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):
|
def test_hash(hash_cls, vectors):
|
||||||
|
|
|
@ -19,8 +19,11 @@ def pbkdf2_vectors():
|
||||||
vectors = []
|
vectors = []
|
||||||
|
|
||||||
if _lib.PWDBASED_ENABLED and _lib.SHA_ENABLED and _lib.HMAC_ENABLED:
|
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(
|
vectors.append(TestVector(
|
||||||
password="pass1234",
|
password="wolfcrypt is the best crypto around",
|
||||||
salt="salt1234",
|
salt="salt1234",
|
||||||
iterations=1000,
|
iterations=1000,
|
||||||
key_length=Sha.digest_size,
|
key_length=Sha.digest_size,
|
||||||
|
|
Loading…
Reference in New Issue