// decode base64 into the pair of lob packets lob_t jwt_decode(char *encoded, size_t len) { lob_t header, claims; char *dot1, *dot2, *end; size_t dlen; if(!encoded) return NULL; if(!len) len = strlen(encoded); end = encoded+(len-1); // make sure the dot separators are there dot1 = strchr(encoded,'.'); if(!dot1 || dot1+1 >= end) return LOG("missing/bad first separator"); dot1++; dot2 = strchr(dot1,'.'); if(!dot2 || (dot2+1) >= end) return LOG("missing/bad second separator"); dot2++; claims = lob_new(); header = lob_link(NULL, claims); // decode claims sig first dlen = base64_decoder(dot2, (end-dot2)+1, (uint8_t*)dot2); lob_body(claims, (uint8_t*)dot2, dlen); // decode claims json dlen = base64_decoder(dot1, (dot2-dot1), (uint8_t*)dot1); lob_head(claims, (uint8_t*)dot1, dlen); // decode header json dlen = base64_decoder(encoded, (dot1-encoded), (uint8_t*)encoded); lob_head(header, (uint8_t*)encoded, dlen); return header; }
// add a custom outgoing handshake packet to all links mesh_t mesh_handshake(mesh_t mesh, lob_t handshake) { if(!mesh) return NULL; if(handshake && !lob_get(handshake,"type")) return LOG("handshake missing a type: %s",lob_json(handshake)); mesh->handshakes = lob_link(handshake, mesh->handshakes); return mesh; }
lob_t util_uri_add_path(lob_t uri, lob_t path) { lob_t keys; lob_t query = lob_linked(uri); if(!uri || !path) return NULL; if(!query) { if(!(query = lob_new())) return LOG("OOM"); lob_link(uri, query); } // encode and add to chain after query if(!(keys = lob_new())) return LOG("OOM"); lob_set_base32(keys,"paths",path->head,path->head_len); lob_link(keys, lob_linked(query)); lob_link(query, keys); return uri; }
lob_t link_handshakes(link_t link) { uint32_t i; uint8_t csid; char *key; lob_t tmp, hs = NULL, handshakes = NULL; if(!link) return NULL; // no keys means we have to generate a handshake for each key if(!link->x) { for(i=0;(key = lob_get_index(link->mesh->keys,i));i+=2) { util_unhex(key,2,&csid); hs = lob_new(); tmp = hashname_im(link->mesh->keys, csid); lob_body(hs, lob_raw(tmp), lob_len(tmp)); lob_free(tmp); handshakes = lob_link(hs, handshakes); } }else{ // generate one just for this csid handshakes = lob_new(); tmp = hashname_im(link->mesh->keys, link->csid); lob_body(handshakes, lob_raw(tmp), lob_len(tmp)); lob_free(tmp); } // add any custom per-link for(hs = link->handshakes; hs; hs = lob_linked(hs)) handshakes = lob_link(lob_copy(hs), handshakes); // add any mesh-wide handshakes for(hs = link->mesh->handshakes; hs; hs = lob_linked(hs)) handshakes = lob_link(lob_copy(hs), handshakes); // encrypt them if we can if(link->x) { tmp = handshakes; handshakes = NULL; for(hs = tmp; hs; hs = lob_linked(hs)) handshakes = lob_link(e3x_exchange_handshake(link->x, hs), handshakes); lob_free(tmp); } return handshakes; }
// 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; }
// decode base64 into the pair of lob packets lob_t jwt_decode(char *encoded, size_t len) { lob_t header, claims; char *dot1, *dot2, *end; if(!encoded) return NULL; if(!len) len = strlen(encoded); end = encoded+len; // make sure the dot separators are there dot1 = memchr(encoded,'.',(end-encoded)); if(!dot1) return LOG_INFO("missing/bad first separator"); dot1++; dot2 = memchr(dot1,'.',(end-dot1)); if(!dot2) return LOG_INFO("missing/bad second separator"); dot2++; // quick sanity check of the base64 if(!base64_decoder(dot2, (end-dot2), NULL)) return LOG_INFO("invalid sig base64: %.*s",(end-dot2),dot2); if(!base64_decoder(dot1, (dot2-dot1)-1, NULL)) return LOG_INFO("invalid claims base64: %.*s",(dot2-dot1)-1,dot1); if(!base64_decoder(encoded, (dot1-encoded)-1, NULL)) return LOG_INFO("invalid header b64: %.*s",(dot1-encoded)-1,encoded); claims = lob_new(); header = lob_link(NULL, claims); // decode claims json lob_head(claims, NULL, base64_decoder(dot1, (dot2-dot1)-1, NULL)); base64_decoder(dot1, (dot2-dot1)-1, lob_head_get(claims)); // decode claims sig lob_body(claims, NULL, base64_decoder(dot2, (end-dot2), NULL)); base64_decoder(dot2, (end-dot2), lob_body_get(claims)); // decode header json lob_head(header, NULL, base64_decoder(encoded, (dot1-encoded)-1, NULL)); base64_decoder(encoded, (dot1-encoded)-1, lob_head_get(header)); return header; }
lob_t util_uri_add_keys(lob_t uri, lob_t keys) { uint32_t i; char *key, *value, csid[5]; lob_t query = lob_linked(uri); if(!uri || !keys) return NULL; if(!query) { query = lob_new(); lob_link(uri, query); } for(i=0;(key = lob_get_index(keys,i));i+=2) { value = lob_get_index(keys,i+1); if(strlen(key) != 2 || !value) continue; // paranoid snprintf(csid,5,"cs%s",key); lob_set(query,csid,value); } return uri; }
// generate an outgoing request, send the response attached to the note chan_t thtp_req(switch_t s, lob_t note) { char *uri, *path, *method; hashname_t to = NULL; lob_t req; chan_t c; if(!s || !note) return NULL; method = lob_get_str(note,"method"); path = lob_get_str(note,"path"); if((uri = lob_get_str(note,"uri")) && strncmp(uri,"thtp://",7) == 0) { uri += 7; path = strchr(uri,'/'); to = hashname_gethex(s->index,uri); } if(!to) to = hashname_gethex(s->index,lob_get_str(note,"to")); if(!to) return NULL; req = lob_linked(note); if(!req) { req = lob_chain(note); lob_set_str(req,"path",path?path:"/"); lob_set_str(req,"method",method?method:"get"); } DEBUG_PRINTF("thtp req %s %s %s %.*s",lob_get_str(req,"method"),lob_get_str(req,"path"),to->hexname,note->json_len,note->json); // open channel and send req c = chan_new(s, to, "thtp", 0); c->arg = lob_link(NULL,note); // create buffer packet w/ the note linked c->handler = ext_thtp; // shortcut chan_reliable(c,10); thtp_send(c,req); return c; }
// just a convenience, generates handshake w/ current e3x_exchange_at value lob_t e3x_exchange_handshake(e3x_exchange_t x, lob_t inner) { lob_t tmp; uint8_t i; uint8_t local = 0; if(!x) return LOG("invalid args"); if(!x->out) return LOG("no out set"); // create deprecated key handshake inner from all supported csets if(!inner) { local = 1; inner = lob_new(); lob_set(inner, "type", "key"); // loop through all ciphersets for any keys for(i=0; i<CS_MAX; i++) { if(!(tmp = x->self->keys[i])) continue; // this csid's key is the body, rest is intermediate in json if(e3x_cipher_sets[i] == x->cs) { lob_body(inner,tmp->body,tmp->body_len); }else{ uint8_t hash[32]; e3x_hash(tmp->body,tmp->body_len,hash); lob_set_base32(inner,e3x_cipher_sets[i]->hex,hash,32); } } } // set standard values lob_set_uint(inner,"at",x->out); tmp = e3x_exchange_message(x, inner); if(!local) return tmp; return lob_link(tmp, inner); }
// processes incoming packet, it will take ownership of p link_t mesh_receive(mesh_t mesh, lob_t outer, pipe_t pipe) { lob_t inner; link_t link; char token[17]; hashname_t id; if(!mesh || !outer || !pipe) return LOG("bad args"); LOG("mesh receiving %s to %s via pipe %s",outer->head_len?"handshake":"channel",hashname_short(mesh->id),pipe->id); // process handshakes if(outer->head_len == 1) { inner = e3x_self_decrypt(mesh->self, outer); if(!inner) { LOG("%02x handshake failed %s",outer->head[0],e3x_err()); lob_free(outer); return NULL; } // couple the two together, inner->outer lob_link(inner,outer); // set the unique id string based on some of the first 16 (routing token) bytes in the body base32_encode(outer->body,10,token,17); lob_set(inner,"id",token); // process the handshake return mesh_receive_handshake(mesh, inner, pipe); } // handle channel packets if(outer->head_len == 0) { if(outer->body_len < 16) { LOG("packet too small %d",outer->body_len); lob_free(outer); return NULL; } route_t route; for(route = mesh->routes;route;route = route->next) if(memcmp(route->token,outer->body,8) == 0) break; link = route ? route->link : NULL; if(!link) { LOG("dropping, no link for token %s",util_hex(outer->body,16,NULL)); lob_free(outer); return NULL; } // forward packet if(!route->flag) { LOG("forwarding route token %s via %s len %d",util_hex(route->token,8,NULL),hashname_short(link->id),lob_len(outer)); return link_send(link, outer); } inner = e3x_exchange_receive(link->x, outer); lob_free(outer); if(!inner) return LOG("channel decryption fail for link %s %s",hashname_short(link->id),e3x_err()); LOG("channel packet %d bytes from %s",lob_len(inner),hashname_short(link->id)); return link_receive(link,inner,pipe); } // transform incoming bare link json format into handshake for discovery if((inner = lob_get_json(outer,"keys"))) { if((id = hashname_vkeys(inner))) { lob_set(outer,"hashname",hashname_char(id)); lob_set_int(outer,"at",0); lob_set(outer,"type","link"); LOG("bare incoming link json being discovered %s",lob_json(outer)); } lob_free(inner); } // run everything else through discovery, usually plain handshakes mesh_discover(mesh, outer, pipe); link = mesh_linked(mesh, lob_get(outer,"hashname"), 0); lob_free(outer); return link; }
// add a custom outgoing handshake packet link_t link_handshake(link_t link, lob_t handshake) { if(!link) return NULL; link->handshakes = lob_link(handshake, link->handshakes); return link; }
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); }