Ejemplo n.º 1
0
/* Public function: Do the appropriate ntor calculations and derive the keys
 * needed to create and authenticate RENDEZVOUS1 cells. Return 0 and place the
 * final key material in <b>hs_ntor_rend_cell_keys_out</b> if all went fine,
 * return -1 if error happened.
 *
 * The relevant calculations are as follows:
 *
 *  rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID
 *  NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc)
 *  verify = MAC(rend_secret_hs_input, t_hsverify)
 *  auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
 *  auth_input_mac = MAC(auth_input, t_hsmac)
 *
 * where:
 * <b>intro_auth_pubkey</b> is AUTH_KEY (intro point auth key),
 * <b>intro_enc_keypair</b> is (b,B) (intro point enc keypair)
 * <b>service_ephemeral_rend_keypair</b> is a fresh (y,Y) keypair
 * <b>client_ephemeral_enc_pubkey</b> is X (CLIENT_PK in INTRODUCE2 cell) */
int
hs_ntor_service_get_rendezvous1_keys(
                    const ed25519_public_key_t *intro_auth_pubkey,
                    const curve25519_keypair_t *intro_enc_keypair,
                    const curve25519_keypair_t *service_ephemeral_rend_keypair,
                    const curve25519_public_key_t *client_ephemeral_enc_pubkey,
                    hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out)
{
  int bad = 0;
  uint8_t rend_secret_hs_input[REND_SECRET_HS_INPUT_LEN];
  uint8_t dh_result1[CURVE25519_OUTPUT_LEN];
  uint8_t dh_result2[CURVE25519_OUTPUT_LEN];

  tor_assert(intro_auth_pubkey);
  tor_assert(intro_enc_keypair);
  tor_assert(service_ephemeral_rend_keypair);
  tor_assert(client_ephemeral_enc_pubkey);
  tor_assert(hs_ntor_rend_cell_keys_out);

  /* Compute EXP(X, y) */
  curve25519_handshake(dh_result1,
                       &service_ephemeral_rend_keypair->seckey,
                       client_ephemeral_enc_pubkey);
  bad |= safe_mem_is_zero(dh_result1, CURVE25519_OUTPUT_LEN);

  /* Compute EXP(X, b) */
  curve25519_handshake(dh_result2,
                       &intro_enc_keypair->seckey,
                       client_ephemeral_enc_pubkey);
  bad |= safe_mem_is_zero(dh_result2, CURVE25519_OUTPUT_LEN);

  /* Get rend_secret_hs_input */
  get_rend_secret_hs_input(dh_result1, dh_result2,
                           intro_auth_pubkey,
                           &intro_enc_keypair->pubkey,
                           client_ephemeral_enc_pubkey,
                           &service_ephemeral_rend_keypair->pubkey,
                           rend_secret_hs_input);

  /* Get NTOR_KEY_SEED and AUTH_INPUT_MAC! */
  bad |= get_rendezvous1_key_material(rend_secret_hs_input,
                                      intro_auth_pubkey,
                                      &intro_enc_keypair->pubkey,
                                      &service_ephemeral_rend_keypair->pubkey,
                                      client_ephemeral_enc_pubkey,
                                      hs_ntor_rend_cell_keys_out);

  memwipe(rend_secret_hs_input, 0, sizeof(rend_secret_hs_input));
  if (bad) {
    memwipe(hs_ntor_rend_cell_keys_out, 0, sizeof(hs_ntor_rend_cell_keys_t));
  }

  return bad ? -1 : 0;
}
Ejemplo n.º 2
0
/* 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;
}
Ejemplo n.º 3
0
/** Helper function: Compute the last part of the HS ntor handshake which
 *  derives key material necessary to create and handle RENDEZVOUS1
 *  cells. Function used by both client and service. The actual calculations is
 *  as follows:
 *
 *    NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc)
 *    verify = MAC(rend_secret_hs_input, t_hsverify)
 *    auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
 *    auth_input_mac = MAC(auth_input, t_hsmac)
 *
 *  where in the above, AUTH_KEY is <b>intro_auth_pubkey</b>, B is
 *  <b>intro_enc_pubkey</b>, Y is <b>service_ephemeral_rend_pubkey</b>, and X
 *  is <b>client_ephemeral_enc_pubkey</b>. The provided
 *  <b>rend_secret_hs_input</b> is of size REND_SECRET_HS_INPUT_LEN.
 *
 *  The final results of NTOR_KEY_SEED and auth_input_mac are placed in
 *  <b>hs_ntor_rend_cell_keys_out</b>. Return 0 if everything went fine. */
