Add signature generation and verification.

pull/27/head
Hayden Roche 2021-12-20 15:26:48 -08:00
parent 10ba23046b
commit 7a847cf9b1
3 changed files with 95 additions and 2 deletions

View File

@ -461,6 +461,11 @@ if ASN_ENABLED:
static const long PRIVATEKEY_TYPE;
static const long PUBLICKEY_TYPE;
static const long CERT_TYPE;
static const long MAX_DER_DIGEST_SZ;
static const long SHAh;
static const long SHA256h;
static const long SHA384h;
static const long SHA512h;
typedef struct DerBuffer {
byte* buffer;
@ -476,6 +481,8 @@ if ASN_ENABLED:
int* keyFormat);
int wc_DerToPemEx(const byte* der, word32 derSz, byte* output, word32 outSz,
byte *cipher_info, int type);
word32 wc_EncodeSignature(byte* out, const byte* digest, word32 digSz,
int hashOID);
"""
ffibuilder.cdef(_cdef)

View File

@ -22,9 +22,16 @@
from wolfcrypt._ffi import ffi as _ffi
from wolfcrypt._ffi import lib as _lib
from wolfcrypt.exceptions import WolfCryptError
if _lib.SHA_ENABLED:
from wolfcrypt.hashes import Sha
if _lib.SHA256_ENABLED:
from wolfcrypt.hashes import Sha256
if _lib.SHA384_ENABLED:
from wolfcrypt.hashes import Sha384
if _lib.SHA512_ENABLED:
from wolfcrypt.hashes import Sha512
if _lib.ASN_ENABLED:
def pem_to_der(pem, pem_type):
@ -52,3 +59,40 @@ if _lib.ASN_ENABLED:
raise WolfCryptError(err)
return _ffi.buffer(pem, pem_length)[:]
def hash_oid_from_class(hash_cls):
if hash_cls == Sha:
return _lib.SHAh
elif hash_cls == Sha256:
return _lib.SHA256h
elif hash_cls == Sha384:
return _lib.SHA384h
elif hash_cls == Sha512:
return _lib.SHA512h
else:
err = "Unknown hash class {}.".format(hash_cls.__name__)
raise WolfCryptError(err)
def make_signature(data, hash_cls, key=None):
hash_obj = hash_cls()
hash_obj.update(data)
digest = hash_obj.digest()
plaintext_sig = _ffi.new("byte[%d]" % _lib.MAX_DER_DIGEST_SZ)
hash_oid = hash_oid_from_class(hash_cls)
plaintext_len = _lib.wc_EncodeSignature(plaintext_sig, digest,
len(digest), hash_oid)
if plaintext_len == 0:
err = "Error calling wc_EncodeSignature. ({})".format(plaintext_len)
raise WolfCryptError(err)
plaintext_sig = _ffi.buffer(plaintext_sig, plaintext_len)[:]
if key:
return key.sign(plaintext_sig)
else:
return plaintext_sig
def check_signature(signature, data, hash_cls, pub_key):
computed_signature = make_signature(data, hash_cls)
decrypted_signature = pub_key.verify(signature)
return computed_signature == decrypted_signature

View File

@ -5,7 +5,7 @@ from wolfcrypt._ffi import lib as _lib
from wolfcrypt.utils import h2b
if _lib.ASN_ENABLED:
from wolfcrypt.asn import pem_to_der, der_to_pem
from wolfcrypt.asn import pem_to_der, der_to_pem, make_signature, check_signature
if _lib.SHA256_ENABLED:
from wolfcrypt.hashes import Sha256
if _lib.RSA_ENABLED:
@ -38,6 +38,42 @@ def pem_der_conversion_vectors():
return vectors
@pytest.fixture
def signature_vectors():
TestVector = namedtuple("TestVector", """data signature hash_cls pub_key
priv_key""")
TestVector.__new__.__defaults__ = (None,) * len(TestVector._fields)
vectors = []
with open(os.path.join(certs_dir, "server-keyPub.pem"), "rb") as f:
pub_key_pem = f.read()
with open(os.path.join(certs_dir, "server-key.pem"), "rb") as f:
priv_key_pem = f.read()
# Signature computed with:
# echo -n "wolfcrypt is the best crypto around" | \
# openssl dgst -hex -sha256 -sign tests/certs/server-key.pem
if _lib.ASN_ENABLED and _lib.SHA256_ENABLED and _lib.RSA_ENABLED:
vectors.append(TestVector(
data="wolfcrypt is the best crypto around",
signature=h2b("1d65f21df8fdc9f3c2351792840423481c6b0f2332105abd9248"
"9e0dc8f6f8c740e267cf49f522f771eabd484f961eaf9f907c97"
"b513bb9de7411b508c4e7ab7dc4438890ca161a9e24addaffd3c"
"86821f2431f55fde5d131dfbe5805dea74e8882bfbfbf451f809"
"ed792dfb0b17c799e6a39f866ed9cf613138c9e5e99f757ea13a"
"2b9c167c294cd89f38365ab40175d4e29c24d672cd5ad2d57fec"
"e9ea2b29c1866235c791ec5b635b858512c2b832b1b8f1dc6854"
"cd4927df5519eefee439848c7f109548b3a3c8265658e009899a"
"51a4edaf9f1199f93e448482f27c43a53e0bc65b04e9848128e3"
"60314e864190e6bb9812bfbf4b40994f2c1d4ca7aad9"),
hash_cls=Sha256,
pub_key=RsaPublic.from_pem(pub_key_pem),
priv_key=RsaPrivate.from_pem(priv_key_pem)
))
return vectors
def test_pem_der_conversion(pem_der_conversion_vectors):
for vector in pem_der_conversion_vectors:
computed_der = pem_to_der(vector.pem, vector.type)
@ -45,3 +81,9 @@ def test_pem_der_conversion(pem_der_conversion_vectors):
computed_pem = der_to_pem(vector.der, vector.type)
assert computed_pem == vector.pem
def test_signature(signature_vectors):
for vector in signature_vectors:
assert make_signature(vector.data, vector.hash_cls, vector.priv_key) == vector.signature
assert check_signature(vector.signature, vector.data, vector.hash_cls,
vector.pub_key)