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
Hayden Roche 2021-09-16 12:55:03 -07:00
parent 8ed0316993
commit b79527f876
6 changed files with 76 additions and 38 deletions

View File

@ -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 += """

View File

@ -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):

View File

@ -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):

View File

@ -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]

View File

@ -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):

View File

@ -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,