Add SSL_CIPHER_get_rfc_name.

OpenSSL's internal names for the ciphers are not the standard ones and are not
easy to consistently map to the standard ones. Add an API to get the real names
out. (WebRTC wants an API to get the standard names out.)

Also change some incorrect flags on SHA-256 TLS 1.2 ciphers;
SSL_HANDSHAKE_MAC_DEFAULT and SSL_HANDSHAKE_MAC_SHA256 are the same after TLS
1.2. A TLS 1.2 cipher should be tagged explicitly with SHA-256. (This avoids
tripping a check in SSL_CIPHER_get_rfc_name which asserts that default-hash
ciphers only ever use SHA-1 or MD5 for the bulk cipher MAC.)

Change-Id: Iaec2fd4aa97df29883094d3c2ae60f0ba003bf07
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 19f9f00..0e75011 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1811,9 +1811,16 @@
 OPENSSL_EXPORT int SSL_CIPHER_get_bits(const SSL_CIPHER *c, int *alg_bits);
 OPENSSL_EXPORT const char *SSL_CIPHER_get_version(const SSL_CIPHER *c);
 OPENSSL_EXPORT const char *SSL_CIPHER_get_name(const SSL_CIPHER *c);
+
 /* SSL_CIPHER_get_kx_name returns a string that describes the key-exchange
- * method used by |c|. For example, "ECDHE-ECDSA". */
+ * method used by |cipher|. For example, "ECDHE-ECDSA". */
 OPENSSL_EXPORT const char *SSL_CIPHER_get_kx_name(const SSL_CIPHER *cipher);
+
+/* SSL_CIPHER_get_rfc_name returns a newly-allocated string with the standard
+ * name for |cipher|. For example, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256". The
+ * caller is responsible for calling |OPENSSL_free| on the result. */
+OPENSSL_EXPORT char *SSL_CIPHER_get_rfc_name(const SSL_CIPHER *cipher);
+
 OPENSSL_EXPORT unsigned long SSL_CIPHER_get_id(const SSL_CIPHER *c);
 
 OPENSSL_EXPORT int SSL_get_fd(const SSL *s);
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index c230427..8ed2be9 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -246,14 +246,14 @@
     {
      1, TLS1_TXT_RSA_WITH_AES_128_SHA256, TLS1_CK_RSA_WITH_AES_128_SHA256,
      SSL_kRSA, SSL_aRSA, SSL_AES128, SSL_SHA256, SSL_TLSV1_2,
-     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
+     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256, 128, 128,
     },
 
     /* Cipher 3D */
     {
      1, TLS1_TXT_RSA_WITH_AES_256_SHA256, TLS1_CK_RSA_WITH_AES_256_SHA256,
      SSL_kRSA, SSL_aRSA, SSL_AES256, SSL_SHA256, SSL_TLSV1_2,
-     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256,
+     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256, 256, 256,
     },
 
     /* Cipher 67 */
@@ -261,7 +261,7 @@
      1, TLS1_TXT_DHE_RSA_WITH_AES_128_SHA256,
      TLS1_CK_DHE_RSA_WITH_AES_128_SHA256, SSL_kEDH, SSL_aRSA, SSL_AES128,
      SSL_SHA256, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
+     SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256, 128, 128,
     },
 
     /* Cipher 6B */
@@ -269,21 +269,21 @@
      1, TLS1_TXT_DHE_RSA_WITH_AES_256_SHA256,
      TLS1_CK_DHE_RSA_WITH_AES_256_SHA256, SSL_kEDH, SSL_aRSA, SSL_AES256,
      SSL_SHA256, SSL_TLSV1_2, SSL_HIGH | SSL_FIPS,
-     SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256,
+     SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256, 256, 256,
     },
 
     /* Cipher 6C */
     {
      1, TLS1_TXT_ADH_WITH_AES_128_SHA256, TLS1_CK_ADH_WITH_AES_128_SHA256,
      SSL_kEDH, SSL_aNULL, SSL_AES128, SSL_SHA256, SSL_TLSV1_2,
-     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 128, 128,
+     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256, 128, 128,
     },
 
     /* Cipher 6D */
     {
      1, TLS1_TXT_ADH_WITH_AES_256_SHA256, TLS1_CK_ADH_WITH_AES_256_SHA256,
      SSL_kEDH, SSL_aNULL, SSL_AES256, SSL_SHA256, SSL_TLSV1_2,
-     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_DEFAULT | TLS1_PRF, 256, 256,
+     SSL_HIGH | SSL_FIPS, SSL_HANDSHAKE_MAC_SHA256 | TLS1_PRF_SHA256, 256, 256,
     },
 
     /* Cipher 8A */
