Use KEM terminology in TLS ECDHE and key_share abstractions

TLS 1.2 ECDHE and TLS 1.3 key shares were originally designed around
Diffie-Hellman-like primitives and use language based on that.
Post-quantum replacements do not look like Diffie-Hellman, where each
part exchanges a public key, but schemes that work differently can still
slot in without protocol changes.

We previously came up with our own Offer/Accept/Finish abstraction for
early post-quantum experiments, but the NIST constructions are all
expressed as KEMs: First, the recipient generates a keypair and sends
the public key. Then the sender encapsulates a symmetric secret and
sends the ciphertext. Finally, the recipient decapsulates the ciphertext
to get the secret.

Align our C++ and Go abstractions to this terminology. The functions are
now called Generate/Encap/Decap, and the output of Encap is called
"ciphertext", which seems to align with what most folks use. (RFC 9180
uses "enc" for "encapsulated key", but they staple a KEM to an AEAD, so
"ciphertext" would be ambiguous.)

Where variable names refer to parts of the protocol, rather than the
the underlying KEM-like construction, I've kept variable names matching
the protocol mechanism, so we still talk about "curves" and "key
shares", but, when using the post-quantum replacements, the terminology
is no longer quite accurate.

I've also not yet renamed SSLKeyShare yet, though the name is now
inaccurate. Also ideally we'd touch that up so the stateful object is
just a KEM private key, for SSLKEMKey. Though at that point, we maybe
should just add EVP_KEM and EVP_KEM_KEY APIs to libcrypto.

Change-Id: Icbcc1840c5d2dfad210ef4caad2a7c4bf8146553
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/57726
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/ssl/extensions.cc b/ssl/extensions.cc
index 0c54c93..ba92360 100644
--- a/ssl/extensions.cc
+++ b/ssl/extensions.cc
@@ -2322,7 +2322,7 @@
   if (!hs->key_shares[0] ||  //
       !CBB_add_u16(cbb.get(), group_id) ||
       !CBB_add_u16_length_prefixed(cbb.get(), &key_exchange) ||
-      !hs->key_shares[0]->Offer(&key_exchange)) {
+      !hs->key_shares[0]->Generate(&key_exchange)) {
     return false;
   }
 
@@ -2331,7 +2331,7 @@
     if (!hs->key_shares[1] ||  //
         !CBB_add_u16(cbb.get(), second_group_id) ||
         !CBB_add_u16_length_prefixed(cbb.get(), &key_exchange) ||
-        !hs->key_shares[1]->Offer(&key_exchange)) {
+        !hs->key_shares[1]->Generate(&key_exchange)) {
       return false;
     }
   }
@@ -2363,10 +2363,10 @@
 bool ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs,
                                          Array<uint8_t> *out_secret,
                                          uint8_t *out_alert, CBS *contents) {
-  CBS peer_key;
+  CBS ciphertext;
   uint16_t group_id;
   if (!CBS_get_u16(contents, &group_id) ||
-      !CBS_get_u16_length_prefixed(contents, &peer_key) ||
+      !CBS_get_u16_length_prefixed(contents, &ciphertext) ||
       CBS_len(contents) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     *out_alert = SSL_AD_DECODE_ERROR;
@@ -2383,7 +2383,7 @@
     key_share = hs->key_shares[1].get();
   }
 
-  if (!key_share->Finish(out_secret, out_alert, peer_key)) {
+  if (!key_share->Decap(out_secret, out_alert, ciphertext)) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return false;
   }
@@ -2448,13 +2448,13 @@
 }
 
 bool ssl_ext_key_share_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
-  CBB kse_bytes, public_key;
+  CBB entry, ciphertext;
   if (!CBB_add_u16(out, TLSEXT_TYPE_key_share) ||
-      !CBB_add_u16_length_prefixed(out, &kse_bytes) ||
-      !CBB_add_u16(&kse_bytes, hs->new_session->group_id) ||
-      !CBB_add_u16_length_prefixed(&kse_bytes, &public_key) ||
-      !CBB_add_bytes(&public_key, hs->ecdh_public_key.data(),
-                     hs->ecdh_public_key.size()) ||
+      !CBB_add_u16_length_prefixed(out, &entry) ||
+      !CBB_add_u16(&entry, hs->new_session->group_id) ||
+      !CBB_add_u16_length_prefixed(&entry, &ciphertext) ||
+      !CBB_add_bytes(&ciphertext, hs->key_share_ciphertext.data(),
+                     hs->key_share_ciphertext.size()) ||
       !CBB_flush(out)) {
     return false;
   }
diff --git a/ssl/handoff.cc b/ssl/handoff.cc
index ccb0b5e..6e5cc2d 100644
--- a/ssl/handoff.cc
+++ b/ssl/handoff.cc
@@ -824,7 +824,7 @@
 //
 // KeyShareHint ::= SEQUENCE {
 //     groupId                 INTEGER,
-//     publicKey               OCTET STRING,
+//     ciphertext              OCTET STRING,
 //     secret                  OCTET STRING,
 // }
 //
@@ -886,12 +886,12 @@
     }
   }
 
