substantial work towards public key signing of MDP frames and

the associated SID:SAS mapping cache and request packets.
This commit is contained in:
gardners 2012-04-15 03:17:36 +09:30
parent 2bcf07144e
commit caab3078e5
4 changed files with 227 additions and 15 deletions

View File

@ -435,7 +435,7 @@ int app_mdp_ping(int argc,char **argv,struct command_line_option *o)
while(1) {
/* Now send the ping packets */
mdp.packetTypeAndFlags=MDP_TX;
if (broadcast) { mdp.packetTypeAndFlags|=MDP_NOCRYPT|MDP_NOSIGN;
if (broadcast) { mdp.packetTypeAndFlags|=MDP_NOCRYPT;
fprintf(stderr,"WARNING: broadcast ping packets will not be encryped.\n"); }
mdp.out.src.port=port;
bcopy(srcsid,mdp.out.src.sid,SID_SIZE);

113
keyring.c
View File

@ -1055,6 +1055,119 @@ int keyring_sanitise_position(keyring_file *k,int *cn,int *in,int *kp)
return 0;
}
unsigned char *keyring_find_sas_private(keyring_file *k,unsigned char *sid)
{
int cn=0,in=0,kp=0;
if (!keyring_find_sid(k,&cn,&in,&kp,sid))
WHYRETNULL("Could not find SID in keyring, so can't find SAS");
for(kp=0;kp<k->contexts[cn]->identities[in]->keypair_count;kp++)
if (k->contexts[cn]->identities[in]->keypairs[kp]->type==KEYTYPE_CRYPTOSIGN)
return k->contexts[cn]->identities[in]->keypairs[kp]->private_key;
WHYRETNULL("Identity lacks SAS");
}
struct sid_sas_mapping {
unsigned char sid[SID_SIZE];
unsigned char sas_public[crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES];
unsigned long long last_request_time_in_ms;
unsigned char validP;
};
#define MAX_SID_SAS_MAPPINGS 1024
int sid_sas_mapping_count=0;
struct sid_sas_mapping sid_sas_mappings[MAX_SID_SAS_MAPPINGS];
int keyring_mapping_request(keyring_file *k,overlay_mdp_frame *req)
{
if (!k) return WHY("keyring is null");
if (!req) return WHY("req is null");
/* The authcryption of the MDP frame proves that the SAS key is owned by the
owner of the SID, and so is absolutely compulsory. */
if (req->packetTypeAndFlags&MDP_NOCRYPT)
return WHY("mapping requests must be performed under authcryption");
if (req->out.payload_length==1) {
/* It's a request, so find the SAS for the SID the request was addressed to,
use that to sign that SID, and then return it in an authcrypted frame. */
WHY("Not implemented");
} else {
/* It's probably a response. */
switch(req->out.payload[0]) {
case KEYTYPE_CRYPTOSIGN:
WHY("Not implemented");
break;
default:
WHY("Key mapping response for unknown key type. Oh well.");
}
}
return WHY("Not implemented");
}
unsigned char *keyring_find_sas_public(keyring_file *k,unsigned char *sid)
{
/* Main issue here is that we need to have the public SAS key for
this sender. This needs to be cached somewhere (possibly persistently,
or not depending on a persons paranoia level, as having a SID:SAS
mapping implies that at least machine to machine contact has occurred
with that identity.
See the Serval Security Framework document for a discussion of some of the
privacy and security issues that vex a persistent store.
For now we will just use a non-persistent cache for safety (and it happens
to be easy to implement as well :)
*/
int i;
long long now=overlay_gettime_ms();
for(i=0;i<sid_sas_mapping_count;i++)
{
if (bcmp(sid,sid_sas_mappings[i].sid,SID_SIZE)) continue;
if (sid_sas_mappings[i].validP) return sid_sas_mappings[i].sas_public;
/* Don't flood the network with mapping requests */
if ((now-sid_sas_mappings[i].last_request_time_in_ms)<1000) return NULL;
/* we can request again, so fall out to where we do that.
i is set to this mapping, so the request process will update this
record. */
break;
}
/* allocate mapping slot or replace one at random, depending on how full things
are */
if (i==sid_sas_mapping_count) {
if (i>=MAX_SID_SAS_MAPPINGS) i=random()%MAX_SID_SAS_MAPPINGS;
else sid_sas_mapping_count++;
}
/* pre-populate mapping slot */
bcopy(&sid[0],&sid_sas_mappings[i].sid[0],SID_SIZE);
bzero(&sid_sas_mappings[i].sas_public,
crypto_sign_edwards25519sha512batch_PUBLICKEYBYTES);
sid_sas_mappings[i].validP=0;
sid_sas_mappings[i].last_request_time_in_ms=now;
/* request mapping. */
overlay_mdp_frame mdp;
mdp.packetTypeAndFlags=MDP_TX;
bcopy(&sid[0],&mdp.out.dst.sid[0],SID_SIZE);
mdp.out.dst.port=MDP_PORT_KEYMAPREQUEST;
mdp.out.src.port=MDP_PORT_KEYMAPREQUEST;
if (k->contexts[0]->identity_count&&
k->contexts[0]->identities[0]->keypair_count&&
k->contexts[0]->identities[0]->keypairs[0]->type
==KEYTYPE_CRYPTOBOX)
bcopy(keyring->contexts[0]->identities[0]->keypairs[0]->public_key,
mdp.out.src.sid,SID_SIZE);
else WHYRETNULL("couldn't request SAS (I don't know who I am)");
mdp.out.payload_length=1;
mdp.out.payload[0]=KEYTYPE_CRYPTOSIGN;
overlay_mdp_dispatch(&mdp,0 /* system generated */,
NULL,0);
return NULL;
}
int keyring_find_sid(keyring_file *k,int *cn,int *in,int *kp,unsigned char *sid)
{

View File

@ -283,6 +283,39 @@ int overlay_saw_mdp_containing_frame(int interface,overlay_frame *f,long long no
return WHY("decryption not implemented");
mdp.packetTypeAndFlags|=MDP_NOSIGN; break;
case OF_CRYPTO_SIGNED:
{
/* This call below will dispatch the request for the SAS if we don't
already have it. In the meantime, we just drop the frame if the SAS
is not available. */
unsigned char *key=keyring_find_sas_public(keyring,mdp.out.src.sid);
if (!key) return WHY("SAS key not currently on record, so cannot verify");
/* get payload and following compacted signature */
b=&f->payload->bytes[0];
len=f->payload->length-crypto_sign_edwards25519sha512batch_BYTES;
/* get hash */
unsigned char hash[crypto_hash_sha512_BYTES];
crypto_hash_sha512(hash,b,len);
/* reconstitute signature by putting hash between two halves of signature */
unsigned char signature[crypto_hash_sha512_BYTES
+crypto_sign_edwards25519sha512batch_BYTES];
bcopy(&b[len],&signature[0],32);
crypto_hash_sha512(&signature[32],b,len);
dump("hash for verification",hash,crypto_hash_sha512_BYTES);
bcopy(&b[len+32],&signature[32+crypto_hash_sha512_BYTES],32);
/* verify signature */
unsigned char m[crypto_hash_sha512_BYTES];
unsigned long long mlen=0;
int result
=crypto_sign_edwards25519sha512batch_open(m,&mlen,
signature,sizeof(signature),
key);
if (result) return WHY("Signature verification failed: incorrect signature");
else WHY("signature check passed");
}
return WHY("signature verification not implemented");
mdp.packetTypeAndFlags|=MDP_NOCRYPT; break;
case OF_CRYPTO_CIPHERED|OF_CRYPTO_SIGNED:
@ -397,7 +430,12 @@ int overlay_saw_mdp_frame(int interface, overlay_mdp_frame *mdp,long long now)
} else {
/* No socket is bound, ignore the packet ... except for magic sockets */
switch(mdp->out.dst.port) {
case 7: /* well known ECHO port for TCP/UDP and now MDP */
case MDP_PORT_KEYMAPREQUEST:
/* Either respond with the appropriate SAS, or record this one if it
verfies out okay. */
WHY("key mapping request");
return keyring_mapping_request(keyring,&mdp);
case MDP_PORT_ECHO: /* well known ECHO port for TCP/UDP and now MDP */
{
/* Echo is easy: we swap the sender and receiver addresses (and thus port
numbers) and send the frame back. */
@ -468,7 +506,8 @@ int overlay_mdp_sanitytest_sourceaddr(sockaddr_mdp *src,int userGeneratedFrameP,
if (!userGeneratedFrameP)
if (overlay_address_is_local(src->sid)) {
switch(src->port) {
case 7:
case MDP_PORT_ECHO:
case MDP_PORT_KEYMAPREQUEST:
/* locally hard-wired port */
return 0;
default:
@ -540,14 +579,10 @@ int overlay_mdp_dispatch(overlay_mdp_frame *mdp,int userGeneratedFrameP,
flag is not set. Also, MDP_NOSIGN must also be applied, until
NaCl cryptobox keys can be used for signing. */
if (broadcast) {
if ((mdp->packetTypeAndFlags&(MDP_NOCRYPT|MDP_NOSIGN))
!=(MDP_NOCRYPT|MDP_NOSIGN))
if (!(mdp->packetTypeAndFlags&MDP_NOCRYPT))
return overlay_mdp_reply_error(mdp_named_socket,
recvaddr,recvaddrlen,5,
"Broadcast packets cannot be encrypted "
"or signed (signing will be possible in"
" a future version).");
}
"Broadcast packets cannot be encrypted "); }
/* Prepare the overlay frame for dispatch */
struct overlay_frame *frame;
@ -643,13 +678,69 @@ int overlay_mdp_dispatch(overlay_mdp_frame *mdp,int userGeneratedFrameP,
return WHY("ciphered MDP packets not implemented");
break;
case MDP_NOCRYPT:
/* clear text, but signed (need to think about how to implement this
while NaCl cannot sign using CryptoBox keys. We could use a
CryptoSign key, and allow queries as to the authenticity of said key
via authcrypted channel between the parties. */
/* Payload is sent unencrypted, but signed.
To save space we do a trick where we hash the payload, and get the
signature of that, but do not send the hash itself, since that can
be reproduced (and indeed must be for verification) at the receiver's
end.
As the signing key is implicit, and the hash is also implicit, we can
chop out part of the signature and thus save some bytes.
*/
frame->modifiers=OF_CRYPTO_SIGNED;
op_free(frame);
return WHY("signed MDP packets not implemented");
/* Prepare payload */
frame->payload=ob_new(1 /* frame type (MDP) */
+1 /* MDP version */
+4 /* dst port */
+4 /* src port */
+crypto_sign_edwards25519sha512batch_BYTES
+mdp->out.payload_length);
{
unsigned char *key=keyring_find_sas_private(keyring,mdp->out.src.sid);
if (!key) { op_free(frame); return WHY("could not find signing key"); }
/* Build plain-text that includes header and hash it so that
we can sign that hash. */
unsigned char hash[crypto_hash_sha512_BYTES];
unsigned char plain[10+mdp->out.payload_length];
/* MDP version 1 */
plain[0]=0x01;
plain[1]=0x01;
/* Ports */
plain[2]=(mdp->out.src.port>>24)&0xff;
plain[3]=(mdp->out.src.port>>16)&0xff;
plain[4]=(mdp->out.src.port>>8)&0xff;
plain[5]=(mdp->out.src.port>>0)&0xff;
plain[6]=(mdp->out.dst.port>>24)&0xff;
plain[7]=(mdp->out.dst.port>>16)&0xff;
plain[8]=(mdp->out.dst.port>>8)&0xff;
plain[9]=(mdp->out.dst.port>>0)&0xff;
/* payload */
bcopy(&mdp->out.payload,&plain[10],mdp->out.payload_length);
/* now hash it */
crypto_hash_sha512(hash,plain,10+mdp->out.payload_length);
unsigned char signature[crypto_hash_sha512_BYTES
+crypto_sign_edwards25519sha512batch_BYTES];
unsigned long long sig_len=0;
crypto_sign_edwards25519sha512batch(signature,&sig_len,
hash,crypto_hash_sha512_BYTES,
key);
if (!sig_len) { op_free(frame); return WHY("Signing MDP frame failed"); }
/* chop hash out of middle of signature since it has to be recomputed
at the far end, anyway, as described above. */
bcopy(&signature[32+64],&signature[32],32);
sig_len-=crypto_hash_sha512_BYTES;
/* ok, now chain plain-text with the signature at the end and send it */
ob_append_bytes(frame->payload,plain,10+mdp->out.payload_length);
/* chop hash out of middle of signature since it has to be recomputed
at the far end, anyway, as described above. */
ob_append_bytes(frame->payload,&signature[0],32);
ob_append_bytes(frame->payload,&signature[32+crypto_hash_sha512_BYTES],32);
}
break;
case MDP_NOSIGN|MDP_NOCRYPT: /* clear text and no signature */
frame->modifiers=0;

View File

@ -244,6 +244,9 @@ int keyring_sanitise_position(keyring_file *k,int *cn,int *in,int *kp);
int keyring_next_identity(keyring_file *k,int *cn,int *in,int *kp);
int keyring_find_did(keyring_file *k,int *cn,int *in,int *kp,char *did);
int keyring_find_sid(keyring_file *k,int *cn,int *in,int *kp,unsigned char *sid);
unsigned char *keyring_find_sas_private(keyring_file *k,unsigned char *sid);
unsigned char *keyring_find_sas_public(keyring_file *k,unsigned char *sid);
int keyring_commit(keyring_file *k);
keyring_identity *keyring_create_identity(keyring_file *k,keyring_context *c,
char *pin);
@ -1082,6 +1085,9 @@ typedef struct sockaddr_mdp {
} sockaddr_mdp;
unsigned char *keyring_get_nm_bytes(sockaddr_mdp *priv,sockaddr_mdp *pub);
#define MDP_PORT_ECHO 7
#define MDP_PORT_KEYMAPREQUEST 0xf0000001
#define MDP_TYPE_MASK 0xff
#define MDP_FLAG_MASK 0xff00
#define MDP_FORCE 0x0100
@ -1138,6 +1144,8 @@ typedef struct overlay_mdp_frame {
};
} overlay_mdp_frame;
int keyring_mapping_request(keyring_file *k,overlay_mdp_frame *req);
int setVerbosity(char *optarg);
/* Client-side MDP function */