From d625986cdb8b6e9e76d0e6808a2ec5d11b3818d5 Mon Sep 17 00:00:00 2001 From: Antony Vennard Date: Sat, 21 Dec 2019 20:31:15 +0000 Subject: [PATCH] Implement e4c_protect and e4c_unprotect for pubkey variant. Does not yet contain tests. --- include/e4/e4.h | 16 +- include/e4/internal/e4c_pk_store_file.h | 2 + mk/pubkey/objects.mk | 2 - mk/pubkey/tests.mk | 19 +- mk/rules.mk | 4 +- mk/symkey/tests.mk | 8 + src/e4c_pk_store_file.c | 45 ++++- src/e4pkcclient.c | 176 +++++++++++++++--- .../ed25519 => test/pubkey}/ed25519_test.c | 14 +- test/pubkey/pubkey_crypto_test.c | 25 --- 10 files changed, 242 insertions(+), 69 deletions(-) rename {src/crypto/ed25519 => test/pubkey}/ed25519_test.c (94%) diff --git a/include/e4/e4.h b/include/e4/e4.h index 8ab23e2..88e57cb 100644 --- a/include/e4/e4.h +++ b/include/e4/e4.h @@ -50,7 +50,12 @@ extern "C" { /* Unable to find public key for device;e4 */ #define E4_ERROR_DEVICEPK_MISSING -109 +/* Signature verification failed */ +#define E4_ERROR_PK_SIGVERIF_FAILED -110 + +/* Size of the timestamp field */ +#define E4_TS_LEN 8 /* Size of the ID, truncated sha3(alias) */ #define E4_ID_LEN 16 @@ -71,6 +76,10 @@ extern "C" { #define E4_PK_EDDSA_PRIVKEY_LEN 64 #define E4_PK_EDDSA_PUBKEY_LEN 32 #define E4_PK_EDDSA_SIG_LEN 32 +#define E4_PK_X25519_PUBKEY_LEN 32 +#define E4_PK_X25519_PRIVKEY_LEN 32 + +#define E4_PK_TOPICMSGHDR_LEN (E4_TAG_LEN + E4_TIMESTAMP_LEN + E4_ID_LEN) struct _e4storage; /* This structure represents storage-specific data to be passed to the e4c @@ -151,7 +160,6 @@ int e4c_set_storagelocation(e4storage *store, const char *path); int e4c_load(e4storage *store, const char *path); int e4c_sync(e4storage *store); int e4c_set_id(e4storage *store, const uint8_t *id); -int e4c_set_idkey(e4storage *store, const uint8_t *key); int e4c_is_device_ctrltopic(e4storage *store, const char *topic); int e4c_getindex(e4storage *store, const char *topic); int e4c_gettopickey(uint8_t *key, e4storage *store, const int index); @@ -159,13 +167,17 @@ int e4c_set_topic_key(e4storage *store, const uint8_t *topic_hash, const uint8_t int e4c_remove_topic(e4storage *store, const uint8_t *topic_hash); int e4c_reset_topics(e4storage *store); +int e4c_set_idkey(e4storage *store, const uint8_t *key); #ifdef E4_MODE_PUBKEY /* pubkey storage apis */ +int e4c_set_idpubkey(e4storage *store, const uint8_t *pubkey); int e4c_getdeviceindex(e4storage *store, const uint8_t* id); int e4c_getdevicekey(uint8_t* key, e4storage *store, const int index); int e4c_set_device_key(e4storage *store, const uint8_t *id, const uint8_t *key); -int e4c_remove_devices(e4storage* store, const uint8_t* id); +int e4c_remove_device(e4storage* store, const uint8_t* id); int e4c_reset_devices(e4storage* store); +int e4c_set_c2_pubkey(e4storage* store, const uint8_t* key); +int e4c_get_c2_pubkey(e4storage* store, uint8_t* key); #endif /*#ifdef DEBUG*/ diff --git a/include/e4/internal/e4c_pk_store_file.h b/include/e4/internal/e4c_pk_store_file.h index 546a199..a35ad32 100644 --- a/include/e4/internal/e4c_pk_store_file.h +++ b/include/e4/internal/e4c_pk_store_file.h @@ -29,6 +29,8 @@ struct _e4storage /* These fields are persisted by the sync command */ uint8_t id[E4_ID_LEN]; uint8_t privkey[E4_PK_EDDSA_PRIVKEY_LEN]; + uint8_t pubkey[E4_PK_EDDSA_PUBKEY_LEN]; + uint8_t c2key[E4_PK_X25519_PUBKEY_LEN]; uint16_t devicecount; uint16_t topiccount; diff --git a/mk/pubkey/objects.mk b/mk/pubkey/objects.mk index 25c9622..35046dc 100644 --- a/mk/pubkey/objects.mk +++ b/mk/pubkey/objects.mk @@ -10,10 +10,8 @@ OBJS = \ $(OBJDIR)/strlcpy.$O \ $(OBJDIR)/crypto/curve25519/curve25519-donna.$O \ $(OBJDIR)/crypto/ed25519/add_scalar.$O \ - $(OBJDIR)/crypto/ed25519/ed25519_test.$O \ $(OBJDIR)/crypto/ed25519/fe.$O \ $(OBJDIR)/crypto/ed25519/ge.$O \ - $(OBJDIR)/crypto/ed25519/key_exchange.$O \ $(OBJDIR)/crypto/ed25519/keypair.$O \ $(OBJDIR)/crypto/ed25519/sc.$O \ $(OBJDIR)/crypto/ed25519/seed.$O \ diff --git a/mk/pubkey/tests.mk b/mk/pubkey/tests.mk index de58874..76c51cc 100644 --- a/mk/pubkey/tests.mk +++ b/mk/pubkey/tests.mk @@ -15,6 +15,9 @@ $(TESTOBJDIR)/pubkey_file.$O: test/pubkey/pubkey_filestore_test.c $(TESTOBJDIR)/pubkey_crypto_test.$O: test/pubkey/pubkey_crypto_test.c $(CC) $(TESTCFLAGS) $(INCLUDES) -c $< -o $@ +$(TESTOBJDIR)/ed25519_test.$O: test/pubkey/ed25519_test.c + $(CC) $(TESTCFLAGS) $(INCLUDES) -c $< -o $@ + $(TESTDIR)/util: $(TESTOBJDIR)/util.$O $(CC) $(TESTLDFLAGS) $< $(LIB) -o $@ @@ -27,10 +30,24 @@ $(TESTDIR)/pubkey_file: $(TESTOBJDIR)/pubkey_file.$O $(TESTDIR)/pubkey_crypto_test: $(TESTOBJDIR)/pubkey_crypto_test.$O $(CC) $(TESTLDFLAGS) $< $(LIB) -o $@ +$(TESTDIR)/ed25519_test: $(TESTOBJDIR)/ed25519_test.$O + $(CC) $(TESTLDFLAGS) $< $(LIB) -o $@ + PUBKEY_TESTS = \ $(TESTDIR)/util \ $(TESTDIR)/crypto \ $(TESTDIR)/pubkey_file \ - $(TESTDIR)/pubkey_crypto_test + $(TESTDIR)/pubkey_crypto_test \ + $(TESTDIR)/ed25519_test E4TESTS += $(PUBKEY_TESTS) + +testexec_pk: + ./$(TESTDIR)/util + ./$(TESTDIR)/crypto + ./$(TESTDIR)/pubkey_file + ./$(TESTDIR)/pubkey_crypto_test + ./$(TESTDIR)/ed25519_test + +E4TESTEXEC += testexec_pk + diff --git a/mk/rules.mk b/mk/rules.mk index 4132a63..53470fa 100644 --- a/mk/rules.mk +++ b/mk/rules.mk @@ -27,7 +27,9 @@ clean: rm -rf $(LIBDIR) -test: clean setup lib $(E4TESTS) +testbuild: clean setup lib $(E4TESTS) + +test: testbuild $(E4TESTEXEC) format: clang-format -i src/*.c src/crypto/*.c include/e4/*.h include/e4/crypto/*.h include/e4/internal/*.h diff --git a/mk/symkey/tests.mk b/mk/symkey/tests.mk index a4875d9..6575a44 100644 --- a/mk/symkey/tests.mk +++ b/mk/symkey/tests.mk @@ -27,3 +27,11 @@ SYMKEY_TESTS = \ $(TESTDIR)/symkey_file E4TESTS += $(SYMKEY_TESTS) + +testexec_sk: + ./$(TESTDIR)/util + ./$(TESTDIR)/crypto + ./$(TESTDIR)/symkey_file + +E4TESTEXEC += testexec_sk + diff --git a/src/e4c_pk_store_file.c b/src/e4c_pk_store_file.c index 0eca9c8..e5b7001 100644 --- a/src/e4c_pk_store_file.c +++ b/src/e4c_pk_store_file.c @@ -111,12 +111,19 @@ int e4c_load(e4storage *store, const char *path) /* derive a topichash for the control topic. */ e4c_derive_topichash(store->ctrltopic, E4_TOPICHASH_LEN, controltopic); + /* read in key material */ rlen = read(fd, store->privkey, sizeof store->privkey); if (rlen != sizeof store->privkey) { goto err; } - + lseek(fd, 4, SEEK_CUR); + + rlen = read(fd, store->pubkey, sizeof store->pubkey); + if (rlen != sizeof store->pubkey) + { + goto err; + } lseek(fd, 4, SEEK_CUR); rlen = read(fd, &store->topiccount, sizeof store->topiccount); @@ -205,6 +212,8 @@ int e4c_sync(e4storage *store) write(fd, &zero, sizeof zero); write(fd, store->privkey, sizeof store->privkey); write(fd, &zero, sizeof zero); + write(fd, store->pubkey, sizeof store->pubkey); + write(fd, &zero, sizeof zero); write(fd, &store->topiccount, sizeof store->topiccount); write(fd, &zero, sizeof zero); @@ -370,6 +379,11 @@ int e4c_reset_topics(e4storage *store) return 0; } +int e4c_set_idpubkey(e4storage *store, const uint8_t *pubkey) { + memmove(store->pubkey, pubkey, sizeof store->pubkey); + return E4_RESULT_OK; +} + int e4c_getdeviceindex(e4storage *store, const uint8_t* id) { int i; @@ -421,7 +435,7 @@ int e4c_set_device_key(e4storage *store, const uint8_t *id, const uint8_t *pubke return e4c_sync(store); } -int e4c_remove_devices(e4storage* store, const uint8_t* id) +int e4c_remove_device(e4storage* store, const uint8_t* id) { int i, j; device_key *devicekeys = store->devices; @@ -462,6 +476,17 @@ int e4c_reset_devices(e4storage* store) return 0; } +int e4c_set_c2_pubkey(e4storage* store, const uint8_t* key) { + memcpy(store->c2key, key, E4_PK_X25519_PUBKEY_LEN); + e4c_sync(store); + return 0; +} + +int e4c_get_c2_pubkey(e4storage* store, uint8_t* key) { + memcpy(key, store->c2key, E4_PK_X25519_PUBKEY_LEN); + return 0; +} + /*#ifdef DEBUG */ void e4c_debug_print(e4storage *store) @@ -476,12 +501,24 @@ void e4c_debug_print(e4storage *store) printf("%02x", store->id[j]); } printf("\n"); - printf(" Key="); - for (j = 0; j < E4_KEY_LEN; j++) + printf(" PrivKey="); + for (j = 0; j < E4_PK_EDDSA_PRIVKEY_LEN; j++) + { + printf("%02x", store->privkey[j]); + } + printf("\n"); + printf(" PubKey="); + for (j = 0; j < E4_PK_EDDSA_PUBKEY_LEN; j++) { printf("%02x", store->privkey[j]); } printf("\n"); + printf(" C2PubKey="); + for (j = 0; j < E4_PK_X25519_PUBKEY_LEN; j++) + { + printf("%02x", store->c2key[j]); + } + printf("\n"); e4c_derive_control_topic(controltopic, E4_CTRLTOPIC_LEN + 1, store->id); printf(" ControlTopic=%s\n", controltopic); diff --git a/src/e4pkcclient.c b/src/e4pkcclient.c index 04ed633..46be4a0 100644 --- a/src/e4pkcclient.c +++ b/src/e4pkcclient.c @@ -49,8 +49,12 @@ int e4c_protect_message(uint8_t *cptr, size_t clen2 = 0; uint8_t key[E4_KEY_LEN]; uint64_t time_now = 0; + uint8_t* signaturep = NULL; + size_t signeddatalen=0; - if (mlen + E4_MSGHDR_LEN > cmax) /* actually: not enough space */ + /* pubkey messages are: Timestamp (8) | id (16) | IV (16) | Ciphertext (n) | sig (64) */ + + if (mlen + E4_PK_TOPICMSGHDR_LEN + E4_PK_EDDSA_SIG_LEN > cmax) /* actually: not enough space */ return E4_ERROR_CIPHERTEXT_TOO_SHORT; *clen = mlen + E4_MSGHDR_LEN; @@ -62,12 +66,8 @@ int e4c_protect_message(uint8_t *cptr, } else { - if (e4c_is_device_ctrltopic(storage, topic) != 0) - { - return E4_ERROR_TOPICKEY_MISSING; - } - /* control topic being used: */ - memcpy(key, storage->key, E4_KEY_LEN); + /* TODO: we do not implement sending back to the C2 at present */ + return E4_ERROR_TOPICKEY_MISSING; } #ifdef __AVR__ @@ -82,16 +82,36 @@ int e4c_protect_message(uint8_t *cptr, time_now >>= 8; } + /* set our ID in the output buffer + * if Avi is reading this code, hope you are enjoying this memcpy. + */ + memcpy(cptr + E4_TS_LEN, storage->id, E4_ID_LEN); + /* encrypt */ clen2 = 0; - aes256_encrypt_siv(cptr + 8, &clen2, cptr, 8, mptr, mlen, key); + aes256_encrypt_siv(cptr + E4_TS_LEN + E4_ID_LEN, &clen2, cptr, 8, mptr, mlen, key); + + /* sign the result */ + signeddatalen = E4_TS_LEN + E4_ID_LEN + E4_TAG_LEN + mlen; + signaturep = &cptr[0] + signeddatalen; + + /* + ed25519_sign(unsigned char *signature, + const unsigned char *message, + size_t message_len, + const unsigned char *public_key, + const unsigned char *private_key); + */ + ed25519_sign(signaturep, + cptr, + signeddatalen, + storage->pubkey, + storage->privkey); return E4_RESULT_OK; } - /* Unprotect message */ - int e4c_unprotect_message(uint8_t *mptr, size_t mmax, size_t *mlen, @@ -104,34 +124,111 @@ int e4c_unprotect_message(uint8_t *mptr, int i = 0, j = 0, r = 0; uint8_t key[E4_KEY_LEN]; uint64_t tstamp; + + uint8_t* assocdata; + size_t assocdatalen = 0; + size_t sivpayloadlen = 0; + #ifndef __AVR__ uint64_t secs1970; secs1970 = (uint64_t)time(NULL); /* this system has a RTC */ #endif - /* bounds checking */ + /* There are two possible message formats: - if (clen < E4_MSGHDR_LEN || mmax < clen - E4_MSGHDR_LEN) - { - return E4_ERROR_CIPHERTEXT_TOO_SHORT; - } + From other clients: Timestamp (8) | id (16) | IV (16) | Ciphertext (n) | sig (64) + From the C2: Timestamp (8) | IV (16) | Ciphertext (n) + */ - /* get the key */ - i = e4c_getindex(storage, topic); - if (i >= 0) + assocdata = cptr; + + if (e4c_is_device_ctrltopic(storage, topic) == 0) { - e4c_gettopickey(key, storage, i); + uint8_t c2pk[E4_PK_X25519_PUBKEY_LEN]; + uint8_t devicesk[E4_PK_X25519_PUBKEY_LEN]; + uint8_t sharedpoint[E4_PK_X25519_PUBKEY_LEN]; + /* control topic being used: */ + control = 1; + + /* bounds checking */ + if (clen < E4_MSGHDR_LEN || mmax < clen - E4_MSGHDR_LEN) + { + return E4_ERROR_CIPHERTEXT_TOO_SHORT; + } + + e4c_get_c2_pubkey(store, c2pk); + + /* convert our key to X25519 */ + xed25519_convert_ed2c_private(devicesk, store->privkey); + + /* key=sha3(X25519(c2, device)) */ + + curve25519(sharedpoint, devicesk, c2pk); + sha3(sharedpoint, sizeof sharedpoint, key, sizeof key); + + /* set things up for symmetric decryption: */ + /* From the C2: Timestamp (8) | IV (16) | Ciphertext (n) */ + assocdatalen = E4_TS_LEN; + sivpayloadlen = clen - E4_TS_LEN; } else { - if (e4c_is_device_ctrltopic(storage, topic) != 0) + int i; + uint8_t sender_pk[E4_PK_EDDSA_PUBKEY_LEN]; + uint8_t* idptr = cptr[8]; + + control = 0; + + /* bounds checking */ + if (clen < E4_PKTOPICMSGHDR_LEN+E4_PK_EDDSA_SIG_LEN || + mmax < clen - (E4_PKTOPICMSGHDR_LEN+E4_PK_EDDSA_SIG_LEN)) + { + return E4_ERROR_CIPHERTEXT_TOO_SHORT; + } + + i = e4c_getdeviceindex(store, idptr); + if (i >= 0) + { + e4c_getdevicekey(sender_pk, store, i); + } + else + { + /* TODO: policies to not validate sigs if key not found + due to storage constraints. + + NOTE: this MUST NOT be conflated with signature verif + failure. If signatures fail to verify we will _always_ + reject the message. + */ + + return E4_ERROR_DEVICEPK_MISSING; + } + + /* check signature attached to end */ + signverifresult = ed25519_verify(cptr[clen-E4_PK_EDDSA_SIG_LEN], + cptr, clen-E4_PK_EDDSA_SIG_LEN, sender_pk); + + if (signverifresult != 1) + { + return E4_ERROR_PK_SIGVERIF_FAILED; + } + + /* find the topic key and set it */ + i = e4c_getindex(storage, topic); + if (i >= 0) + { + e4c_gettopickey(key, storage, i); + } + else { return E4_ERROR_TOPICKEY_MISSING; } - /* control topic being used: */ - memcpy(key, storage->key, E4_KEY_LEN); - control = 1; + + /* set things up for symmetric decryption: */ + /* From other clients: Timestamp (8) | id (16) | IV (16) | Ciphertext (n) | sig (64) */ + assocdatalen = E4_ID_LEN + E4_TS_LEN; + sivpayloadlen = clen - (E4_TS_LEN + E4_ID_LEN + E4_PK_EDDSA_SIG_LEN); } /* Retrieve timestamp encoded as little endian */ @@ -142,13 +239,23 @@ int e4c_unprotect_message(uint8_t *mptr, tstamp += (uint64_t)cptr[j]; } - /* decrypt */ - if (aes256_decrypt_siv(mptr, mlen, cptr, 8, cptr + 8, clen - 8, key) != 0) + /* decrypt + int aes256_decrypt_siv(uint8_t *pt, + size_t *ptlen, /-* out: plaintext *-/ + const uint8_t *ad, + size_t adlen, /-* in: associated data / nonce *-/ + const uint8_t *ct, + size_t ctlen, /-* in: ciphertext *-/ + const uint8_t *key); /-* in: secret key (32 bytes) *-/ + */ + + if (aes256_decrypt_siv(mptr, mlen, assocdata, assocdatalen, + cptr + assocdatalen, sivpayloadlen, key) != 0) { return E4_ERROR_INVALID_TAG; } - /* TODO: this is only valuable for string-type data + /* TODO: this is only valuable for string-type data * we should consider removing it, as it requires that * the plaintext buffer be 1 byte bigger than that which was * encrypted, which is very unnecessary. */ @@ -161,8 +268,8 @@ int e4c_unprotect_message(uint8_t *mptr, if (secs1970 < 946684800) { /* calibrate message if this is a control message */ - if (control) { - secs1970 = tstamp; + if (control) { + secs1970 = tstamp; } } else @@ -212,6 +319,19 @@ int e4c_unprotect_message(uint8_t *mptr, return E4_ERROR_INVALID_COMMAND; r = e4c_set_topic_key(storage, (const uint8_t *)mptr + E4_KEY_LEN + 1, mptr + 1); return r == 0 ? E4_RESULT_OK_CONTROL : r; + case 0x04: /* RemovePubKey(id) */ + if (*mlen != (1 + E4_ID_LEN)) + r = e4c_remove_devices(storage, mptr+1); + return r == 0 ? E4_RESULT_OK_CONTROL : r; + case 0x05: /* RemovePubKeys() */ + if (*mlen != 1) return E4_ERROR_INVALID_COMMAND; + r = e4c_reset_devices(storage); + return r == 0 ? E4_RESULT_OK_CONTROL : r; + case 0x06: /* SetPubKey(pubkey, id) */ + if (*mlen != (1 + E4_ID_LEN + E4_PK_EDDSA_PUBKEY_LEN)) + return E4_ERROR_INVALID_COMMAND; + r = e4c_set_device_key(storage, mptr+1+E4_PK_EDDSA_PUBKEY_LEN, mptr+1); + return r == 0 ? E4_RESULT_OK_CONTROL : r; } return E4_ERROR_INVALID_COMMAND; diff --git a/src/crypto/ed25519/ed25519_test.c b/test/pubkey/ed25519_test.c similarity index 94% rename from src/crypto/ed25519/ed25519_test.c rename to test/pubkey/ed25519_test.c index 845eb04..7e1f04d 100644 --- a/src/crypto/ed25519/ed25519_test.c +++ b/test/pubkey/ed25519_test.c @@ -5,14 +5,14 @@ #include "e4/crypto/ed25519.h" -#include "ge.h" -#include "sc.h" +#include "../../src/crypto/ed25519/ge.h" +#include "../../src/crypto/ed25519/sc.h" int main() { unsigned char public_key[32], private_key[64], seed[32], scalar[32]; unsigned char other_public_key[32], other_private_key[64]; - unsigned char shared_secret[32], other_shared_secret[32]; + /*unsigned char shared_secret[32], other_shared_secret[32];*/ unsigned char signature[64]; clock_t start; @@ -64,7 +64,8 @@ int main() { ed25519_create_seed(seed); ed25519_create_keypair(other_public_key, other_private_key, seed); - /* create two shared secrets - from both perspectives - and check if they're equal */ + /* + /-* create two shared secrets - from both perspectives - and check if they're equal *-/ ed25519_key_exchange(shared_secret, other_public_key, private_key); ed25519_key_exchange(other_shared_secret, public_key, other_private_key); @@ -77,7 +78,7 @@ int main() { if (i == 32) { printf("key exchange was correct\n"); - } + }*/ /* test performance */ printf("testing seed generation performance: "); @@ -136,6 +137,7 @@ int main() { printf("%fus per key\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); + /* printf("testing key exchange performance: "); start = clock(); for (i = 0; i < 10000; ++i) { @@ -144,6 +146,6 @@ int main() { end = clock(); printf("%fus per shared secret\n", ((double) ((end - start) * 1000)) / CLOCKS_PER_SEC / i * 1000); - + */ return 0; } diff --git a/test/pubkey/pubkey_crypto_test.c b/test/pubkey/pubkey_crypto_test.c index 4b3e2b8..c0a2bd8 100644 --- a/test/pubkey/pubkey_crypto_test.c +++ b/test/pubkey/pubkey_crypto_test.c @@ -22,31 +22,6 @@ void printhex(const uint8_t* buffer, size_t len) int main(int argc, char** argv) { - /*uint8_t shared_point[32] = {0}; - uint8_t shared_secret[32] = {0}; - - ed25519_key_exchange((unsigned char*) shared_point, DEVEDWARDS_PUBKEY_1, C2EDWARDS_SECKEY_1); - - sha3(shared_point, sizeof shared_point, shared_secret, sizeof shared_secret); - if (sizeof shared_secret != sizeof DEVSHAREDKEY_1) { - printf("ed25519_kex shared secret size mismatch"); - return 1; - } - - if (memcmp(shared_secret, DEVSHAREDKEY_1, 32) != 0) { - printf("ed25519_kex failed.\n"); - printhex(shared_secret, sizeof shared_secret); - printf("\n"); - printhex(DEVSHAREDKEY_1, sizeof DEVSHAREDKEY_1); - printf("\n"); - printhex(C2SHAREDKEY_1, sizeof DEVSHAREDKEY_1); - printf("\n"); - - //return 1; - } else { - printf("ed25519_kex: OK.\n"); - }*/ - // point conversion tests: uint8_t c2_c255_private[32] = {0};