Adding support for ed448

pull/25/head
Daniele Lacamera 2021-12-13 13:40:12 +01:00
parent e5a2bbe738
commit e3968d1932
6 changed files with 314 additions and 3 deletions

1
.gitignore vendored
View File

@ -68,3 +68,4 @@ venv_*
# code editor preferences
.vscode
.tags

View File

@ -24,7 +24,7 @@ __uri__ = "https://github.com/wolfssl/wolfcrypt-py"
# When bumping the C library version, reset the POST count to 0
__wolfssl_version__ = "v4.1.0-stable"
__wolfssl_version__ = "v5.0.0-stable"
# We're using implicit post releases [PEP 440] to bump package version
# while maintaining the C library version intact for better reference.
@ -32,7 +32,7 @@ __wolfssl_version__ = "v4.1.0-stable"
#
# MAJOR.MINOR.BUILD-POST
__version__ = "4.1.0-0"
__version__ = "5.0.0-0"
__author__ = "wolfSSL Inc."
__email__ = "info@wolfssl.com"

View File

@ -62,6 +62,7 @@ RSA_BLINDING_ENABLED = 1
ECC_TIMING_RESISTANCE_ENABLED = 1
ECC_ENABLED = 1
ED25519_ENABLED = 1
ED448_ENABLED = 1
KEYGEN_ENABLED = 1
CHACHA_ENABLED = 1
PWDBASED_ENABLED = 0
@ -86,6 +87,7 @@ if featureDetection:
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
ED448_ENABLED = 1 if '#define HAVE_ED448' 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
ERROR_STRINGS_ENABLED = 0 if '#define NO_ERROR_STRINGS' in optionsHeaderStr else 1
@ -128,6 +130,7 @@ ffibuilder.set_source(
#include <wolfssl/wolfcrypt/rsa.h>
#include <wolfssl/wolfcrypt/ecc.h>
#include <wolfssl/wolfcrypt/ed25519.h>
#include <wolfssl/wolfcrypt/ed448.h>
#include <wolfssl/wolfcrypt/curve25519.h>
int MPAPI_ENABLED = """ + str(MPAPI_ENABLED) + """;
@ -145,6 +148,7 @@ ffibuilder.set_source(
int ECC_TIMING_RESISTANCE_ENABLED = """ + str(ECC_TIMING_RESISTANCE_ENABLED) + """;
int ECC_ENABLED = """ + str(ECC_ENABLED) + """;
int ED25519_ENABLED = """ + str(ED25519_ENABLED) + """;
int ED448_ENABLED = """ + str(ED448_ENABLED) + """;
int KEYGEN_ENABLED = """ + str(KEYGEN_ENABLED) + """;
int PWDBASED_ENABLED = """ + str(PWDBASED_ENABLED) + """;
int FIPS_ENABLED = """ + str(FIPS_ENABLED) + """;
@ -171,6 +175,7 @@ _cdef = """
extern int ECC_TIMING_RESISTANCE_ENABLED;
extern int ECC_ENABLED;
extern int ED25519_ENABLED;
extern int ED448_ENABLED;
extern int KEYGEN_ENABLED;
extern int PWDBASED_ENABLED;
extern int FIPS_ENABLED;
@ -403,6 +408,43 @@ if ED25519_ENABLED:
int wc_ed25519_priv_size(ed25519_key* key);
"""
if ED448_ENABLED:
_cdef += """
typedef struct {...; } ed448_key;
int wc_ed448_init(ed448_key* ed448);
void wc_ed448_free(ed448_key* ed448);
int wc_ed448_make_key(WC_RNG* rng, int keysize, ed448_key* key);
int wc_ed448_make_public(ed448_key* key, unsigned char* pubKey,
word32 pubKeySz);
int wc_ed448_size(ed448_key* key);
int wc_ed448_sig_size(ed448_key* key);
int wc_ed448_sign_msg(const byte* in, word32 inlen, byte* out,
word32 *outlen, ed448_key* key, byte* ctx,
word32 ctx_len);
int wc_ed448_verify_msg(const byte* sig, word32 siglen, const byte* msg,
word32 msglen, int* stat, ed448_key* key, byte *ctx,
word32 ctx_len);
int wc_Ed448PrivateKeyDecode(const byte*, word32*, ed448_key*, word32);
int wc_Ed448KeyToDer(ed448_key*, byte* output, word32 inLen);
int wc_Ed448PublicKeyDecode(const byte*, word32*, ed448_key*, word32);
int wc_Ed448PublicKeyToDer(ed448_key*, byte* output,
word32 inLen, int with_AlgCurve);
int wc_ed448_import_public(const byte* in, word32 inLen, ed448_key* key);
int wc_ed448_import_private_only(const byte* priv, word32 privSz, ed448_key* key);
int wc_ed448_import_private_key(const byte* priv, word32 privSz, const byte* pub, word32 pubSz, ed448_key* key);
int wc_ed448_export_public(ed448_key*, byte* out, word32* outLen);
int wc_ed448_export_private_only(ed448_key* key, byte* out, word32* outLen);
int wc_ed448_export_private(ed448_key* key, byte* out, word32* outLen);
int wc_ed448_export_key(ed448_key* key, byte* priv, word32 *privSz, byte* pub, word32 *pubSz);
int wc_ed448_check_key(ed448_key* key);
int wc_ed448_pub_size(ed448_key* key);
int wc_ed448_priv_size(ed448_key* key);
"""
if PWDBASED_ENABLED:
_cdef += """
int wc_PBKDF2(byte* output, const byte* passwd, int pLen,

View File

@ -164,6 +164,7 @@ def make_flags(prefix):
flags.append("--enable-rsa")
flags.append("--enable-ecc")
flags.append("--enable-ed25519")
flags.append("--enable-ed448")
flags.append("--enable-curve25519")
flags.append("--enable-keygen")

View File

@ -1057,3 +1057,205 @@ if _lib.ED25519_ENABLED:
raise WolfCryptError("Signature error (%d)" % ret)
return _ffi.buffer(signature, signature_size[0])[:]
if _lib.ED448_ENABLED:
class _Ed448(object): # pylint: disable=too-few-public-methods
def __init__(self):
self.native_object = _ffi.new("ed448_key *")
ret = _lib.wc_ed448_init(self.native_object)
if ret < 0: # pragma: no cover
raise WolfCryptError("Invalid key error (%d)" % ret)
# making sure _lib.wc_ed448_free outlives ed448_key instances
_delete = _lib.wc_ed448_free
def __del__(self):
if self.native_object:
self._delete(self.native_object)
@property
def size(self):
return _lib.wc_ed448_size(self.native_object)
@property
def max_signature_size(self):
return _lib.wc_ed448_sig_size(self.native_object)
class Ed448Public(_Ed448):
def __init__(self, key=None):
_Ed448.__init__(self)
if key:
self.decode_key(key)
def decode_key(self, key):
"""
Decodes an ED448 public key
"""
key = t2b(key)
if (len(key) < _lib.wc_ed448_pub_size(self.native_object)):
raise WolfCryptError("Key decode error: key too short")
idx = _ffi.new("word32*")
idx[0] = 0
ret = _lib.wc_ed448_import_public(key, len(key),
self.native_object)
if ret < 0:
raise WolfCryptError("Key decode error (%d)" % ret)
if self.size <= 0: # pragma: no cover
raise WolfCryptError("Key decode error (%d)" % self.size)
if self.max_signature_size <= 0: # pragma: no cover
raise WolfCryptError(
"Key decode error (%d)" % self.max_signature_size)
def encode_key(self):
"""
Encodes the ED448 public key
Returns the encoded key.
"""
key = _ffi.new("byte[%d]" % (self.size * 4))
size = _ffi.new("word32[1]")
size[0] = _lib.wc_ed448_pub_size(self.native_object)
ret = _lib.wc_ed448_export_public(self.native_object, key, size)
if ret != 0: # pragma: no cover
raise WolfCryptError("Key encode error (%d)" % ret)
return _ffi.buffer(key, size[0])[:]
def verify(self, signature, data, ctx=None):
"""
Verifies **signature**, using the public key data in the object.
Returns **True** in case of a valid signature, otherwise **False**.
"""
data = t2b(data)
status = _ffi.new("int[1]")
ctx_buf = _ffi.NULL
ctx_buf_len = 0
if ctx != None:
ctx_buf = t2b(ctx)
ctx_buf_len = len(ctx_buf)
ret = _lib.wc_ed448_verify_msg(signature, len(signature),
data, len(data), status,
self.native_object, ctx_buf,
ctx_buf_len)
if ret < 0:
raise WolfCryptError("Verify error (%d)" % ret)
return status[0] == 1
class Ed448Private(Ed448Public):
def __init__(self, key=None, pub=None):
_Ed448.__init__(self)
if key and not pub:
self.decode_key(key)
if key and pub:
self.decode_key(key,pub)
@classmethod
def make_key(cls, size, rng=Random()):
"""
Generates a new key pair of desired length **size**.
"""
ed448 = cls()
ret = _lib.wc_ed448_make_key(rng.native_object, size,
ed448.native_object)
if ret < 0:
raise WolfCryptError("Key generation error (%d)" % ret)
return ed448
def decode_key(self, key, pub = None):
"""
Decodes an ED448 private + pub key
"""
key = t2b(key)
if (len(key) < _lib.wc_ed448_priv_size(self.native_object)/2):
raise WolfCryptError("Key decode error: key too short")
idx = _ffi.new("word32*")
idx[0] = 0
if pub:
ret = _lib.wc_ed448_import_private_key(key, len(key), pub,
len(pub), self.native_object);
if ret < 0:
raise WolfCryptError("Key decode error (%d)" % ret)
else:
ret = _lib.wc_ed448_import_private_only(key, len(key),
self.native_object);
if ret < 0:
raise WolfCryptError("Key decode error (%d)" % ret)
pubkey = _ffi.new("byte[%d]" % (self.size * 4))
ret = _lib.wc_ed448_make_public(self.native_object, pubkey,
self.size)
if ret < 0:
raise WolfCryptError("Public key generate error (%d)" % ret)
ret = _lib.wc_ed448_import_public(pubkey, self.size,
self.native_object);
if self.size <= 0: # pragma: no cover
raise WolfCryptError("Key decode error (%d)" % self.size)
if self.max_signature_size <= 0: # pragma: no cover
raise WolfCryptError(
"Key decode error (%d)" % self.max_signature_size)
def encode_key(self):
"""
Encodes the ED448 private key.
Returns the encoded key.
"""
key = _ffi.new("byte[%d]" % (self.size * 4))
pubkey = _ffi.new("byte[%d]" % (self.size * 4))
size = _ffi.new("word32[1]")
size[0] = _lib.wc_ed448_priv_size(self.native_object)
ret = _lib.wc_ed448_export_private_only(self.native_object,
key, size)
if ret != 0: # pragma: no cover
raise WolfCryptError("Private key encode error (%d)" % ret)
ret = _lib.wc_ed448_export_public(self.native_object, pubkey,
size)
if ret != 0: # pragma: no cover
raise WolfCryptError("Public key encode error (%d)" % ret)
return _ffi.buffer(key, size[0])[:], _ffi.buffer(pubkey, size[0])[:]
def sign(self, plaintext, ctx=None):
"""
Signs **plaintext**, using the private key data in the object.
Returns the signature.
"""
plaintext = t2b(plaintext)
signature = _ffi.new("byte[%d]" % self.max_signature_size)
signature_size = _ffi.new("word32[1]")
signature_size[0] = self.max_signature_size
ctx_buf = _ffi.NULL
ctx_buf_len = 0
if (ctx != None):
ctx_buf = t2b(ctx)
ctx_buf_len = len(ctx_buf)
ret = _lib.wc_ed448_sign_msg(plaintext, len(plaintext),
signature, signature_size,
self.native_object, ctx_buf,
ctx_buf_len)
if ret != 0: # pragma: no cover
raise WolfCryptError("Signature error (%d)" % ret)
return _ffi.buffer(signature, signature_size[0])[:]

View File

@ -44,6 +44,9 @@ if _lib.ECC_ENABLED:
if _lib.ED25519_ENABLED:
from wolfcrypt.ciphers import (Ed25519Private, Ed25519Public)
if _lib.ED448_ENABLED:
from wolfcrypt.ciphers import (Ed448Private, Ed448Public)
from wolfcrypt.ciphers import (
MODE_ECB, MODE_CBC, WolfCryptError
)
@ -194,7 +197,19 @@ def vectors():
"FC96"
)
)
if _lib.ED448_ENABLED:
vectorArray[Ed448Private]=TestVector(
key=h2b("c2b29804e9a893c9e275cac1f8a3033f3d4b78b79eb427ed359fdeb8"
"82d657c129c7930936b181971b795167ad18cabeeb52b59b94f115ad"
"59"
)
)
vectorArray[Ed448Public]=TestVector(
key=h2b("89fb2b5a5ab67dd317794cc5f1700cace295b043f3ad73a66299e10a"
"d3fc0a28289ddd1c641598a354113867a42e82ad844b4d858d92e4e7"
"80"
)
)
return vectorArray
algo_params = []
@ -570,3 +585,53 @@ if _lib.ED25519_ENABLED:
# using the known public key.
assert ed25519_private.verify(signature, plaintext)
if _lib.ED448_ENABLED:
@pytest.fixture
def ed448_private(vectors):
return Ed448Private(vectors[Ed448Private].key, vectors[Ed448Public].key)
@pytest.fixture
def ed448_public(vectors):
return Ed448Public(vectors[Ed448Public].key)
def test_new_ed448_raises(vectors):
with pytest.raises(WolfCryptError):
Ed448Private(vectors[Ed448Private].key[:-1]) # invalid key length
with pytest.raises(WolfCryptError):
Ed448Public(vectors[Ed448Public].key[:-1]) # invalid key length
with pytest.raises(WolfCryptError): # invalid key size
Ed448Private.make_key(1024)
def test_ed448_key_encoding(vectors):
priv = Ed448Private()
pub = Ed448Public()
priv.decode_key(vectors[Ed448Private].key)
pub.decode_key(vectors[Ed448Public].key)
assert priv.encode_key()[0] == vectors[Ed448Private].key
assert priv.encode_key()[1] == vectors[Ed448Public].key # Automatically re-generated from private-only
assert pub.encode_key() == vectors[Ed448Public].key
def test_ed448_sign_verify(ed448_private, ed448_public):
plaintext = "Everyone gets Friday off."
# normal usage, sign with private, verify with public
signature = ed448_private.sign(plaintext)
assert len(signature) <= ed448_private.max_signature_size
assert ed448_public.verify(signature, plaintext)
# invalid signature
with pytest.raises(WolfCryptError):
ed448_public.verify(signature[:-1], plaintext)
# private object holds both private and public info, so it can also verify
# using the known public key.
assert ed448_private.verify(signature, plaintext)