mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-01-19 03:06:28 +00:00
substantial work towards public key signing of MDP frames and
the associated SID:SAS mapping cache and request packets.
This commit is contained in:
parent
2bcf07144e
commit
caab3078e5
@ -435,7 +435,7 @@ int app_mdp_ping(int argc,char **argv,struct command_line_option *o)
|
|||||||
while(1) {
|
while(1) {
|
||||||
/* Now send the ping packets */
|
/* Now send the ping packets */
|
||||||
mdp.packetTypeAndFlags=MDP_TX;
|
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"); }
|
fprintf(stderr,"WARNING: broadcast ping packets will not be encryped.\n"); }
|
||||||
mdp.out.src.port=port;
|
mdp.out.src.port=port;
|
||||||
bcopy(srcsid,mdp.out.src.sid,SID_SIZE);
|
bcopy(srcsid,mdp.out.src.sid,SID_SIZE);
|
||||||
|
113
keyring.c
113
keyring.c
@ -1055,6 +1055,119 @@ int keyring_sanitise_position(keyring_file *k,int *cn,int *in,int *kp)
|
|||||||
return 0;
|
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)
|
int keyring_find_sid(keyring_file *k,int *cn,int *in,int *kp,unsigned char *sid)
|
||||||
{
|
{
|
||||||
|
119
overlay_mdp.c
119
overlay_mdp.c
@ -283,6 +283,39 @@ int overlay_saw_mdp_containing_frame(int interface,overlay_frame *f,long long no
|
|||||||
return WHY("decryption not implemented");
|
return WHY("decryption not implemented");
|
||||||
mdp.packetTypeAndFlags|=MDP_NOSIGN; break;
|
mdp.packetTypeAndFlags|=MDP_NOSIGN; break;
|
||||||
case OF_CRYPTO_SIGNED:
|
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");
|
return WHY("signature verification not implemented");
|
||||||
mdp.packetTypeAndFlags|=MDP_NOCRYPT; break;
|
mdp.packetTypeAndFlags|=MDP_NOCRYPT; break;
|
||||||
case OF_CRYPTO_CIPHERED|OF_CRYPTO_SIGNED:
|
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 {
|
} else {
|
||||||
/* No socket is bound, ignore the packet ... except for magic sockets */
|
/* No socket is bound, ignore the packet ... except for magic sockets */
|
||||||
switch(mdp->out.dst.port) {
|
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
|
/* Echo is easy: we swap the sender and receiver addresses (and thus port
|
||||||
numbers) and send the frame back. */
|
numbers) and send the frame back. */
|
||||||
@ -468,7 +506,8 @@ int overlay_mdp_sanitytest_sourceaddr(sockaddr_mdp *src,int userGeneratedFrameP,
|
|||||||
if (!userGeneratedFrameP)
|
if (!userGeneratedFrameP)
|
||||||
if (overlay_address_is_local(src->sid)) {
|
if (overlay_address_is_local(src->sid)) {
|
||||||
switch(src->port) {
|
switch(src->port) {
|
||||||
case 7:
|
case MDP_PORT_ECHO:
|
||||||
|
case MDP_PORT_KEYMAPREQUEST:
|
||||||
/* locally hard-wired port */
|
/* locally hard-wired port */
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
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
|
flag is not set. Also, MDP_NOSIGN must also be applied, until
|
||||||
NaCl cryptobox keys can be used for signing. */
|
NaCl cryptobox keys can be used for signing. */
|
||||||
if (broadcast) {
|
if (broadcast) {
|
||||||
if ((mdp->packetTypeAndFlags&(MDP_NOCRYPT|MDP_NOSIGN))
|
if (!(mdp->packetTypeAndFlags&MDP_NOCRYPT))
|
||||||
!=(MDP_NOCRYPT|MDP_NOSIGN))
|
|
||||||
return overlay_mdp_reply_error(mdp_named_socket,
|
return overlay_mdp_reply_error(mdp_named_socket,
|
||||||
recvaddr,recvaddrlen,5,
|
recvaddr,recvaddrlen,5,
|
||||||
"Broadcast packets cannot be encrypted "
|
"Broadcast packets cannot be encrypted "); }
|
||||||
"or signed (signing will be possible in"
|
|
||||||
" a future version).");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prepare the overlay frame for dispatch */
|
/* Prepare the overlay frame for dispatch */
|
||||||
struct overlay_frame *frame;
|
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");
|
return WHY("ciphered MDP packets not implemented");
|
||||||
break;
|
break;
|
||||||
case MDP_NOCRYPT:
|
case MDP_NOCRYPT:
|
||||||
/* clear text, but signed (need to think about how to implement this
|
/* Payload is sent unencrypted, but signed.
|
||||||
while NaCl cannot sign using CryptoBox keys. We could use a
|
|
||||||
CryptoSign key, and allow queries as to the authenticity of said key
|
To save space we do a trick where we hash the payload, and get the
|
||||||
via authcrypted channel between the parties. */
|
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;
|
frame->modifiers=OF_CRYPTO_SIGNED;
|
||||||
op_free(frame);
|
/* Prepare payload */
|
||||||
return WHY("signed MDP packets not implemented");
|
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;
|
break;
|
||||||
case MDP_NOSIGN|MDP_NOCRYPT: /* clear text and no signature */
|
case MDP_NOSIGN|MDP_NOCRYPT: /* clear text and no signature */
|
||||||
frame->modifiers=0;
|
frame->modifiers=0;
|
||||||
|
8
serval.h
8
serval.h
@ -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_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_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);
|
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);
|
int keyring_commit(keyring_file *k);
|
||||||
keyring_identity *keyring_create_identity(keyring_file *k,keyring_context *c,
|
keyring_identity *keyring_create_identity(keyring_file *k,keyring_context *c,
|
||||||
char *pin);
|
char *pin);
|
||||||
@ -1082,6 +1085,9 @@ typedef struct sockaddr_mdp {
|
|||||||
} sockaddr_mdp;
|
} sockaddr_mdp;
|
||||||
unsigned char *keyring_get_nm_bytes(sockaddr_mdp *priv,sockaddr_mdp *pub);
|
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_TYPE_MASK 0xff
|
||||||
#define MDP_FLAG_MASK 0xff00
|
#define MDP_FLAG_MASK 0xff00
|
||||||
#define MDP_FORCE 0x0100
|
#define MDP_FORCE 0x0100
|
||||||
@ -1138,6 +1144,8 @@ typedef struct overlay_mdp_frame {
|
|||||||
};
|
};
|
||||||
} overlay_mdp_frame;
|
} overlay_mdp_frame;
|
||||||
|
|
||||||
|
int keyring_mapping_request(keyring_file *k,overlay_mdp_frame *req);
|
||||||
|
|
||||||
int setVerbosity(char *optarg);
|
int setVerbosity(char *optarg);
|
||||||
|
|
||||||
/* Client-side MDP function */
|
/* Client-side MDP function */
|
||||||
|
Loading…
Reference in New Issue
Block a user