/** Check whether an RSA-TAP cross-certification is correct. Return 0 if it * is, -1 if it isn't. */ int check_tap_onion_key_crosscert(const uint8_t *crosscert, int crosscert_len, const crypto_pk_t *onion_pkey, const ed25519_public_key_t *master_id_pkey, const uint8_t *rsa_id_digest) { uint8_t *cc = tor_malloc(crypto_pk_keysize(onion_pkey)); int cc_len = crypto_pk_public_checksig(onion_pkey, (char*)cc, crypto_pk_keysize(onion_pkey), (const char*)crosscert, crosscert_len); if (cc_len < 0) { goto err; } if (cc_len < DIGEST_LEN + ED25519_PUBKEY_LEN) { log_warn(LD_DIR, "Short signature on cross-certification with TAP key"); goto err; } if (tor_memneq(cc, rsa_id_digest, DIGEST_LEN) || tor_memneq(cc + DIGEST_LEN, master_id_pkey->pubkey, ED25519_PUBKEY_LEN)) { log_warn(LD_DIR, "Incorrect cross-certification with TAP key"); goto err; } tor_free(cc); return 0; err: tor_free(cc); return -1; }
/** Create new cross-certification object to certify <b>ed_key</b> as the * master ed25519 identity key for the RSA identity key <b>rsa_key</b>. * Allocates and stores the encoded certificate in *<b>cert</b>, and returns * the number of bytes stored. Returns negative on error.*/ ssize_t tor_make_rsa_ed25519_crosscert(const ed25519_public_key_t *ed_key, const crypto_pk_t *rsa_key, time_t expires, uint8_t **cert) { uint8_t *res; rsa_ed_crosscert_t *cc = rsa_ed_crosscert_new(); memcpy(cc->ed_key, ed_key->pubkey, ED25519_PUBKEY_LEN); cc->expiration = (uint32_t) CEIL_DIV(expires, 3600); cc->sig_len = crypto_pk_keysize(rsa_key); rsa_ed_crosscert_setlen_sig(cc, crypto_pk_keysize(rsa_key)); ssize_t alloc_sz = rsa_ed_crosscert_encoded_len(cc); tor_assert(alloc_sz > 0); res = tor_malloc_zero(alloc_sz); ssize_t sz = rsa_ed_crosscert_encode(res, alloc_sz, cc); tor_assert(sz > 0 && sz <= alloc_sz); const int signed_part_len = 32 + 4; int siglen = crypto_pk_private_sign(rsa_key, (char*)rsa_ed_crosscert_getarray_sig(cc), rsa_ed_crosscert_getlen_sig(cc), (char*)res, signed_part_len); tor_assert(siglen > 0 && siglen <= (int)crypto_pk_keysize(rsa_key)); tor_assert(siglen <= UINT8_MAX); cc->sig_len = siglen; rsa_ed_crosscert_setlen_sig(cc, siglen); sz = rsa_ed_crosscert_encode(res, alloc_sz, cc); rsa_ed_crosscert_free(cc); *cert = res; return sz; }
static int mock_crypto_pk_public_checksig__nocheck(const crypto_pk_t *env, char *to, size_t tolen, const char *from, size_t fromlen) { tor_assert(env && to && from); (void)fromlen; /* We could look at from[0..fromlen-1] ... */ tor_assert(tolen >= crypto_pk_keysize(env)); size_t siglen = MIN(20, crypto_pk_keysize(env)); memset(to, 0x01, siglen); return (int)siglen; }
/** Create new cross-certification object to certify <b>ed_key</b> as the * master ed25519 identity key for the RSA identity key <b>rsa_key</b>. * Allocates and stores the encoded certificate in *<b>cert</b>, and returns * the number of bytes stored. Returns negative on error.*/ ssize_t tor_make_rsa_ed25519_crosscert(const ed25519_public_key_t *ed_key, const crypto_pk_t *rsa_key, time_t expires, uint8_t **cert) { // It is later than 1985, since otherwise there would be no C89 // compilers. (Try to diagnose #22466.) tor_assert_nonfatal(expires >= 15 * 365 * 86400); uint8_t *res; rsa_ed_crosscert_t *cc = rsa_ed_crosscert_new(); memcpy(cc->ed_key, ed_key->pubkey, ED25519_PUBKEY_LEN); cc->expiration = (uint32_t) CEIL_DIV(expires, 3600); cc->sig_len = crypto_pk_keysize(rsa_key); rsa_ed_crosscert_setlen_sig(cc, crypto_pk_keysize(rsa_key)); ssize_t alloc_sz = rsa_ed_crosscert_encoded_len(cc); tor_assert(alloc_sz > 0); res = tor_malloc_zero(alloc_sz); ssize_t sz = rsa_ed_crosscert_encode(res, alloc_sz, cc); tor_assert(sz > 0 && sz <= alloc_sz); crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256); crypto_digest_add_bytes(d, RSA_ED_CROSSCERT_PREFIX, strlen(RSA_ED_CROSSCERT_PREFIX)); const int signed_part_len = 32 + 4; crypto_digest_add_bytes(d, (char*)res, signed_part_len); uint8_t digest[DIGEST256_LEN]; crypto_digest_get_digest(d, (char*)digest, sizeof(digest)); crypto_digest_free(d); int siglen = crypto_pk_private_sign(rsa_key, (char*)rsa_ed_crosscert_getarray_sig(cc), rsa_ed_crosscert_getlen_sig(cc), (char*)digest, sizeof(digest)); tor_assert(siglen > 0 && siglen <= (int)crypto_pk_keysize(rsa_key)); tor_assert(siglen <= UINT8_MAX); cc->sig_len = siglen; rsa_ed_crosscert_setlen_sig(cc, siglen); sz = rsa_ed_crosscert_encode(res, alloc_sz, cc); rsa_ed_crosscert_free(cc); *cert = res; return sz; }
static ssize_t make_intro_from_plaintext( void *buf, size_t len, crypto_pk_t *key, void **cell_out) { char *cell = NULL; ssize_t cell_len = -1, r; /* Assemble key digest and ciphertext, then construct the cell */ ssize_t ciphertext_size; if (!(buf && key && len > 0 && cell_out)) goto done; /* * Figure out an upper bound on how big the ciphertext will be * (see crypto_pk_public_hybrid_encrypt()) */ ciphertext_size = PKCS1_OAEP_PADDING_OVERHEAD; ciphertext_size += crypto_pk_keysize(key); ciphertext_size += CIPHER_KEY_LEN; ciphertext_size += len; /* * Allocate space for the cell */ memmgr_init_check_shared_mem(SHARED_SIZE, UIO_DEVICE, BASE_ADDRESS); // cell = tor_malloc(DIGEST_LEN + ciphertext_size); cell = memmgr_alloc(DIGEST_LEN + ciphertext_size); memmgr_assert(buf); /* Compute key digest (will be first DIGEST_LEN octets of cell) */ r = crypto_pk_get_digest(key, cell); tt_assert(r >= 0); /* Do encryption */ r = crypto_pk_public_hybrid_encrypt( key, cell + DIGEST_LEN, ciphertext_size, buf, len, PK_PKCS1_OAEP_PADDING, 0); tt_assert(r >= 0); /* Figure out cell length */ cell_len = DIGEST_LEN + r; /* Output the cell */ *cell_out = cell; done: // memmgr_free(cell); return cell_len; }
/** Given a router's 128 byte public key, * stores the following in onion_skin_out: * - [42 bytes] OAEP padding * - [16 bytes] Symmetric key for encrypting blob past RSA * - [70 bytes] g^x part 1 (inside the RSA) * - [58 bytes] g^x part 2 (symmetrically encrypted) * * Stores the DH private key into handshake_state_out for later completion * of the handshake. * * The meeting point/cookies and auth are zeroed out for now. */ int onion_skin_TAP_create(crypto_pk_t *dest_router_key, crypto_dh_t **handshake_state_out, char *onion_skin_out) /* TAP_ONIONSKIN_CHALLENGE_LEN bytes */ { char challenge[DH1024_KEY_LEN]; crypto_dh_t *dh = NULL; int dhbytes, pkbytes; tor_assert(dest_router_key); tor_assert(handshake_state_out); tor_assert(onion_skin_out); *handshake_state_out = NULL; memset(onion_skin_out, 0, TAP_ONIONSKIN_CHALLENGE_LEN); if (!(dh = crypto_dh_new(DH_TYPE_CIRCUIT))) goto err; dhbytes = crypto_dh_get_bytes(dh); pkbytes = (int) crypto_pk_keysize(dest_router_key); tor_assert(dhbytes == 128); tor_assert(pkbytes == 128); if (crypto_dh_get_public(dh, challenge, dhbytes)) goto err; /* set meeting point, meeting cookie, etc here. Leave zero for now. */ if (crypto_pk_obsolete_public_hybrid_encrypt(dest_router_key, onion_skin_out, TAP_ONIONSKIN_CHALLENGE_LEN, challenge, DH1024_KEY_LEN, PK_PKCS1_OAEP_PADDING, 1)<0) goto err; memwipe(challenge, 0, sizeof(challenge)); *handshake_state_out = dh; return 0; err: /* LCOV_EXCL_START * We only get here if RSA encryption fails or DH keygen fails. Those * shouldn't be possible. */ memwipe(challenge, 0, sizeof(challenge)); if (dh) crypto_dh_free(dh); return -1; /* LCOV_EXCL_STOP */ }
/** Given a router's 128 byte public key, * stores the following in onion_skin_out: * - [42 bytes] OAEP padding * - [16 bytes] Symmetric key for encrypting blob past RSA * - [70 bytes] g^x part 1 (inside the RSA) * - [58 bytes] g^x part 2 (symmetrically encrypted) * * Stores the DH private key into handshake_state_out for later completion * of the handshake. * * The meeting point/cookies and auth are zeroed out for now. */ int onion_skin_TAP_create(crypto_pk_t *dest_router_key, crypto_dh_t **handshake_state_out, char *onion_skin_out) /* TAP_ONIONSKIN_CHALLENGE_LEN bytes */ { char challenge[DH_KEY_LEN]; crypto_dh_t *dh = NULL; int dhbytes, pkbytes; tor_assert(dest_router_key); tor_assert(handshake_state_out); tor_assert(onion_skin_out); *handshake_state_out = NULL; memset(onion_skin_out, 0, TAP_ONIONSKIN_CHALLENGE_LEN); if (!(dh = crypto_dh_new(DH_TYPE_CIRCUIT))) goto err; dhbytes = crypto_dh_get_bytes(dh); pkbytes = (int) crypto_pk_keysize(dest_router_key); tor_assert(dhbytes == 128); tor_assert(pkbytes == 128); if (crypto_dh_get_public(dh, challenge, dhbytes)) goto err; note_crypto_pk_op(ENC_ONIONSKIN); /* set meeting point, meeting cookie, etc here. Leave zero for now. */ if (crypto_pk_public_hybrid_encrypt(dest_router_key, onion_skin_out, TAP_ONIONSKIN_CHALLENGE_LEN, challenge, DH_KEY_LEN, PK_PKCS1_OAEP_PADDING, 1)<0) goto err; memwipe(challenge, 0, sizeof(challenge)); *handshake_state_out = dh; return 0; err: memwipe(challenge, 0, sizeof(challenge)); if (dh) crypto_dh_free(dh); return -1; }
/** Process an AUTHENTICATE cell from an OR connection. * * If it's ill-formed or we weren't supposed to get one or we're not doing a * v3 handshake, then mark the connection. If it does not authenticate the * other side of the connection successfully (because it isn't signed right, * we didn't get a CERTS cell, etc) mark the connection. Otherwise, accept * the identity of the router on the other side of the connection. */ static void command_process_authenticate_cell(var_cell_t *cell, or_connection_t *conn) { uint8_t expected[V3_AUTH_FIXED_PART_LEN]; const uint8_t *auth; int authlen; #define ERR(s) \ do { \ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, \ "Received a bad AUTHENTICATE cell from %s:%d: %s", \ safe_str(conn->_base.address), conn->_base.port, (s)); \ connection_mark_for_close(TO_CONN(conn)); \ return; \ } while (0) if (conn->_base.state != OR_CONN_STATE_OR_HANDSHAKING_V3) ERR("We're not doing a v3 handshake"); if (conn->link_proto < 3) ERR("We're not using link protocol >= 3"); if (conn->handshake_state->started_here) ERR("We originated this connection"); if (conn->handshake_state->received_authenticate) ERR("We already got one!"); if (conn->handshake_state->authenticated) { /* Should be impossible given other checks */ ERR("The peer is already authenticated"); } if (! conn->handshake_state->received_certs_cell) ERR("We never got a certs cell"); if (conn->handshake_state->auth_cert == NULL) ERR("We never got an authentication certificate"); if (conn->handshake_state->id_cert == NULL) ERR("We never got an identity certificate"); if (cell->payload_len < 4) ERR("Cell was way too short"); auth = cell->payload; { uint16_t type = ntohs(get_uint16(auth)); uint16_t len = ntohs(get_uint16(auth+2)); if (4 + len > cell->payload_len) ERR("Authenticator was truncated"); if (type != AUTHTYPE_RSA_SHA256_TLSSECRET) ERR("Authenticator type was not recognized"); auth += 4; authlen = len; } if (authlen < V3_AUTH_BODY_LEN + 1) ERR("Authenticator was too short"); if (connection_or_compute_authenticate_cell_body( conn, expected, sizeof(expected), NULL, 1) < 0) ERR("Couldn't compute expected AUTHENTICATE cell body"); if (tor_memneq(expected, auth, sizeof(expected))) ERR("Some field in the AUTHENTICATE cell body was not as expected"); { crypto_pk_t *pk = tor_tls_cert_get_key( conn->handshake_state->auth_cert); char d[DIGEST256_LEN]; char *signed_data; size_t keysize; int signed_len; if (!pk) ERR("Internal error: couldn't get RSA key from AUTH cert."); crypto_digest256(d, (char*)auth, V3_AUTH_BODY_LEN, DIGEST_SHA256); keysize = crypto_pk_keysize(pk); signed_data = tor_malloc(keysize); signed_len = crypto_pk_public_checksig(pk, signed_data, keysize, (char*)auth + V3_AUTH_BODY_LEN, authlen - V3_AUTH_BODY_LEN); crypto_pk_free(pk); if (signed_len < 0) { tor_free(signed_data); ERR("Signature wasn't valid"); } if (signed_len < DIGEST256_LEN) { tor_free(signed_data); ERR("Not enough data was signed"); } /* Note that we deliberately allow *more* than DIGEST256_LEN bytes here, * in case they're later used to hold a SHA3 digest or something. */ if (tor_memneq(signed_data, d, DIGEST256_LEN)) { tor_free(signed_data); ERR("Signature did not match data to be signed."); } tor_free(signed_data); } /* Okay, we are authenticated. */ conn->handshake_state->received_authenticate = 1; conn->handshake_state->authenticated = 1; conn->handshake_state->digest_received_data = 0; { crypto_pk_t *identity_rcvd = tor_tls_cert_get_key(conn->handshake_state->id_cert); const digests_t *id_digests = tor_cert_get_id_digests(conn->handshake_state->id_cert); /* This must exist; we checked key type when reading the cert. */ tor_assert(id_digests); memcpy(conn->handshake_state->authenticated_peer_id, id_digests->d[DIGEST_SHA1], DIGEST_LEN); connection_or_set_circid_type(conn, identity_rcvd); crypto_pk_free(identity_rcvd); connection_or_init_conn_from_address(conn, &conn->_base.addr, conn->_base.port, (const char*)conn->handshake_state->authenticated_peer_id, 0); log_info(LD_OR, "Got an AUTHENTICATE cell from %s:%d: Looks good.", safe_str(conn->_base.address), conn->_base.port); } #undef ERR }
/** Run unit tests for our public key crypto functions */ static void test_crypto_pk(void) { crypto_pk_t *pk1 = NULL, *pk2 = NULL; char *encoded = NULL; char data1[1024], data2[1024], data3[1024]; size_t size; int i, j, p, len; /* Public-key ciphers */ pk1 = pk_generate(0); pk2 = crypto_pk_new(); test_assert(pk1 && pk2); test_assert(! crypto_pk_write_public_key_to_string(pk1, &encoded, &size)); test_assert(! crypto_pk_read_public_key_from_string(pk2, encoded, size)); test_eq(0, crypto_pk_cmp_keys(pk1, pk2)); /* comparison between keys and NULL */ tt_int_op(crypto_pk_cmp_keys(NULL, pk1), <, 0); tt_int_op(crypto_pk_cmp_keys(NULL, NULL), ==, 0); tt_int_op(crypto_pk_cmp_keys(pk1, NULL), >, 0); test_eq(128, crypto_pk_keysize(pk1)); test_eq(1024, crypto_pk_num_bits(pk1)); test_eq(128, crypto_pk_keysize(pk2)); test_eq(1024, crypto_pk_num_bits(pk2)); test_eq(128, crypto_pk_public_encrypt(pk2, data1, sizeof(data1), "Hello whirled.", 15, PK_PKCS1_OAEP_PADDING)); test_eq(128, crypto_pk_public_encrypt(pk1, data2, sizeof(data1), "Hello whirled.", 15, PK_PKCS1_OAEP_PADDING)); /* oaep padding should make encryption not match */ test_memneq(data1, data2, 128); test_eq(15, crypto_pk_private_decrypt(pk1, data3, sizeof(data3), data1, 128, PK_PKCS1_OAEP_PADDING,1)); test_streq(data3, "Hello whirled."); memset(data3, 0, 1024); test_eq(15, crypto_pk_private_decrypt(pk1, data3, sizeof(data3), data2, 128, PK_PKCS1_OAEP_PADDING,1)); test_streq(data3, "Hello whirled."); /* Can't decrypt with public key. */ test_eq(-1, crypto_pk_private_decrypt(pk2, data3, sizeof(data3), data2, 128, PK_PKCS1_OAEP_PADDING,1)); /* Try again with bad padding */ memcpy(data2+1, "XYZZY", 5); /* This has fails ~ once-in-2^40 */ test_eq(-1, crypto_pk_private_decrypt(pk1, data3, sizeof(data3), data2, 128, PK_PKCS1_OAEP_PADDING,1)); /* File operations: save and load private key */ test_assert(! crypto_pk_write_private_key_to_filename(pk1, get_fname("pkey1"))); /* failing case for read: can't read. */ test_assert(crypto_pk_read_private_key_from_filename(pk2, get_fname("xyzzy")) < 0); write_str_to_file(get_fname("xyzzy"), "foobar", 6); /* Failing case for read: no key. */ test_assert(crypto_pk_read_private_key_from_filename(pk2, get_fname("xyzzy")) < 0); test_assert(! crypto_pk_read_private_key_from_filename(pk2, get_fname("pkey1"))); test_eq(15, crypto_pk_private_decrypt(pk2, data3, sizeof(data3), data1, 128, PK_PKCS1_OAEP_PADDING,1)); /* Now try signing. */ strlcpy(data1, "Ossifrage", 1024); test_eq(128, crypto_pk_private_sign(pk1, data2, sizeof(data2), data1, 10)); test_eq(10, crypto_pk_public_checksig(pk1, data3, sizeof(data3), data2, 128)); test_streq(data3, "Ossifrage"); /* Try signing digests. */ test_eq(128, crypto_pk_private_sign_digest(pk1, data2, sizeof(data2), data1, 10)); test_eq(20, crypto_pk_public_checksig(pk1, data3, sizeof(data3), data2, 128)); test_eq(0, crypto_pk_public_checksig_digest(pk1, data1, 10, data2, 128)); test_eq(-1, crypto_pk_public_checksig_digest(pk1, data1, 11, data2, 128)); /*XXXX test failed signing*/ /* Try encoding */ crypto_pk_free(pk2); pk2 = NULL; i = crypto_pk_asn1_encode(pk1, data1, 1024); test_assert(i>0); pk2 = crypto_pk_asn1_decode(data1, i); test_assert(crypto_pk_cmp_keys(pk1,pk2) == 0); /* Try with hybrid encryption wrappers. */ crypto_rand(data1, 1024); for (i = 0; i < 2; ++i) { for (j = 85; j < 140; ++j) { memset(data2,0,1024); memset(data3,0,1024); p = (i==0)?PK_PKCS1_PADDING:PK_PKCS1_OAEP_PADDING; len = crypto_pk_public_hybrid_encrypt(pk1,data2,sizeof(data2), data1,j,p,0); test_assert(len>=0); len = crypto_pk_private_hybrid_decrypt(pk1,data3,sizeof(data3), data2,len,p,1); test_eq(len,j); test_memeq(data1,data3,j); } } /* Try copy_full */ crypto_pk_free(pk2); pk2 = crypto_pk_copy_full(pk1); test_assert(pk2 != NULL); test_neq_ptr(pk1, pk2); test_assert(crypto_pk_cmp_keys(pk1,pk2) == 0); done: if (pk1) crypto_pk_free(pk1); if (pk2) crypto_pk_free(pk2); tor_free(encoded); }
*len_out = r; return tor_memdup(signature, r); } /** Check whether an RSA-TAP cross-certification is correct. Return 0 if it * is, -1 if it isn't. */ MOCK_IMPL(int, check_tap_onion_key_crosscert,(const uint8_t *crosscert, int crosscert_len, const crypto_pk_t *onion_pkey, const ed25519_public_key_t *master_id_pkey, const uint8_t *rsa_id_digest)) { uint8_t *cc = tor_malloc(crypto_pk_keysize(onion_pkey)); int cc_len = crypto_pk_public_checksig(onion_pkey, (char*)cc, crypto_pk_keysize(onion_pkey), (const char*)crosscert, crosscert_len); if (cc_len < 0) { goto err; } if (cc_len < DIGEST_LEN + ED25519_PUBKEY_LEN) { log_warn(LD_DIR, "Short signature on cross-certification with TAP key"); goto err; } if (tor_memneq(cc, rsa_id_digest, DIGEST_LEN) || tor_memneq(cc + DIGEST_LEN, master_id_pkey->pubkey,