From f95e41374b3532f369a5a90eff607f54c1966851 Mon Sep 17 00:00:00 2001 From: Andrew Bettison Date: Wed, 4 Sep 2013 23:47:17 +0930 Subject: [PATCH] Implement keyring load command, not working yet Write new keyring test, fails with ERROR because assertGrep -F option is not supported. --- keyring.c | 239 ++++++++++++++++++++++++++------------------------ serval.h | 2 +- tests/keyring | 48 ++++++++-- 3 files changed, 166 insertions(+), 123 deletions(-) diff --git a/keyring.c b/keyring.c index 4264ca49..0f651318 100644 --- a/keyring.c +++ b/keyring.c @@ -173,6 +173,20 @@ keyring_file *keyring_open(const char *path, int writeable) return k; } +static void add_subscriber(keyring_identity *id, unsigned keypair) +{ + assert(keypair < id->keypair_count); + assert(id->keypairs[keypair]->type == KEYTYPE_CRYPTOBOX); + id->subscriber = find_subscriber(id->keypairs[keypair]->public_key, SID_SIZE, 1); + if (id->subscriber) { + if (id->subscriber->reachable == REACHABLE_NONE) + id->subscriber->reachable = REACHABLE_SELF; + id->subscriber->identity = id; + if (!my_subscriber) + my_subscriber = id->subscriber; + } +} + void keyring_free(keyring_file *k) { int i; @@ -394,12 +408,32 @@ struct keytype { size_t public_key_size; size_t private_key_size; size_t packed_size; + void (*creator)(keypair *); int (*packer)(const keypair *, struct rotbuf *); int (*unpacker)(keypair *, struct rotbuf *); void (*dumper)(const keypair *, XPRINTF, int); int (*loader)(keypair *, const char *); }; +static void create_cryptobox(keypair *kp) +{ + /* Filter out public keys that start with 0x0, as they are reserved for address + abbreviation. */ + do { + crypto_box_curve25519xsalsa20poly1305_keypair(kp->public_key, kp->private_key); + } while (kp->public_key[0] < 0x10); +} + +static void create_cryptosign(keypair *kp) +{ + crypto_sign_edwards25519sha512batch_keypair(kp->public_key, kp->private_key); +} + +static void create_rhizome(keypair *kp) +{ + urandombytes(kp->private_key, kp->private_key_len); +} + static int pack_private_only(const keypair *kp, struct rotbuf *rb) { rotbuf_putbuf(rb, kp->private_key, kp->private_key_len); @@ -571,6 +605,7 @@ const struct keytype keytypes[] = { .private_key_size = crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES, .public_key_size = crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES, .packed_size = crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES, + .creator = create_cryptobox, .packer = pack_private_only, .unpacker = unpack_private_derive_scalarmult_public, .dumper = dump_raw_hex, @@ -585,6 +620,7 @@ const struct keytype keytypes[] = { .private_key_size = crypto_sign_edwards25519sha512batch_SECRETKEYBYTES, .public_key_size = crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES, .packed_size = crypto_sign_edwards25519sha512batch_SECRETKEYBYTES + crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES, + .creator = create_cryptosign, .packer = pack_private_public, .unpacker = unpack_private_public, .dumper = dump_raw_hex, @@ -597,6 +633,7 @@ const struct keytype keytypes[] = { .private_key_size = 32, .public_key_size = 0, .packed_size = 32, + .creator = create_rhizome, .packer = pack_private_only, .unpacker = unpack_private_only, .dumper = dump_raw_hex, @@ -609,6 +646,7 @@ const struct keytype keytypes[] = { .private_key_size = 32, .public_key_size = 64, .packed_size = 32 + 64, + .creator = NULL, // not included in a newly created identity .packer = pack_did_name, .unpacker = unpack_did_name, .dumper = dump_did_name, @@ -644,8 +682,8 @@ static keypair *keyring_alloc_keypair(unsigned ktype, size_t len) kp->private_key_len = len; kp->public_key_len = 0; } - if ( (kp->private_key_len && (kp->private_key = emalloc_zero(kp->private_key_len)) == NULL) - || (kp->public_key_len && (kp->public_key = emalloc_zero(kp->public_key_len)) == NULL) + if ( (kp->private_key_len && (kp->private_key = emalloc(kp->private_key_len)) == NULL) + || (kp->public_key_len && (kp->public_key = emalloc(kp->public_key_len)) == NULL) ) { keyring_free_keypair(kp); return NULL; @@ -972,15 +1010,8 @@ int keyring_decrypt_pkr(keyring_file *k, keyring_context *c, const char *pin, in // add any unlocked subscribers to our memory table, flagged as local sid's int i=0; for (i=0;ikeypair_count;i++){ - if (id->keypairs[i]->type == KEYTYPE_CRYPTOBOX){ - id->subscriber = find_subscriber(id->keypairs[i]->public_key, SID_SIZE, 1); - if (id->subscriber){ - if (id->subscriber->reachable==REACHABLE_NONE) - id->subscriber->reachable=REACHABLE_SELF; - id->subscriber->identity = id; - if (!my_subscriber) - my_subscriber=id->subscriber; - } + if (id->keypairs[i]->type == KEYTYPE_CRYPTOBOX) { + add_subscriber(id, i); // only one key per identity supported break; } @@ -1080,6 +1111,18 @@ static unsigned find_free_slot(keyring_file *k) return 0; } +static void keyring_commit_identity(keyring_file *k, keyring_context *cx, keyring_identity *id) +{ + set_slot(k, id->slot, 1); + cx->identities[cx->identity_count++] = id; + unsigned keypair_sid; + for (keypair_sid = 0; keypair_sid < id->keypair_count; ++keypair_sid) + if (id->keypairs[keypair_sid]->type == KEYTYPE_CRYPTOBOX) + break; + if (keypair_sid < id->keypair_count) + add_subscriber(id, keypair_sid); +} + /* Create a new identity in the specified context (which supplies the keyring pin) with the specified PKR pin. The crypto_box and crypto_sign key pairs are automatically created, and the PKR @@ -1115,97 +1158,28 @@ keyring_identity *keyring_create_identity(keyring_file *k, keyring_context *c, c } /* Allocate key pairs */ - - /* crypto_box key pair */ - id->keypairs[0] = emalloc_zero(sizeof(keypair)); - if (!id->keypairs[0]) { - WHY("malloc failed preparing first key pair storage"); - goto kci_safeexit; - } - id->keypair_count=1; - id->keypairs[0]->type=KEYTYPE_CRYPTOBOX; - id->keypairs[0]->private_key_len=crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES; - id->keypairs[0]->private_key = emalloc(id->keypairs[0]->private_key_len); - if (!id->keypairs[0]->private_key) { - WHY("malloc failed preparing first private key storage"); - goto kci_safeexit; - } - id->keypairs[0]->public_key_len=crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES; - id->keypairs[0]->public_key = emalloc(id->keypairs[0]->public_key_len); - if (!id->keypairs[0]->public_key) { - WHY("malloc failed preparing first public key storage"); - goto kci_safeexit; - } - /* Filter out public keys that start with 0x0, as they are reserved for address - abbreviation. */ - id->keypairs[0]->public_key[0]=0; - while(id->keypairs[0]->public_key[0]<0x10) - crypto_box_curve25519xsalsa20poly1305_keypair(id->keypairs[0]->public_key, - id->keypairs[0]->private_key); - - /* crypto_sign key pair */ - id->keypairs[1] = emalloc_zero(sizeof(keypair)); - if (!id->keypairs[1]) { - WHY("malloc failed preparing second key pair storage"); - goto kci_safeexit; - } - id->keypair_count=2; - id->keypairs[1]->type=KEYTYPE_CRYPTOSIGN; - id->keypairs[1]->private_key_len=crypto_sign_edwards25519sha512batch_SECRETKEYBYTES; - id->keypairs[1]->private_key = emalloc(id->keypairs[1]->private_key_len); - if (!id->keypairs[1]->private_key) { - WHY("malloc failed preparing second private key storage"); - goto kci_safeexit; - } - id->keypairs[1]->public_key_len=crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES; - id->keypairs[1]->public_key = emalloc(id->keypairs[1]->public_key_len); - if (!id->keypairs[1]->public_key) { - WHY("malloc failed preparing second public key storage"); - goto kci_safeexit; + unsigned ktype; + for (ktype = 1; ktype < NELS(keytypes); ++ktype) { + if (keytypes[ktype].creator) { + keypair *kp = id->keypairs[id->keypair_count] = keyring_alloc_keypair(ktype, 0); + if (kp == NULL) + goto kci_safeexit; + keytypes[ktype].creator(kp); + ++id->keypair_count; + } } - crypto_sign_edwards25519sha512batch_keypair(id->keypairs[1]->public_key, - id->keypairs[1]->private_key); - - /* Rhizome Secret (for protecting Bundle Private Keys) */ - id->keypairs[2] = emalloc_zero(sizeof(keypair)); - if (!id->keypairs[2]) { - WHY("malloc failed preparing second key pair storage"); - goto kci_safeexit; - } - id->keypair_count=3; - id->keypairs[2]->type=KEYTYPE_RHIZOME; - id->keypairs[2]->private_key_len=32; - id->keypairs[2]->private_key = emalloc(id->keypairs[2]->private_key_len); - if (!id->keypairs[2]->private_key) { - WHY("malloc failed preparing second private key storage"); - goto kci_safeexit; - } - id->keypairs[2]->public_key_len=0; - id->keypairs[2]->public_key=NULL; - urandombytes(id->keypairs[2]->private_key,id->keypairs[2]->private_key_len); - - /* Mark slot in use */ - set_slot(k, id->slot, 1); - - /* Add identity to data structure */ - c->identities[c->identity_count++]=id; - - // add new identity to in memory table - id->subscriber = find_subscriber(id->keypairs[0]->public_key, SID_SIZE, 1); - if (id->subscriber) { - if (id->subscriber->reachable==REACHABLE_NONE) - id->subscriber->reachable=REACHABLE_SELF; - id->subscriber->identity = id; - if (!my_subscriber) - my_subscriber=id->subscriber; - } + /* Mark slot as occupied and internalise new identity. */ + assert(id->keypair_count > 0); + assert(id->keypairs[0]->type == KEYTYPE_CRYPTOBOX); + keyring_commit_identity(k, c, id); /* Everything went fine */ return id; kci_safeexit: - if (id) keyring_free_identity(id); + if (id) + keyring_free_identity(id); return NULL; } @@ -1296,7 +1270,7 @@ int keyring_set_did(keyring_identity *id, const char *did, const char *name) return WHY("Too many key pairs"); /* allocate if needed */ if (i >= id->keypair_count) { - if ((id->keypairs[i] = keyring_alloc_keypair(KEYTYPE_DID, keytypes[KEYTYPE_DID].packed_size)) == NULL) + if ((id->keypairs[i] = keyring_alloc_keypair(KEYTYPE_DID, 0)) == NULL) return -1; ++id->keypair_count; if (config.debug.keyring) @@ -1794,47 +1768,78 @@ int keyring_dump(keyring_file *k, XPRINTF xpf, int include_secret) int keyring_load(keyring_file *k, int cn, unsigned pinc, const char **pinv, FILE *input) { + assert(cn < k->context_count); + keyring_context *cx = k->contexts[cn]; clearerr(input); - while (1) { - unsigned id; + char line[1024]; + unsigned pini = 0; + keyring_identity *id = NULL; + unsigned last_idn = 0; + while (fgets(line, sizeof line - 1, input) != NULL) { + // Strip trailing \n or CRLF + size_t linelen = strlen(line); + if (linelen && line[linelen - 1] == '\n') { + line[--linelen] = '\0'; + if (linelen && line[linelen - 1] == '\r') + line[--linelen] = '\0'; + } else + return WHY("line too long"); + unsigned idn; unsigned ktype; - char ktypestr[100]; - char content[1024]; - content[0] = '\0'; - int n = fscanf(input, "%u: type=%u(%99[^)]) ", &id, &ktype, ktypestr); + int i, j; + int n = sscanf(line, "%u: type=%u (%n%*[^)]%n)", &idn, &ktype, &i, &j); if (n == EOF && (ferror(input) || feof(input))) break; - if (n != 3) - return WHY("malformed input"); + if (n != 2) + return WHYF("malformed input n=%u", n); if (ktype == 0) - return WHY("invalid input: key type = 0"); - fgets(content, sizeof content - 1, input); - // Strip trailing \n or CRLF - size_t contentlen = strlen(content); - if (contentlen && content[contentlen - 1] == '\n') { - content[--contentlen] = '\0'; - if (contentlen && content[contentlen - 1] == '\r') - content[--contentlen] = '\0'; - } - //DEBUGF("n=%d content=%s", n, alloca_str_toprint(content)); + return WHY("invalid input: ktype=0"); + const char *ktypestr = &line[i]; + line[j] = '\0'; + const char *content = &line[j + 1]; + //DEBUGF("n=%d i=%u ktypestr=%s j=%u content=%s", n, i, alloca_str_toprint(ktypestr), j, alloca_str_toprint(content)); keypair *kp = keyring_alloc_keypair(ktype, 0); if (kp == NULL) return -1; int (*loader)(keypair *, const char *) = load_raw_hex; if (strcmp(ktypestr, "unknown") != 0 && ktype < NELS(keytypes)) loader = keytypes[ktype].loader; - if (loader(kp, content) == -1) + if (loader(kp, content) == -1) { + keyring_free_keypair(kp); return -1; - /* + } + if (id == NULL || idn != last_idn) { + last_idn = idn; + // TODO: do not commit any identities until entire dump is parsed + // TODO: discard duplicate identities + if (id) + keyring_commit_identity(k, cx, id); + if ((id = emalloc_zero(sizeof(keyring_identity))) == NULL) { + keyring_free_keypair(kp); + return -1; + } + if ((id->PKRPin = str_edup(pini < pinc ? pinv[pini++] : "")) == NULL) { + keyring_free_keypair(kp); + keyring_free_identity(id); + return -1; + } + /* Find free slot in keyring. */ + if ((id->slot = find_free_slot(k)) == 0) { + keyring_free_keypair(kp); + keyring_free_identity(id); + return WHY("no free slot"); + } + } if (!keyring_identity_add_keypair(id, kp)) keyring_free_keypair(kp); - */ /* fprintf(stderr, "%u: ", id); keyring_dump_keypair(kp, XPRINTF_STDIO(stderr), 1); fprintf(stderr, "\n"); */ } + if (id) + keyring_commit_identity(k, cx, id); if (ferror(input)) return WHYF_perror("fscanf"); return 0; diff --git a/serval.h b/serval.h index f457ddd6..268802dd 100644 --- a/serval.h +++ b/serval.h @@ -255,7 +255,7 @@ void keyring_free_context(keyring_context *c); void keyring_free_identity(keyring_identity *id); int keyring_identity_mac(keyring_context *c,keyring_identity *id, unsigned char *pkrsalt,unsigned char *mac); -#define KEYTYPE_CRYPTOBOX 0x01 +#define KEYTYPE_CRYPTOBOX 0x01 // must be lowest #define KEYTYPE_CRYPTOSIGN 0x02 #define KEYTYPE_RHIZOME 0x03 /* DIDs aren't really keys, but the keyring is a real handy place to keep them, diff --git a/tests/keyring b/tests/keyring index 09032faf..d9729042 100755 --- a/tests/keyring +++ b/tests/keyring @@ -25,11 +25,19 @@ shopt -s extglob setup() { setup_servald - executeOk_servald config \ - set log.console.level debug \ - set debug.keyring on - executeOk_servald keyring list - assert_keyring_list 0 + setup_instances +A + set_instance +A +} + +setup_instances() { + for arg; do + set_instance $arg + executeOk_servald config \ + set log.console.level debug \ + set debug.keyring on + executeOk_servald keyring list + assert_keyring_list 0 + done } assert_keyring_list() { @@ -138,6 +146,36 @@ teardown_KeyringAutoCreate() { report_servald_server } +doc_Load="Load keyring entries from a keyring dump" +setup_Load() { + setup_servald + setup_instances +A +B + set_instance +A + executeOk_servald keyring add '' + executeOk_servald keyring add '' + executeOk_servald keyring dump dA + set_instance +B + executeOk_servald keyring add '' + executeOk_servald keyring dump dB + set_instance +A + tfw_cat dA dB + assert ! cmp dA dB +} +test_Load() { + set_instance +B + executeOk_servald keyring load dA + tfw_cat --stderr + executeOk_servald keyring dump dBA + tfw_cat dBA + while read line; do + tfw_log -F "${line#[0-9]}" dBA + assertGrep -- -F "${line#[0-9]}" dBA + done < dA + while read line; do + assertGrep -- -F "${line#[0-9]}" dBA + done < dB +} + doc_CompatibleBack1="Can read old keyring file (1)" setup_CompatibleBack1() { setup_servald