From effb3b621a298923785d1119bffcd054ac47fbda Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 16 Apr 2019 20:32:47 +0200 Subject: [PATCH] Added ecc_sign_raw and ecc_verify_raw + test cases ECC signature can be represented in its raw element R,S --- src/wolfcrypt/_build_ffi.py | 14 ++++++- src/wolfcrypt/ciphers.py | 74 +++++++++++++++++++++++++++++++++++++ tests/test_ciphers.py | 17 +++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/wolfcrypt/_build_ffi.py b/src/wolfcrypt/_build_ffi.py index eb39095..5393772 100644 --- a/src/wolfcrypt/_build_ffi.py +++ b/src/wolfcrypt/_build_ffi.py @@ -59,6 +59,11 @@ ffi.cdef( typedef unsigned char byte; typedef unsigned int word32; + typedef struct { ...; } mp_int; + + int mp_init (mp_int * a); + int mp_to_unsigned_bin (mp_int * a, unsigned char *b); + int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c); typedef struct { ...; } wc_Sha; @@ -96,6 +101,7 @@ ffi.cdef( int wc_HmacFinal(Hmac*, byte*); + typedef struct { ...; } Aes; int wc_AesSetKey(Aes*, const byte*, word32, const byte*, int); @@ -172,8 +178,14 @@ ffi.cdef( const byte* hash, word32 hashlen, int* stat, ecc_key* key); + 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); + typedef struct {...; } ed25519_key; - + int wc_ed25519_init(ed25519_key* ed25519); void wc_ed25519_free(ed25519_key* ed25519); diff --git a/src/wolfcrypt/ciphers.py b/src/wolfcrypt/ciphers.py index 410e1a2..c24932e 100644 --- a/src/wolfcrypt/ciphers.py +++ b/src/wolfcrypt/ciphers.py @@ -530,6 +530,42 @@ class EccPublic(_Ecc): return status[0] == 1 + def verify_raw(self, R, S, data): + """ + Verifies signature from its raw elements **R** and **S**, 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]") + mpR = _ffi.new("mp_int[1]") + mpS = _ffi.new("mp_int[1]") + ret = _lib.mp_init(mpR) + if ret != 0: # pragma: no cover + raise WolfCryptError("wolfCrypt error (%d)" % ret) + ret = _lib.mp_init(mpS) + if ret != 0: # pragma: no cover + raise WolfCryptError("wolfCrypt error (%d)" % ret) + + ret = _lib.mp_read_unsigned_bin(mpR, R, len(R)) + if ret != 0: # pragma: no cover + raise WolfCryptError("wolfCrypt error (%d)" % ret) + + ret = _lib.mp_read_unsigned_bin(mpS, S, len(S)) + if ret != 0: # pragma: no cover + raise WolfCryptError("wolfCrypt error (%d)" % ret) + + + ret = _lib.wc_ecc_verify_hash_ex(mpR, mpS, + data, len(data), + status, self.native_object) + + if ret < 0: + raise WolfCryptError("Verify error (%d)" % ret) + + return status[0] == 1 + class EccPrivate(EccPublic): @classmethod @@ -650,6 +686,43 @@ class EccPrivate(EccPublic): return _ffi.buffer(signature, signature_size[0])[:] + def sign_raw(self, plaintext, rng=Random()): + """ + Signs **plaintext**, using the private key data in the object. + + Returns the signature in its two raw components r, s + """ + plaintext = t2b(plaintext) + R = _ffi.new("mp_int[1]"); + S = _ffi.new("mp_int[1]"); + + R_bin = _ffi.new("unsigned char[%d]" % self.size ) + S_bin = _ffi.new("unsigned char[%d]" % self.size ) + + ret = _lib.mp_init(R) + if ret != 0: # pragma: no cover + raise WolfCryptError("wolfCrypt error (%d)" % ret) + ret = _lib.mp_init(S) + if ret != 0: # pragma: no cover + raise WolfCryptError("wolfCrypt error (%d)" % ret) + + ret = _lib.wc_ecc_sign_hash_ex(plaintext, len(plaintext), + rng.native_object, + self.native_object, + R, S) + if ret != 0: # pragma: no cover + raise WolfCryptError("Signature error (%d)" % ret) + + ret = _lib.mp_to_unsigned_bin(R, R_bin) + if ret != 0: # pragma: no cover + raise WolfCryptError("wolfCrypt error (%d)" % ret) + + ret = _lib.mp_to_unsigned_bin(S, S_bin) + if ret != 0: # pragma: no cover + raise WolfCryptError("wolfCrypt error (%d)" % ret) + + return _ffi.buffer(R_bin, self.size)[:], _ffi.buffer(S_bin, self.size)[:] + class _Ed25519(object): # pylint: disable=too-few-public-methods def __init__(self): self.native_object = _ffi.new("ed25519_key *") @@ -735,6 +808,7 @@ class Ed25519Public(_Ed25519): return status[0] == 1 + class Ed25519Private(Ed25519Public): def __init__(self, key=None, pub=None): _Ed25519.__init__(self) diff --git a/tests/test_ciphers.py b/tests/test_ciphers.py index 5e5116d..9dde93b 100644 --- a/tests/test_ciphers.py +++ b/tests/test_ciphers.py @@ -349,6 +349,23 @@ def test_ecc_sign_verify(ecc_private, ecc_public): with pytest.raises(WolfCryptError): ecc_x963.import_x963(ecc_public.export_x963()[:-1]) +def test_ecc_sign_verify_raw(ecc_private, ecc_public): + plaintext = "Everyone gets Friday off." + + # normal usage, sign with private, verify with public + r,s = ecc_private.sign_raw(plaintext) + + assert len(r) + len(s) <= 2 * ecc_private.size + assert ecc_public.verify_raw(r, s, plaintext) + + # invalid signature + ret = ecc_public.verify_raw(r, s[:-1], plaintext) + assert ret == False + + # private object holds both private and public info, so it can also verify + # using the known public key. + assert ecc_private.verify_raw(r, s, plaintext) + def test_ecc_make_shared_secret(): a = EccPrivate.make_key(32)