Merge branch 'new-keyring' into 'development'

This commit is contained in:
Andrew Bettison 2013-04-30 17:57:59 +09:30
commit 4e48725c93
16 changed files with 879 additions and 308 deletions

View File

@ -31,7 +31,8 @@ MONITORCLIENTSRCS=conf.c \
net.c \
str.c \
strbuf.c \
strbuf_helpers.c
strbuf_helpers.c \
rotbuf.c
MDPCLIENTSRCS=conf.c \
conf_om.c \
@ -47,7 +48,8 @@ MDPCLIENTSRCS=conf.c \
net.c \
str.c \
strbuf.c \
strbuf_helpers.c
strbuf_helpers.c \
rotbuf.c
OBJS= $(SRCS:.c=.o)
SERVAL_OBJS= $(SERVAL_SOURCES:.c=.o)

5
cli.c
View File

@ -322,6 +322,11 @@ int cli_lookup_did(const char *text)
return text[0] == '\0' || strcmp(text, "*") == 0 || str_is_did(text);
}
int cli_path_regular(const char *arg)
{
return arg[0] != '\0' && arg[strlen(arg) - 1] != '/';
}
int cli_absolute_path(const char *arg)
{
return arg[0] == '/' && arg[1] != '\0';

1
cli.h
View File

@ -63,6 +63,7 @@ int _cli_arg(struct __sourceloc __whence, const struct cli_parsed *parsed, char
#define cli_arg(parsed, label, dst, validator, defaultvalue) _cli_arg(__WHENCE__, parsed, label, dst, validator, defaultvalue)
int cli_lookup_did(const char *text);
int cli_path_regular(const char *arg);
int cli_absolute_path(const char *arg);
int cli_optional_sid(const char *arg);
int cli_optional_bundle_key(const char *arg);

View File

@ -1767,6 +1767,26 @@ int app_keyring_create(const struct cli_parsed *parsed, void *context)
return 0;
}
int app_keyring_dump(const struct cli_parsed *parsed, void *context)
{
if (config.debug.verbose)
DEBUG_cli_parsed(parsed);
const char *path;
if (cli_arg(parsed, "file", &path, cli_path_regular, NULL) == -1)
return -1;
int include_secret = 0 == cli_arg(parsed, "--secret", NULL, NULL, NULL);
keyring_file *k = keyring_open_instance_cli(parsed);
if (!k)
return -1;
FILE *fp = path ? fopen(path, "w") : stdout;
if (fp == NULL)
return WHYF_perror("fopen(%s, \"w\")", alloca_str_toprint(path));
int ret = keyring_dump(k, XPRINTF_STDIO(fp), include_secret);
if (fp != stdout && fclose(fp) == EOF)
return WHYF_perror("fclose(%s)", alloca_str_toprint(path));
return ret;
}
int app_keyring_list(const struct cli_parsed *parsed, void *context)
{
if (config.debug.verbose)
@ -2420,12 +2440,14 @@ struct cli_schema command_line_options[]={
"Remove stale and orphaned content from the Rhizome store"},
{app_keyring_create,{"keyring","create",NULL}, 0,
"Create a new keyring file."},
{app_keyring_dump,{"keyring","dump" KEYRING_PIN_OPTIONS,"[--secret]","[<file>]",NULL}, 0,
"Dump all keyring identities that can be accessed using the specified PINs"},
{app_keyring_list,{"keyring","list" KEYRING_PIN_OPTIONS,NULL}, 0,
"List identites in specified key ring that can be accessed using the specified PINs"},
"List identities that can be accessed using the supplied PINs"},
{app_keyring_add,{"keyring","add" KEYRING_PIN_OPTIONS,"[<pin>]",NULL}, 0,
"Create a new identity in the keyring protected by the provided PIN"},
"Create a new identity in the keyring protected by the supplied PIN (empty PIN if not given)"},
{app_keyring_set_did,{"keyring", "set","did" KEYRING_PIN_OPTIONS,"<sid>","<did>","<name>",NULL}, 0,
"Set the DID for the specified SID. Optionally supply PIN to unlock the SID record in the keyring."},
"Set the DID for the specified SID (must supply PIN to unlock the SID record in the keyring)"},
{app_id_self,{"id","self|peers|allpeers",NULL}, 0,
"Return identity(s) as URIs of own node, or of known routable peers, or all known peers"},
{app_route_print, {"route","print",NULL}, 0,

View File

@ -7,6 +7,7 @@ HDRS= fifo.h \
serval.h \
cli.h \
str.h \
rotbuf.h \
mem.h \
os.h \
strbuf.h \

727
keyring.c
View File

@ -16,10 +16,13 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <assert.h>
#include "constants.h"
#include "serval.h"
#include "str.h"
#include "mem.h"
#include "rotbuf.h"
#include "conf.h"
#include "rhizome.h"
#include "nacl.h"
@ -28,28 +31,28 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
/*
Open keyring file, read BAM and create initial context using the
stored salt. */
keyring_file *keyring_open(char *file)
keyring_file *keyring_open(const char *path)
{
/* Allocate structure */
keyring_file *k = emalloc_zero(sizeof(keyring_file));
if (!k)
return NULL;
/* Open keyring file read-write if we can, else use it read-only */
k->file = fopen(file, "r+");
k->file = fopen(path, "r+");
if (!k->file) {
if (errno != EPERM && errno != ENOENT)
WHYF_perror("fopen(%s, \"r+\")", alloca_str_toprint(file));
WHYF_perror("fopen(%s, \"r+\")", alloca_str_toprint(path));
if (config.debug.keyring)
DEBUGF("cannot open %s in \"r+\" mode, falling back to \"r\"", alloca_str_toprint(file));
k->file = fopen(file, "r");
DEBUGF("cannot open %s in \"r+\" mode, falling back to \"r\"", alloca_str_toprint(path));
k->file = fopen(path, "r");
if (!k->file) {
if (errno != EPERM && errno != ENOENT)
WHYF_perror("fopen(%s, \"r\")", alloca_str_toprint(file));
WHYF_perror("fopen(%s, \"r\")", alloca_str_toprint(path));
if (config.debug.keyring)
DEBUGF("cannot open %s in \"r\" mode, falling back to \"w+\"", alloca_str_toprint(file));
k->file = fopen(file, "w+");
DEBUGF("cannot open %s in \"r\" mode, falling back to \"w+\"", alloca_str_toprint(path));
k->file = fopen(path, "w+");
if (!k->file) {
WHYF_perror("fopen(%s, \"w+\")", alloca_str_toprint(file));
WHYF_perror("fopen(%s, \"w+\")", alloca_str_toprint(path));
keyring_free(k);
return NULL;
}
@ -57,7 +60,7 @@ keyring_file *keyring_open(char *file)
}
assert(k->file != NULL);
if (fseeko(k->file, 0, SEEK_END)) {
WHYF_perror("fseeko(%s, 0, SEEK_END)", alloca_str_toprint(file));
WHYF_perror("fseeko(%s, 0, SEEK_END)", alloca_str_toprint(path));
keyring_free(k);
return NULL;
}
@ -66,25 +69,25 @@ keyring_file *keyring_open(char *file)
/* Uninitialised, so write 2KB of zeroes,
followed by 2KB of random bytes as salt. */
if (fseeko(k->file, 0, SEEK_SET)) {
WHYF_perror("fseeko(%s, 0, SEEK_END)", alloca_str_toprint(file));
WHYF_perror("fseeko(%s, 0, SEEK_END)", alloca_str_toprint(path));
keyring_free(k);
return NULL;
}
unsigned char buffer[KEYRING_PAGE_SIZE];
bzero(&buffer[0],KEYRING_BAM_BYTES);
if (fwrite(buffer, 2048, 1, k->file)!=1) {
WHYF_perror("fwrite(%p, 2048, 1, %s)", buffer, alloca_str_toprint(file));
WHYF_perror("fwrite(%p, 2048, 1, %s)", buffer, alloca_str_toprint(path));
WHY("Could not write empty bitmap in fresh keyring file");
keyring_free(k);
return NULL;
}
if (urandombytes(&buffer[0],KEYRING_PAGE_SIZE-KEYRING_BAM_BYTES)) {
WHYF("Could not get random keyring salt to put in fresh keyring file %s", file);
WHYF("Could not get random keyring salt to put in fresh keyring file %s", path);
keyring_free(k);
return NULL;
}
if (fwrite(buffer, KEYRING_PAGE_SIZE - KEYRING_BAM_BYTES, 1, k->file) != 1) {
WHYF_perror("fwrite(%p, %lu, 1, %s)", buffer, (long)(KEYRING_PAGE_SIZE - KEYRING_BAM_BYTES), alloca_str_toprint(file));
WHYF_perror("fwrite(%p, %lu, 1, %s)", buffer, (long)(KEYRING_PAGE_SIZE - KEYRING_BAM_BYTES), alloca_str_toprint(path));
WHYF("Could not write keyring salt in fresh keyring file");
keyring_free(k);
return NULL;
@ -99,14 +102,14 @@ keyring_file *keyring_open(char *file)
/* Read bitmap from slab.
Also, if offset is zero, read the salt */
if (fseeko(k->file,offset,SEEK_SET)) {
WHYF_perror("fseeko(%s, %ld, SEEK_SET)", alloca_str_toprint(file), (long)offset);
WHYF_perror("fseeko(%s, %ld, SEEK_SET)", alloca_str_toprint(path), (long)offset);
WHY("Could not seek to BAM in keyring file");
keyring_free(k);
return NULL;
}
*b = emalloc_zero(sizeof(keyring_bam));
if (!(*b)) {
WHYF("Could not allocate keyring_bam structure for key ring file %s", file);
WHYF("Could not allocate keyring_bam structure for key ring file %s", path);
keyring_free(k);
return NULL;
}
@ -114,7 +117,7 @@ keyring_file *keyring_open(char *file)
/* Read bitmap */
int r=fread((*b)->bitmap, KEYRING_BAM_BYTES, 1, k->file);
if (r!=1) {
WHYF_perror("fread(%p, %ld, 1, %s)", (*b)->bitmap, (long)KEYRING_BAM_BYTES, alloca_str_toprint(file));
WHYF_perror("fread(%p, %ld, 1, %s)", (*b)->bitmap, (long)KEYRING_BAM_BYTES, alloca_str_toprint(path));
WHYF("Could not read BAM from keyring file");
keyring_free(k);
return NULL;
@ -127,7 +130,7 @@ keyring_file *keyring_open(char *file)
if (!offset) {
k->contexts[0] = emalloc_zero(sizeof(keyring_context));
if (!k->contexts[0]) {
WHYF("Could not allocate keyring_context for keyring file %s", file);
WHYF("Could not allocate keyring_context for keyring file %s", path);
keyring_free(k);
return NULL;
}
@ -136,14 +139,14 @@ keyring_file *keyring_open(char *file)
k->contexts[0]->KeyRingSaltLen=KEYRING_PAGE_SIZE-KEYRING_BAM_BYTES;
k->contexts[0]->KeyRingSalt = emalloc(k->contexts[0]->KeyRingSaltLen);
if (!k->contexts[0]->KeyRingSalt) {
WHYF("Could not allocate keyring_context->salt for keyring file %s", file);
WHYF("Could not allocate keyring_context->salt for keyring file %s", path);
keyring_free(k);
return NULL;
}
r = fread(k->contexts[0]->KeyRingSalt, k->contexts[0]->KeyRingSaltLen, 1, k->file);
if (r!=1) {
WHYF_perror("fread(%p, %ld, 1, %s)", k->contexts[0]->KeyRingSalt, k->contexts[0]->KeyRingSaltLen, alloca_str_toprint(file));
WHYF("Could not read salt from keyring file %s", file);
WHYF_perror("fread(%p, %ld, 1, %s)", k->contexts[0]->KeyRingSalt, k->contexts[0]->KeyRingSaltLen, alloca_str_toprint(path));
WHYF("Could not read salt from keyring file %s", path);
keyring_free(k);
return NULL;
}
@ -392,281 +395,384 @@ int keyring_munge_block(unsigned char *block,int len /* includes the first 96 by
#undef APPEND
}
#define slot_byte(X) slot[((PKR_SALT_BYTES+PKR_MAC_BYTES+2)+((X)+rotation)%(KEYRING_PAGE_SIZE-(PKR_SALT_BYTES+PKR_MAC_BYTES+2)))]
int keyring_pack_identity(keyring_context *c,keyring_identity *i,
unsigned char packed[KEYRING_PAGE_SIZE])
static const char *keytype_str(unsigned ktype)
{
unsigned ofs=0;
int exit_code=-1;
/* Convert an identity to a KEYRING_PAGE_SIZE bytes long block that
consists of 32 bytes of random salt, a 64 byte (512 bit) message
authentication code (MAC) and the list of key pairs. */
if (urandombytes(&packed[0],PKR_SALT_BYTES)) return WHY("Could not generate salt");
ofs+=PKR_SALT_BYTES;
/* Calculate MAC */
keyring_identity_mac(c,i,&packed[0] /* pkr salt */,
&packed[0+PKR_SALT_BYTES] /* write mac in after salt */);
ofs+=PKR_MAC_BYTES;
/* Leave 2 bytes for rotation (put zeroes for now) */
int rotate_ofs=ofs;
packed[ofs]=0; packed[ofs+1]=0;
ofs+=2;
/* Write keypairs */
int kp;
for(kp=0;kp<i->keypair_count;kp++)
{
if (ofs>=KEYRING_PAGE_SIZE) {
WHY("too many or too long key pairs");
ofs=0; goto kpi_safeexit;
}
packed[ofs++]=i->keypairs[kp]->type;
switch(i->keypairs[kp]->type) {
case KEYTYPE_RHIZOME:
case KEYTYPE_DID:
/* 32 chars for unpacked DID/rhizome secret,
64 chars for name (for DIDs only) */
if ((ofs
+i->keypairs[kp]->private_key_len
+i->keypairs[kp]->public_key_len
)>=KEYRING_PAGE_SIZE)
{
WHY("too many or too long key pairs");
ofs=0;
goto kpi_safeexit;
}
bcopy(i->keypairs[kp]->private_key,&packed[ofs],
i->keypairs[kp]->private_key_len);
ofs+=i->keypairs[kp]->private_key_len;
if (i->keypairs[kp]->type==KEYTYPE_DID) {
bcopy(i->keypairs[kp]->public_key,&packed[ofs],
i->keypairs[kp]->private_key_len);
ofs+=i->keypairs[kp]->public_key_len;
}
break;
case KEYTYPE_CRYPTOBOX:
/* For cryptobox we only need the private key, as we compute the public
key from it when extracting the identity */
if ((ofs+i->keypairs[kp]->private_key_len)>=KEYRING_PAGE_SIZE)
{
WHY("too many or too long key pairs");
ofs=0;
goto kpi_safeexit;
}
bcopy(i->keypairs[kp]->private_key,&packed[ofs],
i->keypairs[kp]->private_key_len);
ofs+=i->keypairs[kp]->private_key_len;
break;
case KEYTYPE_CRYPTOSIGN:
/* For cryptosign keys there is no public API in NaCl to compute the
public key from the private key (although we could subvert the API
abstraction and do it anyway). But in the interests of niceness we
just store the public and private key pair together */
if ((ofs
+i->keypairs[kp]->private_key_len
+i->keypairs[kp]->public_key_len)>=KEYRING_PAGE_SIZE)
{
WHY("too many or too long key pairs");
ofs=0;
goto kpi_safeexit;
}
/* Write private then public */
bcopy(i->keypairs[kp]->private_key,&packed[ofs],
i->keypairs[kp]->private_key_len);
ofs+=i->keypairs[kp]->private_key_len;
bcopy(i->keypairs[kp]->public_key,&packed[ofs],
i->keypairs[kp]->public_key_len);
ofs+=i->keypairs[kp]->public_key_len;
break;
default:
WHY("unknown key type");
goto kpi_safeexit;
}
}
if (ofs>=KEYRING_PAGE_SIZE) {
WHY("too many or too long key pairs");
ofs=0; goto kpi_safeexit;
switch (ktype) {
case KEYTYPE_CRYPTOBOX: return "CRYPTOBOX";
case KEYTYPE_CRYPTOSIGN: return "CRYPTOSIGN";
case KEYTYPE_RHIZOME: return "RHIZOME";
case KEYTYPE_DID: return "DID";
default: return "";
}
packed[ofs++]=0x00; /* Terminate block */
}
/* We are now all done, give or take the zeroeing of the trailing bytes. */
exit_code=0;
struct keytype {
size_t public_key_size;
size_t private_key_size;
size_t packed_size;
int (*packer)(const struct keytype *, const keypair *, struct rotbuf *);
int (*unpacker)(const struct keytype *, keypair *, struct rotbuf *);
void (*dumper)(const keypair *, XPRINTF, int);
};
static int pack_private_only(const struct keytype *kt, const keypair *kp, struct rotbuf *rb)
{
rotbuf_putbuf(rb, kp->private_key, kt->private_key_size);
return 0;
}
kpi_safeexit:
/* Clear out remainder of block so that we don't leak info.
We could have zeroed the thing to begin with, but that means extra
memory writes that are otherwise avoidable.
Actually, we don't want zeroes (known plain-text attack against most
of the block's contents in the typical case), we want random data. */
if (urandombytes(&packed[ofs],KEYRING_PAGE_SIZE-ofs))
return WHY("urandombytes() failed to back-fill packed identity block");
static int pack_private_public(const struct keytype *kt, const keypair *kp, struct rotbuf *rb)
{
rotbuf_putbuf(rb, kp->private_key, kt->private_key_size);
rotbuf_putbuf(rb, kp->public_key, kt->public_key_size);
return 0;
}
/* Rotate block by a random amount (get the randomness safely) */
unsigned int rotation;
if (urandombytes((unsigned char *)&rotation,sizeof(rotation)))
static void dump_raw_hex(const keypair *kp, XPRINTF xpf, int include_secret)
{
if (kp->public_key_len)
xprintf(xpf, " pub=%s", alloca_tohex(kp->public_key, kp->public_key_len));
if (include_secret && kp->private_key_len)
xprintf(xpf, " sec=%s", alloca_tohex(kp->private_key, kp->private_key_len));
}
static int unpack_private_public(const struct keytype *kt, keypair *kp, struct rotbuf *rb)
{
rotbuf_getbuf(rb, kp->private_key, kt->private_key_size);
rotbuf_getbuf(rb, kp->public_key, kt->public_key_size);
return 0;
}
static int unpack_private_only(const struct keytype *kt, keypair *kp, struct rotbuf *rb)
{
rotbuf_getbuf(rb, kp->private_key, kt->private_key_size);
return 0;
}
static int unpack_private_derive_scalarmult_public(const struct keytype *kt, keypair *kp, struct rotbuf *rb)
{
rotbuf_getbuf(rb, kp->private_key, kt->private_key_size);
/* Compute public key from private key.
*
* Public key calculation as below is taken from section 3 of:
* http://cr.yp.to/highspeed/naclcrypto-20090310.pdf
*
* This can take a while on a mobile phone since it involves a scalarmult operation, so searching
* through all slots for a pin could take a while (perhaps 1 second per pin:slot cominbation).
* This is both good and bad. The other option is to store the public key as well, which would
* make entering a pin faster, but would also make trying an incorrect pin faster, thus
* simplifying some brute-force attacks. We need to make a decision between speed/convenience
* and security here.
*/
if (!rb->wrap)
crypto_scalarmult_curve25519_base(kp->public_key, kp->private_key);
return 0;
}
static int pack_did_name(const struct keytype *kt, const keypair *kp, struct rotbuf *rb)
{
// Ensure name is nul terminated.
if (strnchr((const char *)kp->public_key, kt->public_key_size, '\0') == NULL)
return WHY("missing nul terminator");
return pack_private_public(kt, kp, rb);
}
static int unpack_did_name(const struct keytype *kt, keypair *kp, struct rotbuf *rb)
{
if (unpack_private_public(kt, kp, rb) == -1)
return -1;
// Fail if name is not nul terminated.
return strnchr((const char *)kp->public_key, kt->public_key_size, '\0') == NULL ? -1 : 0;
}
static void dump_did_name(const keypair *kp, XPRINTF xpf, int include_secret)
{
xprintf(xpf, " DID=%s", alloca_str_toprint_quoted((const char *)kp->private_key, "\"\""));
xprintf(xpf, " Name=%s", alloca_str_toprint_quoted((const char *)kp->public_key, "\"\""));
}
/* This is where all the supported key types are declared. In order to preserve backward
* compatibility (reading keyring files from older versions of Serval DNA), DO NOT ERASE OR RE-USE
* ANY KEY TYPE ENTRIES FROM THIS ARRAY. If a key type is no longer used, it must be permanently
* deprecated, ie, recognised and simply skipped. The packer and unpacker functions can be changed
* to NULL.
*/
const struct keytype keytypes[] = {
[KEYTYPE_CRYPTOBOX] = {
/* Only the private key is stored, and the public key (SID) is derived from the private key
* when the keyring is read.
*/
.private_key_size = crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES,
.public_key_size = crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES,
.packed_size = crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES,
.packer = pack_private_only,
.unpacker = unpack_private_derive_scalarmult_public,
.dumper = dump_raw_hex
},
[KEYTYPE_CRYPTOSIGN] = {
/* The NaCl API does not expose any method to derive a cryptosign public key from its private
* key, although there must be an internal NaCl function to do so. Subverting the NaCl API to
* invoke that function risks incompatibility with future releases of NaCl, so instead the
* public key is stored redundantly in the keyring.
*/
.private_key_size = crypto_sign_edwards25519sha512batch_SECRETKEYBYTES,
.public_key_size = crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES,
.packed_size = crypto_sign_edwards25519sha512batch_SECRETKEYBYTES + crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES,
.packer = pack_private_public,
.unpacker = unpack_private_public,
.dumper = dump_raw_hex
},
[KEYTYPE_RHIZOME] = {
/* Only the private key (Rhizome Secret) is stored, because the public key is never used.
*/
.private_key_size = 32,
.public_key_size = 0,
.packed_size = 32,
.packer = pack_private_only,
.unpacker = unpack_private_only,
.dumper = dump_raw_hex
},
[KEYTYPE_DID] = {
/* The DID is stored in unpacked form in the private key field, and the name in nul-terminated
* ASCII form in the public key field.
*/
.private_key_size = 32,
.public_key_size = 64,
.packed_size = 32 + 64,
.packer = pack_did_name,
.unpacker = unpack_did_name,
.dumper = dump_did_name
}
// ADD MORE KEY TYPES HERE
};
static int keyring_pack_identity(keyring_context *c, keyring_identity *id, unsigned char packed[KEYRING_PAGE_SIZE])
{
/* Convert an identity to a KEYRING_PAGE_SIZE bytes long block that consists of 32 bytes of random
* salt, a 64 byte (512 bit) message authentication code (MAC) and the list of key pairs. */
if (urandombytes(packed, PKR_SALT_BYTES) == -1)
return WHY("Could not generate salt");
/* Calculate MAC */
keyring_identity_mac(c, id, packed /* pkr salt */,
packed + PKR_SALT_BYTES /* write mac in after salt */);
/* There was a known plain-text opportunity here: byte 96 must be 0x01, and some other bytes are
* likely deducible, e.g., the location of the trailing 0x00 byte can probably be guessed with
* confidence. Payload rotation will frustrate this attack.
*/
uint16_t rotation;
if (urandombytes((unsigned char *)&rotation, sizeof rotation) == -1)
return WHY("urandombytes() failed to generate random rotation");
rotation&=0xffff;
#ifdef NO_ROTATION
rotation=0;
#endif
unsigned char slot[KEYRING_PAGE_SIZE];
/* XXX There has to be a more efficient way to do this! */
int n;
for(n=0;n<(KEYRING_PAGE_SIZE-(PKR_SALT_BYTES+PKR_MAC_BYTES+2));n++)
slot_byte(n)=packed[PKR_SALT_BYTES+PKR_MAC_BYTES+2+n];
bcopy(&slot[PKR_SALT_BYTES+PKR_MAC_BYTES+2],&packed[PKR_SALT_BYTES+PKR_MAC_BYTES+2],
KEYRING_PAGE_SIZE-(PKR_SALT_BYTES+PKR_MAC_BYTES+2));
packed[rotate_ofs]=rotation>>8;
packed[rotate_ofs+1]=rotation&0xff;
return exit_code;
// The two bytes immediately following the MAC describe the rotation offset.
packed[PKR_SALT_BYTES + PKR_MAC_BYTES] = rotation >> 8;
packed[PKR_SALT_BYTES + PKR_MAC_BYTES + 1] = rotation & 0xff;
/* Pack the key pairs into the rest of the slot as a rotated buffer. */
struct rotbuf rbuf;
rotbuf_init(&rbuf,
packed + PKR_SALT_BYTES + PKR_MAC_BYTES + 2,
KEYRING_PAGE_SIZE - (PKR_SALT_BYTES + PKR_MAC_BYTES + 2),
rotation);
unsigned kp;
for (kp = 0; kp < id->keypair_count && !rbuf.wrap; ++kp) {
unsigned ktype = id->keypairs[kp]->type;
if (ktype == 0x00 || ktype >= NELS(keytypes)) {
WHYF("illegal key type 0x%02x at kp=%u", ktype, kp);
goto scram;
}
const struct keytype *kt = &keytypes[ktype];
if (kt->packer == NULL) {
WARNF("unsupported key type 0x%02x, omitted from keyring file", ktype);
continue;
}
if (config.debug.keyring)
DEBUGF("pack key type = 0x%02x", ktype);
// First byte is the key type code.
rotbuf_putc(&rbuf, ktype);
// The next two bytes are the key pair length, for forward compatibility: so older software can
// skip over key pairs with an unrecognised type. The original four first key types do not
// store the length, for the sake of backward compatibility with legacy keyring files. Their
// entry lengths are hard-coded.
size_t keypair_len = kt->packed_size;
switch (ktype) {
case KEYTYPE_CRYPTOBOX:
case KEYTYPE_CRYPTOSIGN:
case KEYTYPE_RHIZOME:
case KEYTYPE_DID:
break;
default:
rotbuf_putc(&rbuf, (keypair_len >> 8) & 0xff);
rotbuf_putc(&rbuf, keypair_len & 0xff);
break;
}
// The remaining bytes is the key pair in whatever format it uses.
struct rotbuf rbstart = rbuf;
if (kt->packer(kt, id->keypairs[kp], &rbuf) != 0)
break;
// Ensure the correct number of bytes were written.
unsigned packed = rotbuf_delta(&rbstart, &rbuf);
if (packed != keypair_len) {
WHYF("key type 0x%02x packed wrong length (packed %u, expecting %u)", ktype, packed, keypair_len);
goto scram;
}
}
// Final byte is a zero key type code.
rotbuf_putc(&rbuf, 0x00);
if (rbuf.wrap > 1) {
WHY("slot overrun");
goto scram;
}
if (kp < id->keypair_count) {
WHY("error filling slot");
goto scram;
}
/* Randomfill the remaining part of the slot to frustrate any known-plain-text attack on the
* keyring.
*/
{
unsigned char *buf;
size_t len;
while (rotbuf_next_chunk(&rbuf, &buf, &len))
if (urandombytes(buf, len))
return WHY("urandombytes() failed to back-fill packed identity block");
}
return 0;
scram:
/* Randomfill the entire slot to erase any secret keys that may have found their way into it, to
* avoid leaking sensitive information out through a possibly re-used memory buffer.
*/
if (urandombytes(packed, KEYRING_PAGE_SIZE) == -1)
WHY("urandombytes() failed to in-fill packed identity block");
return -1;
}
keyring_identity *keyring_unpack_identity(unsigned char *slot, const char *pin)
static int cmp_keypair(const keypair *a, const keypair *b)
{
int c = a->type < b->type ? -1 : a->type > b->type ? 1 : 0;
if (c == 0 && a->public_key_len) {
assert(a->public_key_len == b->public_key_len);
assert(a->public_key != NULL);
assert(b->public_key != NULL);
c = memcmp(a->public_key, b->public_key, a->public_key_len);
}
if (c == 0 && a->private_key_len) {
assert(a->private_key_len == b->private_key_len);
assert(a->private_key != NULL);
assert(b->private_key != NULL);
c = memcmp(a->private_key, b->private_key, a->private_key_len);
}
return c;
}
static keyring_identity *keyring_unpack_identity(unsigned char *slot, const char *pin)
{
/* Skip salt and MAC */
int i;
keyring_identity *id = emalloc_zero(sizeof(keyring_identity));
if (!id) { WHY("malloc of identity failed"); return NULL; }
if (!slot) { WHY("slot is null"); return NULL; }
id->PKRPin = str_edup(pin);
/* There was a known plain-text opportunity here:
byte 96 must be 0x01, and some other bytes are likely deducible, e.g., the
location of the trailing 0x00 byte can probably be guessed with confidence.
Payload rotation would help here. So let's do that. First two bytes is
rotation in bytes of remainder of block.
*/
int rotation = (slot[PKR_SALT_BYTES+PKR_MAC_BYTES]<<8) | slot[PKR_SALT_BYTES+PKR_MAC_BYTES+1];
unsigned ofs = 0;
while (ofs < KEYRING_PAGE_SIZE - PKR_SALT_BYTES - PKR_MAC_BYTES - 2) {
unsigned char ktype = slot_byte(ofs++);
if (ktype == 0x00)
break; // End of data, stop looking
// The two bytes immediately following the MAC describe the rotation offset.
uint16_t rotation = (slot[PKR_SALT_BYTES + PKR_MAC_BYTES] << 8) | slot[PKR_SALT_BYTES + PKR_MAC_BYTES + 1];
/* Pack the key pairs into the rest of the slot as a rotated buffer. */
struct rotbuf rbuf;
rotbuf_init(&rbuf,
slot + PKR_SALT_BYTES + PKR_MAC_BYTES + 2,
KEYRING_PAGE_SIZE - (PKR_SALT_BYTES + PKR_MAC_BYTES + 2),
rotation);
while (!rbuf.wrap) {
if (id->keypair_count >= PKR_MAX_KEYPAIRS) {
WHY("Too many key pairs in identity");
WHY("too many key pairs");
keyring_free_identity(id);
return NULL;
}
size_t keypair_len = (slot_byte(ofs) << 8) | slot_byte(ofs + 1);
size_t public_key_len = 0;
size_t private_key_len = 0;
struct rotbuf rbo = rbuf;
unsigned char ktype = rotbuf_getc(&rbuf);
if (rbuf.wrap || ktype == 0x00)
break; // End of data, stop looking
const struct keytype *kt = &keytypes[ktype];
size_t keypair_len;
// No length bytes after the original four key types, for backward compatibility. All other key
// types are followed by a two-byte keypair length.
switch (ktype) {
case KEYTYPE_CRYPTOBOX:
// No length bytes after this key type, for backward compatibility.
keypair_len = private_key_len = crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES;
public_key_len = crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES;
break;
case KEYTYPE_CRYPTOSIGN:
// No length bytes after this key type, for backward compatibility.
private_key_len = crypto_sign_edwards25519sha512batch_SECRETKEYBYTES;
public_key_len = crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES;
keypair_len = private_key_len + public_key_len;
break;
case KEYTYPE_RHIZOME:
// No length bytes after this key type, for backward compatibility.
keypair_len = private_key_len = 32;
public_key_len = 0;
break;
case KEYTYPE_DID:
// No length bytes after this key type, for backward compatibility.
private_key_len = 32;
public_key_len = 64;
keypair_len = private_key_len + public_key_len;
keypair_len = kt->packed_size;
break;
// ADD NEW KEY TYPES HERE:
/*
case KEYTYPE_SOMETHING:
ofs += 2; // skip length bytes
private_key_len = ...
public_key_len = ...
break;
*/
default:
// Unrecognised key type, possibly from a future software version. Skip the key pair.
ofs += 2 + keypair_len;
continue;
}
// This is where the ofs should advance to by the time we have read the whole key pair.
unsigned next_ofs = ofs + keypair_len;
// Create keyring entries to hold the key pair.
keypair *kp = NULL;
if ( (kp = id->keypairs[id->keypair_count] = emalloc_zero(sizeof(keypair))) == NULL
|| (private_key_len && (kp->private_key = emalloc(private_key_len)) == NULL)
|| (public_key_len && (kp->public_key = emalloc(public_key_len)) == NULL)
) {
keyring_free_identity(id);
return NULL;
}
kp->type = ktype;
kp->private_key_len = private_key_len;
kp->public_key_len = public_key_len;
// The private key is always stored byte for byte in the keyring.
for (i = 0; i < kp->private_key_len; ++i)
kp->private_key[i] = slot_byte(ofs++);
// How to get the public key depends on the key type.
switch (kp->type) {
case KEYTYPE_CRYPTOBOX:
/* Compute public key from private key.
*
* Public key calculation as below is taken from section 3 of:
* http://cr.yp.to/highspeed/naclcrypto-20090310.pdf
*
* XXX - This can take a while on a mobile phone since it involves a scalarmult operation,
* so searching through all slots for a pin could take a while (perhaps 1 second per
* pin:slot cominbation). This is both good and bad. The other option is to store the
* public key as well, which would make entering a pin faster, but would also make trying an
* incorrect pin faster, thus simplifying some brute-force attacks. We need to make a
* decision between speed/convenience and security here.
*/
crypto_scalarmult_curve25519_base(kp->public_key, kp->private_key);
keypair_len = rotbuf_getc(&rbuf) << 8;
keypair_len |= rotbuf_getc(&rbuf);
break;
case KEYTYPE_DID:
/* The name is stored as ASCII bytes with a nul terminator in the public key part of the key
* pair.
*/
{
int gotnul = 0;
for (i = 0; i < kp->public_key_len; ++i)
if (!(kp->public_key[i] = slot_byte(ofs++)))
gotnul = 1;
if (!gotnul) {
keyring_free_identity(id);
return NULL;
}
}
if (rbuf.wrap)
break;
if (ktype < NELS(keytypes) && kt->unpacker) {
if (config.debug.keyring)
DEBUGF("unpack key type = 0x%02x at offset %u", ktype, rotbuf_position(&rbo));
struct rotbuf rbstart = rbuf;
// Create keyring entries to hold the key pair.
keypair *kp = NULL;
if ( (kp = id->keypairs[id->keypair_count] = emalloc_zero(sizeof(keypair))) == NULL
|| (kt->private_key_size && (kp->private_key = emalloc(kt->private_key_size)) == NULL)
|| (kt->public_key_size && (kp->public_key = emalloc(kt->public_key_size)) == NULL)
) {
keyring_free_identity(id);
return NULL;
}
break;
case KEYTYPE_CRYPTOSIGN:
/* While it is possible to compute the public key from the private key, the NaCl API currently
* does not expose a primitive to do this. Subverting the NaCl API to invoke an internal NaCl
* function risks becoming unsupported in future, so the public key is redundantly stored in
* the keyring.
*/
// FALL THROUGH
default:
/* By default, the public key is stored byte-for-byte in the keyring.
*/
for (i = 0; i < kp->public_key_len; ++i)
kp->public_key[i] = slot_byte(ofs++);
break;
kp->type = ktype;
kp->private_key_len = kt->private_key_size;
kp->public_key_len = kt->public_key_size;
if (kt->unpacker(kt, kp, &rbuf) != 0) {
// If there is an error, it is probably an empty slot.
if (config.debug.keyring)
DEBUGF("key type 0x%02x does not unpack", ktype);
keyring_free_identity(id);
return NULL;
}
// Ensure that the correct number of bytes was consumed.
size_t unpacked = rotbuf_delta(&rbstart, &rbuf);
if (unpacked != keypair_len) {
// If the number of bytes unpacked does not match the keypair length, it is probably an
// empty slot.
if (config.debug.keyring)
DEBUGF("key type 0x%02x unpacked wrong length (unpacked %u, expecting %u)", ktype, unpacked, keypair_len);
keyring_free_identity(id);
return NULL;
}
// Got a valid key pair! Sort the key pairs by (key type, public key, private key) and weed
// out duplicates.
{
int c = 1;
unsigned i = 0;
for (i = 0; i < id->keypair_count && (c = cmp_keypair(id->keypairs[i], id->keypairs[id->keypair_count])) < 0; ++i)
;
if (c > 0) {
keypair *tmp = id->keypairs[id->keypair_count];
unsigned j;
for (j = id->keypair_count; j > i; --j)
id->keypairs[j] = id->keypairs[j - 1];
id->keypairs[i] = tmp;
}
if (c)
++id->keypair_count;
}
} else {
if (config.debug.keyring)
DEBUGF("unsupported key type 0x%02x at offset %u, skipping %u bytes", ktype, rotbuf_position(&rbo), keypair_len);
rotbuf_advance(&rbuf, keypair_len); // skip
}
// Ensure that the correct number of bytes was consumed.
if (ofs != next_ofs) {
keyring_free_identity(id);
return NULL;
}
// Got a valid key pair!
id->keypair_count++;
}
// If the buffer offset overshot, we got an invalid keypair code and length combination.
if (ofs > (KEYRING_PAGE_SIZE - PKR_SALT_BYTES - PKR_MAC_BYTES - 2)) {
if (rbuf.wrap > 1) {
if (config.debug.keyring)
DEBUGF("slot overrun by %u bytes", rbuf.wrap - 1);
keyring_free_identity(id);
return NULL;
}
if (config.debug.keyring)
DEBUGF("unpacked %d key pairs", id->keypair_count);
return id;
}
@ -690,6 +796,7 @@ int keyring_identity_mac(keyring_context *c, keyring_identity *id,
APPEND(id->keypairs[0]->private_key, id->keypairs[0]->private_key_len);
APPEND(id->keypairs[0]->public_key, id->keypairs[0]->public_key_len);
APPEND(id->PKRPin, strlen(id->PKRPin));
#undef APPEND
crypto_hash_sha512(mac, work, ofs);
return 0;
}
@ -719,14 +826,14 @@ int keyring_decrypt_pkr(keyring_file *k,keyring_context *c,
k->contexts[0]->KeyRingSalt,
k->contexts[0]->KeyRingSaltLen,
c->KeyRingPin,pin)) {
WHY("keyring_munge_block() failed");
WHYF("keyring_munge_block() failed, slot=%u", slot_number);
goto kdp_safeexit;
}
/* 3. Unpack contents of slot into a new identity in the provided context. */
if ((id = keyring_unpack_identity(slot, pin)) == NULL)
goto kdp_safeexit; // Not a valid slot
if (id->keypair_count < 1)
if (config.debug.keyring)
DEBUGF("unpack slot %u", slot_number);
if (((id = keyring_unpack_identity(slot, pin)) == NULL) || id->keypair_count < 1)
goto kdp_safeexit; // Not a valid slot
id->slot = slot_number;
@ -1097,28 +1204,18 @@ int keyring_set_did(keyring_identity *id, const char *did, const char *name)
int keyring_find_did(const keyring_file *k,int *cn,int *in,int *kp,char *did)
{
if (keyring_sanitise_position(k,cn,in,kp)) return 0;
while (1) {
/* we know we have a sane position, so see if it is interesting */
if (k->contexts[*cn]->identities[*in]->keypairs[*kp]->type==KEYTYPE_DID)
{
/* Compare DIDs */
if ((!did[0])
||(did[0]=='*'&&did[1]==0)
||(!strcasecmp(did,(char *)k->contexts[*cn]->identities[*in]
->keypairs[*kp]->private_key)))
{
/* match */
return 1;
}
for (; keyring_sanitise_position(k,cn,in,kp) == 0; ++*kp) {
if (k->contexts[*cn]->identities[*in]->keypairs[*kp]->type==KEYTYPE_DID) {
/* Compare DIDs */
if ((!did[0])
||(did[0]=='*'&&did[1]==0)
||(!strcasecmp(did,(char *)k->contexts[*cn]->identities[*in]
->keypairs[*kp]->private_key))
) {
return 1; // match
}
(*kp)++;
if (keyring_sanitise_position(k,cn,in,kp)) return 0;
}
}
return 0;
}
@ -1133,11 +1230,9 @@ int keyring_identity_find_keytype(const keyring_file *k, int cn, int in, int key
int keyring_next_keytype(const keyring_file *k, int *cn, int *in, int *kp, int keytype)
{
while (!keyring_sanitise_position(k, cn, in, kp)) {
for (; keyring_sanitise_position(k, cn, in, kp) == 0; ++*kp)
if (k->contexts[*cn]->identities[*in]->keypairs[*kp]->type == keytype)
return 1;
++*kp;
}
return 0;
}
@ -1150,19 +1245,19 @@ int keyring_sanitise_position(const keyring_file *k,int *cn,int *in,int *kp)
{
if (!k) return 1;
/* Sanity check passed in position */
if ((*cn)>=keyring->context_count) return 1;
if ((*in)>=keyring->contexts[*cn]->identity_count)
if ((*cn)>=k->context_count) return 1;
if ((*in)>=k->contexts[*cn]->identity_count)
{
(*in)=0; (*cn)++;
if ((*cn)>=keyring->context_count) return 1;
if ((*cn)>=k->context_count) return 1;
}
if ((*kp)>=keyring->contexts[*cn]->identities[*in]->keypair_count)
if ((*kp)>=k->contexts[*cn]->identities[*in]->keypair_count)
{
*kp=0; (*in)++;
if ((*in)>=keyring->contexts[*cn]->identity_count)
if ((*in)>=k->contexts[*cn]->identity_count)
{
(*in)=0; (*cn)++;
if ((*cn)>=keyring->context_count) return 1;
if ((*cn)>=k->context_count) return 1;
}
}
return 0;
@ -1354,7 +1449,7 @@ int keyring_send_sas_request(struct subscriber *subscriber){
int keyring_find_sid(const keyring_file *k, int *cn, int *in, int *kp, const unsigned char *sid)
{
for (; !keyring_sanitise_position(k, cn, in, kp); ++*kp)
for (; keyring_sanitise_position(k, cn, in, kp) == 0; ++*kp)
if (k->contexts[*cn]->identities[*in]->keypairs[*kp]->type == KEYTYPE_CRYPTOBOX
&& memcmp(sid, k->contexts[*cn]->identities[*in]->keypairs[*kp]->public_key, SID_SIZE) == 0)
return 1;
@ -1520,7 +1615,51 @@ unsigned char *keyring_get_nm_bytes(unsigned char *known_sid, unsigned char *unk
->contexts[cn]
->identities[in]
->keypairs[kp]->private_key);
RETURN(nm_cache[i].nm_bytes);
OUT();
}
static int cmp_identity_ptrs(const keyring_identity *const *a, const keyring_identity *const *b)
{
int c;
unsigned i;
for (i = 0; i < (*a)->keypair_count && i < (*b)->keypair_count; ++i)
if ((c = cmp_keypair((*a)->keypairs[i], (*b)->keypairs[i])))
return c;
return i == (*a)->keypair_count ? -1 : 1;
}
int keyring_dump(keyring_file *k, XPRINTF xpf, int include_secret)
{
int cn, in, kp;
unsigned nids = 0;
for (cn = in = kp = 0; keyring_sanitise_position(k, &cn, &in, &kp) == 0; ++in)
++nids;
const keyring_identity *idx[nids];
unsigned i = 0;
for (cn = in = kp = 0; keyring_sanitise_position(k, &cn, &in, &kp) == 0; ++in) {
assert(i < nids);
idx[i++] = k->contexts[cn]->identities[in];
}
assert(i == nids);
qsort(idx, nids, sizeof(idx[0]), (int(*)(const void *, const void *)) cmp_identity_ptrs);
for (i = 0; i != nids; ++i) {
const keyring_identity *id = idx[i];
for (kp = 0; kp < id->keypair_count; ++kp) {
keypair *keyp = id->keypairs[kp];
xprintf(xpf, "%u: type=%u", i, keyp->type);
const char *kts = keytype_str(keyp->type);
if (kts && kts[0])
xprintf(xpf, "(%s)", kts);
assert(keyp->type != 0);
assert(keyp->type < NELS(keytypes));
xprintf(xpf, " ");
if (keytypes[keyp->type].dumper)
keytypes[keyp->type].dumper(keyp, xpf, include_secret);
else
dump_raw_hex(keyp, xpf, include_secret);
xprintf(xpf, "\n");
}
}
return 0;
}

10
mem.c
View File

@ -30,6 +30,16 @@ void *_emalloc(struct __sourceloc __whence, size_t bytes)
return new;
}
void *_erealloc(struct __sourceloc __whence, void *ptr, size_t bytes)
{
char *new = realloc(ptr, bytes);
if (!new) {
WHYF_perror("realloc(%p, %lu)", ptr, (unsigned long)bytes);
return NULL;
}
return new;
}
void *_emalloc_zero(struct __sourceloc __whence, size_t bytes)
{
char *new = _emalloc(__whence, bytes);

15
mem.h
View File

@ -40,6 +40,12 @@ void _serval_debug_free(void *p, struct __sourceloc whence);
*/
void *_emalloc(struct __sourceloc, size_t bytes);
/* Equivalent to realloc(3), but logs an error before returning NULL.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
void *_erealloc(struct __sourceloc __whence, void *ptr, size_t bytes);
/* Equivalent to malloc(3) followed by memset(3) to zerofill, but logs an error
* before returning NULL.
*
@ -57,9 +63,10 @@ void *_emalloc_zero(struct __sourceloc, size_t bytes);
char *_str_edup(struct __sourceloc, const char *str);
char *_strn_edup(struct __sourceloc, const char *str, size_t len);
#define emalloc(bytes) _emalloc(__HERE__, (bytes))
#define emalloc_zero(bytes) _emalloc_zero(__HERE__, (bytes))
#define str_edup(str) _str_edup(__HERE__, (str))
#define strn_edup(str, len) _strn_edup(__HERE__, (str), (len))
#define emalloc(bytes) _emalloc(__HERE__, (bytes))
#define erealloc(ptr, bytes) _erealloc(__HERE__, (ptr), (bytes))
#define emalloc_zero(bytes) _emalloc_zero(__HERE__, (bytes))
#define str_edup(str) _str_edup(__HERE__, (str))
#define strn_edup(str, len) _strn_edup(__HERE__, (str), (len))
#endif // __SERVALDNA__MEM_H

62
rotbuf.c Normal file
View File

@ -0,0 +1,62 @@
/*
Serval rotated buffer primitives
Copyright (C) 2013 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#define __ROTBUF_INLINE
#include <assert.h>
#include "rotbuf.h"
ssize_t rotbuf_delta(const struct rotbuf *origin, const struct rotbuf *dest)
{
assert(origin->buf == dest->buf);
assert(origin->ebuf == dest->ebuf);
assert(origin->start == dest->start);
const unsigned char *org = origin->cursor;
const unsigned char *dst = dest->cursor;
if (org < origin->start)
org += origin->ebuf - origin->buf;
assert(org >= origin->start);
if (dst < dest->start)
dst += dest->ebuf - dest->buf;
assert(dst >= dest->start);
return dst - org;
}
/* Return a pointer/length pair describing the contiguous memory region at the current cursor, and
* advance the cursor to the next byte after that region (ie, to the start of the next region). If
* the cursor is already at or past the end of the buffer, returns 0, otherwise sets *buf and *len
* and returns 1.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int rotbuf_next_chunk(struct rotbuf *rb, unsigned char **bufp, size_t *lenp)
{
if (rb->wrap)
return 0;
if (rb->cursor >= rb->start) {
*bufp = rb->cursor;
*lenp = rb->ebuf - rb->cursor;
rb->cursor = rb->buf;
return 1;
}
*bufp = rb->cursor;
*lenp = rb->start - rb->cursor;
rb->cursor = rb->start;
++rb->wrap;
return 1;
}

263
rotbuf.h Normal file
View File

@ -0,0 +1,263 @@
/*
Serval rotated buffer primitives
Copyright (C) 2013 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __ROTBUF_H__
#define __ROTBUF_H__
#include <stdio.h> // for EOF
#include <sys/types.h> // for size_t, ssize_t
#include "log.h"
#ifndef __ROTBUF_INLINE
# if __GNUC__ && !__GNUC_STDC_INLINE__
# define __ROTBUF_INLINE extern inline
# else
# define __ROTBUF_INLINE inline
# endif
#endif
/* A rotated buffer is a simple buffer (pointer and length) in which the initial byte is at a given
* offset within the buffer, and the content wraps around. A rotbuf structure describes the buffer
* using the buf, ebuf and start pointers.
*
* A rotbuf structure provides a single cursor for reading or writing the buffer, analogous to a
* simple memory pointer in a conventional (non-rotated) buffer.
*
* The rotbuf structure provides a wrap counter for detecting when the cursor has wrapped around
* back to the start position. The wrap counter is set to 1 when the cursor has advanced exactly
* len bytes around the buffer, and thereafter the wrap counter is incremented instead of advancing
* the cursor, so that overflowing the buffer will not overwrite the first pass with subsequent
* passes.
*
* The following invariants hold:
*
* - the total cursor advance count (real + attempted read/written bytes) is
* wrap ? (len + wrap - 1) : ((cursor - start) MOD len)
*
* - the total bytes actually read/written in the memory region:
* wrap ? len : ((cursor - start) MOD len)
*
* where MOD is the proper arithmetic modulo, not the C '%' operator which has undefined
* semantics for negative dividends.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
struct rotbuf {
unsigned char *buf;
unsigned char *ebuf;
unsigned char *start;
unsigned char *cursor;
unsigned int wrap;
};
#define RBUF_NULL ((struct rotbuf){.buf = NULL, .ebuf = NULL, .start = NULL, .cursor = NULL, .wrap = 0 })
/* Initialise the given rotbuf structure to use the given memory region (buf, size) as the buffer,
* with the given offset (rot) as the start point. If rot exceeds size or is negative, then it is
* used modulus the length (using proper modulus arithmetic, not the broken C '%' operator
* semantics), to ensure it lies within the buffer.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
__ROTBUF_INLINE void rotbuf_init(struct rotbuf *rb, unsigned char *buf, size_t size, ssize_t rot)
{
rb->buf = buf;
rb->ebuf = buf + size;
rb->start = buf + (rot < 0 ? size - 1 - (-1 - rot) % size : rot % size);
rb->cursor = rb->start;
rb->wrap = 0;
}
/* Reset the given rotbuf structure cursor to the start position.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
__ROTBUF_INLINE void rotbuf_reset(struct rotbuf *rb)
{
rb->cursor = rb->start;
rb->wrap = 0;
}
/* Return the total number of bytes advanced through the given rotated buffer, excluding any
* overrun.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
__ROTBUF_INLINE size_t rotbuf_position(struct rotbuf *rb)
{
if (rb->wrap)
return rb->ebuf - rb->buf;
if (rb->cursor >= rb->start)
return rb->cursor - rb->start;
return (rb->cursor - rb->buf) + (rb->ebuf - rb->start);
}
/* Return the total number of bytes remaining to be advanced to reach the end
* of the given rotated buffer. If the buffer has overrun, this will be zero.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
__ROTBUF_INLINE size_t rotbuf_remain(struct rotbuf *rb)
{
if (rb->wrap)
return 0;
if (rb->cursor < rb->start)
return rb->start - rb->cursor;
return (rb->ebuf - rb->cursor) + (rb->start - rb->buf);
}
/* Return the total number of bytes advanced through the given rotated buffer, including any
* overrun.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
__ROTBUF_INLINE size_t rotbuf_count(struct rotbuf *rb)
{
return rb->wrap ? (rb->ebuf - rb->buf) + rb->wrap - 1 : rotbuf_position(rb);
}
void rotbuf_log(struct __sourceloc __whence, int log_level, const char *prefix, const struct rotbuf *rb);
/* Advance the cursor by a given number of bytes (non negative). Advancing the cursor over the
* final byte in the buffer sets the 'wrap' counter to 1. All further advances are simply added to
* the 'wrap' counter.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
__ROTBUF_INLINE void rotbuf_advance(struct rotbuf *rb, size_t len)
{
if (rb->wrap)
rb->wrap += len;
else if (len) {
if (rb->cursor >= rb->start) {
if ((rb->cursor += len) >= rb->ebuf) {
rb->cursor -= rb->ebuf - rb->buf;
if (rb->cursor >= rb->start) {
rb->wrap = 1 + (rb->cursor - rb->start);
rb->cursor = rb->start;
}
}
} else if ((rb->cursor += len) >= rb->start) {
rb->wrap = 1 + (rb->cursor - rb->start);
rb->cursor = rb->start;
}
}
}
/* Fetch a byte from the rotated buffer at the current cursor position and advance the cursor.
* Fetching the last byte from the buffer sets the 'wrap' pointer to 1. Fetching from the buffer at
* or beyond its length increments the 'wrap' counter and returns EOF.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
__ROTBUF_INLINE int rotbuf_getc(struct rotbuf *rb)
{
if (rb->wrap) {
++rb->wrap;
return EOF;
}
unsigned char c = *rb->cursor++;
if (rb->cursor == rb->ebuf)
rb->cursor = rb->buf;
if (rb->cursor == rb->start)
rb->wrap = 1;
return c;
}
/* Fetch many bytes from the rotated buffer at the current cursor position and advance the cursor
* over the fetched bytes. Bytes from beyond the buffer end are written into the destination as EOF
* and the 'wrap' counter is incremented. Exactly equivalent to:
*
* while (len--)
* *buf++ = rotbuf_getc(rb);
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
__ROTBUF_INLINE void rotbuf_getbuf(struct rotbuf *rb, unsigned char *buf, size_t len)
{
// TODO optimise by using rotbuf_next_chunk() and memcpy()
while (len--)
*buf++ = rotbuf_getc(rb);
}
/* Append a byte to the rotated buffer at the current cursor position and advance the cursor. If
* the byte exactly fills the buffer then the 'wrap' counter is set to 1. Appending to the buffer
* at or beyond its length does not write into the buffer, but instead increments the 'wrap'
* counter.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
__ROTBUF_INLINE void rotbuf_putc(struct rotbuf *rb, unsigned char c)
{
if (rb->wrap)
++rb->wrap;
else {
*rb->cursor++ = c;
if (rb->cursor == rb->ebuf)
rb->cursor = rb->buf;
if (rb->cursor == rb->start)
rb->wrap = 1;
}
}
/* Write many bytes from the rotated buffer at the current cursor position and advance the cursor
* over the written bytes. Bytes are not written beyond the end of the buffer, instead the 'wrap'
* counter is incremented. Exactly equivalent to:
*
* while (len--)
* rotbuf_putc(rb, *buf++);
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
__ROTBUF_INLINE void rotbuf_putbuf(struct rotbuf *rb, const unsigned char *buf, size_t len)
{
// TODO optimise by using rotbuf_next_chunk() and memcpy()
while (len--)
rotbuf_putc(rb, *buf++);
}
/* Return the difference between two cursors in the same rotated buffer. Equivalent to pointer
* subtraction in a normal (non-rotated) buffer.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
ssize_t rotbuf_delta(const struct rotbuf *origin, const struct rotbuf *dest);
/* Return a pointer/length pair describing the contiguous memory region at the current cursor, and
* advance the cursor to the next byte after that region (ie, to the start of the next region). If
* the cursor is already at or past the end of the buffer, returns 0, otherwise sets *buf and *len
* and returns 1.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int rotbuf_next_chunk(struct rotbuf *rb, unsigned char **bufp, size_t *lenp);
__ROTBUF_INLINE void rotbuf_log(struct __sourceloc __whence, int log_level, const char *prefix, const struct rotbuf *rb)
{
LOGF(log_level, "%sbuf=%p ebuf=%p start=%p cursor=%p wrap=%u",
prefix ? prefix : "",
rb->buf,
rb->ebuf,
rb->start,
rb->cursor,
rb->wrap);
}
#endif // __ROTBUF_H__

View File

@ -265,7 +265,7 @@ int keyring_identity_mac(keyring_context *c,keyring_identity *id,
extern keyring_file *keyring;
/* Public calls to keyring management */
keyring_file *keyring_open(char *file);
keyring_file *keyring_open(const char *path);
keyring_file *keyring_open_instance();
keyring_file *keyring_open_instance_cli(const struct cli_parsed *parsed);
int keyring_enter_pin(keyring_file *k, const char *pin);
@ -284,6 +284,7 @@ int keyring_commit(keyring_file *k);
keyring_identity *keyring_create_identity(keyring_file *k,keyring_context *c, const char *pin);
int keyring_seed(keyring_file *k);
void keyring_identity_extract(const keyring_identity *id, const unsigned char **sidp, const char **didp, const char **namep);
int keyring_dump(keyring_file *k, XPRINTF xpf, int include_secret);
/* Make sure we have space to put bytes of the packet as we go along */
#define CHECK_PACKET_LEN(B) {if (((*packet_len)+(B))>=packet_maxlen) { return WHY("Packet composition ran out of space."); } }

View File

@ -54,6 +54,7 @@ SERVAL_SOURCES = $(SERVAL_BASE)audiodevices.c \
$(SERVAL_BASE)rhizome_http.c \
$(SERVAL_BASE)rhizome_packetformats.c \
$(SERVAL_BASE)rhizome_store.c \
$(SERVAL_BASE)rotbuf.c \
$(SERVAL_BASE)serval_packetvisualise.c \
$(SERVAL_BASE)server.c \
$(SERVAL_BASE)sha2.c \

3
str.h
View File

@ -84,7 +84,8 @@ size_t toprint_str_len(const char *srcStr, const char quotes[2]);
size_t str_fromprint(unsigned char *dst, const char *src);
#define alloca_toprint(dstlen,buf,len) toprint((char *)alloca((dstlen) == -1 ? toprint_len((const char *)(buf),(len), "``") + 1 : (dstlen)), (dstlen), (const char *)(buf), (len), "``")
#define alloca_str_toprint(str) toprint_str((char *)alloca(toprint_str_len(str, "``") + 1), -1, (str), "``")
#define alloca_str_toprint_quoted(str, quotes) toprint_str((char *)alloca(toprint_str_len((str), (quotes)) + 1), -1, (str), (quotes))
#define alloca_str_toprint(str) alloca_str_toprint_quoted(str, "``")
/* Like strchr(3), but only looks for 'c' in the first 'n' characters of 's', stopping at the first
* nul char in 's'.

View File

@ -298,6 +298,7 @@ runTests() {
# Environment variables and temporary directories that test cases
# depend upon.
export COLUMNS=80 # for ls(1) multi-column output
export TFWSOURCE="$testSourceFile"
export TFWLOG="$_tfw_logdir_test"
export TFWUNIQUE=$_tfw_unique
export TFWVAR="$_tfw_tmp/var"

View File

@ -1,8 +1,8 @@
#!/bin/bash
# Stress tests for Serval rhizome protocol.
# Tests for Serval keyring
#
# Copyright 2012 Serval Project, Inc.
# Copyright 2012-2013 Serval Project, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -25,7 +25,9 @@ shopt -s extglob
setup() {
setup_servald
executeOk_servald config set debug.keyring on
executeOk_servald config \
set log.console.level debug \
set debug.keyring on
executeOk_servald keyring list
assert_keyring_list 0
}
@ -103,6 +105,24 @@ test_KeyringPinIdentityPinless() {
assert_keyring_list 0
}
doc_KeyringPinIdentityPin="Keyring PIN with identity PINs"
test_KeyringPinIdentityPin() {
executeOk_servald keyring add --keyring-pin=yellow 'duck'
executeOk_servald keyring add --keyring-pin=yellow 'chicken'
executeOk_servald keyring list
assert_keyring_list 0
executeOk_servald keyring list --keyring-pin=yellow
assert_keyring_list 0
executeOk_servald keyring list --keyring-pin=yellow --entry-pin=duck
assert_keyring_list 1
executeOk_servald keyring list --keyring-pin=yellow --entry-pin=chicken
assert_keyring_list 1
executeOk_servald keyring list --keyring-pin=yellow --entry-pin=duck --entry-pin=chicken
assert_keyring_list 2
executeOk_servald keyring list --entry-pin=duck --entry-pin=chicken
assert_keyring_list 0
}
doc_KeyringAutoCreate="Starting a server with no keyring creates a valid identity"
test_KeyringAutoCreate() {
start_servald_server
@ -118,4 +138,39 @@ teardown_KeyringAutoCreate() {
report_servald_server
}
doc_CompatibleBack1="Can read old keyring file (1)"
setup_CompatibleBack1() {
setup_servald
executeOk_servald config \
set log.console.level debug \
set debug.keyring on
assert mkdir -p "$SERVALINSTANCE_PATH"
assert cp "${TFWSOURCE%/*}/testdata/serval.keyring-1" "$SERVALINSTANCE_PATH/serval.keyring"
}
test_CompatibleBack1() {
executeOk_servald keyring dump --secret
assertStdoutLineCount '==' 11
assertStdoutGrep "^0: type=.*(CRYPTOBOX).*pub=6EE9BCA4AE1A264D18F42C18EF910915F98F0C5D9ECD853ECB1547B752119F05\b"
assertStdoutGrep "^0: type=.*(CRYPTOBOX).*sec=3611C4CC6BC7A167088492AD021103048C78F16AC22724B06B96A3E865163137\b"
assertStdoutGrep "^0: type=.*(CRYPTOSIGN).*pub=DA75D0A0EFCC75A9CCA634C2FC36EC19416B0CDE2A014520F911B95D1DB6224A\b"
assertStdoutGrep "^0: type=.*(CRYPTOSIGN).*sec=A90608CE5135D54CDBA922426D425F9D5494C8DB83D658EE518D88BCDAF04B1EDA75D0A0EFCC75A9CCA634C2FC36EC19416B0CDE2A014520F911B95D1DB6224A\b"
assertStdoutGrep "^0: type=.*(DID).*DID=\"555001234\""
assertStdoutGrep "^0: type=.*(DID).*Name=\"Jones\""
assertStdoutGrep "^0: type=.*(RHIZOME).*sec=6FBCD69A8DE983324C414F8B5BA83646EF4421AA877BB06F18AFA78EEC42A4BC\b"
assertStdoutGrep "^1: type=.*(CRYPTOBOX).*pub=C10C91D24BF210DD6733ED2424B4509E6CC4402D34055B6D29F7A778701AA542\b"
assertStdoutGrep "^1: type=.*(CRYPTOBOX).*sec=9D22FF754D90971B062A26BAA1D2E95C03B67688B3C71334C3512DB8FA996570\b"
assertStdoutGrep "^1: type=.*(CRYPTOSIGN).*pub=9F40922AD9435B1AD2880442B43D32CC345CAAFF13599DC241C13E421A4D672A\b"
assertStdoutGrep "^1: type=.*(CRYPTOSIGN).*sec=648F3171A612F82ED01BA1475AD9F538F97C1D95067D1D9F7F324B8F9C0489289F40922AD9435B1AD2880442B43D32CC345CAAFF13599DC241C13E421A4D672A\b"
assertStdoutGrep "^1: type=.*(DID).*DID=\"55500000\""
assertStdoutGrep "^1: type=.*(DID).*Name=\"Smith\""
assertStdoutGrep "^1: type=.*(RHIZOME).*sec=E78E7F80CCEC0179A07E1DECC9D6C3C89089A9FD3787CF5273FD0F8E9CE62780\b"
assertStdoutGrep "^2: type=.*(CRYPTOBOX).*pub=EEBF3AC19E7EE58722A0F6D4A4D5894A72F5C71030C3399FE75808DCF6C6254B\b"
assertStdoutGrep "^2: type=.*(CRYPTOBOX).*sec=5822F44BF118F7A533B74046A400FA8C83A6B69EDD0166D8175D0B70267A2CA1\b"
assertStdoutGrep "^2: type=.*(CRYPTOSIGN).*pub=13A4B69DE4D4A1D89DB22E1D433B0C7DEB2E9C86A2FD98D9F689B7EDEFB64E20\b"
assertStdoutGrep "^2: type=.*(CRYPTOSIGN).*sec=2B221014CED25245A181B5164030D7823B8FC288DE724CAC029CE6A5E195A12713A4B69DE4D4A1D89DB22E1D433B0C7DEB2E9C86A2FD98D9F689B7EDEFB64E20\b"
assertStdoutGrep "^2: type=.*(RHIZOME).*sec=4483DF66540740736A05055A96D8C8B84F05D52808380DD52462B8EA5CCAB0D3\b"
assertStdoutGrep --matches=0 "^2: type=.*(DID)"
assertStdoutGrep --matches=0 "^3:"
}
runTests "$@"

BIN
tests/testdata/serval.keyring-1 vendored Normal file

Binary file not shown.