diff --git a/ssl/ssl_ciph.c b/ssl/ssl_ciph.c
index e792533..ad2e301 100644
--- a/ssl/ssl_ciph.c
+++ b/ssl/ssl_ciph.c
@@ -1322,35 +1322,120 @@
 
   switch (cipher->algorithm_mkey) {
     case SSL_kRSA:
-      return SSL_TXT_RSA;
+      return "RSA";
 
     case SSL_kEDH:
       switch (cipher->algorithm_auth) {
         case SSL_aRSA:
-          return "DHE_" SSL_TXT_RSA;
+          return "DHE_RSA";
         case SSL_aNULL:
-          return SSL_TXT_DH "_anon";
+          return "DH_anon";
         default:
+          assert(0);
           return "UNKNOWN";
       }
 
     case SSL_kEECDH:
       switch (cipher->algorithm_auth) {
         case SSL_aECDSA:
-          return "ECDHE_" SSL_TXT_ECDSA;
+          return "ECDHE_ECDSA";
         case SSL_aRSA:
-          return "ECDHE_" SSL_TXT_RSA;
+          return "ECDHE_RSA";
+        case SSL_aPSK:
+          return "ECDHE_PSK";
         case SSL_aNULL:
-          return SSL_TXT_ECDH "_anon";
+          return "ECDH_anon";
         default:
+          assert(0);
           return "UNKNOWN";
       }
 
+    case SSL_kPSK:
+      assert(cipher->algorithm_auth == SSL_aPSK);
+      return "PSK";
+
     default:
+      assert(0);
       return "UNKNOWN";
   }
 }
 
+static const char *ssl_cipher_get_enc_name(const SSL_CIPHER *cipher) {
+  switch (cipher->algorithm_enc) {
+    case SSL_3DES:
+      return "3DES_EDE_CBC";
+    case SSL_RC4:
+      return "RC4";
+    case SSL_AES128:
+      return "AES_128_CBC";
+    case SSL_AES256:
+      return "AES_256_CBC";
+    case SSL_AES128GCM:
+      return "AES_128_GCM";
+    case SSL_AES256GCM:
+      return "AES_256_GCM";
+    case SSL_CHACHA20POLY1305:
+      return "CHACHA20_POLY1305";
+      break;
+    default:
+      assert(0);
+      return "UNKNOWN";
+  }
+}
+
+static const char *ssl_cipher_get_prf_name(const SSL_CIPHER *cipher) {
+  if ((cipher->algorithm2 & TLS1_PRF) == TLS1_PRF) {
+    /* Before TLS 1.2, the PRF component is the hash used in the HMAC, which is
+     * only ever MD5 or SHA-1. */
+    switch (cipher->algorithm_mac) {
+      case SSL_MD5:
+        return "MD5";
+      case SSL_SHA1:
+        return "SHA";
+      default:
+        assert(0);
+        return "UNKNOWN";
+    }
+  } else if (cipher->algorithm2 & TLS1_PRF_SHA256) {
+    return "SHA256";
+  } else if (cipher->algorithm2 & TLS1_PRF_SHA384) {
+    return "SHA384";
+  } else {
+    assert(0);
+    return "UNKNOWN";
+  }
+}
+
+char *SSL_CIPHER_get_rfc_name(const SSL_CIPHER *cipher) {
+  if (cipher == NULL) {
+    return NULL;
+  }
+
+  const char *kx_name = SSL_CIPHER_get_kx_name(cipher);
+  const char *enc_name = ssl_cipher_get_enc_name(cipher);
+  const char *prf_name = ssl_cipher_get_prf_name(cipher);
+
+  /* The final name is TLS_{kx_name}_WITH_{enc_name}_{prf_name}. */
+  size_t len = 4 + strlen(kx_name) + 6 + strlen(enc_name) + 1 +
+      strlen(prf_name) + 1;
+  char *ret = OPENSSL_malloc(len);
+  if (ret == NULL) {
+    return NULL;
+  }
+  if (BUF_strlcpy(ret, "TLS_", len) >= len ||
+      BUF_strlcat(ret, kx_name, len) >= len ||
+      BUF_strlcat(ret, "_WITH_", len) >= len ||
+      BUF_strlcat(ret, enc_name, len) >= len ||
+      BUF_strlcat(ret, "_", len) >= len ||
+      BUF_strlcat(ret, prf_name, len) >= len) {
+    assert(0);
+    OPENSSL_free(ret);
+    return NULL;
+  }
+  assert(strlen(ret) + 1 == len);
+  return ret;
+}
+
 /* number of bits for symmetric cipher */
 int SSL_CIPHER_get_bits(const SSL_CIPHER *c, int *alg_bits) {
   int ret = 0;
diff --git a/ssl/ssl_test.c b/ssl/ssl_test.c
index 70291a2..de0f8ed 100644
--- a/ssl/ssl_test.c
+++ b/ssl/ssl_test.c
@@ -214,9 +214,13 @@
 
 static int test_cipher_rule(CIPHER_TEST *t) {
   int ret = 0;
-  SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());
+  SSL_CTX *ctx = SSL_CTX_new(TLS_method());
   size_t i;
 
+  if (ctx == NULL) {
+    goto done;
+  }
+
   if (!SSL_CTX_set_cipher_list(ctx, t->rule)) {
     fprintf(stderr, "Error testing cipher rule '%s'\n", t->rule);
     BIO_print_errors_fp(stderr);
@@ -243,7 +247,9 @@
 
   ret = 1;
 done:
-  SSL_CTX_free(ctx);
+  if (ctx != NULL) {
+    SSL_CTX_free(ctx);
+  }
   return ret;
 }
 
@@ -257,8 +263,12 @@
 
   for (i = 0; kBadRules[i] != NULL; i++) {
     SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());
+    if (ctx == NULL) {
+      return 0;
+    }
     if (SSL_CTX_set_cipher_list(ctx, kBadRules[i])) {
       fprintf(stderr, "Cipher rule '%s' unexpectedly succeeded\n", kBadRules[i]);
+      SSL_CTX_free(ctx);
       return 0;
     }
     ERR_clear_error();
@@ -420,7 +430,8 @@
   return ret;
 }
 
