static void clear_passphrase(void) { if (passphrase) { memwipe(passphrase, 0, passphrase_len); tor_free(passphrase); } }
/** 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 */ }
/** Read a tagged-data file from <b>fname</b> into the * <b>data_out_len</b>-byte buffer in <b>data_out</b>. Check that the * typestring matches <b>typestring</b>; store the tag into a newly allocated * string in <b>tag_out</b>. Return -1 on failure, and the number of bytes of * data on success. Preserves the errno from reading the file. */ ssize_t crypto_read_tagged_contents_from_file(const char *fname, const char *typestring, char **tag_out, uint8_t *data_out, ssize_t data_out_len) { char prefix[33]; char *content = NULL; struct stat st; ssize_t r = -1; size_t st_size = 0; int saved_errno = 0; *tag_out = NULL; st.st_size = 0; content = read_file_to_str(fname, RFTS_BIN|RFTS_IGNORE_MISSING, &st); if (! content) { saved_errno = errno; goto end; } if (st.st_size < 32 || st.st_size > 32 + data_out_len) { saved_errno = EINVAL; goto end; } st_size = (size_t)st.st_size; memcpy(prefix, content, 32); prefix[32] = 0; /* Check type, extract tag. */ if (strcmpstart(prefix, "== ") || strcmpend(prefix, " ==") || ! tor_mem_is_zero(prefix+strlen(prefix), 32-strlen(prefix))) { saved_errno = EINVAL; goto end; } if (strcmpstart(prefix+3, typestring) || 3+strlen(typestring) >= 32 || strcmpstart(prefix+3+strlen(typestring), ": ")) { saved_errno = EINVAL; goto end; } *tag_out = tor_strndup(prefix+5+strlen(typestring), strlen(prefix)-8-strlen(typestring)); memcpy(data_out, content+32, st_size-32); r = st_size - 32; end: if (content) memwipe(content, 0, st_size); tor_free(content); if (saved_errno) errno = saved_errno; return r; }
/** Free all space held in <b>pool</b> This makes all pointers returned from * mp_pool_get(<b>pool</b>) invalid. */ void mp_pool_destroy(mp_pool_t *pool) { destroy_chunks(pool->empty_chunks); destroy_chunks(pool->used_chunks); destroy_chunks(pool->full_chunks); memwipe(pool, 0xe0, sizeof(mp_pool_t)); FREE(pool); }
/** Release all storage held for <b>kp</b>. */ void ed25519_keypair_free(ed25519_keypair_t *kp) { if (! kp) return; memwipe(kp, 0, sizeof(*kp)); tor_free(kp); }
static unsigned fill_heap_buffer_memwipe(void) { char *buf = heap_buf = malloc(BUF_LEN); FILL_BUFFER_IMPL() memwipe(buf, 0, BUF_LEN); free(buf); return sum; }
/** * Initialize and return a new fast PRNG, using a strong random seed. * * Note that this object is NOT thread-safe. If you need a thread-safe * prng, use crypto_rand(), or wrap this in a mutex. **/ crypto_fast_rng_t * crypto_fast_rng_new(void) { uint8_t seed[SEED_LEN]; crypto_strongest_rand(seed, sizeof(seed)); crypto_fast_rng_t *result = crypto_fast_rng_new_from_seed(seed); memwipe(seed, 0, sizeof(seed)); return result; }
/** 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; }
int write_encrypted_secret_key(const ed25519_secret_key_t *key, const char *fname) { int r = -1; char pwbuf0[256]; uint8_t *encrypted_key = NULL; size_t encrypted_len = 0; if (do_getpass("Enter new passphrase:", pwbuf0, sizeof(pwbuf0), 1, get_options()) < 0) { log_warn(LD_OR, "NO/failed passphrase"); return -1; } if (strlen(pwbuf0) == 0) { if (get_options()->keygen_force_passphrase == FORCE_PASSPHRASE_ON) return -1; else return 0; } if (crypto_pwbox(&encrypted_key, &encrypted_len, key->seckey, sizeof(key->seckey), pwbuf0, strlen(pwbuf0), 0) < 0) { log_warn(LD_OR, "crypto_pwbox failed!?"); goto done; } if (crypto_write_tagged_contents_to_file(fname, ENC_KEY_HEADER, ENC_KEY_TAG, encrypted_key, encrypted_len) < 0) goto done; r = 1; done: if (encrypted_key) { memwipe(encrypted_key, 0, encrypted_len); tor_free(encrypted_key); } memwipe(pwbuf0, 0, sizeof(pwbuf0)); return r; }
/* Public function: Do the appropriate ntor calculations and derive the keys * needed to decrypt and verify INTRODUCE1 cells. Return 0 and place the final * key material in <b>hs_ntor_intro_cell_keys_out</b> if everything went well, * otherwise return -1; * * The relevant calculations are as follows: * * intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID * info = m_hsexpand | subcredential * hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN) * HS_DEC_KEY = hs_keys[0:S_KEY_LEN] * HS_MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN] * * where: * <b>intro_auth_pubkey</b> is AUTH_KEY (introduction point auth key), * <b>intro_enc_keypair</b> is (b,B) (introduction point encryption keypair), * <b>client_ephemeral_enc_pubkey</b> is X (CLIENT_PK in INTRODUCE2 cell), * <b>subcredential</b> is the HS subcredential (of size DIGEST256_LEN) */ int hs_ntor_service_get_introduce1_keys( const ed25519_public_key_t *intro_auth_pubkey, const curve25519_keypair_t *intro_enc_keypair, const curve25519_public_key_t *client_ephemeral_enc_pubkey, const uint8_t *subcredential, hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out) { int bad = 0; uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN]; uint8_t dh_result[CURVE25519_OUTPUT_LEN]; tor_assert(intro_auth_pubkey); tor_assert(intro_enc_keypair); tor_assert(client_ephemeral_enc_pubkey); tor_assert(subcredential); tor_assert(hs_ntor_intro_cell_keys_out); /* Compute EXP(X, b) */ curve25519_handshake(dh_result, &intro_enc_keypair->seckey, client_ephemeral_enc_pubkey); bad |= safe_mem_is_zero(dh_result, CURVE25519_OUTPUT_LEN); /* Get intro_secret_hs_input */ get_intro_secret_hs_input(dh_result, intro_auth_pubkey, client_ephemeral_enc_pubkey, &intro_enc_keypair->pubkey, secret_input); bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN); /* Get ENC_KEY and MAC_KEY! */ get_introduce1_key_material(secret_input, subcredential, hs_ntor_intro_cell_keys_out); memwipe(secret_input, 0, sizeof(secret_input)); if (bad) { memwipe(hs_ntor_intro_cell_keys_out, 0, sizeof(hs_ntor_intro_cell_keys_t)); } return bad ? -1 : 0; }
/** Release storage held by <b>cipher</b> */ void aes_cipher_free(aes_cnt_cipher_t *cipher) { if (!cipher) return; if (cipher->using_evp) { EVP_CIPHER_CTX_cleanup(&cipher->key.evp); } memwipe(cipher, 0, sizeof(aes_cnt_cipher_t)); tor_free(cipher); }
/** Finish the client side of the DH handshake. * Given the 128 byte DH reply + 20 byte hash as generated by * onion_skin_server_handshake and the handshake state generated by * onion_skin_create, verify H(K) with the first 20 bytes of shared * key material, then generate key_out_len more bytes of shared key * material and store them in key_out. * * After the invocation, call crypto_dh_free on handshake_state. */ int onion_skin_TAP_client_handshake(crypto_dh_t *handshake_state, const char *handshake_reply, /* TAP_ONIONSKIN_REPLY_LEN bytes */ char *key_out, size_t key_out_len, const char **msg_out) { ssize_t len; char *key_material=NULL; size_t key_material_len; tor_assert(crypto_dh_get_bytes(handshake_state) == DH1024_KEY_LEN); key_material_len = DIGEST_LEN + key_out_len; key_material = tor_malloc(key_material_len); len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, handshake_state, handshake_reply, DH1024_KEY_LEN, key_material, key_material_len); if (len < 0) { if (msg_out) *msg_out = "DH computation failed."; goto err; } if (tor_memneq(key_material, handshake_reply+DH1024_KEY_LEN, DIGEST_LEN)) { /* H(K) does *not* match. Something fishy. */ if (msg_out) *msg_out = "Digest DOES NOT MATCH on onion handshake. Bug or attack."; goto err; } /* use the rest of the key material for our shared keys, digests, etc */ memcpy(key_out, key_material+DIGEST_LEN, key_out_len); memwipe(key_material, 0, key_material_len); tor_free(key_material); return 0; err: memwipe(key_material, 0, key_material_len); tor_free(key_material); return -1; }
int ed25519_donna_seckey(unsigned char *sk) { ed25519_secret_key seed; crypto_strongest_rand(seed, 32); ed25519_extsk(sk, seed); memwipe(seed, 0, sizeof(seed)); return 0; }
/** Release all storage held in <b>keys</b>. */ void server_onion_keys_free(server_onion_keys_t *keys) { if (! keys) return; crypto_pk_free(keys->onion_key); crypto_pk_free(keys->last_onion_key); ntor_key_map_free(keys->curve25519_key_map); tor_free(keys->junk_keypair); memwipe(keys, 0, sizeof(server_onion_keys_t)); tor_free(keys); }
/** Helper function: Compute the part of the HS ntor handshake that generates * key material for creating and handling INTRODUCE1 cells. Function used * by both client and service. Specifically, calculate the following: * * info = m_hsexpand | subcredential * hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN) * ENC_KEY = hs_keys[0:S_KEY_LEN] * MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN] * * where intro_secret_hs_input is <b>secret_input</b> (of size * INTRO_SECRET_HS_INPUT_LEN), and <b>subcredential</b> is of size * DIGEST256_LEN. * * If everything went well, fill <b>hs_ntor_intro_cell_keys_out</b> with the * necessary key material, and return 0. */ static void get_introduce1_key_material(const uint8_t *secret_input, const uint8_t *subcredential, hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out) { uint8_t keystream[CIPHER256_KEY_LEN + DIGEST256_LEN]; uint8_t info_blob[INFO_BLOB_LEN]; uint8_t kdf_input[KDF_INPUT_LEN]; crypto_xof_t *xof; uint8_t *ptr; /* Let's build info */ ptr = info_blob; APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND)); APPEND(ptr, subcredential, DIGEST256_LEN); tor_assert(ptr == info_blob + sizeof(info_blob)); /* Let's build the input to the KDF */ ptr = kdf_input; APPEND(ptr, secret_input, INTRO_SECRET_HS_INPUT_LEN); APPEND(ptr, T_HSENC, strlen(T_HSENC)); APPEND(ptr, info_blob, sizeof(info_blob)); tor_assert(ptr == kdf_input + sizeof(kdf_input)); /* Now we need to run kdf_input over SHAKE-256 */ xof = crypto_xof_new(); crypto_xof_add_bytes(xof, kdf_input, sizeof(kdf_input)); crypto_xof_squeeze_bytes(xof, keystream, sizeof(keystream)) ; crypto_xof_free(xof); { /* Get the keys */ memcpy(&hs_ntor_intro_cell_keys_out->enc_key, keystream,CIPHER256_KEY_LEN); memcpy(&hs_ntor_intro_cell_keys_out->mac_key, keystream+CIPHER256_KEY_LEN, DIGEST256_LEN); } memwipe(keystream, 0, sizeof(keystream)); memwipe(kdf_input, 0, sizeof(kdf_input)); }
/** * Initialize a new ed25519 secret key in <b>seckey_out</b>. If * <b>extra_strong</b>, take the RNG inputs directly from the operating * system. Return 0 on success, -1 on failure. */ int ed25519_secret_key_generate(ed25519_secret_key_t *seckey_out, int extra_strong) { int r; uint8_t seed[32]; if (! extra_strong || crypto_strongest_rand(seed, sizeof(seed)) < 0) crypto_rand((char*)seed, sizeof(seed)); r = get_ed_impl()->seckey_expand(seckey_out->seckey, seed); memwipe(seed, 0, sizeof(seed)); return r < 0 ? -1 : 0; }
int crypto_sign_seckey(unsigned char *sk) { unsigned char seed[32]; if (randombytes(seed,32) < 0) return -1; crypto_sign_seckey_expand(sk, seed); memwipe(seed, 0, 32); return 0; }
/** Implement the second half of the client side of the CREATE_FAST handshake. * We sent the server <b>handshake_state</b> ("x") already, and the server * told us <b>handshake_reply_out</b> (y|H(x|y)). Make sure that the hash is * correct, and generate key material in <b>key_out</b>. Return 0 on success, * true on failure. * * NOTE: The "CREATE_FAST" handshake path is distinguishable from regular * "onionskin" handshakes, and is not secure if an adversary can see or modify * the messages. Therefore, it should only be used by clients, and only as * the first hop of a circuit (since the first hop is already authenticated * and protected by TLS). */ int fast_client_handshake(const fast_handshake_state_t *handshake_state, const uint8_t *handshake_reply_out,/*DIGEST_LEN*2 bytes*/ uint8_t *key_out, size_t key_out_len, const char **msg_out) { uint8_t tmp[DIGEST_LEN+DIGEST_LEN]; uint8_t *out; size_t out_len; int r = -1; memcpy(tmp, handshake_state->state, DIGEST_LEN); memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN); out_len = key_out_len+DIGEST_LEN; out = tor_malloc(out_len); if (BUG(crypto_expand_key_material_TAP(tmp, sizeof(tmp), out, out_len))) { /* LCOV_EXCL_START */ if (msg_out) *msg_out = "Failed to expand key material"; goto done; /* LCOV_EXCL_STOP */ } if (tor_memneq(out, handshake_reply_out+DIGEST_LEN, DIGEST_LEN)) { /* H(K) does *not* match. Something fishy. */ if (msg_out) *msg_out = "Digest DOES NOT MATCH on fast handshake. Bug or attack."; goto done; } memcpy(key_out, out+DIGEST_LEN, key_out_len); r = 0; done: memwipe(tmp, 0, sizeof(tmp)); memwipe(out, 0, out_len); tor_free(out); return r; }
/** Deallocate space associated with the cpath node <b>victim</b>. */ static void circuit_free_cpath_node(crypt_path_t *victim) { if (!victim) return; crypto_cipher_free(victim->f_crypto); crypto_cipher_free(victim->b_crypto); crypto_digest_free(victim->f_digest); crypto_digest_free(victim->b_digest); crypto_dh_free(victim->dh_handshake_state); extend_info_free(victim->extend_info); memwipe(victim, 0xBB, sizeof(crypt_path_t)); /* poison memory */ tor_free(victim); }
/** Read the passphrase from the passphrase fd. */ static int load_passphrase(void) { char *cp; char buf[1024]; /* "Ought to be enough for anybody." */ ssize_t n = read_all(passphrase_fd, buf, sizeof(buf), 0); if (n < 0) { log_err(LD_GENERAL, "Couldn't read from passphrase fd: %s", strerror(errno)); return -1; } cp = memchr(buf, '\n', n); passphrase_len = cp-buf; passphrase = tor_strndup(buf, passphrase_len); memwipe(buf, 0, sizeof(buf)); return 0; }
/** * Generate CURVE25519_SECKEY_LEN random bytes in <b>out</b>. If * <b>extra_strong</b> is true, this key is possibly going to get used more * than once, so use a better-than-usual RNG. Return 0 on success, -1 on * failure. * * This function does not adjust the output of the RNG at all; the will caller * will need to clear or set the appropriate bits to make curve25519 work. */ int curve25519_rand_seckey_bytes(uint8_t *out, int extra_strong) { uint8_t k_tmp[CURVE25519_SECKEY_LEN]; if (crypto_rand((char*)out, CURVE25519_SECKEY_LEN) < 0) return -1; if (extra_strong && !crypto_strongest_rand(k_tmp, CURVE25519_SECKEY_LEN)) { /* If they asked for extra-strong entropy and we have some, use it as an * HMAC key to improve not-so-good entropy rather than using it directly, * just in case the extra-strong entropy is less amazing than we hoped. */ crypto_hmac_sha256((char*) out, (const char *)k_tmp, sizeof(k_tmp), (const char *)out, CURVE25519_SECKEY_LEN); } memwipe(k_tmp, 0, sizeof(k_tmp)); return 0; }
/* For a given introduction point and an introduction circuit, send the * ESTABLISH_INTRO cell. The service object is used for logging. This can fail * and if so, the circuit is closed and the intro point object is flagged * that the circuit is not established anymore which is important for the * retry mechanism. */ static void send_establish_intro(const hs_service_t *service, hs_service_intro_point_t *ip, origin_circuit_t *circ) { ssize_t cell_len; uint8_t payload[RELAY_PAYLOAD_SIZE]; tor_assert(service); tor_assert(ip); tor_assert(circ); /* Encode establish intro cell. */ cell_len = hs_cell_build_establish_intro(circ->cpath->prev->rend_circ_nonce, ip, payload); if (cell_len < 0) { log_warn(LD_REND, "Unable to encode ESTABLISH_INTRO cell for service %s " "on circuit %u. Closing circuit.", safe_str_client(service->onion_address), TO_CIRCUIT(circ)->n_circ_id); goto err; } /* Send the cell on the circuit. */ if (relay_send_command_from_edge(CONTROL_CELL_ID, TO_CIRCUIT(circ), RELAY_COMMAND_ESTABLISH_INTRO, (char *) payload, cell_len, circ->cpath->prev) < 0) { log_info(LD_REND, "Unable to send ESTABLISH_INTRO cell for service %s " "on circuit %u.", safe_str_client(service->onion_address), TO_CIRCUIT(circ)->n_circ_id); /* On error, the circuit has been closed. */ goto done; } /* Record the attempt to use this circuit. */ pathbias_count_use_attempt(circ); goto done; err: circuit_mark_for_close(TO_CIRCUIT(circ), END_CIRC_REASON_INTERNAL); done: memwipe(payload, 0, sizeof(payload)); }
/* We are a v2 legacy HS client: Create and return a crypt path for the hidden * service on the other side of the rendezvous circuit <b>circ</b>. Initialize * the crypt path crypto using the body of the RENDEZVOUS1 cell at * <b>rend_cell_body</b> (which must be at least DH_KEY_LEN+DIGEST_LEN bytes). */ static crypt_path_t * create_rend_cpath_legacy(origin_circuit_t *circ, const uint8_t *rend_cell_body) { crypt_path_t *hop = NULL; char keys[DIGEST_LEN+CPATH_KEY_MATERIAL_LEN]; /* first DH_KEY_LEN bytes are g^y from the service. Finish the dh * handshake...*/ tor_assert(circ->build_state); tor_assert(circ->build_state->pending_final_cpath); hop = circ->build_state->pending_final_cpath; tor_assert(hop->rend_dh_handshake_state); if (crypto_dh_compute_secret(LOG_PROTOCOL_WARN, hop->rend_dh_handshake_state, (char*)rend_cell_body, DH_KEY_LEN, keys, DIGEST_LEN+CPATH_KEY_MATERIAL_LEN)<0) { log_warn(LD_GENERAL, "Couldn't complete DH handshake."); goto err; } /* ... and set up cpath. */ if (circuit_init_cpath_crypto(hop, keys+DIGEST_LEN, sizeof(keys)-DIGEST_LEN, 0, 0) < 0) goto err; /* Check whether the digest is right... */ if (tor_memneq(keys, rend_cell_body+DH_KEY_LEN, DIGEST_LEN)) { log_warn(LD_PROTOCOL, "Incorrect digest of key material."); goto err; } /* clean up the crypto stuff we just made */ crypto_dh_free(hop->rend_dh_handshake_state); hop->rend_dh_handshake_state = NULL; goto done; err: hop = NULL; done: memwipe(keys, 0, sizeof(keys)); return hop; }
/** * Given an ed25519 keypair in <b>inp</b>, generate a corresponding * ed25519 keypair in <b>out</b>, blinded by the corresponding 32-byte input * in 'param'. * * Tor uses key blinding for the "next-generation" hidden services design: * service descriptors are encrypted with a key derived from the service's * long-term public key, and then signed with (and stored at a position * indexed by) a short-term key derived by blinding the long-term keys. */ int ed25519_keypair_blind(ed25519_keypair_t *out, const ed25519_keypair_t *inp, const uint8_t *param) { ed25519_public_key_t pubkey_check; get_ed_impl()->blind_secret_key(out->seckey.seckey, inp->seckey.seckey, param); ed25519_public_blind(&pubkey_check, &inp->pubkey, param); ed25519_public_key_generate(&out->pubkey, &out->seckey); tor_assert(fast_memeq(pubkey_check.pubkey, out->pubkey.pubkey, 32)); memwipe(&pubkey_check, 0, sizeof(pubkey_check)); return 0; }
/** Construct cross-certification for the master identity key with * the ntor onion key. Store the sign of the corresponding ed25519 public key * in *<b>sign_out</b>. */ tor_cert_t * make_ntor_onion_key_crosscert(const curve25519_keypair_t *onion_key, const ed25519_public_key_t *master_id_key, time_t now, time_t lifetime, int *sign_out) { tor_cert_t *cert = NULL; ed25519_keypair_t ed_onion_key; if (ed25519_keypair_from_curve25519_keypair(&ed_onion_key, sign_out, onion_key) < 0) goto end; cert = tor_cert_create(&ed_onion_key, CERT_TYPE_ONION_ID, master_id_key, now, lifetime, 0); end: memwipe(&ed_onion_key, 0, sizeof(ed_onion_key)); return cert; }
STATIC int curve25519_impl(uint8_t *output, const uint8_t *secret, const uint8_t *basepoint) { uint8_t bp[CURVE25519_PUBKEY_LEN]; int r; memcpy(bp, basepoint, CURVE25519_PUBKEY_LEN); /* Clear the high bit, in case our backend foolishly looks at it. */ bp[31] &= 0x7f; #ifdef USE_CURVE25519_DONNA r = curve25519_donna(output, secret, bp); #elif defined(USE_CURVE25519_NACL) r = crypto_scalarmult_curve25519(output, secret, bp); #else #error "No implementation of curve25519 is available." #endif memwipe(bp, 0, sizeof(bp)); return r; }
/** * Extract <b>n</b> bytes from <b>rng</b> into the buffer at <b>out</b>. **/ void crypto_fast_rng_getbytes(crypto_fast_rng_t *rng, uint8_t *out, size_t n) { if (PREDICT_UNLIKELY(n > BUFLEN)) { /* The user has asked for a lot of output; generate it from a stream * cipher seeded by the PRNG rather than by pulling it out of the PRNG * directly. */ uint8_t seed[SEED_LEN]; crypto_fast_rng_getbytes_impl(rng, seed, SEED_LEN); crypto_cipher_t *c = cipher_from_seed(seed); memset(out, 0, n); crypto_cipher_crypt_inplace(c, (char*)out, n); crypto_cipher_free(c); memwipe(seed, 0, sizeof(seed)); return; } crypto_fast_rng_getbytes_impl(rng, out, n); }
/* DOCDOC */ int curve25519_keypair_write_to_file(const curve25519_keypair_t *keypair, const char *fname, const char *tag) { uint8_t contents[CURVE25519_SECKEY_LEN + CURVE25519_PUBKEY_LEN]; int r; memcpy(contents, keypair->seckey.secret_key, CURVE25519_SECKEY_LEN); memcpy(contents+CURVE25519_SECKEY_LEN, keypair->pubkey.public_key, CURVE25519_PUBKEY_LEN); r = crypto_write_tagged_contents_to_file(fname, "c25519v1", tag, contents, sizeof(contents)); memwipe(contents, 0, sizeof(contents)); return r; }
/* Encode a reveal element using a given commit object to dst which is a * buffer large enough to put the base64-encoded reveal construction. The * format is as follow: * REVEAL = base64-encode( TIMESTAMP || H(RN) ) * Return base64 encoded length on success else a negative value. */ STATIC int reveal_encode(const sr_commit_t *commit, char *dst, size_t len) { int ret; size_t offset = 0; char buf[SR_REVEAL_LEN] = {0}; tor_assert(commit); tor_assert(dst); set_uint64(buf, tor_htonll(commit->reveal_ts)); offset += sizeof(uint64_t); memcpy(buf + offset, commit->random_number, sizeof(commit->random_number)); /* Let's clean the buffer and then b64 encode it. */ memset(dst, 0, len); ret = base64_encode(dst, len, buf, sizeof(buf), 0); /* Wipe this buffer because it contains our random value. */ memwipe(buf, 0, sizeof(buf)); return ret; }
/** Does the digest for this circuit indicate that this cell is for us? * * Update digest from the payload of cell (with the integrity part set * to 0). If the integrity part is valid, return 1, else restore digest * and cell to their original state and return 0. */ static int relay_digest_matches(crypto_digest_t *digest, cell_t *cell) { uint32_t received_integrity, calculated_integrity; relay_header_t rh; crypto_digest_checkpoint_t backup_digest; crypto_digest_checkpoint(&backup_digest, digest); relay_header_unpack(&rh, cell->payload); memcpy(&received_integrity, rh.integrity, 4); memset(rh.integrity, 0, 4); relay_header_pack(cell->payload, &rh); // log_fn(LOG_DEBUG,"Reading digest of %u %u %u %u from relay cell.", // received_integrity[0], received_integrity[1], // received_integrity[2], received_integrity[3]); crypto_digest_add_bytes(digest, (char*) cell->payload, CELL_PAYLOAD_SIZE); crypto_digest_get_digest(digest, (char*) &calculated_integrity, 4); int rv = 1; if (calculated_integrity != received_integrity) { // log_fn(LOG_INFO,"Recognized=0 but bad digest. Not recognizing."); // (%d vs %d).", received_integrity, calculated_integrity); /* restore digest to its old form */ crypto_digest_restore(digest, &backup_digest); /* restore the relay header */ memcpy(rh.integrity, &received_integrity, 4); relay_header_pack(cell->payload, &rh); rv = 0; } memwipe(&backup_digest, 0, sizeof(backup_digest)); return rv; }