Add EVP_HPKE_KEY_move

Someone requested that bssl::ScopedEVP_HPKE_KEY be movable.

Change-Id: I48058567c776b5fe9a746072ccb7ddd723ef2b68
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/62265
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
diff --git a/crypto/hpke/hpke.c b/crypto/hpke/hpke.c
index 144b127..ff8b17b 100644
--- a/crypto/hpke/hpke.c
+++ b/crypto/hpke/hpke.c
@@ -352,6 +352,13 @@
   return 1;
 }
 
+void EVP_HPKE_KEY_move(EVP_HPKE_KEY *out, EVP_HPKE_KEY *in) {
+  EVP_HPKE_KEY_cleanup(out);
+  // For now, |EVP_HPKE_KEY| is trivially movable.
+  OPENSSL_memcpy(out, in, sizeof(EVP_HPKE_KEY));
+  EVP_HPKE_KEY_zero(in);
+}
+
 int EVP_HPKE_KEY_init(EVP_HPKE_KEY *key, const EVP_HPKE_KEM *kem,
                       const uint8_t *priv_key, size_t priv_key_len) {
   EVP_HPKE_KEY_zero(key);
diff --git a/crypto/hpke/hpke_test.cc b/crypto/hpke/hpke_test.cc
index 03b23b5..30593f9 100644
--- a/crypto/hpke/hpke_test.cc
+++ b/crypto/hpke/hpke_test.cc
@@ -93,13 +93,24 @@
     ScopedEVP_HPKE_KEY base_key;
     ASSERT_TRUE(EVP_HPKE_KEY_init(base_key.get(), kem, secret_key_r_.data(),
                                   secret_key_r_.size()));
-    for (bool copy : {false, true}) {
-      SCOPED_TRACE(copy);
+
+    enum class CopyMode { kOriginal, kCopy, kMove };
+    for (CopyMode copy :
+         {CopyMode::kOriginal, CopyMode::kCopy, CopyMode::kMove}) {
+      SCOPED_TRACE(static_cast<int>(copy));
       const EVP_HPKE_KEY *key = base_key.get();
       ScopedEVP_HPKE_KEY key_copy;
-      if (copy) {
-        ASSERT_TRUE(EVP_HPKE_KEY_copy(key_copy.get(), base_key.get()));
-        key = key_copy.get();
+      switch (copy) {
+        case CopyMode::kOriginal:
+          break;
+        case CopyMode::kCopy:
+          ASSERT_TRUE(EVP_HPKE_KEY_copy(key_copy.get(), base_key.get()));
+          key = key_copy.get();
+          break;
+        case CopyMode::kMove:
+          EVP_HPKE_KEY_move(key_copy.get(), base_key.get());
+          key = key_copy.get();
+          break;
       }
 
       uint8_t public_key[EVP_HPKE_MAX_PUBLIC_KEY_LENGTH];
diff --git a/include/openssl/hpke.h b/include/openssl/hpke.h
index eaf5947..892ab88 100644
--- a/include/openssl/hpke.h
+++ b/include/openssl/hpke.h
@@ -140,6 +140,10 @@
 OPENSSL_EXPORT int EVP_HPKE_KEY_copy(EVP_HPKE_KEY *dst,
                                      const EVP_HPKE_KEY *src);
 
+// EVP_HPKE_KEY_move sets |out|, which must be initialized or in the zero state,
+// to the key in |in|. |in| is mutated and left in the zero state.
+OPENSSL_EXPORT void EVP_HPKE_KEY_move(EVP_HPKE_KEY *out, EVP_HPKE_KEY *in);
+
 // EVP_HPKE_KEY_init decodes |priv_key| as a private key for |kem| and
 // initializes |key| with the result. It returns one on success and zero if
 // |priv_key| was invalid. On success, the caller must call
@@ -389,8 +393,8 @@
     internal::StackAllocated<EVP_HPKE_CTX, void, EVP_HPKE_CTX_zero,
                              EVP_HPKE_CTX_cleanup>;
 using ScopedEVP_HPKE_KEY =
-    internal::StackAllocated<EVP_HPKE_KEY, void, EVP_HPKE_KEY_zero,
-                             EVP_HPKE_KEY_cleanup>;
+    internal::StackAllocatedMovable<EVP_HPKE_KEY, void, EVP_HPKE_KEY_zero,
+                                    EVP_HPKE_KEY_cleanup, EVP_HPKE_KEY_move>;
 
 BORINGSSL_MAKE_DELETER(EVP_HPKE_CTX, EVP_HPKE_CTX_free)
 BORINGSSL_MAKE_DELETER(EVP_HPKE_KEY, EVP_HPKE_KEY_free)