-  if (hints->key_share_group_id != 0 && !hints->key_share_public_key.empty() &&
+  if (hints->key_share_group_id != 0 && !hints->key_share_ciphertext.empty() &&
       !hints->key_share_secret.empty()) {
     if (!CBB_add_asn1(&seq, &child, kKeyShareHintTag) ||
         !CBB_add_asn1_uint64(&child, hints->key_share_group_id) ||
-        !CBB_add_asn1_octet_string(&child, hints->key_share_public_key.data(),
-                                   hints->key_share_public_key.size()) ||
+        !CBB_add_asn1_octet_string(&child, hints->key_share_ciphertext.data(),
+                                   hints->key_share_ciphertext.size()) ||
         !CBB_add_asn1_octet_string(&child, hints->key_share_secret.data(),
                                    hints->key_share_secret.size())) {
       return 0;
@@ -1040,11 +1040,11 @@
 
   if (has_key_share) {
     uint64_t group_id;
-    CBS public_key, secret;
+    CBS ciphertext, secret;
     if (!CBS_get_asn1_uint64(&key_share, &group_id) ||  //
         group_id == 0 || group_id > 0xffff ||
-        !CBS_get_asn1(&key_share, &public_key, CBS_ASN1_OCTETSTRING) ||
-        !hints_obj->key_share_public_key.CopyFrom(public_key) ||
+        !CBS_get_asn1(&key_share, &ciphertext, CBS_ASN1_OCTETSTRING) ||
+        !hints_obj->key_share_ciphertext.CopyFrom(ciphertext) ||
         !CBS_get_asn1(&key_share, &secret, CBS_ASN1_OCTETSTRING) ||
         !hints_obj->key_share_secret.CopyFrom(secret)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_COULD_NOT_PARSE_HINTS);
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc
index 64fd2f2..e7dca1b 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -1468,11 +1468,11 @@
       return ssl_hs_error;
     }
 
-    // Generate the premaster secret.
-    bssl::UniquePtr<SSLKeyShare> key_share =
+    // Generate a premaster secret and encapsulate it.
+    bssl::UniquePtr<SSLKeyShare> kem =
         SSLKeyShare::Create(hs->new_session->group_id);
     uint8_t alert = SSL_AD_DECODE_ERROR;
-    if (!key_share || !key_share->Accept(&child, &pms, &alert, hs->peer_key)) {
+    if (!kem || !kem->Encap(&child, &pms, &alert, hs->peer_key)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
     }
diff --git a/ssl/handshake_server.cc b/ssl/handshake_server.cc
index 70fe983..e50a690 100644
--- a/ssl/handshake_server.cc
+++ b/ssl/handshake_server.cc
@@ -1146,7 +1146,7 @@
         }
       } else {
         // Generate a key, and emit the public half.
-        if (!hs->key_shares[0]->Offer(&child)) {
+        if (!hs->key_shares[0]->Generate(&child)) {
           return ssl_hs_error;
         }
         // If generating hints, save the ECDHE key.
@@ -1490,17 +1490,17 @@
     }
   } else if (alg_k & SSL_kECDHE) {
     // Parse the ClientKeyExchange.
-    CBS peer_key;
-    if (!CBS_get_u8_length_prefixed(&client_key_exchange, &peer_key) ||
+    CBS ciphertext;
+    if (!CBS_get_u8_length_prefixed(&client_key_exchange, &ciphertext) ||
         CBS_len(&client_key_exchange) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
     }
 
-    // Compute the premaster.
+    // Decapsulate the premaster secret.
     uint8_t alert = SSL_AD_DECODE_ERROR;
-    if (!hs->key_shares[0]->Finish(&premaster_secret, &alert, peer_key)) {
+    if (!hs->key_shares[0]->Decap(&premaster_secret, &alert, ciphertext)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
     }
diff --git a/ssl/internal.h b/ssl/internal.h
index fac15b6..e71dd0c 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1051,7 +1051,15 @@
 
 // Key shares.
 
-// SSLKeyShare abstracts over Diffie-Hellman-like key exchanges.
+// SSLKeyShare abstracts over KEM-like constructions, for use with TLS 1.2 ECDHE
+// cipher suites and the TLS 1.3 key_share extension.
+//
+// TODO(davidben): This class is named SSLKeyShare after the TLS 1.3 key_share
+// extension, but it really implements a KEM abstraction. Additionally, we use
+// the same type for Encap, which is a one-off, stateless operation, as Generate
+// and Decap. Slightly tidier would be for Generate to return a new SSLKEMKey
+// (or we introduce EVP_KEM and EVP_KEM_KEY), with a Decap method, and for Encap
+// to be static function.
 class SSLKeyShare {
  public:
   virtual ~SSLKeyShare() {}
@@ -1065,29 +1073,26 @@
   // GroupID returns the group ID.
   virtual uint16_t GroupID() const PURE_VIRTUAL;
 
-  // Offer generates a keypair and writes the public value to
-  // |out_public_key|. It returns true on success and false on error.
-  virtual bool Offer(CBB *out_public_key) PURE_VIRTUAL;
+  // Generate generates a keypair and writes the public key to |out_public_key|.
+  // It returns true on success and false on error.
+  virtual bool Generate(CBB *out_public_key) PURE_VIRTUAL;
 
-  // Accept performs a key exchange against the |peer_key| generated by |Offer|.
-  // On success, it returns true, writes the public value to |out_public_key|,
-  // and sets |*out_secret| to the shared secret. On failure, it returns false
-  // and sets |*out_alert| to an alert to send to the peer.
-  //
-  // The default implementation calls |Offer| and then |Finish|, assuming a key
-  // exchange protocol where the peers are symmetric.
-  virtual bool Accept(CBB *out_public_key, Array<uint8_t> *out_secret,
-                      uint8_t *out_alert, Span<const uint8_t> peer_key);
+  // Encap generates an ephemeral, symmetric secret and encapsulates it with
+  // |peer_key|. On success, it returns true, writes the encapsulated secret to
+  // |out_ciphertext|, and sets |*out_secret| to the shared secret. On failure,
+  // it returns false and sets |*out_alert| to an alert to send to the peer.
+  virtual bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
+                     uint8_t *out_alert,
+                     Span<const uint8_t> peer_key) PURE_VIRTUAL;
 
-  // Finish performs a key exchange against the |peer_key| generated by
-  // |Accept|. On success, it returns true and sets |*out_secret| to the shared
-  // secret. On failure, it returns false and sets |*out_alert| to an alert to
-  // send to the peer.
-  virtual bool Finish(Array<uint8_t> *out_secret, uint8_t *out_alert,
-                      Span<const uint8_t> peer_key) PURE_VIRTUAL;
+  // Decap decapsulates the symmetric secret in |ciphertext|. On success, it
+  // returns true and sets |*out_secret| to the shared secret. On failure, it
+  // returns false and sets |*out_alert| to an alert to send to the peer.
+  virtual bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
+                     Span<const uint8_t> ciphertext) PURE_VIRTUAL;
 
   // SerializePrivateKey writes the private key to |out|, returning true if
-  // successful and false otherwise. It should be called after |Offer|.
+  // successful and false otherwise. It should be called after |Generate|.
   virtual bool SerializePrivateKey(CBB *out) { return false; }
 
   // DeserializePrivateKey initializes the state of the key exchange from |in|,
@@ -1683,7 +1688,7 @@
   Array<uint8_t> server_random_tls13;
 
   uint16_t key_share_group_id = 0;
-  Array<uint8_t> key_share_public_key;
+  Array<uint8_t> key_share_ciphertext;
   Array<uint8_t> key_share_secret;
 
   uint16_t signature_algorithm = 0;
@@ -1843,9 +1848,9 @@
   // key_share_bytes is the key_share extension that the client should send.
   Array<uint8_t> key_share_bytes;
 
-  // ecdh_public_key, for servers, is the key share to be sent to the client in
-  // TLS 1.3.
-  Array<uint8_t> ecdh_public_key;
+  // key_share_ciphertext, for servers, is encapsulated shared secret to be sent
+  // to the client in the TLS 1.3 key_share extension.
+  Array<uint8_t> key_share_ciphertext;
 
   // peer_sigalgs are the signature algorithms that the peer supports. These are
   // taken from the contents of the signature algorithms extension for a server
diff --git a/ssl/ssl_key_share.cc b/ssl/ssl_key_share.cc
index c604c87..a81b917 100644
--- a/ssl/ssl_key_share.cc
+++ b/ssl/ssl_key_share.cc
@@ -43,7 +43,7 @@
 
   uint16_t GroupID() const override { return group_id_; }
 
-  bool Offer(CBB *out) override {
+  bool Generate(CBB *out) override {
     assert(!private_key_);
     // Generate a private key.
     private_key_.reset(BN_new());
@@ -66,8 +66,16 @@
     return true;
   }
 
-  bool Finish(Array<uint8_t> *out_secret, uint8_t *out_alert,
-              Span<const uint8_t> peer_key) override {
+  bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
+             uint8_t *out_alert, Span<const uint8_t> peer_key) override {
+    // ECDH may be fit into a KEM-like abstraction by using a second keypair's
+    // public key as the ciphertext.
+    *out_alert = SSL_AD_INTERNAL_ERROR;
+    return Generate(out_ciphertext) && Decap(out_secret, out_alert, peer_key);
+  }
+
+  bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
+             Span<const uint8_t> ciphertext) override {
     assert(group_);
     assert(private_key_);
     *out_alert = SSL_AD_INTERNAL_ERROR;
@@ -79,9 +87,9 @@
       return false;
     }
 
-    if (peer_key.empty() || peer_key[0] != POINT_CONVERSION_UNCOMPRESSED ||
-        !EC_POINT_oct2point(group_, peer_point.get(), peer_key.data(),
-                            peer_key.size(), /*ctx=*/nullptr)) {
+    if (ciphertext.empty() || ciphertext[0] != POINT_CONVERSION_UNCOMPRESSED ||
+        !EC_POINT_oct2point(group_, peer_point.get(), ciphertext.data(),
+                            ciphertext.size(), /*ctx=*/nullptr)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
       *out_alert = SSL_AD_DECODE_ERROR;
       return false;
@@ -133,14 +141,22 @@
 
   uint16_t GroupID() const override { return SSL_CURVE_X25519; }
 
-  bool Offer(CBB *out) override {
+  bool Generate(CBB *out) override {
     uint8_t public_key[32];
     X25519_keypair(public_key, private_key_);
     return !!CBB_add_bytes(out, public_key, sizeof(public_key));
   }
 
-  bool Finish(Array<uint8_t> *out_secret, uint8_t *out_alert,
-              Span<const uint8_t> peer_key) override {
+  bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
+             uint8_t *out_alert, Span<const uint8_t> peer_key) override {
+    // X25519 may be fit into a KEM-like abstraction by using a second keypair's
+    // public key as the ciphertext.
+    *out_alert = SSL_AD_INTERNAL_ERROR;
+    return Generate(out_ciphertext) && Decap(out_secret, out_alert, peer_key);
+  }
+
+  bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
+             Span<const uint8_t> ciphertext) override {
     *out_alert = SSL_AD_INTERNAL_ERROR;
 
     Array<uint8_t> secret;
@@ -148,8 +164,8 @@
       return false;
     }
 
-    if (peer_key.size() != 32 ||
-        !X25519(secret.data(), private_key_, peer_key.data())) {
+    if (ciphertext.size() != 32 ||  //
+        !X25519(secret.data(), private_key_, ciphertext.data())) {
       *out_alert = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
       return false;
@@ -181,7 +197,7 @@
 
   uint16_t GroupID() const override { return SSL_CURVE_CECPQ2; }
 
-  bool Offer(CBB *out) override {
+  bool Generate(CBB *out) override {
     uint8_t x25519_public_key[32];
     X25519_keypair(x25519_public_key, x25519_private_key_);
 
@@ -205,8 +221,8 @@
     return true;
   }
 
-  bool Accept(CBB *out_public_key, Array<uint8_t> *out_secret,
-              uint8_t *out_alert, Span<const uint8_t> peer_key) override {
+  bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
+             uint8_t *out_alert, Span<const uint8_t> peer_key) override {
     Array<uint8_t> secret;
     if (!secret.Init(32 + HRSS_KEY_BYTES)) {
       return false;
@@ -230,9 +246,9 @@
 
     if (!HRSS_encap(ciphertext, secret.data() + 32, &peer_public_key,
                     entropy) ||
-        !CBB_add_bytes(out_public_key, x25519_public_key,
+        !CBB_add_bytes(out_ciphertext, x25519_public_key,
                        sizeof(x25519_public_key)) ||
-        !CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) {
+        !CBB_add_bytes(out_ciphertext, ciphertext, sizeof(ciphertext))) {
       return false;
     }
 
@@ -240,8 +256,8 @@
     return true;
   }
 
-  bool Finish(Array<uint8_t> *out_secret, uint8_t *out_alert,
-              Span<const uint8_t> peer_key) override {
+  bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
+             Span<const uint8_t> ciphertext) override {
     *out_alert = SSL_AD_INTERNAL_ERROR;
 
     Array<uint8_t> secret;
@@ -249,15 +265,15 @@
       return false;
     }
 
-    if (peer_key.size() != 32 + HRSS_CIPHERTEXT_BYTES ||
-        !X25519(secret.data(), x25519_private_key_, peer_key.data())) {
+    if (ciphertext.size() != 32 + HRSS_CIPHERTEXT_BYTES ||
+        !X25519(secret.data(), x25519_private_key_, ciphertext.data())) {
       *out_alert = SSL_AD_DECODE_ERROR;
       OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
       return false;
     }
 
     if (!HRSS_decap(secret.data() + 32, &hrss_private_key_,
-                    peer_key.data() + 32, peer_key.size() - 32)) {
+                    ciphertext.data() + 32, ciphertext.size() - 32)) {
       return false;
     }
 
@@ -276,20 +292,19 @@
 
   uint16_t GroupID() const override { return SSL_CURVE_X25519KYBER768; }
 
-  bool Offer(CBB *out) override {
+  bool Generate(CBB *out) override {
     // There is no implementation on Kyber in BoringSSL. BoringSSL must be
-    // patched for this key agreement to be workable. It is not enabled by
-    // default.
+    // patched for this KEM to be workable. It is not enabled by default.
     return false;
   }
 
-  bool Accept(CBB *out_public_key, Array<uint8_t> *out_secret,
-              uint8_t *out_alert, Span<const uint8_t> peer_key) override {
+  bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
+             uint8_t *out_alert, Span<const uint8_t> peer_key) override {
     return false;
   }
 
-  bool Finish(Array<uint8_t> *out_secret, uint8_t *out_alert,
-              Span<const uint8_t> peer_key) override {
+  bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
+             Span<const uint8_t> ciphertext) override {
     return false;
   }
 };
@@ -300,20 +315,19 @@
 
   uint16_t GroupID() const override { return SSL_CURVE_P256KYBER768; }
 
-  bool Offer(CBB *out) override {
+  bool Generate(CBB *out) override {
     // There is no implementation on Kyber in BoringSSL. BoringSSL must be
-    // patched for this key agreement to be workable. It is not enabled by
-    // default.
+    // patched for this KEM to be workable. It is not enabled by default.
     return false;
   }
 
-  bool Accept(CBB *out_public_key, Array<uint8_t> *out_secret,
-              uint8_t *out_alert, Span<const uint8_t> peer_key) override {
+  bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
+             uint8_t *out_alert, Span<const uint8_t> peer_key) override {
     return false;
   }
 
-  bool Finish(Array<uint8_t> *out_secret, uint8_t *out_alert,
-              Span<const uint8_t> peer_key) override {
+  bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
+             Span<const uint8_t> ciphertext) override {
     return false;
   }
 };
@@ -359,13 +373,6 @@
   }
 }
 
-bool SSLKeyShare::Accept(CBB *out_public_key, Array<uint8_t> *out_secret,
-                         uint8_t *out_alert, Span<const uint8_t> peer_key) {
-  *out_alert = SSL_AD_INTERNAL_ERROR;
-  return Offer(out_public_key) &&
-         Finish(out_secret, out_alert, peer_key);
-}
-
 bool ssl_nid_to_group_id(uint16_t *out_group_id, int nid) {
   for (const auto &group : kNamedGroups) {
     if (group.nid == nid) {
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index 5d04994..42f0534 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -33,7 +33,7 @@
 	echHPKEContext *hpke.Context
 	suite          *cipherSuite
 	finishedHash   finishedHash
-	keyShares      map[CurveID]ecdhCurve
+	keyShares      map[CurveID]kemImplementation
 	masterSecret   []byte
 	session        *ClientSessionState
 	finishedBytes  []byte
@@ -98,7 +98,7 @@
 
 	hs := &clientHandshakeState{
 		c:         c,
-		keyShares: make(map[CurveID]ecdhCurve),
+		keyShares: make(map[CurveID]kemImplementation),
 	}
 
 	// Pick a session to resume.
@@ -643,11 +643,11 @@
 				if !curvesToSend[curveID] {
 					continue
 				}
-				curve, ok := curveForCurveID(curveID, c.config)
+				kem, ok := kemForCurveID(curveID, c.config)
 				if !ok {
 					continue
 				}
-				publicKey, err := curve.offer(c.config.rand())
+				publicKey, err := kem.generate(c.config.rand())
 				if err != nil {
 					return nil, err
 				}
@@ -663,7 +663,7 @@
 					group:       curveID,
 					keyExchange: publicKey,
 				})
-				hs.keyShares[curveID] = curve
+				hs.keyShares[curveID] = kem
 
 				if c.config.Bugs.DuplicateKeyShares {
 					hello.keyShares = append(hello.keyShares, hello.keyShares[len(hello.keyShares)-1])
@@ -1122,7 +1122,7 @@
 	// Resolve ECDHE and compute the handshake secret.
 	ecdheSecret := zeroSecret
 	if !c.config.Bugs.MissingKeyShare && !c.config.Bugs.SecondClientHelloMissingKeyShare {
-		curve, ok := hs.keyShares[hs.serverHello.keyShare.group]
+		kem, ok := hs.keyShares[hs.serverHello.keyShare.group]
 		if !ok {
 			c.sendAlert(alertHandshakeFailure)
 			return errors.New("tls: server selected an unsupported group")
@@ -1130,7 +1130,7 @@
 		c.curveID = hs.serverHello.keyShare.group
 
 		var err error
-		ecdheSecret, err = curve.finish(hs.serverHello.keyShare.keyExchange)
+		ecdheSecret, err = kem.decap(hs.serverHello.keyShare.keyExchange)
 		if err != nil {
 			return err
 		}
@@ -1529,15 +1529,15 @@
 			c.sendAlert(alertHandshakeFailure)
 			return errors.New("tls: received invalid HelloRetryRequest")
 		}
-		curve, ok := curveForCurveID(group, c.config)
+		kem, ok := kemForCurveID(group, c.config)
 		if !ok {
 			return errors.New("tls: Unable to get curve requested in HelloRetryRequest")
 		}
-		publicKey, err := curve.offer(c.config.rand())
+		publicKey, err := kem.generate(c.config.rand())
 		if err != nil {
 			return err
 		}
-		hs.keyShares[group] = curve
+		hs.keyShares[group] = kem
 		hello.keyShares = []keyShareEntry{{
 			group:       group,
 			keyExchange: publicKey,
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 5ae7d93..de297a6 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -957,7 +957,7 @@
 		// Once a curve has been selected and a key share identified,
 		// the server needs to generate a public value and send it in
 		// the ServerHello.
-		curve, ok := curveForCurveID(selectedCurve, config)
+		kem, ok := kemForCurveID(selectedCurve, config)
 		if !ok {
 			panic("tls: server failed to look up curve ID")
 		}
@@ -967,9 +967,9 @@
 		if config.Bugs.SkipHelloRetryRequest {
 			// If skipping HelloRetryRequest, use a random key to
 			// avoid crashing.
-			curve2, _ := curveForCurveID(selectedCurve, config)
+			kem2, _ := kemForCurveID(selectedCurve, config)
 			var err error
-			peerKey, err = curve2.offer(config.rand())
+			peerKey, err = kem2.generate(config.rand())
 			if err != nil {
 				return err
 			}
@@ -977,7 +977,7 @@
 			peerKey = selectedKeyShare.keyExchange
 		}
 
-		publicKey, ecdheSecret, err := curve.accept(config.rand(), peerKey)
+		ciphertext, ecdheSecret, err := kem.encap(config.rand(), peerKey)
 		if err != nil {
 			c.sendAlert(alertHandshakeFailure)
 			return err
@@ -991,19 +991,19 @@
 			curveID = config.Bugs.SendCurve
 		}
 		if c.config.Bugs.InvalidECDHPoint {
-			publicKey[0] ^= 0xff
+			ciphertext[0] ^= 0xff
 		}
 
 		hs.hello.keyShare = keyShareEntry{
 			group:       curveID,
-			keyExchange: publicKey,
+			keyExchange: ciphertext,
 		}
 
 		if config.Bugs.EncryptedExtensionsWithKeyShare {
 			encryptedExtensions.extensions.hasKeyShare = true
 			encryptedExtensions.extensions.keyShare = keyShareEntry{
 				group:       curveID,
-				keyExchange: publicKey,
+				keyExchange: ciphertext,
 			}
 		}
 	} else {
diff --git a/ssl/test/runner/key_agreement.go b/ssl/test/runner/key_agreement.go
index bfd35f6..47cdbb8 100644
--- a/ssl/test/runner/key_agreement.go
+++ b/ssl/test/runner/key_agreement.go
@@ -231,28 +231,29 @@
 	return 0
 }
 
-// A ecdhCurve is an instance of ECDH-style key agreement for TLS.
-type ecdhCurve interface {
-	// offer generates a keypair using rand. It returns the encoded |publicKey|.
-	offer(rand io.Reader) (publicKey []byte, err error)
+// A kemImplementation is an instance of KEM-style construction for TLS.
+type kemImplementation interface {
+	// generate generates a keypair using rand. It returns the encoded public key.
+	generate(rand io.Reader) (publicKey []byte, err error)
 
-	// accept responds to the |peerKey| generated by |offer| with the acceptor's
-	// |publicKey|, and returns agreed-upon |preMasterSecret| to the acceptor.
-	accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error)
+	// encap generates a symmetric, shared secret, encapsulates it with |peerKey|.
+	// It returns the encapsulated shared secret and the secret itself.
+	encap(rand io.Reader, peerKey []byte) (ciphertext []byte, secret []byte, err error)
 
-	// finish returns the computed |preMasterSecret|, given the |peerKey|
-	// generated by |accept|.
-	finish(peerKey []byte) (preMasterSecret []byte, err error)
+	// decap decapsulates |ciphertext| and returns the resulting shared secret.
+	decap(ciphertext []byte) (secret []byte, err error)
 }
 
-// ellipticECDHCurve implements ecdhCurve with an elliptic.Curve.
-type ellipticECDHCurve struct {
+// ecdhKEM implements kemImplementation with an elliptic.Curve.
+//
+// TODO(davidben): Move this to Go's crypto/ecdh.
+type ecdhKEM struct {
 	curve          elliptic.Curve
 	privateKey     []byte
 	sendCompressed bool
 }
 
-func (e *ellipticECDHCurve) offer(rand io.Reader) (publicKey []byte, err error) {
+func (e *ecdhKEM) generate(rand io.Reader) (publicKey []byte, err error) {
 	var x, y *big.Int
 	e.privateKey, x, y, err = elliptic.GenerateKey(e.curve, rand)
 	if err != nil {
@@ -269,38 +270,37 @@
 	return ret, nil
 }
 
-func (e *ellipticECDHCurve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) {
-	publicKey, err = e.offer(rand)
+func (e *ecdhKEM) encap(rand io.Reader, peerKey []byte) (ciphertext []byte, secret []byte, err error) {
+	ciphertext, err = e.generate(rand)
 	if err != nil {
 		return nil, nil, err
 	}
-	preMasterSecret, err = e.finish(peerKey)
+	secret, err = e.decap(peerKey)
 	if err != nil {
 		return nil, nil, err
 	}
 	return
 }
 
-func (e *ellipticECDHCurve) finish(peerKey []byte) (preMasterSecret []byte, err error) {
-	x, y := elliptic.Unmarshal(e.curve, peerKey)
+func (e *ecdhKEM) decap(ciphertext []byte) (secret []byte, err error) {
+	x, y := elliptic.Unmarshal(e.curve, ciphertext)
 	if x == nil {
 		return nil, errors.New("tls: invalid peer key")
 	}
 	x, _ = e.curve.ScalarMult(x, y, e.privateKey)
-	preMasterSecret = make([]byte, (e.curve.Params().BitSize+7)>>3)
+	secret = make([]byte, (e.curve.Params().BitSize+7)>>3)
 	xBytes := x.Bytes()
-	copy(preMasterSecret[len(preMasterSecret)-len(xBytes):], xBytes)
-
-	return preMasterSecret, nil
+	copy(secret[len(secret)-len(xBytes):], xBytes)
+	return secret, nil
 }
 
-// x25519ECDHCurve implements ecdhCurve with X25519.
-type x25519ECDHCurve struct {
+// x25519KEM implements kemImplementation with X25519.
+type x25519KEM struct {
 	privateKey [32]byte
 	setHighBit bool
 }
 
-func (e *x25519ECDHCurve) offer(rand io.Reader) (publicKey []byte, err error) {
+func (e *x25519KEM) generate(rand io.Reader) (publicKey []byte, err error) {
 	_, err = io.ReadFull(rand, e.privateKey[:])
 	if err != nil {
 		return
@@ -313,25 +313,24 @@
 	return out[:], nil
 }
 
-func (e *x25519ECDHCurve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) {
-	publicKey, err = e.offer(rand)
+func (e *x25519KEM) encap(rand io.Reader, peerKey []byte) (ciphertext []byte, secret []byte, err error) {
+	ciphertext, err = e.generate(rand)
 	if err != nil {
 		return nil, nil, err
 	}
-	preMasterSecret, err = e.finish(peerKey)
+	secret, err = e.decap(peerKey)
 	if err != nil {
 		return nil, nil, err
 	}
 	return
 }
 
-func (e *x25519ECDHCurve) finish(peerKey []byte) (preMasterSecret []byte, err error) {
-	if len(peerKey) != 32 {
+func (e *x25519KEM) decap(ciphertext []byte) (secret []byte, err error) {
+	if len(ciphertext) != 32 {
 		return nil, errors.New("tls: invalid peer key")
 	}
-	var out, peerKeyCopy [32]byte
-	copy(peerKeyCopy[:], peerKey)
-	curve25519.ScalarMult(&out, &e.privateKey, &peerKeyCopy)
+	var out [32]byte
+	curve25519.ScalarMult(&out, &e.privateKey, (*[32]byte)(ciphertext))
 
 	// Per RFC 7748, reject the all-zero value in constant time.
 	var zeros [32]byte
@@ -342,13 +341,13 @@
 	return out[:], nil
 }
 
-// cecpq2Curve implements CECPQ2, which is HRSS+SXY combined with X25519.
-type cecpq2Curve struct {
+// cecpq2KEM implements CECPQ2, which is HRSS+SXY combined with X25519.
+type cecpq2KEM struct {
 	x25519PrivateKey [32]byte
 	hrssPrivateKey   hrss.PrivateKey
 }
 
-func (e *cecpq2Curve) offer(rand io.Reader) (publicKey []byte, err error) {
+func (e *cecpq2KEM) generate(rand io.Reader) (publicKey []byte, err error) {
 	if _, err := io.ReadFull(rand, e.x25519PrivateKey[:]); err != nil {
 		return nil, err
 	}
@@ -365,7 +364,7 @@
 	return ret, nil
 }
 
-func (e *cecpq2Curve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) {
+func (e *cecpq2KEM) encap(rand io.Reader, peerKey []byte) (ciphertext []byte, secret []byte, err error) {
 	if len(peerKey) != 32+hrss.PublicKeySize {
 		return nil, nil, errors.New("tls: bad length CECPQ2 offer")
 	}
@@ -392,21 +391,21 @@
 
 	hrssCiphertext, hrssShared := hrssPublicKey.Encap(rand)
 
-	publicKey = append(publicKey, x25519Public[:]...)
-	publicKey = append(publicKey, hrssCiphertext...)
-	preMasterSecret = append(preMasterSecret, x25519Shared[:]...)
-	preMasterSecret = append(preMasterSecret, hrssShared...)
+	ciphertext = append(ciphertext, x25519Public[:]...)
+	ciphertext = append(ciphertext, hrssCiphertext...)
+	secret = append(secret, x25519Shared[:]...)
+	secret = append(secret, hrssShared...)
 
-	return publicKey, preMasterSecret, nil
+	return ciphertext, secret, nil
 }
 
-func (e *cecpq2Curve) finish(peerKey []byte) (preMasterSecret []byte, err error) {
-	if len(peerKey) != 32+hrss.CiphertextSize {
+func (e *cecpq2KEM) decap(ciphertext []byte) (secret []byte, err error) {
+	if len(ciphertext) != 32+hrss.CiphertextSize {
 		return nil, errors.New("tls: bad length CECPQ2 reply")
 	}
 
 	var x25519Shared, x25519PeerKey [32]byte
-	copy(x25519PeerKey[:], peerKey)
+	copy(x25519PeerKey[:], ciphertext)
 	curve25519.ScalarMult(&x25519Shared, &e.x25519PrivateKey, &x25519PeerKey)
 
 	// Per RFC 7748, reject the all-zero value in constant time.
@@ -415,31 +414,31 @@
 		return nil, errors.New("tls: X25519 value with wrong order")
 	}
 
-	hrssShared, ok := e.hrssPrivateKey.Decap(peerKey[32:])
+	hrssShared, ok := e.hrssPrivateKey.Decap(ciphertext[32:])
 	if !ok {
 		return nil, errors.New("tls: invalid HRSS ciphertext")
 	}
 
-	preMasterSecret = append(preMasterSecret, x25519Shared[:]...)
-	preMasterSecret = append(preMasterSecret, hrssShared...)
+	secret = append(secret, x25519Shared[:]...)
+	secret = append(secret, hrssShared...)
 
-	return preMasterSecret, nil
+	return secret, nil
 }
 
-func curveForCurveID(id CurveID, config *Config) (ecdhCurve, bool) {
+func kemForCurveID(id CurveID, config *Config) (kemImplementation, bool) {
 	switch id {
 	case CurveP224:
-		return &ellipticECDHCurve{curve: elliptic.P224(), sendCompressed: config.Bugs.SendCompressedCoordinates}, true
+		return &ecdhKEM{curve: elliptic.P224(), sendCompressed: config.Bugs.SendCompressedCoordinates}, true
 	case CurveP256:
-		return &ellipticECDHCurve{curve: elliptic.P256(), sendCompressed: config.Bugs.SendCompressedCoordinates}, true
+		return &ecdhKEM{curve: elliptic.P256(), sendCompressed: config.Bugs.SendCompressedCoordinates}, true
 	case CurveP384:
-		return &ellipticECDHCurve{curve: elliptic.P384(), sendCompressed: config.Bugs.SendCompressedCoordinates}, true
+		return &ecdhKEM{curve: elliptic.P384(), sendCompressed: config.Bugs.SendCompressedCoordinates}, true
 	case CurveP521:
-		return &ellipticECDHCurve{curve: elliptic.P521(), sendCompressed: config.Bugs.SendCompressedCoordinates}, true
+		return &ecdhKEM{curve: elliptic.P521(), sendCompressed: config.Bugs.SendCompressedCoordinates}, true
 	case CurveX25519:
-		return &x25519ECDHCurve{setHighBit: config.Bugs.SetX25519HighBit}, true
+		return &x25519KEM{setHighBit: config.Bugs.SetX25519HighBit}, true
 	case CurveCECPQ2:
-		return &cecpq2Curve{}, true
+		return &cecpq2KEM{}, true
 	default:
 		return nil, false
 	}
@@ -576,7 +575,7 @@
 // either be ECDSA or RSA.
 type ecdheKeyAgreement struct {
 	auth    keyAgreementAuthentication
-	curve   ecdhCurve
+	kem     kemImplementation
 	curveID CurveID
 	peerKey []byte
 }
@@ -605,12 +604,12 @@
 	}
 
 	var ok bool
-	if ka.curve, ok = curveForCurveID(curveid, config); !ok {
+	if ka.kem, ok = kemForCurveID(curveid, config); !ok {
 		return nil, errors.New("tls: preferredCurves includes unsupported curve")
 	}
 	ka.curveID = curveid
 
-	publicKey, err := ka.curve.offer(config.rand())
+	publicKey, err := ka.kem.generate(config.rand())
 	if err != nil {
 		return nil, err
 	}
@@ -636,7 +635,7 @@
 	if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
 		return nil, errClientKeyExchange
 	}
-	return ka.curve.finish(ckx.ciphertext[1:])
+	return ka.kem.decap(ckx.ciphertext[1:])
 }
 
 func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, key crypto.PublicKey, skx *serverKeyExchangeMsg) error {
@@ -646,11 +645,11 @@
 	if skx.key[0] != 3 { // named curve
 		return errors.New("tls: server selected unsupported curve")
 	}
-	curveid := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
-	ka.curveID = curveid
+	curveID := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
+	ka.curveID = curveID
 
 	var ok bool
-	if ka.curve, ok = curveForCurveID(curveid, config); !ok {
+	if ka.kem, ok = kemForCurveID(curveID, config); !ok {
 		return errors.New("tls: server selected unsupported curve")
 	}
 
@@ -668,24 +667,24 @@
 }
 
 func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
-	if ka.curve == nil {
+	if ka.kem == nil {
 		return nil, nil, errors.New("missing ServerKeyExchange message")
 	}
 
-	publicKey, preMasterSecret, err := ka.curve.accept(config.rand(), ka.peerKey)
+	ciphertext, secret, err := ka.kem.encap(config.rand(), ka.peerKey)
 	if err != nil {
 		return nil, nil, err
 	}
 
 	ckx := new(clientKeyExchangeMsg)
-	ckx.ciphertext = make([]byte, 1+len(publicKey))
-	ckx.ciphertext[0] = byte(len(publicKey))
-	copy(ckx.ciphertext[1:], publicKey)
+	ckx.ciphertext = make([]byte, 1+len(ciphertext))
+	ckx.ciphertext[0] = byte(len(ciphertext))
+	copy(ckx.ciphertext[1:], ciphertext)
 	if config.Bugs.InvalidECDHPoint {
 		ckx.ciphertext[1] ^= 0xff
 	}
 
-	return preMasterSecret, ckx, nil
+	return secret, ckx, nil
 }
 
 func (ka *ecdheKeyAgreement) peerSignatureAlgorithm() signatureAlgorithm {
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc
index ca43624..0bbe97f 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -66,25 +66,25 @@
   SSL_HANDSHAKE_HINTS *const hints = hs->hints.get();
   if (hints && !hs->hints_requested && hints->key_share_group_id == group_id &&
       !hints->key_share_secret.empty()) {
-    // Copy DH secret from hints.
-    if (!hs->ecdh_public_key.CopyFrom(hints->key_share_public_key) ||
+    // Copy the key_share secret from hints.
+    if (!hs->key_share_ciphertext.CopyFrom(hints->key_share_ciphertext) ||
         !secret.CopyFrom(hints->key_share_secret)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return false;
     }
   } else {
-    ScopedCBB public_key;
+    ScopedCBB ciphertext;
     UniquePtr<SSLKeyShare> key_share = SSLKeyShare::Create(group_id);
     if (!key_share ||  //
-        !CBB_init(public_key.get(), 32) ||
-        !key_share->Accept(public_key.get(), &secret, &alert, peer_key) ||
-        !CBBFinishArray(public_key.get(), &hs->ecdh_public_key)) {
+        !CBB_init(ciphertext.get(), 32) ||
+        !key_share->Encap(ciphertext.get(), &secret, &alert, peer_key) ||
+        !CBBFinishArray(ciphertext.get(), &hs->key_share_ciphertext)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return false;
     }
     if (hints && hs->hints_requested) {
       hints->key_share_group_id = group_id;
-      if (!hints->key_share_public_key.CopyFrom(hs->ecdh_public_key) ||
+      if (!hints->key_share_ciphertext.CopyFrom(hs->key_share_ciphertext) ||
           !hints->key_share_secret.CopyFrom(secret)) {
         ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
         return false;
@@ -790,7 +790,7 @@
     return ssl_hs_error;
   }
 
-  hs->ecdh_public_key.Reset();  // No longer needed.
+  hs->key_share_ciphertext.Reset();  // No longer needed.
   if (!ssl->s3->used_hello_retry_request &&
       !ssl->method->add_change_cipher_spec(ssl)) {
     return ssl_hs_error;