-int test_default_version(uint16_t version, const SSL_METHOD *(*method)(void)) {
+static int test_default_version(uint16_t version,
+                                const SSL_METHOD *(*method)(void)) {
   SSL_CTX *ctx;
   int ret;
 
@@ -434,6 +445,78 @@
   return ret;
 }
 
+static char *cipher_get_rfc_name(const char *name) {
+  SSL_CTX *ctx = SSL_CTX_new(TLS_method());
+  char *ret = NULL;
+
+  if (ctx == NULL) {
+    goto done;
+  }
+
+  if (!SSL_CTX_set_cipher_list(ctx, name) ||
+      sk_SSL_CIPHER_num(ctx->cipher_list->ciphers) != 1) {
+    fprintf(stderr, "Error finding cipher '%s'\n", name);
+    BIO_print_errors_fp(stderr);
+    goto done;
+  }
+
+  const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ctx->cipher_list->ciphers, 0);
+  ret = SSL_CIPHER_get_rfc_name(cipher);
+
+done:
+  if (ctx != NULL) {
+    SSL_CTX_free(ctx);
+  }
+  return ret;
+}
+
+typedef struct {
+  const char *name;
+  const char *rfc_name;
+} CIPHER_RFC_NAME_TEST;
+
+static const CIPHER_RFC_NAME_TEST kCipherRFCNameTests[] = {
+  { "DES-CBC3-SHA", "TLS_RSA_WITH_3DES_EDE_CBC_SHA" },
+  { "RC4-MD5", "TLS_RSA_WITH_RC4_MD5" },
+  { "AES128-SHA", "TLS_RSA_WITH_AES_128_CBC_SHA" },
+  { "ADH-AES128-SHA", "TLS_DH_anon_WITH_AES_128_CBC_SHA" },
+  { "DHE-RSA-AES256-SHA", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA" },
+  { "DHE-RSA-AES256-SHA256", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256" },
+  { "AECDH-AES128-SHA", "TLS_ECDH_anon_WITH_AES_128_CBC_SHA" },
+  { "ECDHE-RSA-AES128-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256" },
+  { "ECDHE-RSA-AES256-SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384" },
+  { "ECDHE-RSA-AES128-GCM-SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" },
+  { "ECDHE-ECDSA-AES128-GCM-SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" },
+  { "ECDHE-ECDSA-AES256-GCM-SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" },
+  { "PSK-RC4-SHA", "TLS_PSK_WITH_RC4_SHA" },
+  /* These names are non-standard: */
+  { "ECDHE-RSA-CHACHA20-POLY1305", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" },
+  { "ECDHE-ECDSA-CHACHA20-POLY1305", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" },
+  { "ECDHE-PSK-WITH-AES-128-GCM-SHA256", "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256" },
+};
+
+static int test_cipher_get_rfc_name() {
+  size_t i;
+
+  for (i = 0; i < sizeof(kCipherRFCNameTests) / sizeof(kCipherRFCNameTests[0]);
+       i++) {
+    const CIPHER_RFC_NAME_TEST *test = &kCipherRFCNameTests[i];
+    char *rfc_name = cipher_get_rfc_name(test->name);
+    if (rfc_name == NULL) {
+      fprintf(stderr, "cipher_get_rfc_name failed on '%s'\n", test->name);
+      return 0;
+    }
+    if (strcmp(rfc_name, test->rfc_name) != 0) {
+      fprintf(stderr, "SSL_CIPHER_get_rfc_name: got '%s', wanted '%s'\n",
+              rfc_name, test->rfc_name);
+      OPENSSL_free(rfc_name);
+      return 0;
+    }
+    OPENSSL_free(rfc_name);
+  }
+  return 1;
+}
+
 int main(void) {
   SSL_library_init();
 
@@ -447,7 +530,8 @@
       !test_default_version(TLS1_2_VERSION, &TLSv1_2_method) ||
       !test_default_version(0, &DTLS_method) ||
       !test_default_version(DTLS1_VERSION, &DTLSv1_method) ||
-      !test_default_version(DTLS1_2_VERSION, &DTLSv1_2_method)) {
+      !test_default_version(DTLS1_2_VERSION, &DTLSv1_2_method) ||
+      !test_cipher_get_rfc_name()) {
     return 1;
   }