Пример #1
0
int tls1_change_cipher_state(SSL *ssl, int which) {
  /* Ensure the key block is set up. */
  if (!tls1_setup_key_block(ssl)) {
    return 0;
  }

  /* is_read is true if we have just read a ChangeCipherSpec message - i.e. we
   * need to update the read cipherspec. Otherwise we have just written one. */
  const char is_read = (which & SSL3_CC_READ) != 0;
  /* use_client_keys is true if we wish to use the keys for the "client write"
   * direction. This is the case if we're a client sending a ChangeCipherSpec,
   * or a server reading a client's ChangeCipherSpec. */
  const char use_client_keys = which == SSL3_CHANGE_CIPHER_CLIENT_WRITE ||
                               which == SSL3_CHANGE_CIPHER_SERVER_READ;

  size_t mac_secret_len = ssl->s3->tmp.new_mac_secret_len;
  size_t key_len = ssl->s3->tmp.new_key_len;
  size_t iv_len = ssl->s3->tmp.new_fixed_iv_len;
  assert((mac_secret_len + key_len + iv_len) * 2 ==
         ssl->s3->tmp.key_block_length);

  const uint8_t *key_data = ssl->s3->tmp.key_block;
  const uint8_t *client_write_mac_secret = key_data;
  key_data += mac_secret_len;
  const uint8_t *server_write_mac_secret = key_data;
  key_data += mac_secret_len;
  const uint8_t *client_write_key = key_data;
  key_data += key_len;
  const uint8_t *server_write_key = key_data;
  key_data += key_len;
  const uint8_t *client_write_iv = key_data;
  key_data += iv_len;
  const uint8_t *server_write_iv = key_data;
  key_data += iv_len;

  const uint8_t *mac_secret, *key, *iv;
  if (use_client_keys) {
    mac_secret = client_write_mac_secret;
    key = client_write_key;
    iv = client_write_iv;
  } else {
    mac_secret = server_write_mac_secret;
    key = server_write_key;
    iv = server_write_iv;
  }

  SSL_AEAD_CTX *aead_ctx =
      SSL_AEAD_CTX_new(is_read ? evp_aead_open : evp_aead_seal,
                       ssl3_protocol_version(ssl), ssl->s3->tmp.new_cipher, key,
                       key_len, mac_secret, mac_secret_len, iv, iv_len);
  if (aead_ctx == NULL) {
    return 0;
  }

  if (is_read) {
    return ssl->method->set_read_state(ssl, aead_ctx);
  }

  return ssl->method->set_write_state(ssl, aead_ctx);
}
Пример #2
0
int tls1_change_cipher_state(SSL *s, int which) {
  /* is_read is true if we have just read a ChangeCipherSpec message - i.e. we
   * need to update the read cipherspec. Otherwise we have just written one. */
  const char is_read = (which & SSL3_CC_READ) != 0;
  /* use_client_keys is true if we wish to use the keys for the "client write"
   * direction. This is the case if we're a client sending a ChangeCipherSpec,
   * or a server reading a client's ChangeCipherSpec. */
  const char use_client_keys = which == SSL3_CHANGE_CIPHER_CLIENT_WRITE ||
                               which == SSL3_CHANGE_CIPHER_SERVER_READ;
  const uint8_t *client_write_mac_secret, *server_write_mac_secret, *mac_secret;
  const uint8_t *client_write_key, *server_write_key, *key;
  const uint8_t *client_write_iv, *server_write_iv, *iv;
  const EVP_AEAD *aead = s->s3->tmp.new_aead;
  size_t key_len, iv_len, mac_secret_len;
  const uint8_t *key_data;

  /* Reset sequence number to zero. */
  if (!SSL_IS_DTLS(s)) {
    memset(is_read ? s->s3->read_sequence : s->s3->write_sequence, 0, 8);
  }

  mac_secret_len = s->s3->tmp.new_mac_secret_len;
  iv_len = s->s3->tmp.new_fixed_iv_len;

  if (aead == NULL) {
    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
    return 0;
  }

  key_len = EVP_AEAD_key_length(aead);
  if (mac_secret_len > 0) {
    /* For "stateful" AEADs (i.e. compatibility with pre-AEAD cipher
     * suites) the key length reported by |EVP_AEAD_key_length| will
     * include the MAC and IV key bytes. */
    if (key_len < mac_secret_len + iv_len) {
      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
      return 0;
    }
    key_len -= mac_secret_len + iv_len;
  }

  key_data = s->s3->tmp.key_block;
  client_write_mac_secret = key_data;
  key_data += mac_secret_len;
  server_write_mac_secret = key_data;
  key_data += mac_secret_len;
  client_write_key = key_data;
  key_data += key_len;
  server_write_key = key_data;
  key_data += key_len;
  client_write_iv = key_data;
  key_data += iv_len;
  server_write_iv = key_data;
  key_data += iv_len;

  if (use_client_keys) {
    mac_secret = client_write_mac_secret;
    key = client_write_key;
    iv = client_write_iv;
  } else {
    mac_secret = server_write_mac_secret;
    key = server_write_key;
    iv = server_write_iv;
  }

  if (key_data - s->s3->tmp.key_block != s->s3->tmp.key_block_length) {
    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
    return 0;
  }

  if (is_read) {
    SSL_AEAD_CTX_free(s->aead_read_ctx);
    s->aead_read_ctx = SSL_AEAD_CTX_new(
        evp_aead_open, ssl3_version_from_wire(s, s->version),
        s->s3->tmp.new_cipher, key, key_len, mac_secret, mac_secret_len, iv,
        iv_len);
    return s->aead_read_ctx != NULL;
  }

  SSL_AEAD_CTX_free(s->aead_write_ctx);
  s->aead_write_ctx = SSL_AEAD_CTX_new(
      evp_aead_seal, ssl3_version_from_wire(s, s->version),
      s->s3->tmp.new_cipher, key, key_len, mac_secret, mac_secret_len, iv,
      iv_len);
  if (s->aead_write_ctx == NULL) {
    return 0;
  }

  s->s3->need_record_splitting = 0;
  if (!SSL_USE_EXPLICIT_IV(s) &&
      (s->mode & SSL_MODE_CBC_RECORD_SPLITTING) != 0 &&
      SSL_CIPHER_is_block_cipher(s->s3->tmp.new_cipher)) {
    /* Enable 1/n-1 record-splitting to randomize the IV. See
     * https://www.openssl.org/~bodo/tls-cbc.txt and the BEAST attack. */
    s->s3->need_record_splitting = 1;
  }
  return 1;
}
Пример #3
0
int tls13_set_traffic_key(SSL *ssl, enum tls_record_type_t type,
                          enum evp_aead_direction_t direction,
                          const uint8_t *traffic_secret,
                          size_t traffic_secret_len) {
    if (traffic_secret_len > 0xff) {
        OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
        return 0;
    }

    const char *phase;
    switch (type) {
    case type_early_handshake:
        phase = "early handshake key expansion, ";
        break;
    case type_early_data:
        phase = "early application data key expansion, ";
        break;
    case type_handshake:
        phase = "handshake key expansion, ";
        break;
    case type_data:
        phase = "application data key expansion, ";
        break;
    default:
        return 0;
    }
    size_t phase_len = strlen(phase);

    const char *purpose = "client write key";
    if ((ssl->server && direction == evp_aead_seal) ||
            (!ssl->server && direction == evp_aead_open)) {
        purpose = "server write key";
    }
    size_t purpose_len = strlen(purpose);

    /* The longest label has length 38 (type_early_data) + 16 (either purpose
     * value). */
    uint8_t label[38 + 16];
    size_t label_len = phase_len + purpose_len;
    if (label_len > sizeof(label)) {
        assert(0);
        return 0;
    }
    memcpy(label, phase, phase_len);
    memcpy(label + phase_len, purpose, purpose_len);

    /* Look up cipher suite properties. */
    const EVP_AEAD *aead;
    const EVP_MD *digest = ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl));
    size_t mac_secret_len, fixed_iv_len;
    if (!ssl_cipher_get_evp_aead(&aead, &mac_secret_len, &fixed_iv_len,
                                 ssl->session->cipher,
                                 ssl3_protocol_version(ssl))) {
        return 0;
    }

    /* Derive the key. */
    size_t key_len = EVP_AEAD_key_length(aead);
    uint8_t key[EVP_AEAD_MAX_KEY_LENGTH];
    if (!hkdf_expand_label(key, digest, traffic_secret, traffic_secret_len, label,
                           label_len, NULL, 0, key_len)) {
        return 0;
    }

    /* The IV's label ends in "iv" instead of "key". */
    if (label_len < 3) {
        assert(0);
        return 0;
    }
    label_len--;
    label[label_len - 2] = 'i';
    label[label_len - 1] = 'v';

    /* Derive the IV. */
    size_t iv_len = EVP_AEAD_nonce_length(aead);
    uint8_t iv[EVP_AEAD_MAX_NONCE_LENGTH];
    if (!hkdf_expand_label(iv, digest, traffic_secret, traffic_secret_len, label,
                           label_len, NULL, 0, iv_len)) {
        return 0;
    }

    SSL_AEAD_CTX *traffic_aead = SSL_AEAD_CTX_new(direction,
                                 ssl3_protocol_version(ssl),
                                 ssl->session->cipher,
                                 key, key_len, NULL, 0,
                                 iv, iv_len);
    if (traffic_aead == NULL) {
        return 0;
    }

    if (direction == evp_aead_open) {
        if (!ssl->method->set_read_state(ssl, traffic_aead)) {
            return 0;
        }
    } else {
        if (!ssl->method->set_write_state(ssl, traffic_aead)) {
            return 0;
        }
    }

    /* Save the traffic secret. */
    if (direction == evp_aead_open) {
        memcpy(ssl->s3->read_traffic_secret, traffic_secret, traffic_secret_len);
        ssl->s3->read_traffic_secret_len = traffic_secret_len;
    } else {
        memcpy(ssl->s3->write_traffic_secret, traffic_secret, traffic_secret_len);
        ssl->s3->write_traffic_secret_len = traffic_secret_len;
    }

    return 1;
}