static int
get_rendezvous1_key_material(const uint8_t *rend_secret_hs_input,
                  const ed25519_public_key_t *intro_auth_pubkey,
                  const curve25519_public_key_t *intro_enc_pubkey,
                  const curve25519_public_key_t *service_ephemeral_rend_pubkey,
                  const curve25519_public_key_t *client_ephemeral_enc_pubkey,
                  hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out)
{
  int bad = 0;
  uint8_t ntor_key_seed[DIGEST256_LEN];
  uint8_t ntor_verify[DIGEST256_LEN];
  uint8_t rend_auth_input[REND_AUTH_INPUT_LEN];
  uint8_t rend_cell_auth[DIGEST256_LEN];
  uint8_t *ptr;

  /* Let's build NTOR_KEY_SEED */
  crypto_mac_sha3_256(ntor_key_seed, sizeof(ntor_key_seed),
                      rend_secret_hs_input, REND_SECRET_HS_INPUT_LEN,
                      (const uint8_t *)T_HSENC, strlen(T_HSENC));
  bad |= safe_mem_is_zero(ntor_key_seed, DIGEST256_LEN);

  /* Let's build ntor_verify */
  crypto_mac_sha3_256(ntor_verify, sizeof(ntor_verify),
                      rend_secret_hs_input, REND_SECRET_HS_INPUT_LEN,
                      (const uint8_t *)T_HSVERIFY, strlen(T_HSVERIFY));
  bad |= safe_mem_is_zero(ntor_verify, DIGEST256_LEN);

  /* Let's build auth_input: */
  ptr = rend_auth_input;
  /* Append ntor_verify */
  APPEND(ptr, ntor_verify, sizeof(ntor_verify));
  /* Append AUTH_KEY */
  APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN);
  /* Append B */
  APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
  /* Append Y */
  APPEND(ptr,
         service_ephemeral_rend_pubkey->public_key, CURVE25519_PUBKEY_LEN);
  /* Append X */
  APPEND(ptr,
         client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
  /* Append PROTOID */
  APPEND(ptr, PROTOID, strlen(PROTOID));
  /* Append "Server" */
  APPEND(ptr, SERVER_STR, strlen(SERVER_STR));
  tor_assert(ptr == rend_auth_input + sizeof(rend_auth_input));

  /* Let's build auth_input_mac that goes in RENDEZVOUS1 cell */
  crypto_mac_sha3_256(rend_cell_auth, sizeof(rend_cell_auth),
                      rend_auth_input, sizeof(rend_auth_input),
                      (const uint8_t *)T_HSMAC, strlen(T_HSMAC));
  bad |= safe_mem_is_zero(ntor_verify, DIGEST256_LEN);

  { /* Get the computed RENDEZVOUS1 material! */
    memcpy(&hs_ntor_rend_cell_keys_out->rend_cell_auth_mac,
           rend_cell_auth, DIGEST256_LEN);
    memcpy(&hs_ntor_rend_cell_keys_out->ntor_key_seed,
           ntor_key_seed, DIGEST256_LEN);
  }

  memwipe(rend_cell_auth, 0, sizeof(rend_cell_auth));
  memwipe(rend_auth_input, 0, sizeof(rend_auth_input));
  memwipe(ntor_key_seed, 0, sizeof(ntor_key_seed));

  return bad;
}
Ejemplo n.º 4
0
/**
 * Perform the final client side of the ntor handshake, using the state in
 * <b>handshake_state</b> and the server's NTOR_REPLY_LEN-byte reply in
 * <b>handshake_reply</b>.  Generate <b>key_out_len</b> bytes of key material
 * in <b>key_out</b>. Return 0 on success, -1 on failure.
 */
