diff --git a/bip32.c b/bip32.c index 7a510f3b1..50f1466dd 100644 --- a/bip32.c +++ b/bip32.c @@ -49,6 +49,7 @@ #include "ed25519-donna/modm-donna-32bit.h" #include "blake2b.h" #include "bip39.h" +#include "pbkdf2.h" #endif #include "memzero.h" @@ -262,49 +263,56 @@ int hdnode_private_ckd(HDNode *inout, uint32_t i) } #if USE_CARDANO +/* sk1 is zl8 and contains only 29 bytes of active data, + * so it's not going to overflow when adding to sk2 */ +void scalar_add_no_overflow(const uint8_t * sk1, const uint8_t * sk2, uint8_t * res) +{ + uint16_t r = 0; int i; + for (i = 0; i < 32; i++) { + r = (uint16_t) sk1[i] + (uint16_t) sk2[i] + r; + res[i] = (uint8_t) r; + r >>= 8; + } +} + static void multiply8(uint8_t *dst, uint8_t *src, int bytes) { + int i; uint8_t prev_acc = 0; - for (int i = 0; i < bytes; i++) { - dst[i] = (src[i] << 3) + (prev_acc & 0x8); + for (i = 0; i < bytes; i++) { + dst[i] = (src[i] << 3) + (prev_acc & 0x7); prev_acc = src[i] >> 5; } + dst[bytes] = src[bytes-1] >> 5; } static void add_256bits(uint8_t *dst, uint8_t *src1, uint8_t *src2) { - for (int i = 0; i < 32; i++) { + int i; uint8_t carry = 0; + for (i = 0; i < 32; i++) { uint8_t a = src1[i]; uint8_t b = src2[i]; - uint16_t r = a + b; + uint16_t r = (uint16_t) a + (uint16_t) b + (uint16_t) carry; dst[i] = r & 0xff; + carry = (r >= 0x100) ? 1 : 0; } } -static int ed25519_scalar_add(const uint8_t *sk1, const uint8_t *sk2, uint8_t *res) -{ - bignum256modm s1, s2; - expand256_modm(s1, sk1, 32); - expand256_modm(s2, sk2, 32); - add256_modm(s1, s1, s2); - contract256_modm(res, s1); - return 0; -} - -int hdnode_private_ckd_cardano(HDNode *inout, uint32_t i) +int hdnode_private_ckd_cardano(HDNode *inout, uint32_t index) { // checks for hardened/non-hardened derivation, keysize 32 means we are dealing with public key and thus non-h, keysize 64 is for private key int keysize = 32; - if (i & 0x80000000) { + if (index & 0x80000000) { keysize = 64; } static CONFIDENTIAL uint8_t data[1 + 64 + 4]; - static CONFIDENTIAL uint8_t I[32 + 32]; - static CONFIDENTIAL bignum256 a, b; + static CONFIDENTIAL uint8_t z[32 + 32]; static CONFIDENTIAL uint8_t priv_key[64]; static CONFIDENTIAL uint8_t res_key[64]; + write_le(data + keysize + 1, index); + memcpy(priv_key, inout->private_key, 32); memcpy(priv_key + 32, inout->private_key_extension, 32); @@ -317,19 +325,23 @@ int hdnode_private_ckd_cardano(HDNode *inout, uint32_t i) data[0] = 2; memcpy(data + 1, inout->public_key + 1, 32); } - write_be(data + keysize + 1, i); - - bn_read_be(priv_key, &a); static CONFIDENTIAL HMAC_SHA512_CTX ctx; hmac_sha512_Init(&ctx, inout->chain_code, 32); hmac_sha512_Update(&ctx, data, 1 + keysize + 4); - hmac_sha512_Final(&ctx, I); + hmac_sha512_Final(&ctx, z); static CONFIDENTIAL uint8_t zl8[32]; - multiply8(zl8,I,32); - ed25519_scalar_add(zl8,priv_key,res_key); - add_256bits(res_key+32,I+32,priv_key+32); + memset(zl8, 0, 32); + + /* get 8 * Zl */ + multiply8(zl8, z, 28); + /* Kl = 8*Zl + parent(K)l */ + scalar_add_no_overflow(zl8, priv_key, res_key); + + /* Kr = Zr + parent(K)r */ + add_256bits(res_key + 32, z+32, priv_key+32); + memcpy(inout->private_key, res_key, 32); memcpy(inout->private_key_extension, res_key + 32, 32); @@ -340,112 +352,42 @@ int hdnode_private_ckd_cardano(HDNode *inout, uint32_t i) } hmac_sha512_Init(&ctx, inout->chain_code, 32); hmac_sha512_Update(&ctx, data, 1 + keysize + 4); - hmac_sha512_Final(&ctx, I); + hmac_sha512_Final(&ctx, z); - memcpy(inout->chain_code, I + 32, 32); + memcpy(inout->chain_code, z + 32, 32); inout->depth++; - inout->child_num = i; + inout->child_num = index; memzero(inout->public_key, sizeof(inout->public_key)); // making sure to wipe our memory - memzero(&a, sizeof(a)); - memzero(&b, sizeof(b)); - memzero(I, sizeof(I)); + memzero(z, sizeof(z)); memzero(data, sizeof(data)); memzero(priv_key, sizeof(priv_key)); memzero(res_key, sizeof(res_key)); return 1; } -int hdnode_from_seed_cardano(uint8_t *seed, int seed_len, HDNode *out) { - uint8_t hash[32]; - uint8_t cbor[32+2]; +int hdnode_from_seed_cardano(uint8_t *pass, int pass_len, uint8_t *seed, int seed_len, HDNode *out) { + uint8_t secret[96]; + pbkdf2_hmac_sha512(pass, pass_len, seed, seed_len, 4096, secret, 96); + + secret[0] &= 248; + secret[31] &= 31; + secret[31] |= 64; - if (seed_len < 24) { - // cbor encodes length directly into first byte if its smaller than 24 - seed[1] = 64 | seed_len; - blake2b(seed + 1, seed_len + 1, hash, 32); - } else { - seed[0] = 88; - seed[1] = seed_len; - blake2b(seed, seed_len + 2, hash, 32); - } - - cbor[0] = 88; // 64 means its byte array, 24 means its length has 8 bits - cbor[1] = 32; // length of the byte array - memcpy(cbor + 2, hash, 32); - uint8_t salt[21]; - memcpy(salt, "Root Seed Chain ", 16); - uint8_t hmac[64]; - uint8_t secret[64]; - uint8_t public[32]; - uint8_t chain_code[32]; - int failed = 1; memset(out, 0, sizeof(HDNode)); out->depth = 0; out->child_num = 0; out->curve = get_curve_by_name(ED25519_CARDANO_NAME); - int saltlen; - static CONFIDENTIAL HMAC_SHA512_CTX ctx; - for (int i = 1; i <= 1000; i++){ - hmac_sha512_Init(&ctx, cbor, 34); - if (i < 10) { - salt[16] = '0' + (i); - saltlen = 16 + 1; - } else if (i < 100) { - salt[16] = '0' + (i / 10); - salt[17] = '0' + (i % 10); - saltlen = 16 + 2; - } else if (i < 1000) { - salt[16] = '0' + (i / 100); - salt[17] = '0' + ((i / 10) % 10); - salt[18] = '0' + (i % 10); - saltlen = 16 + 3; - } else { - salt[16] = '0' + (i / 1000); - salt[17] = '0' + ((i / 100) % 10); - salt[18] = '0' + ((i / 10) % 10); - salt[19] = '0' + (i % 10); - saltlen = 16 + 4; - } - hmac_sha512_Update(&ctx, salt, saltlen); - hmac_sha512_Final(&ctx, hmac); - ed25519_publickey(hmac, public); - sha512_Raw(hmac, 32, secret); - secret[0] &= 248; - secret[31] &= 127; - secret[31] |= 64; - if (secret[31] & 0x20) { - continue; - } - memcpy(chain_code, hmac + 32, 32); - failed = 0; - break; - } - - memzero(hash, sizeof(hash)); - memzero(cbor, sizeof(cbor)); - memzero(salt, sizeof(salt)); - - if (failed) { - memzero(seed, sizeof(seed)); - memzero(secret, sizeof(secret)); - memzero(chain_code, sizeof(chain_code)); - memzero(hmac, sizeof(hmac)); - return 0; - } - memcpy(out->private_key, secret, 32); memcpy(out->private_key_extension, secret + 32, 32); - memcpy(out->chain_code, chain_code, 32); - out->public_key[0] = 1; - memcpy(out->public_key + 1, public, 32); - memzero(seed, sizeof(seed)); + out->public_key[0] = 0; + hdnode_fill_public_key(out); + + memcpy(out->chain_code, secret + 64, 32); memzero(secret, sizeof(secret)); - memzero(chain_code, sizeof(chain_code)); - memzero(hmac, sizeof(hmac)); return 1; } diff --git a/bip32.h b/bip32.h index f633c532a..2c2f799ae 100644 --- a/bip32.h +++ b/bip32.h @@ -63,7 +63,7 @@ int hdnode_from_seed(const uint8_t *seed, int seed_len, const char *curve, HDNod int hdnode_private_ckd(HDNode *inout, uint32_t i); #if USE_CARDANO int hdnode_private_ckd_cardano(HDNode *inout, uint32_t i); -int hdnode_from_seed_cardano(uint8_t *seed, int seed_len, HDNode *out); +int hdnode_from_seed_cardano(uint8_t *pass, int pass_len, uint8_t *seed, int seed_len, HDNode *out); #endif int hdnode_public_ckd_cp(const ecdsa_curve *curve, const curve_point *parent, const uint8_t *parent_chain_code, uint32_t i, curve_point *child, uint8_t *child_chain_code); diff --git a/bip39.c b/bip39.c index ac4e4fb36..8608fd456 100644 --- a/bip39.c +++ b/bip39.c @@ -243,7 +243,7 @@ void mnemonic_to_seed(const char *mnemonic, const char *passphrase, uint8_t seed memcpy(salt, "mnemonic", 8); memcpy(salt + 8, passphrase, passphraselen); static CONFIDENTIAL PBKDF2_HMAC_SHA512_CTX pctx; - pbkdf2_hmac_sha512_Init(&pctx, (const uint8_t *)mnemonic, strlen(mnemonic), salt, passphraselen + 8); + pbkdf2_hmac_sha512_Init(&pctx, (const uint8_t *)mnemonic, strlen(mnemonic), salt, passphraselen + 8, 1); if (progress_callback) { progress_callback(0, BIP39_PBKDF2_ROUNDS); } diff --git a/pbkdf2.c b/pbkdf2.c index e6dbd008b..04c11e8af 100644 --- a/pbkdf2.c +++ b/pbkdf2.c @@ -27,10 +27,11 @@ #include "sha2.h" #include "memzero.h" -void pbkdf2_hmac_sha256_Init(PBKDF2_HMAC_SHA256_CTX *pctx, const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen) +#define CEILING_POS(X) ((X-(uint32_t)(X)) > 0 ? (uint32_t)(X+1) : (uint32_t)(X)) + +void pbkdf2_hmac_sha256_Init(PBKDF2_HMAC_SHA256_CTX *pctx, const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen, uint32_t blocknr) { SHA256_CTX ctx; - uint32_t blocknr = 1; #if BYTE_ORDER == LITTLE_ENDIAN REVERSE32(blocknr, blocknr); #endif @@ -78,18 +79,34 @@ void pbkdf2_hmac_sha256_Final(PBKDF2_HMAC_SHA256_CTX *pctx, uint8_t *key) memzero(pctx, sizeof(PBKDF2_HMAC_SHA256_CTX)); } -void pbkdf2_hmac_sha256(const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen, uint32_t iterations, uint8_t *key) +void pbkdf2_hmac_sha256(const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen, uint32_t iterations, uint8_t *key, int keylen) { - PBKDF2_HMAC_SHA256_CTX pctx; - pbkdf2_hmac_sha256_Init(&pctx, pass, passlen, salt, saltlen); - pbkdf2_hmac_sha256_Update(&pctx, iterations); - pbkdf2_hmac_sha256_Final(&pctx, key); + uint32_t blocks_count = (uint32_t) CEILING_POS((float) keylen / SHA256_DIGEST_LENGTH); + + int unfinished_key_size = keylen; + for (uint32_t blocknr = 1; blocknr <= blocks_count; blocknr++) { + PBKDF2_HMAC_SHA256_CTX pctx; + pbkdf2_hmac_sha256_Init(&pctx, pass, passlen, salt, saltlen, blocknr); + pbkdf2_hmac_sha256_Update(&pctx, iterations); + + unsigned int key_offset = (blocknr - 1) * SHA256_DIGEST_LENGTH; + uint8_t diggest[SHA256_DIGEST_LENGTH]; + + pbkdf2_hmac_sha256_Final(&pctx, diggest); + + if (unfinished_key_size > SHA256_DIGEST_LENGTH) { + memcpy(key + key_offset, diggest, SHA256_DIGEST_LENGTH); + unfinished_key_size -= SHA256_DIGEST_LENGTH; + } else { + memcpy(key + key_offset, diggest, unfinished_key_size); + unfinished_key_size = 0; + } + } } -void pbkdf2_hmac_sha512_Init(PBKDF2_HMAC_SHA512_CTX *pctx, const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen) +void pbkdf2_hmac_sha512_Init(PBKDF2_HMAC_SHA512_CTX *pctx, const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen, uint32_t blocknr) { SHA512_CTX ctx; - uint32_t blocknr = 1; #if BYTE_ORDER == LITTLE_ENDIAN REVERSE32(blocknr, blocknr); #endif @@ -138,10 +155,26 @@ void pbkdf2_hmac_sha512_Final(PBKDF2_HMAC_SHA512_CTX *pctx, uint8_t *key) memzero(pctx, sizeof(PBKDF2_HMAC_SHA512_CTX)); } -void pbkdf2_hmac_sha512(const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen, uint32_t iterations, uint8_t *key) +void pbkdf2_hmac_sha512(const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen, uint32_t iterations, uint8_t *key, int keylen) { - PBKDF2_HMAC_SHA512_CTX pctx; - pbkdf2_hmac_sha512_Init(&pctx, pass, passlen, salt, saltlen); - pbkdf2_hmac_sha512_Update(&pctx, iterations); - pbkdf2_hmac_sha512_Final(&pctx, key); + uint32_t blocks_count = (uint32_t) CEILING_POS((float) keylen / SHA512_DIGEST_LENGTH); + + int unfinished_key_size = keylen; + for (uint32_t blocknr = 1; blocknr <= blocks_count; blocknr++) { + PBKDF2_HMAC_SHA512_CTX pctx; + pbkdf2_hmac_sha512_Init(&pctx, pass, passlen, salt, saltlen, blocknr); + pbkdf2_hmac_sha512_Update(&pctx, iterations); + + unsigned int key_offset = (blocknr - 1) * SHA512_DIGEST_LENGTH; + uint8_t diggest[SHA512_DIGEST_LENGTH]; + pbkdf2_hmac_sha512_Final(&pctx, diggest); + + if (unfinished_key_size > SHA512_DIGEST_LENGTH) { + memcpy(key + key_offset, diggest, SHA512_DIGEST_LENGTH); + unfinished_key_size -= SHA512_DIGEST_LENGTH; + } else { + memcpy(key + key_offset, diggest, unfinished_key_size); + unfinished_key_size = 0; + } + } } diff --git a/pbkdf2.h b/pbkdf2.h index e77f29187..e3f440c8f 100644 --- a/pbkdf2.h +++ b/pbkdf2.h @@ -43,14 +43,14 @@ typedef struct _PBKDF2_HMAC_SHA512_CTX { char first; } PBKDF2_HMAC_SHA512_CTX; -void pbkdf2_hmac_sha256_Init(PBKDF2_HMAC_SHA256_CTX *pctx, const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen); +void pbkdf2_hmac_sha256_Init(PBKDF2_HMAC_SHA256_CTX *pctx, const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen, uint32_t blocknr); void pbkdf2_hmac_sha256_Update(PBKDF2_HMAC_SHA256_CTX *pctx, uint32_t iterations); void pbkdf2_hmac_sha256_Final(PBKDF2_HMAC_SHA256_CTX *pctx, uint8_t *key); -void pbkdf2_hmac_sha256(const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen, uint32_t iterations, uint8_t *key); +void pbkdf2_hmac_sha256(const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen, uint32_t iterations, uint8_t *key, int keylen); -void pbkdf2_hmac_sha512_Init(PBKDF2_HMAC_SHA512_CTX *pctx, const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen); +void pbkdf2_hmac_sha512_Init(PBKDF2_HMAC_SHA512_CTX *pctx, const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen, uint32_t blocknr); void pbkdf2_hmac_sha512_Update(PBKDF2_HMAC_SHA512_CTX *pctx, uint32_t iterations); void pbkdf2_hmac_sha512_Final(PBKDF2_HMAC_SHA512_CTX *pctx, uint8_t *key); -void pbkdf2_hmac_sha512(const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen, uint32_t iterations, uint8_t *key); +void pbkdf2_hmac_sha512(const uint8_t *pass, int passlen, const uint8_t *salt, int saltlen, uint32_t iterations, uint8_t *key, int keylen); #endif diff --git a/tests/test_check.c b/tests/test_check.c index d3a552333..6c6564564 100644 --- a/tests/test_check.c +++ b/tests/test_check.c @@ -2586,19 +2586,19 @@ START_TEST(test_pbkdf2_hmac_sha256) uint8_t k[40], s[40]; strcpy((char *)s, "salt"); - pbkdf2_hmac_sha256((uint8_t *)"password", 8, s, 4, 1, k); + pbkdf2_hmac_sha256((uint8_t *)"password", 8, s, 4, 1, k, 32); ck_assert_mem_eq(k, fromhex("120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b"), 32); strcpy((char *)s, "salt"); - pbkdf2_hmac_sha256((uint8_t *)"password", 8, s, 4, 2, k); + pbkdf2_hmac_sha256((uint8_t *)"password", 8, s, 4, 2, k, 32); ck_assert_mem_eq(k, fromhex("ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43"), 32); strcpy((char *)s, "salt"); - pbkdf2_hmac_sha256((uint8_t *)"password", 8, s, 4, 4096, k); + pbkdf2_hmac_sha256((uint8_t *)"password", 8, s, 4, 4096, k, 32); ck_assert_mem_eq(k, fromhex("c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a"), 32); strcpy((char *)s, "saltSALTsaltSALTsaltSALTsaltSALTsalt"); - pbkdf2_hmac_sha256((uint8_t *)"passwordPASSWORDpassword", 3*8, s, 9*4, 4096, k); + pbkdf2_hmac_sha256((uint8_t *)"passwordPASSWORDpassword", 3*8, s, 9*4, 4096, k, 32); ck_assert_mem_eq(k, fromhex("348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1"), 32); } END_TEST @@ -2609,19 +2609,19 @@ START_TEST(test_pbkdf2_hmac_sha512) uint8_t k[64], s[40]; strcpy((char *)s, "salt"); - pbkdf2_hmac_sha512((uint8_t *)"password", 8, s, 4, 1, k); + pbkdf2_hmac_sha512((uint8_t *)"password", 8, s, 4, 1, k, 64); ck_assert_mem_eq(k, fromhex("867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252c02d470a285a0501bad999bfe943c08f050235d7d68b1da55e63f73b60a57fce"), 64); strcpy((char *)s, "salt"); - pbkdf2_hmac_sha512((uint8_t *)"password", 8, s, 4, 2, k); + pbkdf2_hmac_sha512((uint8_t *)"password", 8, s, 4, 2, k, 64); ck_assert_mem_eq(k, fromhex("e1d9c16aa681708a45f5c7c4e215ceb66e011a2e9f0040713f18aefdb866d53cf76cab2868a39b9f7840edce4fef5a82be67335c77a6068e04112754f27ccf4e"), 64); strcpy((char *)s, "salt"); - pbkdf2_hmac_sha512((uint8_t *)"password", 8, s, 4, 4096, k); + pbkdf2_hmac_sha512((uint8_t *)"password", 8, s, 4, 4096, k, 64); ck_assert_mem_eq(k, fromhex("d197b1b33db0143e018b12f3d1d1479e6cdebdcc97c5c0f87f6902e072f457b5143f30602641b3d55cd335988cb36b84376060ecd532e039b742a239434af2d5"), 64); strcpy((char *)s, "saltSALTsaltSALTsaltSALTsaltSALTsalt"); - pbkdf2_hmac_sha512((uint8_t *)"passwordPASSWORDpassword", 3*8, s, 9*4, 4096, k); + pbkdf2_hmac_sha512((uint8_t *)"passwordPASSWORDpassword", 3*8, s, 9*4, 4096, k, 64); ck_assert_mem_eq(k, fromhex("8c0511f4c6e597c6ac6315d8f0362e225f3c501495ba23b868c005174dc4ee71115b59f9e60cd9532fa33e0f75aefe30225c583a186cd82bd4daea9724a3d3b8"), 64); } END_TEST @@ -5063,6 +5063,7 @@ Suite *test_suite(void) #if USE_CARDANO tc = tcase_create("bip32-cardano"); + tcase_add_test(tc, test_bip32_cardano_hdnode_vector_1); tcase_add_test(tc, test_bip32_cardano_hdnode_vector_2); tcase_add_test(tc, test_bip32_cardano_hdnode_vector_3); @@ -5070,6 +5071,7 @@ Suite *test_suite(void) tcase_add_test(tc, test_bip32_cardano_hdnode_vector_5); tcase_add_test(tc, test_bip32_cardano_hdnode_vector_6); tcase_add_test(tc, test_bip32_cardano_hdnode_vector_7); + tcase_add_test(tc, test_ed25519_cardano_sign_vectors); suite_add_tcase(s,tc); #endif diff --git a/tests/test_check_cardano.h b/tests/test_check_cardano.h index 58f87b989..8c1f5be2a 100644 --- a/tests/test_check_cardano.h +++ b/tests/test_check_cardano.h @@ -1,18 +1,92 @@ // https://github.com/input-output-hk/cardano-crypto/blob/master/tests/goldens/cardano/crypto/wallet/BIP39-128 +START_TEST(test_ed25519_cardano_sign_vectors) +{ + ed25519_public_key public_key; + ed25519_secret_key secret_key; + ed25519_secret_key secret_key_extension; + ed25519_signature signature; + + static const char *vectors[] = { + "6065a956b1b34145c4416fdc3ba3276801850e91a77a31a7be782463288aea53", // private key + "60ba6e25b1a02157fb69c5d1d7b96c4619736e545447069a6a6f0ba90844bc8e", // private key extension + "64b20fa082b3143d6b5eed42c6ef63f99599d0888afe060620abc1b319935fe1", // public key + "45b1a75fe3119e13c6f60ab9ba674b42f946fdc558e07c83dfa0751c2eba69c79331bd8a4a975662b23628a438a0eba76367e44c12ca91b39ec59063f860f10d", // signature + + "e7d27516538403a53a8b041656a3f570909df641a0ab811fe7d87c9ba02a830c", // private key + "794a2c54ad8b525b781773c87d38cbf4197636bc427a9d551368286fe4c294a4", // private key extension + "95bb82ffd5707716bc65170ab4e8dafeed90fbe0ce9258713b7751e962d931df", // public key + "f2c9171782e7df7665126ac545ae53b05964b0160536efdb545e2460dbbec2b19ec6b338b8f1bf4dfee94360ed024b115e37b1d7e6f3f9ae4beb79539428560f", // signature + + "9b5a3d9a4c60bcd49bb64b72c082b164314d0f61d842f2575fd1d4fb30a28a0c", // private key + "b093e376f41eb7bf80abcd0073a52455d25b5d21815bc758e5f6f81536aedebb", // private key extension + "79fc8154554b97e4c56ef2f9dbb4c1421ff19509688931a1e964bda5dec0f19f", // public key + "2ba1439ae648a7e8da7c9ab1ee6da94fd4ebe37abd0978306e8fba2afa8f111a88a993dbf008bedae9167f4f68409e4c9ddaf02cba12418447b1848907ad800f", // signature + + "52e0c98aa600cfdcd1ff28fcda5227ed87063f4a98547a78b771052cf102b40c", // private key + "6c18d9f8075b1a6a1833540607479bd58b7beb8a83d2bb01ca7ae02452a25803", // private key extension + "dc907c7c06e6314eedd9e18c9f6c6f9cc4e205fb1c70da608234c319f1f7b0d6", // public key + "0cd34f84e0d2fcb1800bdb0e869b9041349955ced66aedbe6bda187ebe8d36a62a05b39647e92fcc42aa7a7368174240afba08b8c81f981a22f942d6bd781602", // signature + + "11fd6462a3a92b35c22703f6f1c124ddcf36b7c2b09cc2784f320e1cfa12ec04", // private key + "c2785803c61c46aeca192a1bb1b7b20a8c4cc7fa01db57fc5d1d8a5473402352", // private key extension + "839775a41876e328986aa26168958bba1176e67819b357eea84afceab8b1db78", // public key + "e41f73db2f8d2896a687802b2be76b7cabb73dfbb4891494883a0cbd9bbb9e5f9d3e14d2d0b06c6674333508496db660936737c0efd9511514147dac79fa4905", // signature + + "5b1e5cad02274ba461f4708d8598d3497faf8fe3e894a379573aa6ac3a03e505", // private key + "ba179d2e3c67aabb486c48d16002b51ad32eab434c738a1550962313b07098cd", // private key extension + "75eb8d197ec8627c85af88e66aa1e49065dd8ac98ed8991db52ece01635dfb76", // public key + "631015357cee3051116b4c2ff4d1c5beb13b6e5023635aa1eeb0563cadf0d4fbc10bd5e31b4a4220c67875558c41b5cc0328104ae39cc7ff20ff0c2bda598906", // signature + + "624b47150f58dfa44284fbc63c9f99b9b79f808c4955a461f0e2be44eb0be50d", // private key + "097aa006d694b165ef37cf23562e5967c96e49255d2f20faae478dee83aa5b02", // private key extension + "0588589cd9b51dfc028cf225674069cbe52e0e70deb02dc45b79b26ee3548b00", // public key + "1de1d275428ba9491a433cd473cd076c027f61e7a8b5391df9dea5cb4bc88d8a57b095906a30b13e68259851a8dd3f57b6f0ffa37a5d3ffc171240f2d404f901", // signature + + 0, 0, + }; + + const char **test_data; + test_data = vectors; + while (*test_data) { + memcpy(secret_key, fromhex(*test_data), 32); + MARK_SECRET_DATA(secret_key, sizeof(secret_key)); + + memcpy(secret_key_extension, fromhex(*(test_data + 1)), 32); + MARK_SECRET_DATA(secret_key_extension, sizeof(secret_key_extension)); + + ed25519_publickey_ext(secret_key, secret_key_extension, public_key); + UNMARK_SECRET_DATA(public_key, sizeof(public_key)); + + ck_assert_mem_eq(public_key, fromhex(*(test_data + 2)), 32); + + const uint8_t * message = (const uint8_t *) "Hello World"; + ed25519_sign_ext(message, 11, secret_key, secret_key_extension, public_key, signature); + UNMARK_SECRET_DATA(signature, sizeof(signature)); + + ck_assert_mem_eq(signature, fromhex(*(test_data + 3)), 64); + + UNMARK_SECRET_DATA(secret_key, sizeof(secret_key)); + UNMARK_SECRET_DATA(secret_key_extension, sizeof(secret_key_extension)); + + test_data += 4; + } +} +END_TEST + START_TEST(test_bip32_cardano_hdnode_vector_1) { HDNode node; uint8_t seed[66]; - int seed_len = mnemonic_to_entropy("ring crime symptom enough erupt lady behave ramp apart settle citizen junk", seed + 2); + int seed_len = mnemonic_to_entropy("ring crime symptom enough erupt lady behave ramp apart settle citizen junk", seed); ck_assert_int_eq(seed_len, 132); - hdnode_from_seed_cardano(seed, seed_len / 8, &node); + hdnode_from_seed_cardano(NULL, 0, seed, seed_len / 8, &node); - ck_assert_mem_eq(node.chain_code, fromhex("739f4b3caca4c9ad4fcd4bdc2ef42c8601af8d6946999ef85ef6ae84f66e72eb"), 32); - ck_assert_mem_eq(node.private_key, fromhex("6065a956b1b34145c4416fdc3ba3276801850e91a77a31a7be782463288aea53"), 32); - ck_assert_mem_eq(node.private_key_extension, fromhex("60ba6e25b1a02157fb69c5d1d7b96c4619736e545447069a6a6f0ba90844bc8e"), 32); + ck_assert_mem_eq(node.chain_code, fromhex("affbc325d9027c0f2d9f925b1dcf6c12bf5c1dd08904474066a4f2c00db56173"), 32); + ck_assert_mem_eq(node.private_key, fromhex("08a14df748e477a69d21c97c56db151fc19e2521f31dd0ac5360f269e5b6ea46"), 32); + ck_assert_mem_eq(node.private_key_extension, fromhex("daeb991f2d2128e2525415c56a07f4366baa26c1e48572a5e073934b6de35fbc"), 32); hdnode_fill_public_key(&node); - ck_assert_mem_eq(node.public_key + 1, fromhex("64b20fa082b3143d6b5eed42c6ef63f99599d0888afe060620abc1b319935fe1"), 32); + ck_assert_mem_eq(node.public_key + 1, fromhex("9a1d04808b4c0682816961cf666e82a7fd35949658aba5354c517eccf12aacb4"), 32); } END_TEST @@ -21,17 +95,17 @@ START_TEST(test_bip32_cardano_hdnode_vector_2) HDNode node; uint8_t seed[66]; - int seed_len = mnemonic_to_entropy("ring crime symptom enough erupt lady behave ramp apart settle citizen junk", seed + 2); + int seed_len = mnemonic_to_entropy("ring crime symptom enough erupt lady behave ramp apart settle citizen junk", seed); ck_assert_int_eq(seed_len, 132); - hdnode_from_seed_cardano(seed, seed_len / 8, &node); + hdnode_from_seed_cardano(NULL, 0, seed, seed_len / 8, &node); hdnode_private_ckd_cardano(&node, 0x80000000); - ck_assert_mem_eq(node.chain_code, fromhex("6755cb82e892d6614c007a5efbceb21d95a5244e269d0e206b48b9a495390b03"), 32); - ck_assert_mem_eq(node.private_key, fromhex("e7d27516538403a53a8b041656a3f570909df641a0ab811fe7d87c9ba02a830c"), 32); - ck_assert_mem_eq(node.private_key_extension, fromhex("794a2c54ad8b525b781773c87d38cbf4197636bc427a9d551368286fe4c294a4"), 32); + ck_assert_mem_eq(node.chain_code, fromhex("104c6a0736e501c9bfe2966ba3773f5320495b19c3f2ed222234850af2ccd5b1"), 32); + ck_assert_mem_eq(node.private_key, fromhex("6064bf06b2e981d7c9792b1482eeecd40ec3cfa12143f4a1f149d48ce8b6ea46"), 32); + ck_assert_mem_eq(node.private_key_extension, fromhex("64aa9a16331f14c981b769efcf96addcc4c6db44047fe7a7feae0be23d33bf54"), 32); hdnode_fill_public_key(&node); - ck_assert_mem_eq(node.public_key + 1, fromhex("95bb82ffd5707716bc65170ab4e8dafeed90fbe0ce9258713b7751e962d931df"), 32); + ck_assert_mem_eq(node.public_key + 1, fromhex("c651c14a13c2311fc30a7acf244add1fdac3683e7ba89b4571e4cbcab509b915"), 32); } END_TEST @@ -40,17 +114,17 @@ START_TEST(test_bip32_cardano_hdnode_vector_3) HDNode node; uint8_t seed[66]; - int seed_len = mnemonic_to_entropy("ring crime symptom enough erupt lady behave ramp apart settle citizen junk", seed + 2); + int seed_len = mnemonic_to_entropy("ring crime symptom enough erupt lady behave ramp apart settle citizen junk", seed); ck_assert_int_eq(seed_len, 132); - hdnode_from_seed_cardano(seed, seed_len / 8, &node); + hdnode_from_seed_cardano(NULL, 0, seed, seed_len / 8, &node); hdnode_private_ckd_cardano(&node, 0x80000001); - ck_assert_mem_eq(node.chain_code, fromhex("47a242713bd18608231147c066b6083bfc1e9066fec9f621844c84fed6228a34"), 32); - ck_assert_mem_eq(node.private_key, fromhex("9b5a3d9a4c60bcd49bb64b72c082b164314d0f61d842f2575fd1d4fb30a28a0c"), 32); - ck_assert_mem_eq(node.private_key_extension, fromhex("b093e376f41eb7bf80abcd0073a52455d25b5d21815bc758e5f6f81536aedebb"), 32); + ck_assert_mem_eq(node.chain_code, fromhex("da99870d7e69de2a76f255ba8c7ed22428c7e5b0a8df978753c707c95ec3d4ca"), 32); + ck_assert_mem_eq(node.private_key, fromhex("c85fa69f4a1891fd98d1d1fc5f0cf9b1d6e44b0e6906744ab23ea766edb6ea46"), 32); + ck_assert_mem_eq(node.private_key_extension, fromhex("b4fc241feffe840b8a54a26ab447f5a5caa31032db3a8091fca14f38b86ed539"), 32); hdnode_fill_public_key(&node); - ck_assert_mem_eq(node.public_key + 1, fromhex("79fc8154554b97e4c56ef2f9dbb4c1421ff19509688931a1e964bda5dec0f19f"), 32); + ck_assert_mem_eq(node.public_key + 1, fromhex("5a5b0c92530cd366f05cf072509c806f904262c259e79a0080bbd5ee35706bb1"), 32); } END_TEST @@ -59,18 +133,18 @@ START_TEST(test_bip32_cardano_hdnode_vector_4) HDNode node; uint8_t seed[66]; - int seed_len = mnemonic_to_entropy("ring crime symptom enough erupt lady behave ramp apart settle citizen junk", seed + 2); + int seed_len = mnemonic_to_entropy("ring crime symptom enough erupt lady behave ramp apart settle citizen junk", seed); ck_assert_int_eq(seed_len, 132); - hdnode_from_seed_cardano(seed, seed_len / 8, &node); + hdnode_from_seed_cardano(NULL, 0, seed, seed_len / 8, &node); hdnode_private_ckd_cardano(&node, 0x80000000); hdnode_private_ckd_cardano(&node, 0x80000001); - ck_assert_mem_eq(node.chain_code, fromhex("d6798491b9fa4612370ae5ef3c623a0b6872f3ad8f26970885fa67c83bdc425e"), 32); - ck_assert_mem_eq(node.private_key, fromhex("52e0c98aa600cfdcd1ff28fcda5227ed87063f4a98547a78b771052cf102b40c"), 32); - ck_assert_mem_eq(node.private_key_extension, fromhex("6c18d9f8075b1a6a1833540607479bd58b7beb8a83d2bb01ca7ae02452a25803"), 32); + ck_assert_mem_eq(node.chain_code, fromhex("b40c44dfd9be08591b62be7f9991c85f812d8196927f3c824d9fcb17d275089e"), 32); + ck_assert_mem_eq(node.private_key, fromhex("d064dcf1449d9c3e47f5b422680343561989035bf2e4e23fc34cb61fedb6ea46"), 32); + ck_assert_mem_eq(node.private_key_extension, fromhex("a3071959013af95aaecf78a7a2e1b9838bbbc4864d6a8a2295243782078345cd"), 32); hdnode_fill_public_key(&node); - ck_assert_mem_eq(node.public_key + 1, fromhex("dc907c7c06e6314eedd9e18c9f6c6f9cc4e205fb1c70da608234c319f1f7b0d6"), 32); + ck_assert_mem_eq(node.public_key + 1, fromhex("aaaca5e7adc69a03ef1f5c017ed02879e8ca871df028461ed9bf19fb8fa15038"), 32); } END_TEST @@ -79,19 +153,19 @@ START_TEST(test_bip32_cardano_hdnode_vector_5) HDNode node; uint8_t seed[66]; - int seed_len = mnemonic_to_entropy("ring crime symptom enough erupt lady behave ramp apart settle citizen junk", seed + 2); + int seed_len = mnemonic_to_entropy("ring crime symptom enough erupt lady behave ramp apart settle citizen junk", seed); ck_assert_int_eq(seed_len, 132); - hdnode_from_seed_cardano(seed, seed_len / 8, &node); + hdnode_from_seed_cardano(NULL, 0, seed, seed_len / 8, &node); hdnode_private_ckd_cardano(&node, 0x80000000); hdnode_private_ckd_cardano(&node, 0x80000001); hdnode_private_ckd_cardano(&node, 0x80000002); - ck_assert_mem_eq(node.chain_code, fromhex("4169a2a32e3618a903e930bd1a713033a38f92389093408394e29ac37a1752ea"), 32); - ck_assert_mem_eq(node.private_key, fromhex("11fd6462a3a92b35c22703f6f1c124ddcf36b7c2b09cc2784f320e1cfa12ec04"), 32); - ck_assert_mem_eq(node.private_key_extension, fromhex("c2785803c61c46aeca192a1bb1b7b20a8c4cc7fa01db57fc5d1d8a5473402352"), 32); + ck_assert_mem_eq(node.chain_code, fromhex("2593896baf92f6ab2c0f253787ab16be0244ba95e0d48ba09da1a7fd3f926c72"), 32); + ck_assert_mem_eq(node.private_key, fromhex("0811b6d5d6f7120cb05d4ce5453d6ce42825c2a8e53b6d370a6b05ccf4b6ea46"), 32); + ck_assert_mem_eq(node.private_key_extension, fromhex("5bebf1eea68acd04932653d944b064b10baaf5886dd73c185cc285059bf93363"), 32); hdnode_fill_public_key(&node); - ck_assert_mem_eq(node.public_key + 1, fromhex("839775a41876e328986aa26168958bba1176e67819b357eea84afceab8b1db78"), 32); + ck_assert_mem_eq(node.public_key + 1, fromhex("1c87a32c5babad2fe33e0586bdc523574c6126f8368bc76598e17ea46201f980"), 32); } END_TEST @@ -100,20 +174,20 @@ START_TEST(test_bip32_cardano_hdnode_vector_6) HDNode node; uint8_t seed[66]; - int seed_len = mnemonic_to_entropy("ring crime symptom enough erupt lady behave ramp apart settle citizen junk", seed + 2); + int seed_len = mnemonic_to_entropy("ring crime symptom enough erupt lady behave ramp apart settle citizen junk", seed); ck_assert_int_eq(seed_len, 132); - hdnode_from_seed_cardano(seed, seed_len / 8, &node); + hdnode_from_seed_cardano(NULL, 0, seed, seed_len / 8, &node); hdnode_private_ckd_cardano(&node, 0x80000000); hdnode_private_ckd_cardano(&node, 0x80000001); hdnode_private_ckd_cardano(&node, 0x80000002); hdnode_private_ckd_cardano(&node, 0x80000002); - ck_assert_mem_eq(node.chain_code, fromhex("3ae9c99a5925cba2dcf121baf3a0254f3dea23c129f9eb70a8a7e8897c5199ba"), 32); - ck_assert_mem_eq(node.private_key, fromhex("5b1e5cad02274ba461f4708d8598d3497faf8fe3e894a379573aa6ac3a03e505"), 32); - ck_assert_mem_eq(node.private_key_extension, fromhex("ba179d2e3c67aabb486c48d16002b51ad32eab434c738a1550962313b07098cd"), 32); + ck_assert_mem_eq(node.chain_code, fromhex("fe8c6c2ab1e30385513fcffb49dcfe3e7805260425ea76b3b72b9f5bbe3b3d40"), 32); + ck_assert_mem_eq(node.private_key, fromhex("6019b9f5ef6ca530b657bcdb500de5455db8d51afb951fa045b6fbb3f6b6ea46"), 32); + ck_assert_mem_eq(node.private_key_extension, fromhex("466332cb097934b43008701e7e27044aa56c7859019e4eba18d91a3bea23dff7"), 32); hdnode_fill_public_key(&node); - ck_assert_mem_eq(node.public_key + 1, fromhex("75eb8d197ec8627c85af88e66aa1e49065dd8ac98ed8991db52ece01635dfb76"), 32); + ck_assert_mem_eq(node.public_key + 1, fromhex("0b8f04755481ced76b4e5795aaafdb3cbd757c10fe60e9c58f48cf29a7ec3575"), 32); } END_TEST @@ -122,9 +196,9 @@ START_TEST(test_bip32_cardano_hdnode_vector_7) HDNode node; uint8_t seed[66]; - int seed_len = mnemonic_to_entropy("ring crime symptom enough erupt lady behave ramp apart settle citizen junk", seed + 2); + int seed_len = mnemonic_to_entropy("ring crime symptom enough erupt lady behave ramp apart settle citizen junk", seed); ck_assert_int_eq(seed_len, 132); - hdnode_from_seed_cardano(seed, seed_len / 8, &node); + hdnode_from_seed_cardano(NULL, 0, seed, seed_len / 8, &node); hdnode_private_ckd_cardano(&node, 0x80000000); hdnode_private_ckd_cardano(&node, 0x80000001); @@ -132,85 +206,10 @@ START_TEST(test_bip32_cardano_hdnode_vector_7) hdnode_private_ckd_cardano(&node, 0x80000002); hdnode_private_ckd_cardano(&node, 0xBB9ACA00); - ck_assert_mem_eq(node.chain_code, fromhex("15c450b86dd7dd83b31951d9ee03eb1a7925161d817bd517c69cf09e3671f1ca"), 32); - ck_assert_mem_eq(node.private_key, fromhex("624b47150f58dfa44284fbc63c9f99b9b79f808c4955a461f0e2be44eb0be50d"), 32); - ck_assert_mem_eq(node.private_key_extension, fromhex("097aa006d694b165ef37cf23562e5967c96e49255d2f20faae478dee83aa5b02"), 32); + ck_assert_mem_eq(node.chain_code, fromhex("ff77c08d37471c1d4cedd3fae2642c009324d9712492efc74dedab09c9bf973c"), 32); + ck_assert_mem_eq(node.private_key, fromhex("488f34840bba516f7920f91676b8681d0dd833b4ce14468e0810b255f9b6ea46"), 32); + ck_assert_mem_eq(node.private_key_extension, fromhex("01eccef768a79859f824a1d3c3e35e131184e2940c3fca9a4c9b307741f65363"), 32); hdnode_fill_public_key(&node); - ck_assert_mem_eq(node.public_key + 1, fromhex("0588589cd9b51dfc028cf225674069cbe52e0e70deb02dc45b79b26ee3548b00"), 32); -} -END_TEST - -// https://github.com/input-output-hk/cardano-crypto/blob/master/tests/goldens/cardano/crypto/wallet/BIP39-128 -START_TEST(test_ed25519_cardano_sign_vectors) -{ - ed25519_public_key public_key; - ed25519_secret_key secret_key; - ed25519_secret_key secret_key_extension; - ed25519_signature signature; - - static const char *vectors[] = { - "6065a956b1b34145c4416fdc3ba3276801850e91a77a31a7be782463288aea53", // private key - "60ba6e25b1a02157fb69c5d1d7b96c4619736e545447069a6a6f0ba90844bc8e", // private key extension - "64b20fa082b3143d6b5eed42c6ef63f99599d0888afe060620abc1b319935fe1", // public key - "45b1a75fe3119e13c6f60ab9ba674b42f946fdc558e07c83dfa0751c2eba69c79331bd8a4a975662b23628a438a0eba76367e44c12ca91b39ec59063f860f10d", // signature - - "e7d27516538403a53a8b041656a3f570909df641a0ab811fe7d87c9ba02a830c", // private key - "794a2c54ad8b525b781773c87d38cbf4197636bc427a9d551368286fe4c294a4", // private key extension - "95bb82ffd5707716bc65170ab4e8dafeed90fbe0ce9258713b7751e962d931df", // public key - "f2c9171782e7df7665126ac545ae53b05964b0160536efdb545e2460dbbec2b19ec6b338b8f1bf4dfee94360ed024b115e37b1d7e6f3f9ae4beb79539428560f", // signature - - "9b5a3d9a4c60bcd49bb64b72c082b164314d0f61d842f2575fd1d4fb30a28a0c", // private key - "b093e376f41eb7bf80abcd0073a52455d25b5d21815bc758e5f6f81536aedebb", // private key extension - "79fc8154554b97e4c56ef2f9dbb4c1421ff19509688931a1e964bda5dec0f19f", // public key - "2ba1439ae648a7e8da7c9ab1ee6da94fd4ebe37abd0978306e8fba2afa8f111a88a993dbf008bedae9167f4f68409e4c9ddaf02cba12418447b1848907ad800f", // signature - - "52e0c98aa600cfdcd1ff28fcda5227ed87063f4a98547a78b771052cf102b40c", // private key - "6c18d9f8075b1a6a1833540607479bd58b7beb8a83d2bb01ca7ae02452a25803", // private key extension - "dc907c7c06e6314eedd9e18c9f6c6f9cc4e205fb1c70da608234c319f1f7b0d6", // public key - "0cd34f84e0d2fcb1800bdb0e869b9041349955ced66aedbe6bda187ebe8d36a62a05b39647e92fcc42aa7a7368174240afba08b8c81f981a22f942d6bd781602", // signature - - "11fd6462a3a92b35c22703f6f1c124ddcf36b7c2b09cc2784f320e1cfa12ec04", // private key - "c2785803c61c46aeca192a1bb1b7b20a8c4cc7fa01db57fc5d1d8a5473402352", // private key extension - "839775a41876e328986aa26168958bba1176e67819b357eea84afceab8b1db78", // public key - "e41f73db2f8d2896a687802b2be76b7cabb73dfbb4891494883a0cbd9bbb9e5f9d3e14d2d0b06c6674333508496db660936737c0efd9511514147dac79fa4905", // signature - - "5b1e5cad02274ba461f4708d8598d3497faf8fe3e894a379573aa6ac3a03e505", // private key - "ba179d2e3c67aabb486c48d16002b51ad32eab434c738a1550962313b07098cd", // private key extension - "75eb8d197ec8627c85af88e66aa1e49065dd8ac98ed8991db52ece01635dfb76", // public key - "631015357cee3051116b4c2ff4d1c5beb13b6e5023635aa1eeb0563cadf0d4fbc10bd5e31b4a4220c67875558c41b5cc0328104ae39cc7ff20ff0c2bda598906", // signature - - "624b47150f58dfa44284fbc63c9f99b9b79f808c4955a461f0e2be44eb0be50d", // private key - "097aa006d694b165ef37cf23562e5967c96e49255d2f20faae478dee83aa5b02", // private key extension - "0588589cd9b51dfc028cf225674069cbe52e0e70deb02dc45b79b26ee3548b00", // public key - "1de1d275428ba9491a433cd473cd076c027f61e7a8b5391df9dea5cb4bc88d8a57b095906a30b13e68259851a8dd3f57b6f0ffa37a5d3ffc171240f2d404f901", // signature - - 0, 0, - }; - - const char **test_data; - test_data = vectors; - while (*test_data) { - memcpy(secret_key, fromhex(*test_data), 32); - MARK_SECRET_DATA(secret_key, sizeof(secret_key)); - - memcpy(secret_key_extension, fromhex(*(test_data + 1)), 32); - MARK_SECRET_DATA(secret_key_extension, sizeof(secret_key_extension)); - - ed25519_publickey_ext(secret_key, secret_key_extension, public_key); - UNMARK_SECRET_DATA(public_key, sizeof(public_key)); - - ck_assert_mem_eq(public_key, fromhex(*(test_data + 2)), 32); - - const uint8_t * message = (const uint8_t *) "Hello World"; - ed25519_sign_ext(message, 11, secret_key, secret_key_extension, public_key, signature); - UNMARK_SECRET_DATA(signature, sizeof(signature)); - - ck_assert_mem_eq(signature, fromhex(*(test_data + 3)), 64); - - UNMARK_SECRET_DATA(secret_key, sizeof(secret_key)); - UNMARK_SECRET_DATA(secret_key_extension, sizeof(secret_key_extension)); - - test_data += 4; - } + ck_assert_mem_eq(node.public_key + 1, fromhex("148605be54585773b44ba87e79265149ae444c4cc37cb1f8db8c08482fba293b"), 32); } END_TEST