Example #1
0
lob_t local_decrypt(local_t local, lob_t outer)
{
  uint8_t secret[crypto_box_BEFORENMBYTES];
  lob_t inner, tmp;

//  * `KEY` - 32 bytes, the sending exchange's ephemeral public key
//  * `NONCE` - 24 bytes, randomly generated
//  * `CIPHERTEXT` - the inner packet bytes encrypted using secretbox() using the `NONCE` as the nonce and the shared secret (derived from the recipients endpoint key and the included ephemeral key) as the key
//  * `AUTH` - 16 bytes, the calculated onetimeauth(`KEY` + `INNER`, SHA256(`NONCE` + secret)) using the shared secret derived from both endpoint keys, the hashing is to minimize the chance that the same key input is ever used twice

  if(outer->body_len <= (32+24+crypto_secretbox_MACBYTES+16)) return NULL;
  tmp = lob_new();
  if(!lob_body(tmp,NULL,outer->body_len-(32+24+crypto_secretbox_MACBYTES+16))) return lob_free(tmp);

  // get the shared secret
  crypto_box_beforenm(secret, outer->body, local->secret);

  // decrypt the inner
  if(crypto_secretbox_open_easy(tmp->body,
    outer->body+32+24,
    tmp->body_len+crypto_secretbox_MACBYTES,
    outer->body+32,
    secret) != 0) return lob_free(tmp);

  // load inner packet
  inner = lob_parse(tmp->body,tmp->body_len);
  lob_free(tmp);

  return inner;
}
Example #2
0
lob_t local_decrypt(local_t local, lob_t outer)
{
  uint8_t key[uECC_BYTES*2], shared[uECC_BYTES], iv[16], hash[32];
  lob_t inner, tmp;

//  * `KEY` - 21 bytes, the sender's ephemeral exchange public key in compressed format
//  * `IV` - 4 bytes, a random but unique value determined by the sender
//  * `INNER` - (minimum 21+2 bytes) the AES-128-CTR encrypted inner packet ciphertext
//  * `HMAC` - 4 bytes, the calculated HMAC of all of the previous KEY+INNER bytes

  if(outer->body_len <= (21+4+0+4)) return NULL;
  tmp = lob_new();
  if(!lob_body(tmp,NULL,outer->body_len-(4+21+4))) return lob_free(tmp);

  // get the shared secret to create the iv+key for the open aes
  uECC_decompress(outer->body,key);
  if(!uECC_shared_secret(key, local->secret, shared)) return lob_free(tmp);
  e3x_hash(shared,uECC_BYTES,hash);
  fold1(hash,hash);
  memset(iv,0,16);
  memcpy(iv,outer->body+21,4);

  // decrypt the inner
  aes_128_ctr(hash,tmp->body_len,iv,outer->body+4+21,tmp->body);

  // load inner packet
  inner = lob_parse(tmp->body,tmp->body_len);
  lob_free(tmp);
  return inner;
}
Example #3
0
lob_t ephemeral_decrypt(ephemeral_t ephem, lob_t outer)
{
  crypto_secretbox_open_easy(outer->body+16+24,
    outer->body+16+24,
    outer->body_len-(16+24),
    outer->body+16,
    ephem->deckey);

  return lob_parse(outer->body+16+24, outer->body_len-(16+24+crypto_secretbox_MACBYTES));
}
Example #4
0
// process an incoming handshake
link_t link_receive_handshake(link_t link, lob_t inner, pipe_t pipe)
{
  link_t ready;
  uint32_t out, err;
  seen_t seen;
  uint8_t csid = 0;
  char *hexid;
  lob_t attached, outer = lob_linked(inner);

  if(!link || !inner || !outer) return LOG("bad args");
  hexid = lob_get(inner, "csid");
  if(!lob_get(link->mesh->keys, hexid)) return LOG("unsupported csid %s",hexid);
  util_unhex(hexid, 2, &csid);
  attached = lob_parse(inner->body, inner->body_len);
  if(!link->key && link_key(link->mesh, attached, csid) != link) return LOG("invalid/mismatch link handshake");
  if((err = e3x_exchange_verify(link->x,outer))) return LOG("handshake verification fail: %d",err);

  out = e3x_exchange_out(link->x,0);
  ready = link_up(link);

  // if bad at, always send current handshake
  if(e3x_exchange_in(link->x, lob_get_uint(inner,"at")) < out)
  {
    LOG("old/bad at: %s (%d,%d,%d)",lob_json(inner),lob_get_int(inner,"at"),e3x_exchange_in(link->x,0),e3x_exchange_out(link->x,0));
    // just reset pipe seen and call link_sync to resend handshake
    for(seen = link->pipes;pipe && seen;seen = seen->next) if(seen->pipe == pipe) seen->at = 0;
    lob_free(link_sync(link));
    return NULL;
  }

  // trust/add this pipe
  if(pipe) link_pipe(link,pipe);

  // try to sync ephemeral key
  if(!e3x_exchange_sync(link->x,outer)) return LOG("sync failed");
  
  // we may need to re-sync
  if(out != e3x_exchange_out(link->x,0)) lob_free(link_sync(link));
  
  // notify of ready state change
  if(!ready && link_up(link))
  {
    LOG("link ready");
    mesh_link(link->mesh, link);
  }
  
  return link;
}
Example #5
0
// get paths from host and query
lob_t util_uri_paths(lob_t uri)
{
  uint32_t i;
  uint16_t port;
  uint8_t *buf;
  size_t len;
  char *key, *value;
  lob_t paths, query = lob_linked(uri);
  if(!query) return NULL;
  paths = NULL;
  
  // gen paths from host/port
  if((port = lob_get_uint(uri,"port")))
  {
    key = lob_get(uri,"host");
    paths = lob_chain(paths);
    lob_set(paths,"type","upd4");
    lob_set(paths,"ip",key);
    lob_set_uint(paths,"port",port);
    paths = lob_chain(paths);
    lob_set(paths,"type","tcp4");
    lob_set(paths,"ip",key);
    lob_set_uint(paths,"port",port);
  }

  // loop through all keyval pairs to find paths
  buf = NULL;
  for(i=0;(key = lob_get_index(query,i));i+=2)
  {
    value = lob_get_index(query,i+1);
    if(util_cmp(key,"paths") != 0 || !value) continue;
    len = base32_decode_floor(strlen(value));
    buf = util_reallocf(buf,len);
    if(!buf) continue;
    if(base32_decode(value,strlen(value),buf,len) < len) continue;
    paths = lob_link(lob_parse(buf,len), paths);
  }
  free(buf);
  
  return paths;
}
Example #6
0
lob_t ephemeral_decrypt(ephemeral_t ephem, lob_t outer)
{
  uint8_t iv[16], hmac[32];

  memset(iv,0,16);
  memcpy(iv,outer->body+16,4);

  memcpy(hmac,ephem->deckey,16);
  memcpy(hmac+16,iv,4);
  // mac just the ciphertext
  hmac_256(hmac,16+4,outer->body+16+4,outer->body_len-(4+16+4),hmac);
  fold3(hmac,hmac);

  if(util_ct_memcmp(hmac,outer->body+(outer->body_len-4),4) != 0) return LOG("hmac failed");

  // decrypt in place
  aes_128_ctr(ephem->deckey,outer->body_len-(16+4+4),iv,outer->body+16+4,outer->body+16+4);

  // return parse attempt
  return lob_parse(outer->body+16+4, outer->body_len-(16+4+4));
}
Example #7
0
int main(int argc, char **argv)
{
  lob_t packet;
  packet = lob_new();
  fail_unless(packet);
  lob_free(packet);

  uint8_t buf[1024];
  char *hex = "001d7b2274797065223a2274657374222c22666f6f223a5b22626172225d7d616e792062696e61727921";
  uint8_t len = strlen(hex)/2;
  util_unhex(hex,strlen(hex),buf);
  packet = lob_parse(buf,len);
  fail_unless(packet);
  fail_unless(lob_len(packet));
  fail_unless(packet->head_len == 29);
  fail_unless(packet->body_len == 11);
  fail_unless(util_cmp(lob_get(packet,"type"),"test") == 0);
  fail_unless(util_cmp(lob_get(packet,"foo"),"[\"bar\"]") == 0);
  
  lob_free(packet);
  packet = lob_new();
  lob_set_base32(packet,"32",buf,len);
  fail_unless(lob_get(packet,"32"));
  fail_unless(strlen(lob_get(packet,"32")) == (base32_encode_length(len)-1));
  lob_t bin = lob_get_base32(packet,"32");
  fail_unless(bin);
  fail_unless(bin->body_len == len);

  lob_set(packet,"key","value");
  fail_unless(lob_keys(packet) == 2);

  // test sorting
  lob_set(packet,"zz","value");
  lob_set(packet,"a","value");
  lob_set(packet,"z","value");
  lob_sort(packet);
  fail_unless(util_cmp(lob_get_index(packet,0),"32") == 0);
  fail_unless(util_cmp(lob_get_index(packet,2),"a") == 0);
  fail_unless(util_cmp(lob_get_index(packet,4),"key") == 0);
  fail_unless(util_cmp(lob_get_index(packet,6),"z") == 0);
  fail_unless(util_cmp(lob_get_index(packet,8),"zz") == 0);
  lob_free(packet);
  
  // minimal comparison test
  lob_t a = lob_new();
  lob_set(a,"foo","bar");
  lob_t b = lob_new();
  lob_set(b,"foo","bar");
  fail_unless(lob_cmp(a,b) == 0);
  lob_set(b,"bar","foo");
  fail_unless(lob_cmp(a,b) != 0);

  // lots of basic list testing
  lob_t list = lob_new();
  lob_t item = lob_new();
  fail_unless(lob_push(list,item));
  fail_unless(lob_pop(list) == item);
  list = item->next;
  fail_unless((list = lob_unshift(list,item)));
  fail_unless(lob_shift(list) == item);
  list = item->next;
  fail_unless(lob_push(list,item));
  fail_unless(list->next == item);
  lob_t insert = lob_new();
  fail_unless(lob_insert(list,list,insert));
  fail_unless(list->next == insert);
  fail_unless(insert->next == item);
  fail_unless(lob_splice(list,insert));
  fail_unless(list->next == item);

  lob_t array = lob_array(list);
  fail_unless(array);
  fail_unless(util_cmp(lob_json(array),"[,]") == 0);

  fail_unless(lob_freeall(list) == NULL);

  // simple index testing
  lob_t index = lob_new();
  lob_t c1 = lob_new();
  lob_set(c1,"id","c1");
  lob_push(index,c1);
  lob_t c2 = lob_new();
  lob_set(c2,"id","c2");
  lob_push(index,c2);
  fail_unless(lob_match(index,"id","c1") == c1);
  fail_unless(lob_match(index,"id","c2") == c2);
  
  float f = 42.42;
  lob_t ft = lob_new();
  lob_head(ft,(uint8_t*)"{\"foo\":42.42}",13);
  fail_unless(lob_get_float(ft,"foo") == f);
  lob_set_float(ft,"bar2",f,2);
  fail_unless(lob_get_float(ft,"bar2") == f);
  lob_set_float(ft,"bar1",f,1);
  fail_unless(lob_get_cmp(ft,"bar1","42.4") == 0);
  lob_set_float(ft,"bar0",f,0);
  fail_unless(lob_get_int(ft,"bar0") == 42);
  LOG("floats %s",lob_json(ft));

  return 0;
}
Example #8
0
// the next frame of data in/out, if data NULL bool is just ready check
util_frames_t util_frames_inbox(util_frames_t frames, uint8_t *data, uint8_t *meta)
{
  if(!frames) return LOG("bad args");
  if(frames->err) return LOG("frame state error");
  if(!data) return util_frames_await(frames);
  
  // conveniences for code readability
  uint8_t size = PAYLOAD(frames);
  uint32_t hash1;
  memcpy(&(hash1),data+size,4);
  uint32_t hash2 = murmur4(data,size);
  
//  LOG("frame sz %u hash rx %lu check %lu",size,hash1,hash2);
  
  // meta frames are self contained
  if(hash1 == hash2)
  {
//    LOG("meta frame %s",util_hex(data,size+4,NULL));

    // if requested, copy in metadata block
    if(meta) memcpy(meta,data+10,size-10);

    // verify sender's last rx'd hash
    uint32_t rxd;
    memcpy(&rxd,data,4);
    uint8_t *bin = lob_raw(frames->outbox);
    uint32_t len = lob_len(frames->outbox);
    uint32_t rxs = frames->outbase;
    uint8_t i;
    for(i = 0;i <= frames->out;i++)
    {
      // verify/reset to last rx'd frame
      if(rxd == rxs)
      {
        frames->out = i;
        break;
      }

      // handle tail hash correctly like sender
      uint32_t at = i * size;
      rxs ^= murmur4((bin+at), ((at+size) > len) ? (len - at) : size);
      rxs += i;
    }
    if(rxd != rxs)
    {
      LOG("invalid received frame hash %lu check %lu",rxd,rxs);
      frames->err = 1;
      return NULL;
    }
    
    // advance full packet once confirmed
    if((frames->out * size) > len)
    {
      frames->out = 0;
      frames->outbase = rxd;
      lob_t done = lob_shift(frames->outbox);
      frames->outbox = done->next;
      done->next = NULL;
      lob_free(done);
    }

    // sender's last tx'd hash changes flush state
    if(memcmp(data+4,&(frames->inlast),4) == 0)
    {
      frames->flush = 0;
    }else{
      frames->flush = 1;
      LOG("flushing mismatch, last %lu",frames->inlast);
    }
    
    return frames;
  }
  
  // dedup, if identical to last received one
  if(hash1 == frames->inlast) return frames;

  // full data frames must match combined w/ previous
  hash2 ^= frames->inlast;
  hash2 += frames->in;
  if(hash1 == hash2)
  {
    if(!util_frame_new(frames)) return LOG("OOM");
    // append, update inlast, continue
    memcpy(frames->cache->data,data,size);
    frames->flush = 0;
    frames->inlast = hash1;
//    LOG("got data frame %lu",hash1);
    return frames;
  }
  
  // check if it's a tail data frame
  uint8_t tail = data[size-1];
  if(tail >= size)
  {
    frames->flush = 1;
    return LOG("invalid frame data length: %u %s",tail,util_hex(data+(size-4),8,NULL));
  }
  
  // hash must match
  hash2 = murmur4(data,tail);
  hash2 ^= frames->inlast;
  hash2 += frames->in;
  if(hash1 != hash2)
  {
    frames->flush = 1;
    return LOG("invalid frame %u tail (%u) hash %lu != %lu last %lu",frames->in,tail,hash1,hash2,frames->inlast);
  }
  
  // process full packet w/ tail, update inlast, set flush
//  LOG("got frame tail of %u",tail);
  frames->flush = 1;
  frames->inlast = hash1;
  
  size_t tlen = (frames->in * size) + tail;

  // TODO make a lob_new that creates space to prevent double-copy here
  uint8_t *buf = malloc(tlen);
  if(!buf) return LOG("OOM");
  
  // copy in tail
  memcpy(buf+(frames->in * size), data, tail);
  
  // eat cached frames copying in reverse
  util_frame_t frame = frames->cache;
  while(frames->in && frame)
  {
    frames->in--;
    memcpy(buf+(frames->in*size),frame->data,size);
    frame = frame->prev;
  }
  frames->cache = util_frame_free(frames->cache);
  
  lob_t packet = lob_parse(buf,tlen);
  if(!packet) LOG("packet parsing failed: %s",util_hex(buf,tlen,NULL));
  free(buf);
  frames->inbox = lob_push(frames->inbox,packet);
  return frames;
}
Example #9
0
// process any unencrypted handshake packet
link_t mesh_receive_handshake(mesh_t mesh, lob_t handshake, pipe_t pipe)
{
  uint32_t now;
  hashname_t from;
  link_t link;

  if(!mesh || !handshake) return LOG("bad args");
  if(!lob_get(handshake,"id"))
  {
    LOG("bad handshake, no id: %s",lob_json(handshake));
    lob_free(handshake);
    return NULL;
  }
  now = util_sys_seconds();
  
  // normalize handshake
  handshake->id = now; // save when we cached it
  if(!lob_get(handshake,"type")) lob_set(handshake,"type","link"); // default to link type
  if(!lob_get_uint(handshake,"at")) lob_set_uint(handshake,"at",now); // require an at
  LOG("handshake at %d id %s",now,lob_get(handshake,"id"));
  
  // validate/extend link handshakes immediately
  if(util_cmp(lob_get(handshake,"type"),"link") == 0)
  {
    // get the csid
    uint8_t csid = 0;
    lob_t outer;
    if((outer = lob_linked(handshake)))
    {
      csid = outer->head[0];
    }else if(lob_get(handshake,"csid")){
      util_unhex(lob_get(handshake,"csid"),2,&csid);
    }
    if(!csid)
    {
      LOG("bad link handshake, no csid: %s",lob_json(handshake));
      lob_free(handshake);
      return NULL;
    }
    char hexid[3] = {0};
    util_hex(&csid, 1, hexid);
      
    // get attached hashname
    lob_t tmp = lob_parse(handshake->body, handshake->body_len);
    from = hashname_vkey(tmp, csid);
    if(!from)
    {
      LOG("bad link handshake, no hashname: %s",lob_json(handshake));
      lob_free(tmp);
      lob_free(handshake);
      return NULL;
    }
    lob_set(handshake,"csid",hexid);
    lob_set(handshake,"hashname",hashname_char(from));
    lob_set_raw(handshake,hexid,2,"true",4); // intermediate format
    lob_body(handshake, tmp->body, tmp->body_len); // re-attach as raw key
    lob_free(tmp);

    // short-cut, if it's a key from an existing link, pass it on
    // TODO: using mesh_linked here is a stack issue during loopback peer test!
    if((link = mesh_linkid(mesh,from))) return link_receive_handshake(link, handshake, pipe);
    LOG("no link found for handshake from %s",hashname_char(from));

    // extend the key json to make it compatible w/ normal patterns
    tmp = lob_new();
    lob_set_base32(tmp,hexid,handshake->body,handshake->body_len);
    lob_set_raw(handshake,"keys",0,(char*)tmp->head,tmp->head_len);
    lob_free(tmp);
    // add the path if one
    if(pipe && pipe->path)
    {
      char *paths = malloc(pipe->path->head_len+3);
      sprintf(paths,"[%.*s]",(int)pipe->path->head_len,(char*)pipe->path->head);
      lob_set_raw(handshake,"paths",0,paths,pipe->path->head_len+2);
      free(paths);
    }
  }

  // always add to the front of the cached list if needed in the future
  mesh->cached = lob_unshift(mesh->cached, handshake);

  // tell anyone listening about the newly discovered handshake
  mesh_discover(mesh, handshake, pipe);
  
  return NULL;
}
Example #10
0
void ext_thtp(chan_t c)
{
  lob_t p, buf, req, match, note;
  char *path;
  thtp_t t = thtp_get(c->s);

  // incoming note as an answer
  if((note = chan_notes(c)))
  {
    DEBUG_PRINTF("got note resp %.*s",note->json_len,note->json);
    thtp_send(c,lob_linked(note));
    lob_free(note);
    return;
  }

  while((p = chan_pop(c)))
  {
    if(!c->arg)
    {
      c->arg = buf = p;
    }else{
      buf = c->arg;
      lob_append(buf,p->body,p->body_len);
      lob_free(p);
    }
    // for now we're processing whole-requests-at-once, to do streaming we can try parsing note->body for the headers anytime
    if(c->ended) continue;

    // parse the payload
    p = lob_parse(buf->body,buf->body_len);

    // this is a response, send it
    if((note = lob_unlink(buf)))
    {
      lob_free(buf);
      if(p)
      {
        DEBUG_PRINTF("got response %.*s for %.*s",p->json_len,p->json,note->json_len,note->json);        
      }
      lob_link(note,p);
      lob_set_str(note,"thtp","resp");
      chan_reply(c,note);
      chan_end(c,NULL);
      return;
    }

    // this is an incoming request
    lob_free(buf);
    if(!p) return (void)chan_fail(c,"422");
    req = p;

    DEBUG_PRINTF("thtp req packet %.*s", req->json_len, req->json);
    path = lob_get_str(req,"path");
    match = xht_get(t->index,path);
    if(!match) match = _thtp_glob(t,path);
    if(!match)
    {
      chan_fail(c,"404");
      lob_free(req);
      return;
    }

    // built in response
    if(lob_linked(match))
    {
      thtp_send(c,lob_linked(match));
      lob_free(req);
      return;
    }
    
    // attach and route request to a new note
    note = lob_copy(match);
    lob_link(note,req);
    lob_set_str(note,"thtp","req");
    if(chan_reply(c,note) == 0) return;

    chan_fail(c,"500");
    lob_free(req);
  }
  
  // optionally sends ack if needed
  chan_ack(c);
}