int
onion_skin_ntor_client_handshake(
                             const ntor_handshake_state_t *handshake_state,
                             const uint8_t *handshake_reply,
                             uint8_t *key_out,
                             size_t key_out_len,
                             const char **msg_out)
{
  const tweakset_t *T = &proto1_tweaks;
  /* Sensitive stack-allocated material. Kept in an anonymous struct to make
   * it easy to wipe. */
  struct {
    curve25519_public_key_t pubkey_Y;
    uint8_t secret_input[SECRET_INPUT_LEN];
    uint8_t verify[DIGEST256_LEN];
    uint8_t auth_input[AUTH_INPUT_LEN];
    uint8_t auth[DIGEST256_LEN];
  } s;
  uint8_t *ai = s.auth_input, *si = s.secret_input;
  const uint8_t *auth_candidate;
  int bad;

  /* Decode input */
  memcpy(s.pubkey_Y.public_key, handshake_reply, CURVE25519_PUBKEY_LEN);
  auth_candidate = handshake_reply + CURVE25519_PUBKEY_LEN;

  /* See note in server_handshake above about checking points.  The
   * circumstances under which we'd need to check Y for membership are
   * different than those under which we'd be checking X. */

  /* Compute secret_input */
  curve25519_handshake(si, &handshake_state->seckey_x, &s.pubkey_Y);
  bad = safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN);
  si += CURVE25519_OUTPUT_LEN;
  curve25519_handshake(si, &handshake_state->seckey_x,
                       &handshake_state->pubkey_B);
  bad |= (safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN) << 1);
  si += CURVE25519_OUTPUT_LEN;
  APPEND(si, handshake_state->router_id, DIGEST_LEN);
  APPEND(si, handshake_state->pubkey_B.public_key, CURVE25519_PUBKEY_LEN);
  APPEND(si, handshake_state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
  APPEND(si, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
  APPEND(si, PROTOID, PROTOID_LEN);
  tor_assert(si == s.secret_input + sizeof(s.secret_input));

  /* Compute verify from secret_input */
  h_tweak(s.verify, s.secret_input, sizeof(s.secret_input), T->t_verify);

  /* Compute auth_input */
  APPEND(ai, s.verify, DIGEST256_LEN);
  APPEND(ai, handshake_state->router_id, DIGEST_LEN);
  APPEND(ai, handshake_state->pubkey_B.public_key, CURVE25519_PUBKEY_LEN);
  APPEND(ai, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
  APPEND(ai, handshake_state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
  APPEND(ai, PROTOID, PROTOID_LEN);
  APPEND(ai, SERVER_STR, SERVER_STR_LEN);
  tor_assert(ai == s.auth_input + sizeof(s.auth_input));

  /* Compute auth */
  h_tweak(s.auth, s.auth_input, sizeof(s.auth_input), T->t_mac);

  bad |= (tor_memneq(s.auth, auth_candidate, DIGEST256_LEN) << 2);

  crypto_expand_key_material_rfc5869_sha256(
                           s.secret_input, sizeof(s.secret_input),
                           (const uint8_t*)T->t_key, strlen(T->t_key),
                           (const uint8_t*)T->m_expand, strlen(T->m_expand),
                           key_out, key_out_len);

  memwipe(&s, 0, sizeof(s));

  if (bad) {
    if (bad & 4) {
      if (msg_out)
        *msg_out = NULL; /* Don't report this one; we probably just had the
                          * wrong onion key.*/
      log_fn(LOG_INFO, LD_PROTOCOL,
             "Invalid result from curve25519 handshake: %d", bad);
    }
    if (bad & 3) {
      if (msg_out)
        *msg_out = "Zero output from curve25519 handshake";
      log_fn(LOG_WARN, LD_PROTOCOL,
             "Invalid result from curve25519 handshake: %d", bad);
    }
  }

  return bad ? -1 : 0;
}
Ejemplo n.º 5
0
/**
 * Perform the server side of an ntor handshake. Given an
 * NTOR_ONIONSKIN_LEN-byte message in <b>onion_skin</b>, our own identity
 * fingerprint as <b>my_node_id</b>, and an associative array mapping public
 * onion keys to curve25519_keypair_t in <b>private_keys</b>, attempt to
 * perform the handshake.  Use <b>junk_keys</b> if present if the handshake
 * indicates an unrecognized public key.  Write an NTOR_REPLY_LEN-byte
 * message to send back to the client into <b>handshake_reply_out</b>, and
 * generate <b>key_out_len</b> bytes of key material in <b>key_out</b>. Return
 * 0 on success, -1 on failure.
 */
int
onion_skin_ntor_server_handshake(const uint8_t *onion_skin,
                                 const di_digest256_map_t *private_keys,
                                 const curve25519_keypair_t *junk_keys,
                                 const uint8_t *my_node_id,
                                 uint8_t *handshake_reply_out,
                                 uint8_t *key_out,
                                 size_t key_out_len)
{
  const tweakset_t *T = &proto1_tweaks;
  /* Sensitive stack-allocated material. Kept in an anonymous struct to make
   * it easy to wipe. */
  struct {
    uint8_t secret_input[SECRET_INPUT_LEN];
    uint8_t auth_input[AUTH_INPUT_LEN];
    curve25519_public_key_t pubkey_X;
    curve25519_secret_key_t seckey_y;
    curve25519_public_key_t pubkey_Y;
    uint8_t verify[DIGEST256_LEN];
  } s;
  uint8_t *si = s.secret_input, *ai = s.auth_input;
  const curve25519_keypair_t *keypair_bB;
  int bad;

  /* Decode the onion skin */
  /* XXXX Does this possible early-return business threaten our security? */
  if (tor_memneq(onion_skin, my_node_id, DIGEST_LEN))
    return -1;
  /* Note that on key-not-found, we go through with this operation anyway,
   * using "junk_keys". This will result in failed authentication, but won't
   * leak whether we recognized the key. */
  keypair_bB = dimap_search(private_keys, onion_skin + DIGEST_LEN,
                            (void*)junk_keys);
  if (!keypair_bB)
    return -1;

  memcpy(s.pubkey_X.public_key, onion_skin+DIGEST_LEN+DIGEST256_LEN,
         CURVE25519_PUBKEY_LEN);

  /* Make y, Y */
  curve25519_secret_key_generate(&s.seckey_y, 0);
  curve25519_public_key_generate(&s.pubkey_Y, &s.seckey_y);

  /* NOTE: If we ever use a group other than curve25519, or a different
   * representation for its points, we may need to perform different or
   * additional checks on X here and on Y in the client handshake, or lose our
   * security properties. What checks we need would depend on the properties
   * of the group and its representation.
   *
   * In short: if you use anything other than curve25519, this aspect of the
   * code will need to be reconsidered carefully. */

  /* build secret_input */
  curve25519_handshake(si, &s.seckey_y, &s.pubkey_X);
  bad = safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN);
  si += CURVE25519_OUTPUT_LEN;
  curve25519_handshake(si, &keypair_bB->seckey, &s.pubkey_X);
  bad |= safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN);
  si += CURVE25519_OUTPUT_LEN;

  APPEND(si, my_node_id, DIGEST_LEN);
  APPEND(si, keypair_bB->pubkey.public_key, CURVE25519_PUBKEY_LEN);
  APPEND(si, s.pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
  APPEND(si, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
  APPEND(si, PROTOID, PROTOID_LEN);
  tor_assert(si == s.secret_input + sizeof(s.secret_input));

  /* Compute hashes of secret_input */
  h_tweak(s.verify, s.secret_input, sizeof(s.secret_input), T->t_verify);

  /* Compute auth_input */
  APPEND(ai, s.verify, DIGEST256_LEN);
  APPEND(ai, my_node_id, DIGEST_LEN);
  APPEND(ai, keypair_bB->pubkey.public_key, CURVE25519_PUBKEY_LEN);
  APPEND(ai, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
  APPEND(ai, s.pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
  APPEND(ai, PROTOID, PROTOID_LEN);
  APPEND(ai, SERVER_STR, SERVER_STR_LEN);
  tor_assert(ai == s.auth_input + sizeof(s.auth_input));

  /* Build the reply */
  memcpy(handshake_reply_out, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
  h_tweak(handshake_reply_out+CURVE25519_PUBKEY_LEN,
          s.auth_input, sizeof(s.auth_input),
          T->t_mac);

  /* Generate the key material */
  crypto_expand_key_material_rfc5869_sha256(
                           s.secret_input, sizeof(s.secret_input),
                           (const uint8_t*)T->t_key, strlen(T->t_key),
                           (const uint8_t*)T->m_expand, strlen(T->m_expand),
                           key_out, key_out_len);

  /* Wipe all of our local state */
  memwipe(&s, 0, sizeof(s));

  return bad ? -1 : 0;
}
Ejemplo n.º 6
0
/**
 * Return true iff a curve25519_public_key_t seems valid. (It's not necessary
 * to see if the point is on the curve, since the twist is also secure, but we
 * do need to make sure that it isn't the point at infinity.) */
int
curve25519_public_key_is_ok(const curve25519_public_key_t *key)
{
  return !safe_mem_is_zero(key->public_key, CURVE25519_PUBKEY_LEN);
}