diff --git a/.gitignore b/.gitignore index 561bd57..678db73 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,7 @@ __pycache__ *.egg-info/ *.a *.o +*.obj *.so +*.exe .DS_Store diff --git a/README.md b/README.md index 111321b..5612dd0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ micro-ecc ========== -A small and fast ECDH and ECDSA implementation for 8-bit, 32-bit, and 64-bit processors. +A small and fast ECDH, ECDSA, and EC-SDSA (Schnorr) implementation for 8-bit, 32-bit, and 64-bit processors. The static version of micro-ecc (ie, where the curve was selected at compile-time) can be found in the "static" branch. @@ -14,6 +14,9 @@ Features * Small code size. * No dynamic memory allocation. * Support for 5 standard curves: secp160r1, secp192r1, secp224r1, secp256r1, and secp256k1. + * ECDSA sign and verify. + * EC-SDSA (Schnorr) sign and verify — two variants (optimized and standard), each independently gated by a preprocessor macro. + * Optional built-in SHA-256 and SHA-512 hash adapters (`uECC_hash.h` / `uECC_hash.c`), disabled by default so projects with an existing hash library incur zero extra code size. * BSD 2-clause license. Usage Notes @@ -27,9 +30,69 @@ Private keys are represented in the standard format. I recommend just copying (or symlink) the uECC files into your project. Then just `#include "uECC.h"` to use the micro-ecc functions. +For EC-SDSA, also copy `uECC_hash.h` and `uECC_hash.c` if you want the built-in hash adapters (see [Hash Support](#hash-support) below). + For use with Arduino, you can use the Library Manager to download micro-ecc (**Sketch**=>**Include Library**=>**Manage Libraries**). You can then use uECC just like any other Arduino library (uECC should show up in the **Sketch**=>**Import Library** submenu). -See uECC.h for documentation for each function. +See `uECC.h` for documentation for each function. + +### EC-SDSA (Schnorr Signatures) ### + +EC-SDSA is a Schnorr signature scheme. Two variants are provided; they differ only in what is hashed to produce the challenge value R: + +| Variant | Hash input | API | +|---------|-----------|-----| +| Optimized | `Hash(x1 \|\| M)` — x-coordinate only | `uECC_ecsdsa_sign/verify_optimized` | +| Standard | `Hash(x1 \|\| y1 \|\| M)` — both coordinates | `uECC_ecsdsa_sign/verify_standard` | + +Both functions accept any hash algorithm through the `uECC_HashContext` interface, so they work with SHA-256, SHA-512, or any other hash you supply. + +**Signature format:** `R || S` where `|R| = hash_context->result_size` and `|S| = curve->num_bytes`. + +**Quick example (optimized variant, SHA-256):** + +```c +#define uECC_SUPPORTS_SHA256 1 +#include "uECC_hash.h" + +uECC_SHA256_HashContext hctx; +uECC_SHA256_HashContext_init(&hctx); + +uint8_t sig[32 + 32]; /* R(32) + S(32) for secp256r1 + SHA-256 */ +uECC_ecsdsa_sign_optimized(private_key, message, message_len, &hctx.uECC, sig, curve); +uECC_ecsdsa_verify_optimized(public_key, message, message_len, &hctx.uECC, sig, curve); +``` + +### Hash Support ### + +`uECC_hash.h` and `uECC_hash.c` provide self-contained SHA-256 and SHA-512 `uECC_HashContext` adapters. Both are **disabled by default** — projects that already include a hash library (mbed TLS, wolfCrypt, etc.) should implement the three `uECC_HashContext` callbacks against their existing library instead. + +To enable the built-in implementations, define the relevant macro **before** including `uECC_hash.h`: + +```c +#define uECC_SUPPORTS_SHA256 1 /* enable built-in SHA-256 */ +#define uECC_SUPPORTS_SHA512 1 /* enable built-in SHA-512 */ +#include "uECC_hash.h" +``` + +You must also compile `uECC_hash.c` (with the same macros defined) and link it into your project. + +Each context type (`uECC_SHA256_HashContext`, `uECC_SHA512_HashContext`) embeds its own scratch buffer, so no heap allocation is required. + +### Preprocessor Feature Gates ### + +All features are individually enabled or disabled at compile time: + +| Macro | Default | Controls | +|-------|---------|----------| +| `uECC_SUPPORTS_ECDSA` | `1` | `uECC_sign`, `uECC_sign_deterministic`, `uECC_verify` | +| `uECC_SUPPORTS_ECSDSA` | `1` | Master switch for both EC-SDSA variants | +| `uECC_SUPPORTS_ECSDSA_OPTIMIZED` | `uECC_SUPPORTS_ECSDSA` | Optimized variant only | +| `uECC_SUPPORTS_ECSDSA_STANDARD` | `uECC_SUPPORTS_ECSDSA` | Standard variant only | +| `uECC_SUPPORTS_SHA256` | `0` | Built-in SHA-256 adapter (in `uECC_hash.c`) | +| `uECC_SUPPORTS_SHA512` | `0` | Built-in SHA-512 adapter (in `uECC_hash.c`) | + +Setting a macro to `0` strips the corresponding code entirely from the build, which is useful on flash-constrained targets. ### Compilation Notes ### diff --git a/test/test_ecsdsa.c b/test/test_ecsdsa.c new file mode 100644 index 0000000..5458d42 --- /dev/null +++ b/test/test_ecsdsa.c @@ -0,0 +1,705 @@ +/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. + * + * ECSDSA sign/verify tests - known-answer test vectors plus random round-trip tests. + * + * Two variants (differ only in what is hashed to produce R): + * Optimized: R = h(x1 || M) -- x coordinate only + * Standard: R = h(x1 || y1 || M) -- both coordinates + */ + +#include "uECC.h" + +#if !(uECC_SUPPORTS_ECSDSA_OPTIMIZED || uECC_SUPPORTS_ECSDSA_STANDARD) +#include +int main(void) { printf("ECSDSA disabled\n"); return 0; } +#else + +/* + * Enable the built-in hash adapters for this test. + * + * NOTE: uECC_SUPPORTS_SHA256 and uECC_SUPPORTS_SHA512 default to 0 in + * uECC_hash.h so that projects which already carry a hash library do not + * compile a second copy. We opt in explicitly here for testing purposes. + */ +#define uECC_SUPPORTS_SHA256 1 +#define uECC_SUPPORTS_SHA512 1 +#include "uECC_hash.h" + +#include +#include +#include + +/* ---- helpers ---- */ + +static void print_hex(const char *label, const uint8_t *data, unsigned len) { + unsigned i; + printf("%s: ", label); + for (i = 0; i < len; ++i) printf("%02x", data[i]); + printf("\n"); +} + +/* ---- deterministic RNG for sign vector tests ---- + * + * Injects a known k on the first call (reversed to native LE VLI layout). + * Returns value 1 on the second call (blinding factor). + */ +static const uint8_t *s_rng_k_be; +static unsigned s_rng_k_len; +static int s_rng_calls; + +static int vector_sign_rng(uint8_t *dest, unsigned size) { + unsigned i; + if (s_rng_calls == 0 && size == s_rng_k_len) { + for (i = 0; i < size; ++i) + dest[i] = s_rng_k_be[size - 1 - i]; + s_rng_calls++; + return 1; + } + memset(dest, 0, size); + if (size > 0) dest[0] = 1; + s_rng_calls++; + return 1; +} + +/* ---- EC-SDSA known-answer test vectors ---- + * + * Vector 1: secp256r1 / SHA-256 / M = "abc" + * Vector 2: secp256r1 / SHA-512 / M = "abc" (same key and k as vector 1) + * private key, public key, ephemeral k, and both variant signatures. + */ + +/* Shared key material and k for vectors 1 and 2 */ +static const uint8_t vec_priv[32] = { + 0xC9,0xAF,0xA9,0xD8,0x45,0xBA,0x75,0x16,0x6B,0x5C,0x21,0x57,0x67,0xB1,0xD6,0x93, + 0x4E,0x50,0xC3,0xDB,0x36,0xE8,0x9B,0x12,0x7B,0x8A,0x62,0x2B,0x12,0x0F,0x67,0x21 +}; +static const uint8_t vec_pub[64] = { + /* Qx */ + 0x60,0xFE,0xD4,0xBA,0x25,0x5A,0x9D,0x31,0xC9,0x61,0xEB,0x74,0xC6,0x35,0x6D,0x68, + 0xC0,0x49,0xB8,0x92,0x3B,0x61,0xFA,0x6C,0xE6,0x69,0x62,0x2E,0x60,0xF2,0x9F,0xB6, + /* Qy */ + 0x79,0x03,0xFE,0x10,0x08,0xB8,0xBC,0x99,0xA4,0x1A,0xE9,0xE9,0x56,0x28,0xBC,0x64, + 0xF2,0xF1,0xB2,0x0C,0x2D,0x7E,0x9F,0x51,0x77,0xA3,0xC2,0x94,0xD4,0x46,0x22,0x99 +}; +static const uint8_t vec_k_be[32] = { + 0xAF,0x2B,0xDB,0xE1,0xAA,0x9B,0x6E,0xC1,0xE2,0xAD,0xE1,0xD6,0x94,0xF4,0x1F,0xC7, + 0x1A,0x83,0x1D,0x02,0x68,0xE9,0x89,0x15,0x62,0x11,0x3D,0x8A,0x62,0xAD,0xD1,0xBF +}; +static const uint8_t vec_msg[] = { 0x61, 0x62, 0x63 }; /* "abc" */ + +/* ========================================================================= + * SHA-256 vector tests + * ========================================================================= */ + +#if uECC_SUPPORTS_ECSDSA_OPTIMIZED && uECC_SUPPORTS_secp256r1 +static int test_ecsdsa_vector_sha256_optimized(void) { + /* Vector 1 optimized-variant signature: R = h256(x1 || M) */ + static const uint8_t sig[64] = { + /* R */ + 0x59,0x50,0x47,0x06,0x6F,0xDC,0x55,0x53,0x7D,0xCB,0x03,0xC8,0x8F,0xCA,0xDC,0x73, + 0xAD,0x60,0x0F,0x39,0x9A,0x10,0x12,0xC1,0xA9,0xC2,0x85,0x1F,0x68,0x89,0x80,0xE3, + /* S */ + 0x69,0x65,0x9E,0x5C,0x8A,0xD4,0x86,0xC8,0x31,0xEE,0x12,0x20,0xE1,0x9F,0x09,0x34, + 0x73,0x74,0xF5,0x2F,0xB9,0xD8,0xEB,0x8A,0xCA,0x98,0x7F,0x65,0xFF,0x72,0x66,0x1C + }; + uECC_SHA256_HashContext hctx; + uECC_SHA256_HashContext_init(&hctx); + uECC_Curve curve = uECC_secp256r1(); + uint8_t computed_sig[64]; + uint8_t bad[64]; + uECC_RNG_Function old_rng; + + old_rng = uECC_get_rng(); + s_rng_k_be = vec_k_be; + s_rng_k_len = 32; + s_rng_calls = 0; + uECC_set_rng(vector_sign_rng); + if (!uECC_ecsdsa_sign_optimized(vec_priv, vec_msg, sizeof(vec_msg), + &hctx.uECC, computed_sig, curve)) { + uECC_set_rng(old_rng); + printf("FAIL: vec1 sha256 optimized sign failed\n"); + return 0; + } + uECC_set_rng(old_rng); + + if (memcmp(computed_sig, sig, 64) != 0) { + printf("FAIL: vec1 sha256 optimized sign: R or S mismatch\n"); + print_hex(" got R", computed_sig, 32); + print_hex(" exp R", sig, 32); + print_hex(" got S", computed_sig + 32, 32); + print_hex(" exp S", sig + 32, 32); + return 0; + } + + if (!uECC_ecsdsa_verify_optimized(vec_pub, vec_msg, sizeof(vec_msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: vec1 sha256 optimized verify: valid sig rejected\n"); + return 0; + } + memcpy(bad, sig, 64); bad[0] ^= 0x01; + if (uECC_ecsdsa_verify_optimized(vec_pub, vec_msg, sizeof(vec_msg), + &hctx.uECC, bad, curve)) { + printf("FAIL: vec1 sha256 optimized verify: modified R accepted\n"); + return 0; + } + memcpy(bad, sig, 64); bad[32] ^= 0x01; + if (uECC_ecsdsa_verify_optimized(vec_pub, vec_msg, sizeof(vec_msg), + &hctx.uECC, bad, curve)) { + printf("FAIL: vec1 sha256 optimized verify: modified S accepted\n"); + return 0; + } + { + uint8_t bad_msg[] = { 0x61, 0x62, 0x64 }; + if (uECC_ecsdsa_verify_optimized(vec_pub, bad_msg, sizeof(bad_msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: vec1 sha256 optimized verify: modified message accepted\n"); + return 0; + } + } + printf("PASS: EC-SDSA optimized secp256r1/SHA-256 vector (sign+verify)\n"); + return 1; +} +#endif + +#if uECC_SUPPORTS_ECSDSA_STANDARD && uECC_SUPPORTS_secp256r1 +static int test_ecsdsa_vector_sha256_standard(void) { + /* Vector 1 standard-variant signature: R = h256(x1 || y1 || M) */ + static const uint8_t sig[64] = { + /* R */ + 0x41,0xA8,0x23,0x68,0xB5,0x1B,0x34,0xD7,0x77,0x44,0xFC,0x08,0x2A,0xAD,0xC7,0x36, + 0xB4,0xFE,0x19,0x65,0xDA,0xB7,0x29,0xBC,0x65,0xD6,0x18,0x5B,0x8D,0x3F,0x37,0xF8, + /* S */ + 0xA4,0x67,0x45,0x2B,0x0C,0x04,0xAC,0x5B,0x4F,0x92,0xE0,0x96,0x8D,0xA1,0x42,0xA6, + 0x61,0x19,0xC2,0xEE,0xE5,0x0E,0x5A,0x50,0xB7,0x7B,0x12,0xB8,0x30,0x33,0x84,0x74 + }; + uECC_SHA256_HashContext hctx; + uECC_SHA256_HashContext_init(&hctx); + uECC_Curve curve = uECC_secp256r1(); + uint8_t computed_sig[64]; + uint8_t bad[64]; + uECC_RNG_Function old_rng; + + old_rng = uECC_get_rng(); + s_rng_k_be = vec_k_be; + s_rng_k_len = 32; + s_rng_calls = 0; + uECC_set_rng(vector_sign_rng); + if (!uECC_ecsdsa_sign_standard(vec_priv, vec_msg, sizeof(vec_msg), + &hctx.uECC, computed_sig, curve)) { + uECC_set_rng(old_rng); + printf("FAIL: vec1 sha256 standard sign failed\n"); + return 0; + } + uECC_set_rng(old_rng); + + if (memcmp(computed_sig, sig, 64) != 0) { + printf("FAIL: vec1 sha256 standard sign: R or S mismatch\n"); + print_hex(" got R", computed_sig, 32); + print_hex(" exp R", sig, 32); + print_hex(" got S", computed_sig + 32, 32); + print_hex(" exp S", sig + 32, 32); + return 0; + } + + if (!uECC_ecsdsa_verify_standard(vec_pub, vec_msg, sizeof(vec_msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: vec1 sha256 standard verify: valid sig rejected\n"); + return 0; + } + memcpy(bad, sig, 64); bad[0] ^= 0x01; + if (uECC_ecsdsa_verify_standard(vec_pub, vec_msg, sizeof(vec_msg), + &hctx.uECC, bad, curve)) { + printf("FAIL: vec1 sha256 standard verify: modified R accepted\n"); + return 0; + } + memcpy(bad, sig, 64); bad[32] ^= 0x01; + if (uECC_ecsdsa_verify_standard(vec_pub, vec_msg, sizeof(vec_msg), + &hctx.uECC, bad, curve)) { + printf("FAIL: vec1 sha256 standard verify: modified S accepted\n"); + return 0; + } + { + uint8_t bad_msg[] = { 0x61, 0x62, 0x64 }; + if (uECC_ecsdsa_verify_standard(vec_pub, bad_msg, sizeof(bad_msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: vec1 sha256 standard verify: modified message accepted\n"); + return 0; + } + } + printf("PASS: EC-SDSA standard secp256r1/SHA-256 vector (sign+verify)\n"); + return 1; +} +#endif + +/* ========================================================================= + * SHA-512 vector tests + * sig format: R(64 bytes) || S(32 bytes) = 96 bytes for secp256r1 + * ========================================================================= */ + +#if uECC_SUPPORTS_SHA512 + +#if uECC_SUPPORTS_ECSDSA_OPTIMIZED && uECC_SUPPORTS_secp256r1 +static int test_ecsdsa_vector_sha512_optimized(void) { + /* Vector 2 optimized-variant signature: R = h512(x1 || M) */ + static const uint8_t sig[96] = { + /* R (64 bytes) */ + 0xD7,0x6F,0xA5,0x59,0xAC,0x1D,0x6C,0x15,0x18,0xAF,0xDF,0x3A,0x94,0x37,0xB4,0x05, + 0x21,0xEA,0x59,0xA4,0x7B,0x58,0xF1,0xF0,0xF6,0xA2,0x03,0x27,0x1F,0x75,0x65,0x4E, + 0x88,0xAF,0x12,0x3F,0x44,0x0B,0x46,0x7C,0x20,0x02,0x62,0xA1,0xE6,0x0F,0xB8,0x8A, + 0xA4,0xF6,0x0F,0x93,0xDA,0x58,0x96,0xD6,0x18,0xD6,0xD3,0x89,0x59,0xD0,0x8C,0x77, + /* S (32 bytes) */ + 0x0F,0x58,0x0A,0x62,0xC8,0x78,0x67,0x5C,0xCD,0xE9,0x37,0x0A,0x26,0x5A,0x5B,0x41, + 0x10,0x30,0xA1,0xA7,0xDC,0x94,0x36,0xED,0x32,0x76,0x3C,0x57,0xE1,0xA8,0xD3,0x55 + }; + uECC_SHA512_HashContext hctx; + uECC_SHA512_HashContext_init(&hctx); + uECC_Curve curve = uECC_secp256r1(); + uint8_t computed_sig[96]; + uint8_t bad[96]; + uECC_RNG_Function old_rng; + + old_rng = uECC_get_rng(); + s_rng_k_be = vec_k_be; + s_rng_k_len = 32; + s_rng_calls = 0; + uECC_set_rng(vector_sign_rng); + if (!uECC_ecsdsa_sign_optimized(vec_priv, vec_msg, sizeof(vec_msg), + &hctx.uECC, computed_sig, curve)) { + uECC_set_rng(old_rng); + printf("FAIL: vec2 sha512 optimized sign failed\n"); + return 0; + } + uECC_set_rng(old_rng); + + if (memcmp(computed_sig, sig, 96) != 0) { + printf("FAIL: vec2 sha512 optimized sign: R or S mismatch\n"); + print_hex(" got R", computed_sig, 64); + print_hex(" exp R", sig, 64); + print_hex(" got S", computed_sig + 64, 32); + print_hex(" exp S", sig + 64, 32); + return 0; + } + + if (!uECC_ecsdsa_verify_optimized(vec_pub, vec_msg, sizeof(vec_msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: vec2 sha512 optimized verify: valid sig rejected\n"); + return 0; + } + memcpy(bad, sig, 96); bad[0] ^= 0x01; + if (uECC_ecsdsa_verify_optimized(vec_pub, vec_msg, sizeof(vec_msg), + &hctx.uECC, bad, curve)) { + printf("FAIL: vec2 sha512 optimized verify: modified R accepted\n"); + return 0; + } + memcpy(bad, sig, 96); bad[64] ^= 0x01; + if (uECC_ecsdsa_verify_optimized(vec_pub, vec_msg, sizeof(vec_msg), + &hctx.uECC, bad, curve)) { + printf("FAIL: vec2 sha512 optimized verify: modified S accepted\n"); + return 0; + } + { + uint8_t bad_msg[] = { 0x61, 0x62, 0x64 }; + if (uECC_ecsdsa_verify_optimized(vec_pub, bad_msg, sizeof(bad_msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: vec2 sha512 optimized verify: modified message accepted\n"); + return 0; + } + } + printf("PASS: EC-SDSA optimized secp256r1/SHA-512 vector (sign+verify)\n"); + return 1; +} +#endif + +#if uECC_SUPPORTS_ECSDSA_STANDARD && uECC_SUPPORTS_secp256r1 +static int test_ecsdsa_vector_sha512_standard(void) { + /* Vector 2 standard-variant signature: R = h512(x1 || y1 || M) */ + static const uint8_t sig[96] = { + /* R (64 bytes) */ + 0x08,0xAA,0xD4,0xF7,0x82,0x89,0xCE,0xED,0xCC,0xD4,0x76,0xD7,0x71,0x61,0xF2,0xE2, + 0x28,0xF2,0xE1,0x3F,0x44,0x09,0x21,0x5C,0x80,0xF1,0x4C,0x70,0xE3,0xD9,0xD5,0xCE, + 0x8F,0x0F,0xFA,0x9B,0xA8,0x43,0xEC,0xEF,0xA0,0x66,0x26,0x4A,0x22,0xF4,0x25,0x76, + 0x2D,0x62,0xC0,0x94,0x02,0x4A,0xAC,0x7E,0x3D,0xF3,0x87,0xDA,0xB7,0xB7,0x39,0xDF, + /* S (32 bytes) */ + 0x43,0xEA,0x89,0xF6,0x75,0xF0,0x32,0x9D,0x3F,0xDF,0x95,0xCA,0xDB,0x45,0xFB,0x97, + 0x4E,0xAD,0xE7,0x0A,0x3C,0x19,0x98,0x6A,0xE3,0xC7,0x64,0x8F,0x6A,0xFA,0xE3,0x90 + }; + uECC_SHA512_HashContext hctx; + uECC_SHA512_HashContext_init(&hctx); + uECC_Curve curve = uECC_secp256r1(); + uint8_t computed_sig[96]; + uint8_t bad[96]; + uECC_RNG_Function old_rng; + + old_rng = uECC_get_rng(); + s_rng_k_be = vec_k_be; + s_rng_k_len = 32; + s_rng_calls = 0; + uECC_set_rng(vector_sign_rng); + if (!uECC_ecsdsa_sign_standard(vec_priv, vec_msg, sizeof(vec_msg), + &hctx.uECC, computed_sig, curve)) { + uECC_set_rng(old_rng); + printf("FAIL: vec2 sha512 standard sign failed\n"); + return 0; + } + uECC_set_rng(old_rng); + + if (memcmp(computed_sig, sig, 96) != 0) { + printf("FAIL: vec2 sha512 standard sign: R or S mismatch\n"); + print_hex(" got R", computed_sig, 64); + print_hex(" exp R", sig, 64); + print_hex(" got S", computed_sig + 64, 32); + print_hex(" exp S", sig + 64, 32); + return 0; + } + + if (!uECC_ecsdsa_verify_standard(vec_pub, vec_msg, sizeof(vec_msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: vec2 sha512 standard verify: valid sig rejected\n"); + return 0; + } + memcpy(bad, sig, 96); bad[0] ^= 0x01; + if (uECC_ecsdsa_verify_standard(vec_pub, vec_msg, sizeof(vec_msg), + &hctx.uECC, bad, curve)) { + printf("FAIL: vec2 sha512 standard verify: modified R accepted\n"); + return 0; + } + memcpy(bad, sig, 96); bad[64] ^= 0x01; + if (uECC_ecsdsa_verify_standard(vec_pub, vec_msg, sizeof(vec_msg), + &hctx.uECC, bad, curve)) { + printf("FAIL: vec2 sha512 standard verify: modified S accepted\n"); + return 0; + } + { + uint8_t bad_msg[] = { 0x61, 0x62, 0x64 }; + if (uECC_ecsdsa_verify_standard(vec_pub, bad_msg, sizeof(bad_msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: vec2 sha512 standard verify: modified message accepted\n"); + return 0; + } + } + printf("PASS: EC-SDSA standard secp256r1/SHA-512 vector (sign+verify)\n"); + return 1; +} +#endif + +#endif /* uECC_SUPPORTS_SHA512 */ + +/* ========================================================================= + * Random round-trip tests + * sig buffer: 96 bytes covers R(64)+S(32) for SHA-512+secp256r1 (largest case) + * ========================================================================= */ + +#if uECC_SUPPORTS_ECSDSA_OPTIMIZED +static int test_ecsdsa_optimized_sign_verify(uECC_Curve curve, int verbose) { + uint8_t private_key[32] = {0}; + uint8_t public_key[64] = {0}; + uint8_t sig[96] = {0}; + uint8_t msg[32]; + uECC_SHA256_HashContext hctx; + uECC_SHA256_HashContext_init(&hctx); + int num_bytes = uECC_curve_private_key_size(curve); + + if (!uECC_make_key(public_key, private_key, curve)) { + printf("FAIL: uECC_make_key\n"); return 0; + } + memcpy(msg, public_key, sizeof(msg)); + + if (!uECC_ecsdsa_sign_optimized(private_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: uECC_ecsdsa_sign_optimized\n"); return 0; + } + if (!uECC_ecsdsa_verify_optimized(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: uECC_ecsdsa_verify_optimized (valid sig rejected)\n"); return 0; + } + + msg[0] ^= 0x01; + if (uECC_ecsdsa_verify_optimized(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: optimized verify accepted modified message\n"); return 0; + } + msg[0] ^= 0x01; + + sig[0] ^= 0x01; + if (uECC_ecsdsa_verify_optimized(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: optimized verify accepted modified R\n"); return 0; + } + sig[0] ^= 0x01; + + sig[32] ^= 0x01; + if (uECC_ecsdsa_verify_optimized(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: optimized verify accepted modified S\n"); return 0; + } + sig[32] ^= 0x01; + + if (verbose) { + print_hex(" priv", private_key, num_bytes); + print_hex(" pub ", public_key, 2 * num_bytes); + print_hex(" R ", sig, 32); + print_hex(" S ", sig + 32, num_bytes); + } + return 1; +} +#endif /* uECC_SUPPORTS_ECSDSA_OPTIMIZED */ + +#if uECC_SUPPORTS_ECSDSA_STANDARD +static int test_ecsdsa_standard_sign_verify(uECC_Curve curve, int verbose) { + uint8_t private_key[32] = {0}; + uint8_t public_key[64] = {0}; + uint8_t sig[96] = {0}; + uint8_t msg[32]; + uECC_SHA256_HashContext hctx; + uECC_SHA256_HashContext_init(&hctx); + int num_bytes = uECC_curve_private_key_size(curve); + + if (!uECC_make_key(public_key, private_key, curve)) { + printf("FAIL: uECC_make_key\n"); return 0; + } + memcpy(msg, public_key, sizeof(msg)); + + if (!uECC_ecsdsa_sign_standard(private_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: uECC_ecsdsa_sign_standard\n"); return 0; + } + if (!uECC_ecsdsa_verify_standard(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: uECC_ecsdsa_verify_standard (valid sig rejected)\n"); return 0; + } + + msg[0] ^= 0x01; + if (uECC_ecsdsa_verify_standard(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: standard verify accepted modified message\n"); return 0; + } + msg[0] ^= 0x01; + + sig[0] ^= 0x01; + if (uECC_ecsdsa_verify_standard(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: standard verify accepted modified R\n"); return 0; + } + sig[0] ^= 0x01; + + sig[32] ^= 0x01; + if (uECC_ecsdsa_verify_standard(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: standard verify accepted modified S\n"); return 0; + } + sig[32] ^= 0x01; + + if (verbose) { + print_hex(" priv", private_key, num_bytes); + print_hex(" pub ", public_key, 2 * num_bytes); + print_hex(" R ", sig, 32); + print_hex(" S ", sig + 32, num_bytes); + } + return 1; +} +#endif /* uECC_SUPPORTS_ECSDSA_STANDARD */ + +#if uECC_SUPPORTS_SHA512 + +#if uECC_SUPPORTS_ECSDSA_OPTIMIZED +static int test_ecsdsa_sha512_optimized_sign_verify(uECC_Curve curve) { + uint8_t private_key[32] = {0}; + uint8_t public_key[64] = {0}; + uint8_t sig[96] = {0}; /* R(64) + S(up to 32) */ + uint8_t msg[32]; + uECC_SHA512_HashContext hctx; + uECC_SHA512_HashContext_init(&hctx); + + if (!uECC_make_key(public_key, private_key, curve)) { + printf("FAIL: uECC_make_key\n"); return 0; + } + memcpy(msg, public_key, sizeof(msg)); + + if (!uECC_ecsdsa_sign_optimized(private_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: uECC_ecsdsa_sign_optimized (sha512)\n"); return 0; + } + if (!uECC_ecsdsa_verify_optimized(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: uECC_ecsdsa_verify_optimized sha512 (valid sig rejected)\n"); return 0; + } + + msg[0] ^= 0x01; + if (uECC_ecsdsa_verify_optimized(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: sha512 optimized verify accepted modified message\n"); return 0; + } + msg[0] ^= 0x01; + + sig[0] ^= 0x01; + if (uECC_ecsdsa_verify_optimized(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: sha512 optimized verify accepted modified R\n"); return 0; + } + sig[0] ^= 0x01; + + sig[64] ^= 0x01; + if (uECC_ecsdsa_verify_optimized(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: sha512 optimized verify accepted modified S\n"); return 0; + } + sig[64] ^= 0x01; + + return 1; +} +#endif + +#if uECC_SUPPORTS_ECSDSA_STANDARD +static int test_ecsdsa_sha512_standard_sign_verify(uECC_Curve curve) { + uint8_t private_key[32] = {0}; + uint8_t public_key[64] = {0}; + uint8_t sig[96] = {0}; /* R(64) + S(up to 32) */ + uint8_t msg[32]; + uECC_SHA512_HashContext hctx; + uECC_SHA512_HashContext_init(&hctx); + + if (!uECC_make_key(public_key, private_key, curve)) { + printf("FAIL: uECC_make_key\n"); return 0; + } + memcpy(msg, public_key, sizeof(msg)); + + if (!uECC_ecsdsa_sign_standard(private_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: uECC_ecsdsa_sign_standard (sha512)\n"); return 0; + } + if (!uECC_ecsdsa_verify_standard(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: uECC_ecsdsa_verify_standard sha512 (valid sig rejected)\n"); return 0; + } + + msg[0] ^= 0x01; + if (uECC_ecsdsa_verify_standard(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: sha512 standard verify accepted modified message\n"); return 0; + } + msg[0] ^= 0x01; + + sig[0] ^= 0x01; + if (uECC_ecsdsa_verify_standard(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: sha512 standard verify accepted modified R\n"); return 0; + } + sig[0] ^= 0x01; + + sig[64] ^= 0x01; + if (uECC_ecsdsa_verify_standard(public_key, msg, sizeof(msg), + &hctx.uECC, sig, curve)) { + printf("FAIL: sha512 standard verify accepted modified S\n"); return 0; + } + sig[64] ^= 0x01; + + return 1; +} +#endif + +#endif /* uECC_SUPPORTS_SHA512 */ + +/* ========================================================================= + * main + * ========================================================================= */ + +int main(void) { + int c, i; + const struct uECC_Curve_t *curves[5]; + const char *names[5]; + int num_curves = 0; + +#if uECC_SUPPORTS_secp160r1 + curves[num_curves] = uECC_secp160r1(); names[num_curves++] = "secp160r1"; +#endif +#if uECC_SUPPORTS_secp192r1 + curves[num_curves] = uECC_secp192r1(); names[num_curves++] = "secp192r1"; +#endif +#if uECC_SUPPORTS_secp224r1 + curves[num_curves] = uECC_secp224r1(); names[num_curves++] = "secp224r1"; +#endif +#if uECC_SUPPORTS_secp256r1 + curves[num_curves] = uECC_secp256r1(); names[num_curves++] = "secp256r1"; +#endif +#if uECC_SUPPORTS_secp256k1 + curves[num_curves] = uECC_secp256k1(); names[num_curves++] = "secp256k1"; +#endif + + printf("Testing EC-SDSA\n"); + + /* ---- SHA-256 vector tests ---- */ +#if uECC_SUPPORTS_ECSDSA_OPTIMIZED && uECC_SUPPORTS_secp256r1 + printf("\nSHA-256 optimized variant vector test:\n"); + if (!test_ecsdsa_vector_sha256_optimized()) return 1; +#endif +#if uECC_SUPPORTS_ECSDSA_STANDARD && uECC_SUPPORTS_secp256r1 + printf("\nSHA-256 standard variant vector test:\n"); + if (!test_ecsdsa_vector_sha256_standard()) return 1; +#endif + + /* ---- SHA-512 vector tests ---- */ +#if uECC_SUPPORTS_SHA512 +#if uECC_SUPPORTS_ECSDSA_OPTIMIZED && uECC_SUPPORTS_secp256r1 + printf("\nSHA-512 optimized variant vector test:\n"); + if (!test_ecsdsa_vector_sha512_optimized()) return 1; +#endif +#if uECC_SUPPORTS_ECSDSA_STANDARD && uECC_SUPPORTS_secp256r1 + printf("\nSHA-512 standard variant vector test:\n"); + if (!test_ecsdsa_vector_sha512_standard()) return 1; +#endif +#endif /* uECC_SUPPORTS_SHA512 */ + + /* ---- SHA-256 random round-trip tests ---- */ +#if uECC_SUPPORTS_ECSDSA_OPTIMIZED + printf("\nEC-SDSA SHA-256 optimized random round-trip tests (64 per curve):\n"); + for (c = 0; c < num_curves; ++c) { + printf(" %s:", names[c]); fflush(stdout); + for (i = 0; i < 64; ++i) { + printf("."); fflush(stdout); + if (!test_ecsdsa_optimized_sign_verify(curves[c], 0)) return 1; + } + printf(" PASS\n"); + } +#endif +#if uECC_SUPPORTS_ECSDSA_STANDARD + printf("\nEC-SDSA SHA-256 standard random round-trip tests (64 per curve):\n"); + for (c = 0; c < num_curves; ++c) { + printf(" %s:", names[c]); fflush(stdout); + for (i = 0; i < 64; ++i) { + printf("."); fflush(stdout); + if (!test_ecsdsa_standard_sign_verify(curves[c], 0)) return 1; + } + printf(" PASS\n"); + } +#endif + + /* ---- SHA-512 random round-trip tests ---- */ +#if uECC_SUPPORTS_SHA512 +#if uECC_SUPPORTS_ECSDSA_OPTIMIZED + printf("\nEC-SDSA SHA-512 optimized random round-trip tests (64 per curve):\n"); + for (c = 0; c < num_curves; ++c) { + printf(" %s:", names[c]); fflush(stdout); + for (i = 0; i < 64; ++i) { + printf("."); fflush(stdout); + if (!test_ecsdsa_sha512_optimized_sign_verify(curves[c])) return 1; + } + printf(" PASS\n"); + } +#endif +#if uECC_SUPPORTS_ECSDSA_STANDARD + printf("\nEC-SDSA SHA-512 standard random round-trip tests (64 per curve):\n"); + for (c = 0; c < num_curves; ++c) { + printf(" %s:", names[c]); fflush(stdout); + for (i = 0; i < 64; ++i) { + printf("."); fflush(stdout); + if (!test_ecsdsa_sha512_standard_sign_verify(curves[c])) return 1; + } + printf(" PASS\n"); + } +#endif +#endif /* uECC_SUPPORTS_SHA512 */ + + return 0; +} + +#endif /* uECC_SUPPORTS_ECSDSA_OPTIMIZED || uECC_SUPPORTS_ECSDSA_STANDARD */ diff --git a/uECC.c b/uECC.c index 1be5ffd..b5ce134 100644 --- a/uECC.c +++ b/uECC.c @@ -1208,6 +1208,8 @@ int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uEC /* -------- ECDSA code -------- */ +/* bits2int and smax are shared with ECSDSA verify. */ + static void bits2int(uECC_word_t *native, const uint8_t *bits, unsigned bits_size, @@ -1246,6 +1248,12 @@ static void bits2int(uECC_word_t *native, } } +static bitcount_t smax(bitcount_t a, bitcount_t b) { + return (a > b ? a : b); +} + +#if uECC_SUPPORTS_ECDSA + static int uECC_sign_with_k_internal(const uint8_t *private_key, const uint8_t *message_hash, unsigned hash_size, @@ -1485,10 +1493,6 @@ int uECC_sign_deterministic(const uint8_t *private_key, return 0; } -static bitcount_t smax(bitcount_t a, bitcount_t b) { - return (a > b ? a : b); -} - int uECC_verify(const uint8_t *public_key, const uint8_t *message_hash, unsigned hash_size, @@ -1602,6 +1606,274 @@ int uECC_verify(const uint8_t *public_key, return (int)(uECC_vli_equal(rx, r, num_words)); } +#endif /* uECC_SUPPORTS_ECDSA */ + +/* -------- ECSDSA -------- */ + +#if uECC_SUPPORTS_ECSDSA_OPTIMIZED || uECC_SUPPORTS_ECSDSA_STANDARD + +/* + * Shared sign implementation. + * include_y=0 → optimized variant: R = Hash(x1 || M) + * include_y=1 → standard variant: R = Hash(x1 || y1 || M) + */ +static int ecsdsa_sign_impl(const uint8_t *private_key, + const uint8_t *message, unsigned message_size, + const uECC_HashContext *hash_context, + uint8_t *signature, uECC_Curve curve, + int include_y) { + uECC_word_t k[uECC_MAX_WORDS]; + uECC_word_t kreg0[uECC_MAX_WORDS]; + uECC_word_t kreg1[uECC_MAX_WORDS]; + uECC_word_t *k2[2] = {kreg0, kreg1}; + uECC_word_t *initial_Z = 0; + uECC_word_t d[uECC_MAX_WORDS]; + uECC_word_t r[uECC_MAX_WORDS]; + uECC_word_t s[uECC_MAX_WORDS]; + uECC_word_t P[uECC_MAX_WORDS * 2]; + uint8_t X1[uECC_MAX_WORDS * uECC_WORD_SIZE]; /* big-endian x1 */ + uint8_t Y1[uECC_MAX_WORDS * uECC_WORD_SIZE]; /* big-endian y1 (standard only) */ + uECC_word_t carry; + uECC_word_t tries; + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + bitcount_t num_n_bits = curve->num_n_bits; + unsigned hash_size = hash_context->result_size; + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *)d, private_key, BITS_TO_BYTES(curve->num_n_bits)); +#else + uECC_vli_bytesToNative(d, private_key, BITS_TO_BYTES(curve->num_n_bits)); +#endif + + for (tries = 0; tries < uECC_RNG_MAX_TRIES; ++tries) { + if (!uECC_generate_random_int(k, curve->n, num_n_words)) { + return 0; + } + carry = regularize_k(k, kreg0, kreg1, curve); + + if (g_rng_function) { + if (!uECC_generate_random_int(k2[carry], curve->p, num_words)) { + return 0; + } + initial_Z = k2[carry]; + } + + EccPoint_mult(P, curve->G, k2[!carry], initial_Z, num_n_bits + 1, curve); + if (EccPoint_isZero(P, curve)) { + continue; + } + + /* R = Hash(x1 || [y1 ||] M) */ + uECC_vli_nativeToBytes(X1, curve->num_bytes, P); + hash_context->init_hash(hash_context); + hash_context->update_hash(hash_context, X1, curve->num_bytes); + if (include_y) { + uECC_vli_nativeToBytes(Y1, curve->num_bytes, P + num_words); + hash_context->update_hash(hash_context, Y1, curve->num_bytes); + } + hash_context->update_hash(hash_context, message, message_size); + hash_context->finish_hash(hash_context, signature); + + bits2int(r, signature, hash_size, curve); + if (uECC_vli_isZero(r, num_n_words)) { + continue; + } + + /* s = (k + r*d) mod n */ + uECC_vli_modMult(s, r, d, curve->n, num_n_words); + uECC_vli_modAdd(s, k, s, curve->n, num_n_words); + if (uECC_vli_isZero(s, num_n_words)) { + continue; + } + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *)signature + hash_size, (uint8_t *)s, curve->num_bytes); +#else + uECC_vli_nativeToBytes(signature + hash_size, curve->num_bytes, s); +#endif + return 1; + } + return 0; +} + +/* + * Shared verify implementation. + * Computes (x2,y2) = s*G - r*Q via Shamir's trick with -Q, then checks + * R' = Hash(x2 || [y2 ||] M) == R. + */ +static int ecsdsa_verify_impl(const uint8_t *public_key, + const uint8_t *message, unsigned message_size, + const uECC_HashContext *hash_context, + const uint8_t *signature, uECC_Curve curve, + int include_y) { + uECC_word_t r[uECC_MAX_WORDS]; + uECC_word_t s[uECC_MAX_WORDS]; + uECC_word_t neg_Q[uECC_MAX_WORDS * 2]; + uECC_word_t sum[uECC_MAX_WORDS * 2]; + uECC_word_t rx[uECC_MAX_WORDS]; + uECC_word_t ry[uECC_MAX_WORDS]; + uECC_word_t tx[uECC_MAX_WORDS]; + uECC_word_t ty[uECC_MAX_WORDS]; + uECC_word_t tz[uECC_MAX_WORDS]; + uECC_word_t z[uECC_MAX_WORDS]; + const uECC_word_t *points[4]; + const uECC_word_t *point; + bitcount_t num_bits; + bitcount_t i; + uint8_t X2[uECC_MAX_WORDS * uECC_WORD_SIZE]; + uint8_t Y2[uECC_MAX_WORDS * uECC_WORD_SIZE]; /* standard variant only */ + uint8_t R_prime[64]; +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + uECC_word_t *_public = (uECC_word_t *)public_key; +#else + uECC_word_t _public[uECC_MAX_WORDS * 2]; +#endif + wordcount_t num_words = curve->num_words; + wordcount_t num_n_words = BITS_TO_WORDS(curve->num_n_bits); + unsigned hash_size = hash_context->result_size; + unsigned j; + + if (hash_size > sizeof(R_prime)) { + return 0; + } + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN == 0 + uECC_vli_bytesToNative(_public, public_key, curve->num_bytes); + uECC_vli_bytesToNative(_public + num_words, public_key + curve->num_bytes, + curve->num_bytes); +#endif + + r[num_n_words - 1] = 0; + s[num_n_words - 1] = 0; + +#if uECC_VLI_NATIVE_LITTLE_ENDIAN + bcopy((uint8_t *)s, signature + hash_size, curve->num_bytes); +#else + uECC_vli_bytesToNative(s, signature + hash_size, curve->num_bytes); +#endif + + bits2int(r, signature, hash_size, curve); + + if (uECC_vli_isZero(r, num_n_words)) { + return 0; + } + if (uECC_vli_isZero(s, num_n_words) || + uECC_vli_cmp_unsafe(curve->n, s, num_n_words) != 1) { + return 0; + } + + /* -Q = (Q.x, p - Q.y) */ + uECC_vli_set(neg_Q, _public, num_words); + if (uECC_vli_isZero(_public + num_words, num_words)) { + uECC_vli_clear(neg_Q + num_words, num_words); + } else { + uECC_vli_sub(neg_Q + num_words, curve->p, _public + num_words, num_words); + } + + /* Precompute G + (-Q) = G - Q for Shamir's trick */ + uECC_vli_set(sum, neg_Q, num_words); + uECC_vli_set(sum + num_words, neg_Q + num_words, num_words); + uECC_vli_set(tx, curve->G, num_words); + uECC_vli_set(ty, curve->G + num_words, num_words); + uECC_vli_modSub(z, sum, tx, curve->p, num_words); + XYcZ_add(tx, ty, sum, sum + num_words, tx, curve); + uECC_vli_modInv(z, z, curve->p, num_words); + apply_z(sum, sum + num_words, z, curve); + + /* s*G + r*(-Q) = s*G - r*Q */ + points[0] = 0; + points[1] = curve->G; + points[2] = neg_Q; + points[3] = sum; + num_bits = smax(uECC_vli_numBits(s, num_n_words), + uECC_vli_numBits(r, num_n_words)); + + point = points[(!!uECC_vli_testBit(s, num_bits - 1)) | + ((!!uECC_vli_testBit(r, num_bits - 1)) << 1)]; + uECC_vli_set(rx, point, num_words); + uECC_vli_set(ry, point + num_words, num_words); + uECC_vli_clear(z, num_words); + z[0] = 1; + + for (i = num_bits - 2; i >= 0; --i) { + uECC_word_t index; + curve->double_jacobian(rx, ry, z, curve); + index = (!!uECC_vli_testBit(s, i)) | ((!!uECC_vli_testBit(r, i)) << 1); + point = points[index]; + if (point) { + uECC_vli_set(tx, point, num_words); + uECC_vli_set(ty, point + num_words, num_words); + apply_z(tx, ty, z, curve); + uECC_vli_modSub(tz, rx, tx, curve->p, num_words); + XYcZ_add(tx, ty, rx, ry, tx, curve); + uECC_vli_modMult_fast(z, z, tz, curve); + } + } + + uECC_vli_modInv(z, z, curve->p, num_words); + apply_z(rx, ry, z, curve); + + /* R' = Hash(x2 || [y2 ||] M) */ + uECC_vli_nativeToBytes(X2, curve->num_bytes, rx); + hash_context->init_hash(hash_context); + hash_context->update_hash(hash_context, X2, curve->num_bytes); + if (include_y) { + uECC_vli_nativeToBytes(Y2, curve->num_bytes, ry); + hash_context->update_hash(hash_context, Y2, curve->num_bytes); + } + hash_context->update_hash(hash_context, message, message_size); + hash_context->finish_hash(hash_context, R_prime); + + { + uECC_word_t diff = 0; + for (j = 0; j < hash_size; ++j) { + diff |= R_prime[j] ^ signature[j]; + } + return (diff == 0); + } +} + +#endif /* uECC_SUPPORTS_ECSDSA_OPTIMIZED || uECC_SUPPORTS_ECSDSA_STANDARD */ + +/* ---- Public API wrappers ---- */ + +#if uECC_SUPPORTS_ECSDSA_OPTIMIZED +int uECC_ecsdsa_sign_optimized(const uint8_t *private_key, + const uint8_t *message, unsigned message_size, + const uECC_HashContext *hash_context, + uint8_t *signature, uECC_Curve curve) { + return ecsdsa_sign_impl(private_key, message, message_size, + hash_context, signature, curve, 0); +} + +int uECC_ecsdsa_verify_optimized(const uint8_t *public_key, + const uint8_t *message, unsigned message_size, + const uECC_HashContext *hash_context, + const uint8_t *signature, uECC_Curve curve) { + return ecsdsa_verify_impl(public_key, message, message_size, + hash_context, signature, curve, 0); +} +#endif /* uECC_SUPPORTS_ECSDSA_OPTIMIZED */ + +#if uECC_SUPPORTS_ECSDSA_STANDARD +int uECC_ecsdsa_sign_standard(const uint8_t *private_key, + const uint8_t *message, unsigned message_size, + const uECC_HashContext *hash_context, + uint8_t *signature, uECC_Curve curve) { + return ecsdsa_sign_impl(private_key, message, message_size, + hash_context, signature, curve, 1); +} + +int uECC_ecsdsa_verify_standard(const uint8_t *public_key, + const uint8_t *message, unsigned message_size, + const uECC_HashContext *hash_context, + const uint8_t *signature, uECC_Curve curve) { + return ecsdsa_verify_impl(public_key, message, message_size, + hash_context, signature, curve, 1); +} +#endif /* uECC_SUPPORTS_ECSDSA_STANDARD */ + #if uECC_ENABLE_VLI_API unsigned uECC_curve_num_words(uECC_Curve curve) { diff --git a/uECC.h b/uECC.h index dcbdbfa..e7f9aff 100644 --- a/uECC.h +++ b/uECC.h @@ -69,6 +69,27 @@ the same endianness. */ #define uECC_SUPPORTS_secp256k1 1 #endif +/* Master ECDSA switch. Set to 0 to exclude ECDSA sign/verify code. */ +#ifndef uECC_SUPPORTS_ECDSA + #define uECC_SUPPORTS_ECDSA 1 +#endif + +/* Master ECSDSA switch. Set to 0 to exclude all ECSDSA code. */ +#ifndef uECC_SUPPORTS_ECSDSA + #define uECC_SUPPORTS_ECSDSA 1 +#endif + +/* Fine-grained variant controls. Both default to the master switch so that + * setting uECC_SUPPORTS_ECSDSA=0 disables both, while individual variants can + * be excluded independently (e.g. define uECC_SUPPORTS_ECSDSA_STANDARD=0 to + * keep only the optimized variant and save a few hundred bytes of flash). */ +#ifndef uECC_SUPPORTS_ECSDSA_OPTIMIZED + #define uECC_SUPPORTS_ECSDSA_OPTIMIZED uECC_SUPPORTS_ECSDSA +#endif +#ifndef uECC_SUPPORTS_ECSDSA_STANDARD + #define uECC_SUPPORTS_ECSDSA_STANDARD uECC_SUPPORTS_ECSDSA +#endif + /* Specifies whether compressed point format is supported. Set to 0 to disable point compression/decompression functions. */ #ifndef uECC_SUPPORT_COMPRESSED_POINT @@ -240,6 +261,8 @@ Returns 1 if the key was computed successfully, 0 if an error occurred. */ int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve); +#if uECC_SUPPORTS_ECDSA + /* uECC_sign() function. Generate an ECDSA signature for a given hash value. @@ -263,6 +286,8 @@ int uECC_sign(const uint8_t *private_key, uint8_t *signature, uECC_Curve curve); +#endif /* uECC_SUPPORTS_ECDSA */ + /* uECC_HashContext structure. This is used to pass in an arbitrary hash function to uECC_sign_deterministic(). The structure will be used for multiple hash computations; each time a new hash @@ -312,6 +337,8 @@ typedef struct uECC_HashContext { uint8_t *tmp; /* Must point to a buffer of at least (2 * result_size + block_size) bytes. */ } uECC_HashContext; +#if uECC_SUPPORTS_ECDSA + /* uECC_sign_deterministic() function. Generate an ECDSA signature for a given hash value, using a deterministic algorithm (see RFC 6979). You do not need to set the RNG using uECC_set_rng() before calling @@ -360,6 +387,59 @@ int uECC_verify(const uint8_t *public_key, const uint8_t *signature, uECC_Curve curve); +#endif /* uECC_SUPPORTS_ECDSA */ + +/* + * ECSDSA — Schnorr signature (two variants). + * + * Both variants share the same key format and s = (k + r*d) mod n equation. + * They differ only in what is hashed to form R: + * + * Optimized: R = Hash(x1 || M) -- x coordinate only + * Standard: R = Hash(x1 || y1 || M) -- both coordinates + * + * Signature format: R || S + * |R| = hash_context->result_size bytes + * |S| = curve->num_bytes (private-key size) + * + * sign() requires an RNG (uECC_set_rng()). + * verify() requires hash_context->result_size <= 64. + */ + +#if uECC_SUPPORTS_ECSDSA_OPTIMIZED +/* Optimized EC-SDSA: R = Hash(x1 || M). */ +int uECC_ecsdsa_sign_optimized(const uint8_t *private_key, + const uint8_t *message, + unsigned message_size, + const uECC_HashContext *hash_context, + uint8_t *signature, + uECC_Curve curve); + +int uECC_ecsdsa_verify_optimized(const uint8_t *public_key, + const uint8_t *message, + unsigned message_size, + const uECC_HashContext *hash_context, + const uint8_t *signature, + uECC_Curve curve); +#endif /* uECC_SUPPORTS_ECSDSA_OPTIMIZED */ + +#if uECC_SUPPORTS_ECSDSA_STANDARD +/* Standard EC-SDSA: R = Hash(x1 || y1 || M). */ +int uECC_ecsdsa_sign_standard(const uint8_t *private_key, + const uint8_t *message, + unsigned message_size, + const uECC_HashContext *hash_context, + uint8_t *signature, + uECC_Curve curve); + +int uECC_ecsdsa_verify_standard(const uint8_t *public_key, + const uint8_t *message, + unsigned message_size, + const uECC_HashContext *hash_context, + const uint8_t *signature, + uECC_Curve curve); +#endif /* uECC_SUPPORTS_ECSDSA_STANDARD */ + #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/uECC_hash.c b/uECC_hash.c new file mode 100644 index 0000000..63456c1 --- /dev/null +++ b/uECC_hash.c @@ -0,0 +1,309 @@ +/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +/* + * uECC_hash.c — built-in SHA-256 and SHA-512 hash adapters. + * + * Both implementations are DISABLED BY DEFAULT. See uECC_hash.h for how to + * opt in. If you have already included a hash library in your project you + * should NOT compile this file; implement the three uECC_HashContext callbacks + * (init_hash / update_hash / finish_hash) against your existing library + * instead. + */ + +#include "uECC_hash.h" + +/* ========================================================================= + * SHA-256 + * ========================================================================= */ + +#if uECC_SUPPORTS_SHA256 + +#define ROR32(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) + +#define SHA256_S0(x) (ROR32(x, 2) ^ ROR32(x, 13) ^ ROR32(x, 22)) +#define SHA256_S1(x) (ROR32(x, 6) ^ ROR32(x, 11) ^ ROR32(x, 25)) +#define SHA256_s0(x) (ROR32(x, 7) ^ ROR32(x, 18) ^ ((x) >> 3)) +#define SHA256_s1(x) (ROR32(x, 17) ^ ROR32(x, 19) ^ ((x) >> 10)) +#define SHA256_CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +#define SHA256_MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +static const uint32_t sha256_K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +static void sha256_compress(uECC_SHA256_HashContext *ctx, const uint8_t *block) { + uint32_t W[64]; + uint32_t a, b, c, d, e, f, g, h, T1, T2; + int i; + + for (i = 0; i < 16; ++i) { + W[i] = ((uint32_t)block[i*4 ] << 24) + | ((uint32_t)block[i*4 + 1] << 16) + | ((uint32_t)block[i*4 + 2] << 8) + | (uint32_t)block[i*4 + 3]; + } + for (i = 16; i < 64; ++i) { + W[i] = SHA256_s1(W[i-2]) + W[i-7] + SHA256_s0(W[i-15]) + W[i-16]; + } + + a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3]; + e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7]; + + for (i = 0; i < 64; ++i) { + T1 = h + SHA256_S1(e) + SHA256_CH(e, f, g) + sha256_K[i] + W[i]; + T2 = SHA256_S0(a) + SHA256_MAJ(a, b, c); + h = g; g = f; f = e; e = d + T1; + d = c; c = b; b = a; a = T1 + T2; + } + + ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d; + ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h; +} + +static void sha256_init_hash(const uECC_HashContext *base) { + uECC_SHA256_HashContext *ctx = (uECC_SHA256_HashContext *)base; + ctx->state[0] = 0x6a09e667; ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; ctx->state[7] = 0x5be0cd19; + ctx->bits = 0; + ctx->buf_len = 0; +} + +static void sha256_update_hash(const uECC_HashContext *base, + const uint8_t *data, unsigned len) { + uECC_SHA256_HashContext *ctx = (uECC_SHA256_HashContext *)base; + unsigned i; + ctx->bits += (uint64_t)len * 8; + for (i = 0; i < len; ++i) { + ctx->buf[ctx->buf_len++] = data[i]; + if (ctx->buf_len == 64) { + sha256_compress(ctx, ctx->buf); + ctx->buf_len = 0; + } + } +} + +static void sha256_finish_hash(const uECC_HashContext *base, uint8_t *out) { + uECC_SHA256_HashContext *ctx = (uECC_SHA256_HashContext *)base; + uint64_t bits = ctx->bits; + int i; + + ctx->buf[ctx->buf_len++] = 0x80; + if (ctx->buf_len > 56) { + while (ctx->buf_len < 64) ctx->buf[ctx->buf_len++] = 0; + sha256_compress(ctx, ctx->buf); + ctx->buf_len = 0; + } + while (ctx->buf_len < 56) ctx->buf[ctx->buf_len++] = 0; + for (i = 7; i >= 0; --i) { + ctx->buf[56 + (7 - i)] = (uint8_t)(bits >> (i * 8)); + } + sha256_compress(ctx, ctx->buf); + + for (i = 0; i < 8; ++i) { + out[i*4 ] = (uint8_t)(ctx->state[i] >> 24); + out[i*4 + 1] = (uint8_t)(ctx->state[i] >> 16); + out[i*4 + 2] = (uint8_t)(ctx->state[i] >> 8); + out[i*4 + 3] = (uint8_t)(ctx->state[i]); + } +} + +void uECC_SHA256_HashContext_init(uECC_SHA256_HashContext *ctx) { + ctx->uECC.init_hash = sha256_init_hash; + ctx->uECC.update_hash = sha256_update_hash; + ctx->uECC.finish_hash = sha256_finish_hash; + ctx->uECC.block_size = 64; + ctx->uECC.result_size = 32; + ctx->uECC.tmp = ctx->tmp; +} + +#undef ROR32 +#undef SHA256_S0 +#undef SHA256_S1 +#undef SHA256_s0 +#undef SHA256_s1 +#undef SHA256_CH +#undef SHA256_MAJ + +#endif /* uECC_SUPPORTS_SHA256 */ + +/* ========================================================================= + * SHA-512 + * ========================================================================= */ + +#if uECC_SUPPORTS_SHA512 + +#define ROR64(x, n) (((x) >> (n)) | ((x) << (64 - (n)))) + +#define SHA512_S0(x) (ROR64(x, 28) ^ ROR64(x, 34) ^ ROR64(x, 39)) +#define SHA512_S1(x) (ROR64(x, 14) ^ ROR64(x, 18) ^ ROR64(x, 41)) +#define SHA512_s0(x) (ROR64(x, 1) ^ ROR64(x, 8) ^ ((x) >> 7)) +#define SHA512_s1(x) (ROR64(x, 19) ^ ROR64(x, 61) ^ ((x) >> 6)) +#define SHA512_CH(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +#define SHA512_MAJ(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +static const uint64_t sha512_K[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +static void sha512_compress(uECC_SHA512_HashContext *ctx, const uint8_t *block) { + uint64_t W[80]; + uint64_t a, b, c, d, e, f, g, h, T1, T2; + int i; + + for (i = 0; i < 16; ++i) { + W[i] = ((uint64_t)block[i*8 ] << 56) + | ((uint64_t)block[i*8 + 1] << 48) + | ((uint64_t)block[i*8 + 2] << 40) + | ((uint64_t)block[i*8 + 3] << 32) + | ((uint64_t)block[i*8 + 4] << 24) + | ((uint64_t)block[i*8 + 5] << 16) + | ((uint64_t)block[i*8 + 6] << 8) + | (uint64_t)block[i*8 + 7]; + } + for (i = 16; i < 80; ++i) { + W[i] = SHA512_s1(W[i-2]) + W[i-7] + SHA512_s0(W[i-15]) + W[i-16]; + } + + a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3]; + e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7]; + + for (i = 0; i < 80; ++i) { + T1 = h + SHA512_S1(e) + SHA512_CH(e, f, g) + sha512_K[i] + W[i]; + T2 = SHA512_S0(a) + SHA512_MAJ(a, b, c); + h = g; g = f; f = e; e = d + T1; + d = c; c = b; b = a; a = T1 + T2; + } + + ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c; ctx->state[3] += d; + ctx->state[4] += e; ctx->state[5] += f; ctx->state[6] += g; ctx->state[7] += h; +} + +static void sha512_init_hash(const uECC_HashContext *base) { + uECC_SHA512_HashContext *ctx = (uECC_SHA512_HashContext *)base; + ctx->state[0] = 0x6a09e667f3bcc908ULL; ctx->state[1] = 0xbb67ae8584caa73bULL; + ctx->state[2] = 0x3c6ef372fe94f82bULL; ctx->state[3] = 0xa54ff53a5f1d36f1ULL; + ctx->state[4] = 0x510e527fade682d1ULL; ctx->state[5] = 0x9b05688c2b3e6c1fULL; + ctx->state[6] = 0x1f83d9abfb41bd6bULL; ctx->state[7] = 0x5be0cd19137e2179ULL; + ctx->bits = 0; + ctx->buf_len = 0; +} + +static void sha512_update_hash(const uECC_HashContext *base, + const uint8_t *data, unsigned len) { + uECC_SHA512_HashContext *ctx = (uECC_SHA512_HashContext *)base; + unsigned i; + ctx->bits += (uint64_t)len * 8; + for (i = 0; i < len; ++i) { + ctx->buf[ctx->buf_len++] = data[i]; + if (ctx->buf_len == 128) { + sha512_compress(ctx, ctx->buf); + ctx->buf_len = 0; + } + } +} + +static void sha512_finish_hash(const uECC_HashContext *base, uint8_t *out) { + uECC_SHA512_HashContext *ctx = (uECC_SHA512_HashContext *)base; + uint64_t bits = ctx->bits; + int i; + + ctx->buf[ctx->buf_len++] = 0x80; + if (ctx->buf_len > 112) { + while (ctx->buf_len < 128) ctx->buf[ctx->buf_len++] = 0; + sha512_compress(ctx, ctx->buf); + ctx->buf_len = 0; + } + while (ctx->buf_len < 112) ctx->buf[ctx->buf_len++] = 0; + /* 128-bit length field: high 64 bits are always 0 (supports up to 2^64-1 bits) */ + for (i = 0; i < 8; ++i) ctx->buf[112 + i] = 0; + for (i = 7; i >= 0; --i) { + ctx->buf[120 + (7 - i)] = (uint8_t)(bits >> (i * 8)); + } + sha512_compress(ctx, ctx->buf); + + for (i = 0; i < 8; ++i) { + out[i*8 ] = (uint8_t)(ctx->state[i] >> 56); + out[i*8 + 1] = (uint8_t)(ctx->state[i] >> 48); + out[i*8 + 2] = (uint8_t)(ctx->state[i] >> 40); + out[i*8 + 3] = (uint8_t)(ctx->state[i] >> 32); + out[i*8 + 4] = (uint8_t)(ctx->state[i] >> 24); + out[i*8 + 5] = (uint8_t)(ctx->state[i] >> 16); + out[i*8 + 6] = (uint8_t)(ctx->state[i] >> 8); + out[i*8 + 7] = (uint8_t)(ctx->state[i]); + } +} + +void uECC_SHA512_HashContext_init(uECC_SHA512_HashContext *ctx) { + ctx->uECC.init_hash = sha512_init_hash; + ctx->uECC.update_hash = sha512_update_hash; + ctx->uECC.finish_hash = sha512_finish_hash; + ctx->uECC.block_size = 128; + ctx->uECC.result_size = 64; + ctx->uECC.tmp = ctx->tmp; +} + +#undef ROR64 +#undef SHA512_S0 +#undef SHA512_S1 +#undef SHA512_s0 +#undef SHA512_s1 +#undef SHA512_CH +#undef SHA512_MAJ + +#endif /* uECC_SUPPORTS_SHA512 */ diff --git a/uECC_hash.h b/uECC_hash.h new file mode 100644 index 0000000..e20b305 --- /dev/null +++ b/uECC_hash.h @@ -0,0 +1,104 @@ +/* Copyright 2014, Kenneth MacKay. Licensed under the BSD 2-clause license. */ + +#ifndef uECC_HASH_H_ +#define uECC_HASH_H_ + +/* + * uECC_hash.h — optional built-in SHA-256 and SHA-512 hash context adapters + * for use with uECC_sign_deterministic() and uECC_ecsdsa_*(). + * + * DISABLED BY DEFAULT. Many embedded projects already carry a hash library + * (mbed TLS, wolfCrypt, RIOT crypto, etc.) and should not pay the code-size + * cost of a second implementation. Enable only what you actually need: + * + * #define uECC_SUPPORTS_SHA256 1 // before including this header + * #define uECC_SUPPORTS_SHA512 1 // before including this header + * + * Both macros default to 0 so that simply including uECC_hash.h has no effect + * unless you opt in. + */ + +#include "uECC.h" +#include + +/* ---- opt-in gates (default: disabled) ----------------------------------- */ + +#ifndef uECC_SUPPORTS_SHA256 + #define uECC_SUPPORTS_SHA256 0 /* set to 1 to enable the built-in SHA-256 */ +#endif + +#ifndef uECC_SUPPORTS_SHA512 + #define uECC_SUPPORTS_SHA512 0 /* set to 1 to enable the built-in SHA-512 */ +#endif + +/* ---- SHA-256 ------------------------------------------------------------- */ + +#if uECC_SUPPORTS_SHA256 + +/* + * Self-contained SHA-256 uECC_HashContext. + * + * The uECC_HashContext pointer (first member) is safe to cast to/from a + * pointer to this struct. The embedded tmp[] array satisfies the + * uECC_HashContext.tmp requirement of >= (2*result_size + block_size) bytes + * without any external allocation. + * + * Usage: + * uECC_SHA256_HashContext ctx; + * uECC_SHA256_HashContext_init(&ctx); + * uECC_ecsdsa_sign_optimized(priv, msg, len, &ctx.uECC, sig, curve); + */ +typedef struct { + uECC_HashContext uECC; /* must be first — cast-compatible */ + uint32_t state[8]; + uint8_t buf[64]; + uint64_t bits; + uint32_t buf_len; + uint8_t tmp[2 * 32 + 64]; /* scratch for uECC_HashContext.tmp */ +} uECC_SHA256_HashContext; + +/* + * Initialise *ctx and wire all function pointers. + * Must be called before passing &ctx.uECC to any uECC function. + * Safe to call multiple times (re-initialises). + */ +void uECC_SHA256_HashContext_init(uECC_SHA256_HashContext *ctx); + +#endif /* uECC_SUPPORTS_SHA256 */ + +/* ---- SHA-512 ------------------------------------------------------------- */ + +#if uECC_SUPPORTS_SHA512 + +/* + * Self-contained SHA-512 uECC_HashContext. + * + * With SHA-512 the ECSDSA R value is 64 bytes; for 256-bit curves only the + * leading 256 bits are used as the scalar r (bits2int truncates to curve + * order width). The signature format is R(64) || S(num_bytes). + * + * Usage: + * uECC_SHA512_HashContext ctx; + * uECC_SHA512_HashContext_init(&ctx); + * uECC_ecsdsa_sign_optimized(priv, msg, len, &ctx.uECC, sig, curve); + * // sig must be at least 64 + curve->num_bytes bytes + */ +typedef struct { + uECC_HashContext uECC; /* must be first — cast-compatible */ + uint64_t state[8]; + uint8_t buf[128]; + uint64_t bits; /* bit count (supports messages up to 2^64-1 bits) */ + uint32_t buf_len; + uint8_t tmp[2 * 64 + 128]; /* scratch for uECC_HashContext.tmp */ +} uECC_SHA512_HashContext; + +/* + * Initialise *ctx and wire all function pointers. + * Must be called before passing &ctx.uECC to any uECC function. + * Safe to call multiple times (re-initialises). + */ +void uECC_SHA512_HashContext_init(uECC_SHA512_HashContext *ctx); + +#endif /* uECC_SUPPORTS_SHA512 */ + +#endif /* uECC_HASH_H_ */