diff --git a/bip32.c b/bip32.c index e58755071..7c3417c91 100644 --- a/bip32.c +++ b/bip32.c @@ -25,6 +25,7 @@ #include #include +#include "aes.h" #include "address.h" #include "bignum.h" #include "hmac.h" @@ -476,6 +477,65 @@ int hdnode_get_nem_shared_key(const HDNode *node, const ed25519_public_key peer_ keccak_256(shared_key, 32, shared_key); return 1; } + +int hdnode_nem_encrypt(const HDNode *node, const ed25519_public_key public_key, uint8_t *iv, const uint8_t *salt, const uint8_t *payload, size_t size, uint8_t *buffer) { + uint8_t last_block[AES_BLOCK_SIZE]; + uint8_t remainder = size % AES_BLOCK_SIZE; + + // Round down to last whole block + size -= remainder; + // Copy old last block + memcpy(last_block, &payload[size], remainder); + // Pad new last block with number of missing bytes + memset(&last_block[remainder], AES_BLOCK_SIZE - remainder, AES_BLOCK_SIZE - remainder); + + uint8_t shared_key[SHA3_256_DIGEST_LENGTH]; + if (!hdnode_get_nem_shared_key(node, public_key, salt, NULL, shared_key)) { + return 0; + } + + aes_encrypt_ctx ctx; + + int ret = aes_encrypt_key256(shared_key, &ctx); + MEMSET_BZERO(shared_key, sizeof(shared_key)); + + if (ret != EXIT_SUCCESS) { + return 0; + } + + if (aes_cbc_encrypt(payload, buffer, size, iv, &ctx) != EXIT_SUCCESS) { + return 0; + } + + if (aes_cbc_encrypt(last_block, &buffer[size], sizeof(last_block), iv, &ctx) != EXIT_SUCCESS) { + return 0; + } + + return 1; +} + +int hdnode_nem_decrypt(const HDNode *node, const ed25519_public_key public_key, uint8_t *iv, const uint8_t *salt, const uint8_t *payload, size_t size, uint8_t *buffer) { + uint8_t shared_key[SHA3_256_DIGEST_LENGTH]; + + if (!hdnode_get_nem_shared_key(node, public_key, salt, NULL, shared_key)) { + return 0; + } + + aes_decrypt_ctx ctx; + + int ret = aes_decrypt_key256(shared_key, &ctx); + MEMSET_BZERO(shared_key, sizeof(shared_key)); + + if (ret != EXIT_SUCCESS) { + return 0; + } + + if (aes_cbc_decrypt(payload, buffer, size, iv, &ctx) != EXIT_SUCCESS) { + return 0; + } + + return 1; +} #endif // msg is a data to be signed diff --git a/bip32.h b/bip32.h index 3499fdc75..043c50c58 100644 --- a/bip32.h +++ b/bip32.h @@ -76,6 +76,8 @@ int hdnode_get_ethereum_pubkeyhash(const HDNode *node, uint8_t *pubkeyhash); #if USE_NEM int hdnode_get_nem_address(HDNode *node, uint8_t version, char *address); int hdnode_get_nem_shared_key(const HDNode *node, const ed25519_public_key peer_public_key, const uint8_t *salt, ed25519_public_key mul, uint8_t *shared_key); +int hdnode_nem_encrypt(const HDNode *node, const ed25519_public_key public_key, uint8_t *iv, const uint8_t *salt, const uint8_t *payload, size_t size, uint8_t *buffer); +int hdnode_nem_decrypt(const HDNode *node, const ed25519_public_key public_key, uint8_t *iv, const uint8_t *salt, const uint8_t *payload, size_t size, uint8_t *buffer); #endif int hdnode_sign(HDNode *node, const uint8_t *msg, uint32_t msg_len, uint8_t *sig, uint8_t *pby, int (*is_canonical)(uint8_t by, uint8_t sig[64])); diff --git a/nem.h b/nem.h index 2e3de9480..f4b181a00 100644 --- a/nem.h +++ b/nem.h @@ -49,6 +49,14 @@ #define NEM_TRANSACTION_TYPE_MOSAIC_CREATION 0x4001 #define NEM_TRANSACTION_TYPE_MOSAIC_SUPPLY_CHANGE 0x4002 +#define NEM_SALT_SIZE sizeof(ed25519_public_key) + +#define NEM_ENCRYPTED_SIZE(size) (((size) + AES_BLOCK_SIZE - 1) / AES_BLOCK_SIZE * AES_BLOCK_SIZE) +#define NEM_ENCRYPTED_PAYLOAD_SIZE(size) (AES_BLOCK_SIZE + NEM_SALT_SIZE + NEM_ENCRYPTED_SIZE(size)) + +#define NEM_DECRYPTED_SIZE(buffer, size) ((size) - ((buffer)[(size) - 1])) +#define NEM_DECRYPTED_PAYLOAD_SIZE(buffer, size) NEM_DECRYPTED_SIZE((buffer), (size) - AES_BLOCK_SIZE - NEM_SALT_SIZE) + typedef struct { ed25519_public_key public_key; uint8_t *buffer; diff --git a/tools/nem_test_vectors.rb b/tools/nem_test_vectors.rb index 8d43239cd..9bb487927 100755 --- a/tools/nem_test_vectors.rb +++ b/tools/nem_test_vectors.rb @@ -59,6 +59,9 @@ end def load_data_line(line) abort 'Line does not begin with colon' unless line.slice!(0) == ':' + line.strip! + line.chomp!(',') + values = line.split(':').each(&:strip!) values.pop if values.last.empty?