EVP_KEM: Expose external entropy encap methods for testing only

Change-Id: I4f521dbd53063d1965e06a80fd2c195e6a6a6964
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/102147
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: Lily Chen <chlily@google.com>
diff --git a/crypto/err/evp.errordata b/crypto/err/evp.errordata
index b929efa..558fe9e 100644
--- a/crypto/err/evp.errordata
+++ b/crypto/err/evp.errordata
@@ -14,6 +14,7 @@
 EVP,141,INVALID_CIPHERTEXT_LENGTH
 EVP,110,INVALID_DIGEST_LENGTH
 EVP,111,INVALID_DIGEST_TYPE
+EVP,143,INVALID_ENTROPY_LENGTH
 EVP,112,INVALID_KEYBITS
 EVP,113,INVALID_MGF1_MD
 EVP,114,INVALID_OPERATION
diff --git a/crypto/evp/evp_kem.cc b/crypto/evp/evp_kem.cc
index e350433..e07ea3e 100644
--- a/crypto/evp/evp_kem.cc
+++ b/crypto/evp/evp_kem.cc
@@ -55,8 +55,10 @@
   return kem->ciphertext_len;
 }
 
-size_t EVP_KEM_secret_len(const EVP_KEM *kem) {
-  return kem->secret_len;
+size_t EVP_KEM_secret_len(const EVP_KEM *kem) { return kem->secret_len; }
+
+size_t EVP_KEM_entropy_len_for_testing(const EVP_KEM *kem) {
+  return kem->entropy_len;
 }
 
 int EVP_KEM_encap(const EVP_KEM *kem, uint8_t *out_ciphertext,
@@ -66,8 +68,25 @@
   if (!check_kem_invocation(kem, &ciphertext_len, secret_len, pkey_impl)) {
     return 0;
   }
-  return kem->encap(out_ciphertext, ciphertext_len, out_secret, secret_len,
-                    pkey_impl);
+  return kem->encap(Span(out_ciphertext, ciphertext_len),
+                    Span(out_secret, secret_len), pkey_impl);
+}
+
+int EVP_KEM_encap_external_entropy_for_testing(
+    const EVP_KEM *kem, uint8_t *out_ciphertext, size_t ciphertext_len,
+    uint8_t *out_secret, size_t secret_len, const EVP_PKEY *peer_key,
+    const uint8_t *entropy, size_t entropy_len) {
+  auto *pkey_impl = FromOpaque(peer_key);
+  if (!check_kem_invocation(kem, &ciphertext_len, secret_len, pkey_impl)) {
+    return 0;
+  }
+  if (entropy_len != kem->entropy_len) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_ENTROPY_LENGTH);
+    return 0;
+  }
+  return kem->encap_external_entropy(Span(out_ciphertext, ciphertext_len),
+                                     Span(out_secret, secret_len), pkey_impl,
+                                     Span(entropy, entropy_len));
 }
 
 int EVP_KEM_decap(const EVP_KEM *kem, uint8_t *out_secret, size_t secret_len,
@@ -77,6 +96,6 @@
   if (!check_kem_invocation(kem, nullptr, secret_len, pkey_impl)) {
     return 0;
   }
-  return kem->decap(out_secret, secret_len, ciphertext, ciphertext_len,
-                    pkey_impl);
+  return kem->decap(Span(out_secret, secret_len),
+                    Span(ciphertext, ciphertext_len), pkey_impl);
 }
diff --git a/crypto/evp/evp_test.cc b/crypto/evp/evp_test.cc
index b310bc0..cf23c64 100644
--- a/crypto/evp/evp_test.cc
+++ b/crypto/evp/evp_test.cc
@@ -42,10 +42,13 @@
 #include <openssl/xwing.h>
 
 #include "../bytestring/internal.h"
+#include "../fipsmodule/bcm_interface.h"
 #include "../test/der_trailing_data.h"
 #include "../test/file_test.h"
 #include "../test/test_util.h"
 #include "../test/wycheproof_util.h"
+#include "internal.h"
+
 
 BSSL_NAMESPACE_BEGIN
 namespace {
@@ -847,8 +850,10 @@
 // that the output of encapsulation is successfully decapsulated to the same
 // shared secret value. If only performing decapsulation, this reads ciphertext
 // input from the test vectors file and checks the decapsulation result against
-// known output. If only performing encapsulation, this only checks that the
-// operation succeeds.
+// known output. If performing encapsulation without external entropy, this only
+// checks that the operation succeeds. If an "Entropy" attribute is present on a
+// test vector that performs encapsulation, it is carried out both with and
+// without passing external entropy.
 bool TestKem(FileTest *t, EVP_PKEY *pkey, bool copy_ctx, bool encapsulate,
              bool decapsulate) {
   std::string alg_name;
@@ -887,6 +892,11 @@
   std::vector<uint8_t> ciphertext, secret, decapsulated_secret;
   size_t ciphertext_size, secret_size;
 
+  // Fields populated for test vectors providing entropy for deterministic
+  // encapsulate.
+  bool has_encap_entropy = false;
+  std::vector<uint8_t> entropy, expected_ciphertext, expected_secret;
+
   const auto resize_output_buffers =
       [&](std::optional<size_t> new_ciphertext_len,
           std::optional<size_t> new_secret_len,
@@ -904,23 +914,32 @@
         }
       };
 
-  const auto reset_test_state = [&]() {
+  const auto reset_test_state = [&]() -> bool {
     ctx.reset(EVP_PKEY_CTX_new(pkey, nullptr));
     resize_output_buffers(0, 0);
 
     // Read values from the test vector file.
     if (decapsulate && !encapsulate) {
-      if (!t->GetBytes(&ciphertext, "Input")) {
-        ADD_FAILURE() << "Input not found.";
-      }
-      if (!t->HasAttribute("DecapsulateFail") &&
-          !t->GetBytes(&secret, "Output")) {
-        ADD_FAILURE() << "Output not found.";
+      if (!t->GetBytes(&ciphertext, "Input") ||
+          (!t->HasAttribute("DecapsulateFail") &&
+           !t->GetBytes(&secret, "Output"))) {
+        return false;
       }
     }
+    if (encapsulate && t->HasAttribute("Entropy")) {
+      has_encap_entropy = true;
+      if (!t->GetBytes(&entropy, "Entropy") ||
+          !t->GetBytes(&expected_ciphertext, "Ciphertext") ||
+          !t->GetBytes(&expected_secret, "Secret")) {
+        return false;
+      }
+    }
+    return true;
   };
 
-  reset_test_state();
+  if (!reset_test_state()) {
+    return false;
+  }
 
   // Perform encapsulation.
   if (encapsulate) {
@@ -1005,52 +1024,50 @@
   }
 
   // Repeat everything the EVP_KEM way, which is simpler.
-  reset_test_state();
+  if (!reset_test_state()) {
+    return false;
+  }
 
   EXPECT_EQ(EVP_KEM_ciphertext_len(alg_info.kem), expected_ciphertext_len);
   EXPECT_EQ(EVP_KEM_secret_len(alg_info.kem), expected_secret_len);
 
-  if (encapsulate) {
+  const auto test_kem_encap = [&](const auto invoke_encap,
+                                  bool check_encap_output) {
     ciphertext.resize(ciphertext_size);
 
     // Passing the wrong sizes fails (even if larger than required).
     resize_output_buffers(expected_ciphertext_len - 1, expected_secret_len);
-    EXPECT_EQ(EVP_KEM_encap(alg_info.kem, ciphertext.data(), ciphertext.size(),
-                            secret.data(), secret.size(), pkey),
-              0);
+    EXPECT_EQ(invoke_encap(), 0);
     EXPECT_TRUE(ErrorEquals(ERR_get_error(), ERR_LIB_EVP,
                             EVP_R_INVALID_CIPHERTEXT_LENGTH));
     ERR_clear_error();
     resize_output_buffers(expected_ciphertext_len + 1, expected_secret_len);
-    EXPECT_EQ(EVP_KEM_encap(alg_info.kem, ciphertext.data(), ciphertext.size(),
-                            secret.data(), secret.size(), pkey),
-              0);
+    EXPECT_EQ(invoke_encap(), 0);
     EXPECT_TRUE(ErrorEquals(ERR_get_error(), ERR_LIB_EVP,
                             EVP_R_INVALID_CIPHERTEXT_LENGTH));
     ERR_clear_error();
     resize_output_buffers(expected_ciphertext_len, expected_secret_len - 1);
-    EXPECT_EQ(EVP_KEM_encap(alg_info.kem, ciphertext.data(), ciphertext.size(),
-                            secret.data(), secret.size(), pkey),
-              0);
+    EXPECT_EQ(invoke_encap(), 0);
     EXPECT_TRUE(
         ErrorEquals(ERR_get_error(), ERR_LIB_EVP, EVP_R_INVALID_SECRET_LENGTH));
     ERR_clear_error();
     resize_output_buffers(expected_ciphertext_len, expected_secret_len + 1);
-    EXPECT_EQ(EVP_KEM_encap(alg_info.kem, ciphertext.data(), ciphertext.size(),
-                            secret.data(), secret.size(), pkey),
-              0);
+    EXPECT_EQ(invoke_encap(), 0);
     EXPECT_TRUE(
         ErrorEquals(ERR_get_error(), ERR_LIB_EVP, EVP_R_INVALID_SECRET_LENGTH));
     ERR_clear_error();
 
     // Only the correct sizes are accepted.
     resize_output_buffers(expected_ciphertext_len, expected_secret_len);
-    EXPECT_EQ(EVP_KEM_encap(alg_info.kem, ciphertext.data(), ciphertext.size(),
-                            secret.data(), secret.size(), pkey),
-              1);
-  }
+    EXPECT_EQ(invoke_encap(), 1);
 
-  if (decapsulate) {
+    if (check_encap_output) {
+      EXPECT_EQ(Bytes(ciphertext), Bytes(expected_ciphertext));
+      EXPECT_EQ(Bytes(secret), Bytes(expected_secret));
+    }
+  };
+
+  const auto test_kem_decap = [&]() {
     // Passing the wrong sizes fails (even if larger than required).
     resize_output_buffers(std::nullopt, expected_secret_len - 1, true);
     EXPECT_EQ(EVP_KEM_decap(alg_info.kem, decapsulated_secret.data(),
@@ -1073,6 +1090,55 @@
     check_decapsulate_result(EVP_KEM_decap(
         alg_info.kem, decapsulated_secret.data(), decapsulated_secret.size(),
         ciphertext.data(), ciphertext.size(), pkey));
+  };
+
+  if (encapsulate) {
+    test_kem_encap(
+        [&]() {
+          return EVP_KEM_encap(alg_info.kem, ciphertext.data(),
+                               ciphertext.size(), secret.data(), secret.size(),
+                               pkey);
+        },
+        /*check_encap_output=*/false);
+  }
+  if (decapsulate) {
+    test_kem_decap();
+  }
+
+  // Repeat the test using the provided external entropy.
+  if (has_encap_entropy) {
+    if (!reset_test_state()) {
+      return false;
+    }
+    resize_output_buffers(expected_ciphertext_len, expected_secret_len);
+
+    // Test that the entropy must be the right length.
+    const size_t original_entropy_len = entropy.size();
+    EXPECT_FALSE(EVP_KEM_encap_external_entropy_for_testing(
+        alg_info.kem, ciphertext.data(), ciphertext.size(), secret.data(),
+        secret.size(), pkey, entropy.data(), entropy.size() - 1));
+    EXPECT_TRUE(ErrorEquals(ERR_get_error(), ERR_LIB_EVP,
+                            EVP_R_INVALID_ENTROPY_LENGTH));
+    ERR_clear_error();
+    entropy.resize(original_entropy_len + 1);
+    EXPECT_FALSE(EVP_KEM_encap_external_entropy_for_testing(
+        alg_info.kem, ciphertext.data(), ciphertext.size(), secret.data(),
+        secret.size(), pkey, entropy.data(), entropy.size()));
+    EXPECT_TRUE(ErrorEquals(ERR_get_error(), ERR_LIB_EVP,
+                            EVP_R_INVALID_ENTROPY_LENGTH));
+    ERR_clear_error();
+    entropy.resize(original_entropy_len);
+
+    test_kem_encap(
+        [&]() {
+          return EVP_KEM_encap_external_entropy_for_testing(
+              alg_info.kem, ciphertext.data(), ciphertext.size(), secret.data(),
+              secret.size(), pkey, entropy.data(), entropy.size());
+        },
+        /*check_encap_output=*/true);
+    if (decapsulate) {
+      test_kem_decap();
+    }
   }
 
   return true;
diff --git a/crypto/evp/internal.h b/crypto/evp/internal.h
index 6692c58..2c83190 100644
--- a/crypto/evp/internal.h
+++ b/crypto/evp/internal.h
@@ -314,13 +314,17 @@
   // Constant lengths of ciphertexts and secrets produced/consumed by this KEM.
   size_t ciphertext_len;
   size_t secret_len;
+  // Fixed length of external entropy for testing.
+  size_t entropy_len;
 
-  int (*encap)(uint8_t *out_ciphertext, size_t ciphertext_len,
-               uint8_t *out_secret, size_t secret_len,
-               const EVP_PKEY *peer_key);
-  int (*decap)(uint8_t *out_secret, size_t secret_len,
-               const uint8_t *ciphertext, size_t ciphertext_len,
-               const EVP_PKEY *key);
+  int (*encap)(bssl::Span<uint8_t> out_ciphertext,
+               bssl::Span<uint8_t> out_secret, const EVP_PKEY *peer_key);
+  int (*encap_external_entropy)(bssl::Span<uint8_t> out_ciphertext,
+                                bssl::Span<uint8_t> out_secret,
+                                const EVP_PKEY *peer_key,
+                                bssl::Span<const uint8_t> entropy);
+  int (*decap)(bssl::Span<uint8_t> out_secret,
+               bssl::Span<const uint8_t> ciphertext, const EVP_PKEY *key);
 } /* EVP_KEM */;
 
 BSSL_NAMESPACE_BEGIN
@@ -349,8 +353,8 @@
       OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
       return 0;
     }
-    if (KEM.encap(out_ciphertext, KEM.ciphertext_len, out_secret,
-                  KEM.secret_len, ctx->pkey.get())) {
+    if (KEM.encap(Span(out_ciphertext, KEM.ciphertext_len),
+                  Span(out_secret, KEM.secret_len), ctx->pkey.get())) {
       *out_ciphertext_len = KEM.ciphertext_len;
       *out_secret_len = KEM.secret_len;
       return 1;
@@ -369,8 +373,8 @@
       OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL);
       return 0;
     }
-    if (KEM.decap(out_secret, KEM.secret_len, ciphertext, ciphertext_len,
-                  ctx->pkey.get())) {
+    if (KEM.decap(Span(out_secret, KEM.secret_len),
+                  Span(ciphertext, ciphertext_len), ctx->pkey.get())) {
       *out_secret_len = KEM.secret_len;
       return 1;
     }
diff --git a/crypto/evp/p_mlkem.cc b/crypto/evp/p_mlkem.cc
index 2687ba8..c62c35e 100644
--- a/crypto/evp/p_mlkem.cc
+++ b/crypto/evp/p_mlkem.cc
@@ -56,6 +56,8 @@
     static constexpr auto PublicOfPrivate = &BCM_mlkem##x##_public_of_private; \
     static constexpr auto PublicKeysEqual = &BCM_mlkem##x##_public_keys_equal; \
     static constexpr auto Encap = &MLKEM##x##_encap;                           \
+    static constexpr auto EncapExternalEntropy =                               \
+        &BCM_mlkem##x##_encap_external_entropy;                                \
     static constexpr auto Decap = &MLKEM##x##_decap;                           \
     static constexpr auto MarshalPublicKey = &MLKEM##x##_marshal_public_key;   \
     static constexpr auto ParsePublicKey = &MLKEM##x##_parse_public_key;       \
@@ -354,42 +356,65 @@
     return 1;
   }
 
-  static int KemEncap(uint8_t *out_ciphertext, size_t ciphertext_len,
-                      uint8_t *out_secret, size_t secret_len,
+  static int KemEncap(Span<uint8_t> out_ciphertext, Span<uint8_t> out_secret,
                       const EVP_PKEY *peer_key) {
     const auto *peer_pubkey = GetKeyData(FromOpaque(peer_key))->GetPublicKey();
-    if (ciphertext_len != Traits::kCiphertextBytes) {
+    if (out_ciphertext.size() != Traits::kCiphertextBytes) {
       OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_CIPHERTEXT_LENGTH);
       return 0;
     }
-    if (secret_len != MLKEM_SHARED_SECRET_BYTES) {
+    if (out_secret.size() != MLKEM_SHARED_SECRET_BYTES) {
       OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_SECRET_LENGTH);
       return 0;
     }
-    Traits::Encap(out_ciphertext, out_secret, peer_pubkey);
+    Traits::Encap(out_ciphertext.data(), out_secret.data(), peer_pubkey);
     return 1;
   }
 
-  static int KemDecap(uint8_t *out_secret, size_t secret_len,
-                      const uint8_t *ciphertext, size_t ciphertext_len,
+  static int KemEncapExternalEntropy(Span<uint8_t> out_ciphertext,
+                                     Span<uint8_t> out_secret,
+                                     const EVP_PKEY *peer_key,
+                                     Span<const uint8_t> entropy) {
+    const auto *peer_pubkey = GetKeyData(FromOpaque(peer_key))->GetPublicKey();
+    if (out_ciphertext.size() != Traits::kCiphertextBytes) {
+      OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_CIPHERTEXT_LENGTH);
+      return 0;
+    }
+    if (out_secret.size() != MLKEM_SHARED_SECRET_BYTES) {
+      OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_SECRET_LENGTH);
+      return 0;
+    }
+    if (entropy.size() != BCM_MLKEM_ENCAP_ENTROPY) {
+      OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_ENTROPY_LENGTH);
+      return 0;
+    }
+    Traits::EncapExternalEntropy(out_ciphertext.data(), out_secret.data(),
+                                 peer_pubkey, entropy.data());
+    return 1;
+  }
+
+  static int KemDecap(Span<uint8_t> out_secret, Span<const uint8_t> ciphertext,
                       const EVP_PKEY *key) {
     const auto *priv = GetKeyData(FromOpaque(key))->AsPrivateKeyData();
     if (priv == nullptr) {
       OPENSSL_PUT_ERROR(EVP, EVP_R_NOT_A_PRIVATE_KEY);
       return 0;
     }
-    if (secret_len != MLKEM_SHARED_SECRET_BYTES) {
+    if (out_secret.size() != MLKEM_SHARED_SECRET_BYTES) {
       OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_SECRET_LENGTH);
       return 0;
     }
-    return Traits::Decap(out_secret, ciphertext, ciphertext_len, &priv->priv);
+    return Traits::Decap(out_secret.data(), ciphertext.data(),
+                         ciphertext.size(), &priv->priv);
   }
 
   static constexpr EVP_KEM evp_kem = {
       /*pkey_id=*/Traits::kType,
       /*ciphertext_len=*/Traits::kCiphertextBytes,
       /*secret_len=*/MLKEM_SHARED_SECRET_BYTES,
+      /*entropy_len=*/BCM_MLKEM_ENCAP_ENTROPY,
       &KemEncap,
+      &KemEncapExternalEntropy,
       &KemDecap,
   };
 
diff --git a/crypto/evp/p_xwing.cc b/crypto/evp/p_xwing.cc
index 3e2b1f2..8479bf6 100644
--- a/crypto/evp/p_xwing.cc
+++ b/crypto/evp/p_xwing.cc
@@ -39,6 +39,9 @@
 extern const EVP_PKEY_ASN1_METHOD xwing_asn1_meth;
 extern const EVP_PKEY_CTX_METHOD xwing_pkey_meth;
 
+// This is the length of `eseed` in draft-connolly-cfrg-xwing-kem-06.
+constexpr size_t kXwingEncapEntropyBytes = 64;
+
 static void xwing_free(EvpPkey *pkey) {
   Delete(reinterpret_cast<XWING_KEY *>(pkey->pkey));
 }
@@ -161,49 +164,73 @@
   return 1;
 }
 
-static int xwing_kem_encap(uint8_t *out_ciphertext, size_t ciphertext_len,
-                           uint8_t *out_secret, size_t secret_len,
-                           const EVP_PKEY *peer_key) {
-  if (ciphertext_len != XWING_CIPHERTEXT_BYTES) {
+static int xwing_kem_encap(Span<uint8_t> out_ciphertext,
+                           Span<uint8_t> out_secret, const EVP_PKEY *peer_key) {
+  if (out_ciphertext.size() != XWING_CIPHERTEXT_BYTES) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_CIPHERTEXT_LENGTH);
     return 0;
   }
-  if (secret_len != XWING_SHARED_SECRET_BYTES) {
+  if (out_secret.size() != XWING_SHARED_SECRET_BYTES) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_SECRET_LENGTH);
     return 0;
   }
   const XWING_KEY *peer_pubkey =
       reinterpret_cast<XWING_KEY *>(FromOpaque(peer_key)->pkey);
-  return XWING_encap(out_ciphertext, out_secret, peer_pubkey->pub);
+  return XWING_encap(out_ciphertext.data(), out_secret.data(),
+                     peer_pubkey->pub);
 }
 
-static int xwing_kem_decap(uint8_t *out_secret, size_t secret_len,
-                           const uint8_t *ciphertext, size_t ciphertext_len,
+static int xwing_kem_encap_external_entropy(Span<uint8_t> out_ciphertext,
+                                            Span<uint8_t> out_secret,
+                                            const EVP_PKEY *peer_key,
+                                            Span<const uint8_t> entropy) {
+  if (out_ciphertext.size() != XWING_CIPHERTEXT_BYTES) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_CIPHERTEXT_LENGTH);
+    return 0;
+  }
+  if (out_secret.size() != XWING_SHARED_SECRET_BYTES) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_SECRET_LENGTH);
+    return 0;
+  }
+  if (entropy.size() != kXwingEncapEntropyBytes) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_ENTROPY_LENGTH);
+    return 0;
+  }
+  const XWING_KEY *peer_pubkey =
+      reinterpret_cast<XWING_KEY *>(FromOpaque(peer_key)->pkey);
+  return XWING_encap_external_entropy(out_ciphertext.data(), out_secret.data(),
+                                      peer_pubkey->pub, entropy.data());
+}
+
+static int xwing_kem_decap(Span<uint8_t> out_secret,
+                           Span<const uint8_t> ciphertext,
                            const EVP_PKEY *key) {
   const XWING_KEY *priv = reinterpret_cast<XWING_KEY *>(FromOpaque(key)->pkey);
   if (priv == nullptr || !priv->has_private) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_NOT_A_PRIVATE_KEY);
     return 0;
   }
-  if (secret_len != XWING_SHARED_SECRET_BYTES) {
+  if (out_secret.size() != XWING_SHARED_SECRET_BYTES) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_SECRET_LENGTH);
     return 0;
   }
   // XWING_decap does not accept wrong ciphertext lengths, so we must check for
   // the proper length here. For consistency, we don't add an error to the error
   // queue when a KEM decap fails due to incorrect ciphertext length.
-  if (ciphertext_len != XWING_CIPHERTEXT_BYTES) {
+  if (ciphertext.size() != XWING_CIPHERTEXT_BYTES) {
     return 0;
   }
-  return XWING_decap(out_secret, ciphertext, &priv->priv);
+  return XWING_decap(out_secret.data(), ciphertext.data(), &priv->priv);
 }
 
 static const EVP_KEM xwing_evp_kem = {
-    EVP_PKEY_XWING,             //
-    XWING_CIPHERTEXT_BYTES,     //
-    XWING_SHARED_SECRET_BYTES,  //
-    &xwing_kem_encap,           //
-    &xwing_kem_decap,           //
+    EVP_PKEY_XWING,                     //
+    XWING_CIPHERTEXT_BYTES,             //
+    XWING_SHARED_SECRET_BYTES,          //
+    kXwingEncapEntropyBytes,            //
+    &xwing_kem_encap,                   //
+    &xwing_kem_encap_external_entropy,  //
+    &xwing_kem_decap,                   //
 };
 
 const EVP_PKEY_CTX_METHOD xwing_pkey_meth = {
diff --git a/crypto/evp/test/mlkem_tests.txt b/crypto/evp/test/mlkem_tests.txt
index 1b2287d..662fd11 100644
--- a/crypto/evp/test/mlkem_tests.txt
+++ b/crypto/evp/test/mlkem_tests.txt
@@ -62,9 +62,15 @@
 
 EncapsulateDecapsulate = ML-KEM-768-Wycheproof/Private
 Algorithm = ML-KEM-768
+Entropy = 147c03f7a5bebba406c8fae1874d7f13c80efe79a3a9a874cc09fe76f6997615
+Ciphertext = c8391085b8d3ea9794212541b2914f08964d33521d3f67ad66096ebfb1f706424b49558f755b5625bae236f2e0079601c766f7d960808f7e2bb0c7a5e066ed346de628f8c57eebabbb0c22d911548463693ef3ce52a53f7ff415f00e657ae1c5a48fa5ec6e4be5cf462daffc84d2f6d5ff55dc9bbe8bb0d725ec64fd4cd4bd8dba0a844e8b5ce4b6a28934d7f7a050991fe185b506b451dabfad52d52cb2114ca7d9a5cf986c8fdc1bc10ec0c1869e50c03c55a76192a1049aca636ba9020bdaa8d0f58c763b0b89845ca06d4c4ddc21433e16b9c62e44871fdbc05ba218af871fdd7dcfa464e60faa5265264ce1391bd9a8c5faa7626d5f159b9805b975710a3503a0b858a11c6a647cc0e19ac88b1be9056c95b4d2087d0951d1d2f4992491117e6347794ba54571ec49bba71af3413d38a30bf5872248d1f6d07c86baf782e73d2637f043d341a00921857d8b21ddf3e1d6310036ed27af49e5de1b900fe4de79808ff29f9570859612b15adc01fbb265b305b1e3a12ae419da5b74261fa284c101da3d8dca8b2e4521aca571ef44a058e844ff32b16d5aaea05f7f3af8e2ab16222e347662eddfb891d0ecc2a55c5638f9dde92d9a3d544a5f901ac501acd1ea6a010201fcb10ad702c425a94bdf5890d500a2a147eee1d1fcba8c3abe7c2dfe70f346f033d816a0b2791b4f0b2d956d9ee5971715399a5688302495e2e07c1c8c01527184bcd0c208bc159f2e13318c0bb3dd24a6a7fc849f83385ed4dba07fe1d7bd5640cc9ed5ccfdd68763cb0d0edf61b292177fc1d2d3c11dd0495056bcb12558aebcfddef9feb4aebc57afd9023c65cfe65a24e33f1b00111e92e63e011eaf0b212cf95743cd07f5189ece1f205b7f6fcb2e6b1961b5404cebe47c8cd13b8599d5b49e6d87eeda36e9b8fc4c00635896aa2b75896e336d1b612ee13db811e1f07e61748d920f4865f3f11741399dc6162c91ca168a02329dff821d58198712dd558abb099b3a0baf9da1b730b2aa73bcf58d74f357b06f7211c804b6c8af16ff3509fad1d35b14bfdced7db8a6a25c48e5956480724daa057cd660b67ee3e472574182679d485838a6476eac02141075c812af7967ba7c9185cc2abd2a4545b80f3d3104d58d654a57792dcfabbe9c0715e8de2ef81ef404c8168fd7a43efab3d448e686a088efd26a26159948926723d7eccc39e3c1b719cf8becb7be7e964f22cd8cb1b7e25e800ea97d60a64cc0bbd9cb407a3ab9f88f5e29169eeafd4e0322fde6590ae093ce8feeae98b622caa7556ff426c9e7a404ce69355830a7a67767a76c7d9a97b84bfcf50a02f75c235d2f9c671138049ffc7c8055926c03eb3fb87f9695185a42eca9a41655873d30a6b3bf428b246223484a8ff61ee3eeafff10e99c2c13a76284d063e56ab711a35a85b5383df81da23490f66e8ea3fcba067f5530c6541c2b8f74717c35023e7b9b3956c3ee2ff84ba03ccf4b4b5321b9240895481bc6d63c1693c1847852f8e97f50a133532ac3ee1e52d464
+Secret = e7184a0975ee3470878d2d159ec83129c8aec253d4ee17b4810311d198cd0368
 
 Encapsulate = ML-KEM-768-Wycheproof
 Algorithm = ML-KEM-768
+Entropy = 147c03f7a5bebba406c8fae1874d7f13c80efe79a3a9a874cc09fe76f6997615
+Ciphertext = c8391085b8d3ea9794212541b2914f08964d33521d3f67ad66096ebfb1f706424b49558f755b5625bae236f2e0079601c766f7d960808f7e2bb0c7a5e066ed346de628f8c57eebabbb0c22d911548463693ef3ce52a53f7ff415f00e657ae1c5a48fa5ec6e4be5cf462daffc84d2f6d5ff55dc9bbe8bb0d725ec64fd4cd4bd8dba0a844e8b5ce4b6a28934d7f7a050991fe185b506b451dabfad52d52cb2114ca7d9a5cf986c8fdc1bc10ec0c1869e50c03c55a76192a1049aca636ba9020bdaa8d0f58c763b0b89845ca06d4c4ddc21433e16b9c62e44871fdbc05ba218af871fdd7dcfa464e60faa5265264ce1391bd9a8c5faa7626d5f159b9805b975710a3503a0b858a11c6a647cc0e19ac88b1be9056c95b4d2087d0951d1d2f4992491117e6347794ba54571ec49bba71af3413d38a30bf5872248d1f6d07c86baf782e73d2637f043d341a00921857d8b21ddf3e1d6310036ed27af49e5de1b900fe4de79808ff29f9570859612b15adc01fbb265b305b1e3a12ae419da5b74261fa284c101da3d8dca8b2e4521aca571ef44a058e844ff32b16d5aaea05f7f3af8e2ab16222e347662eddfb891d0ecc2a55c5638f9dde92d9a3d544a5f901ac501acd1ea6a010201fcb10ad702c425a94bdf5890d500a2a147eee1d1fcba8c3abe7c2dfe70f346f033d816a0b2791b4f0b2d956d9ee5971715399a5688302495e2e07c1c8c01527184bcd0c208bc159f2e13318c0bb3dd24a6a7fc849f83385ed4dba07fe1d7bd5640cc9ed5ccfdd68763cb0d0edf61b292177fc1d2d3c11dd0495056bcb12558aebcfddef9feb4aebc57afd9023c65cfe65a24e33f1b00111e92e63e011eaf0b212cf95743cd07f5189ece1f205b7f6fcb2e6b1961b5404cebe47c8cd13b8599d5b49e6d87eeda36e9b8fc4c00635896aa2b75896e336d1b612ee13db811e1f07e61748d920f4865f3f11741399dc6162c91ca168a02329dff821d58198712dd558abb099b3a0baf9da1b730b2aa73bcf58d74f357b06f7211c804b6c8af16ff3509fad1d35b14bfdced7db8a6a25c48e5956480724daa057cd660b67ee3e472574182679d485838a6476eac02141075c812af7967ba7c9185cc2abd2a4545b80f3d3104d58d654a57792dcfabbe9c0715e8de2ef81ef404c8168fd7a43efab3d448e686a088efd26a26159948926723d7eccc39e3c1b719cf8becb7be7e964f22cd8cb1b7e25e800ea97d60a64cc0bbd9cb407a3ab9f88f5e29169eeafd4e0322fde6590ae093ce8feeae98b622caa7556ff426c9e7a404ce69355830a7a67767a76c7d9a97b84bfcf50a02f75c235d2f9c671138049ffc7c8055926c03eb3fb87f9695185a42eca9a41655873d30a6b3bf428b246223484a8ff61ee3eeafff10e99c2c13a76284d063e56ab711a35a85b5383df81da23490f66e8ea3fcba067f5530c6541c2b8f74717c35023e7b9b3956c3ee2ff84ba03ccf4b4b5321b9240895481bc6d63c1693c1847852f8e97f50a133532ac3ee1e52d464
+Secret = e7184a0975ee3470878d2d159ec83129c8aec253d4ee17b4810311d198cd0368
 
 Decapsulate = ML-KEM-768-Wycheproof/Private
 Algorithm = ML-KEM-768
@@ -80,9 +86,15 @@
 
 EncapsulateDecapsulate = ML-KEM-1024-Wycheproof/Private
 Algorithm = ML-KEM-1024
+Entropy = 147c03f7a5bebba406c8fae1874d7f13c80efe79a3a9a874cc09fe76f6997615
+Ciphertext = c9bead6b0c1114389bd4761c73ab9095b5809daac9f659bb564af226173052a4a3e7f2e5fd47d2b02aaeb5189e06b9f4ae98b619cb63efbdf3989a94b36e8ea0d700633b950a0ae2a78ed92e85c85c70e13e626fb263fac9681521c3ab22fdab29173c9616a2b037083ff7b2e019b5bcde068fac257ef8f12798411693c1bdcc65420997a513a8a69502620be8e4ce7362e412a76cf51c1f2433f1ab64ce0e5d2f56d7c9ade994d0e35d0aeef3ac515b482437664d8c1d25e5a5507cf80f970d3ea7226aacdc457cbf88a0560aa35bb2c5c455867e2159910a35810befe3aa10eb04d8d57147cb8f66d2b070bac43d1f1ffdd57a9399951f64965727bcb9f66ad42309dafc799c1c540af1af93eff68a86d61f5115db662dee7ac9a362677762b6a164a0fa0a4d859e4b8c8dbdb4e183f5e6808fc52229650caf7cf3e16de3d895d148c35448ab8c2753c9831b24bd4921497eaa192565cabfd83c0c68dfe7d392abf5e5e6f84bb9f5af4b7118c0b558105f9c10c9b6d70682e1de6e0689d7106a6374bd34aed7229e6cb356f2ea65e680ce7b1e2c3704e116a38542826e8a001141baf2e34de37a03040986d4c0cd5d57f0701ce930986fd9525b58e2e59f45b8dd04c0f35b0f47970cc67079618eb9e6d91e9b0f8c6d2e165cf448a2c1ebf71b6537e0f375185dfafef698b6239bb35580b315bcb5ed408c357f192def89bc1b75cdd6aae8b5faf0c3e13803f6bdfa76fb407fcbda790c329b3ee42fd3d3b03bd5003f0bc432f7ba39631112452dfd12140433ff8980eb6a526ba85ef99477378b4dc76635a5cd5040e43b8c1fe4ee5e158e423bfc0c893c1d5613bed08da719c9073184eeb36fd357380fb1873d8cbd36e2255e985b1b76819743a6584a9b3a580996c9c2eed9bbbfff78a6204b5e5eeae5f4efd2660078b37f0754ab5da862e666b145b5f23f3d0977799929dfa2aedda53d152eda1d0d0e4ea43f6ed889bb965eefe0a7c685bb36770eaa874242c0e229cf6ce56defa5aeae64d0c40dda8aa26eaeb31458f070a3bc72e1619ee9b5f642291c56df5b7e43db6c802fc74f4f3f9b5c0d355c3aae520aa31229d12f3e7cc5d48e691191a36b283765f4133f0ff1fe2f01c6648b2798a74eb5d842a248f524a7e7f8974211297b44f0dd19f386e86be6ba782de77fde887226f37a1c77bc5eddeee5bf46b67fb7478d559865f262caa84d64a8ce59e4df0818e14861526acd3483600f3dae7959d35d8181ca6a81ce791be00752da7759446a2cfbe00b8248b93491debd520220b755416d2fc6b7c8af2ff75e5bcbb8e7537380a5721c77484957a69271d8bafce0f166735ff869232de5d381afbf0e44d69172b79a35191949de09703b94222b13c385c6081e6d2ede1e57fe184ef8f60196b9a3a7b7eff7497191ca8741b5a01e79cb69a61142e6f5d080fbb3e566f79e146f75c8a1097860841b4747df604dba954e4a8d9e0dccc1f609d05cf8d31219ecd60c312de684552f09227cb829291c645732c5f5d4d711639f42a23080aa34fe1420f219bd6bcf4e3b29b9d02293b2da81383e0a51d2bb186c7b0a211a0cd63acbfc0210401e985d436b3803d5601c24136afd1562522e45b457cb439178be4a87cce40346d34ae0f3c39103c8a3ebc9c86c8db8fc5561eb0f3a143d4e9fe93a5cba6f6fcae5650d3f43d2668a5956c922893b816647ded0afc052a6c3d9d01a3d3af0f1ba807ff10491e131dc15e165cfd0650a1f2c313d7956141edcc61cb90e9e7abf2fe35fc9dc1bde88939fa11f7bbe3eb4d8ffa643b074d74f45113586e9bb12060003d71941f2da098dc0e96cad3255cf328ea2d3308c1f4585e89c613c426b7e798e1ec4e98fe6c71e7491f5eca0cd05115861bd160e3fe73a58a026ba538e0e256b92f1d7a2497570594856860ffd06b601ac575592f4ac612b5de7866042123ebc60c55768e3a7600a3260551f2bea22bbf6b6c8246e80f9125c4bb9db354dd64ae695c15f5071f4abb9639207cac7331b310f69a05f54b995de529a023f033b055db95287a14ba30a7cc526bb724c417fba290636a996f286e3e9e939e4fe1c398b5c6599959d0b4445a327ec469a1653cfaea7552cecec085ccaa68938ae4ac3c424f7e480439ebd2c992b5f6f95ec244b657dbdeaa9ae110aaf4d68bf4e27410d43ceef3e88e9c717dd44c9ee
+Secret = 489dd1e9c2be4af3482bdb35bb26ce760e6e414da6ecbe489985748a825f1cd6
 
 Encapsulate = ML-KEM-1024-Wycheproof
 Algorithm = ML-KEM-1024
+Entropy = 147c03f7a5bebba406c8fae1874d7f13c80efe79a3a9a874cc09fe76f6997615
+Ciphertext = c9bead6b0c1114389bd4761c73ab9095b5809daac9f659bb564af226173052a4a3e7f2e5fd47d2b02aaeb5189e06b9f4ae98b619cb63efbdf3989a94b36e8ea0d700633b950a0ae2a78ed92e85c85c70e13e626fb263fac9681521c3ab22fdab29173c9616a2b037083ff7b2e019b5bcde068fac257ef8f12798411693c1bdcc65420997a513a8a69502620be8e4ce7362e412a76cf51c1f2433f1ab64ce0e5d2f56d7c9ade994d0e35d0aeef3ac515b482437664d8c1d25e5a5507cf80f970d3ea7226aacdc457cbf88a0560aa35bb2c5c455867e2159910a35810befe3aa10eb04d8d57147cb8f66d2b070bac43d1f1ffdd57a9399951f64965727bcb9f66ad42309dafc799c1c540af1af93eff68a86d61f5115db662dee7ac9a362677762b6a164a0fa0a4d859e4b8c8dbdb4e183f5e6808fc52229650caf7cf3e16de3d895d148c35448ab8c2753c9831b24bd4921497eaa192565cabfd83c0c68dfe7d392abf5e5e6f84bb9f5af4b7118c0b558105f9c10c9b6d70682e1de6e0689d7106a6374bd34aed7229e6cb356f2ea65e680ce7b1e2c3704e116a38542826e8a001141baf2e34de37a03040986d4c0cd5d57f0701ce930986fd9525b58e2e59f45b8dd04c0f35b0f47970cc67079618eb9e6d91e9b0f8c6d2e165cf448a2c1ebf71b6537e0f375185dfafef698b6239bb35580b315bcb5ed408c357f192def89bc1b75cdd6aae8b5faf0c3e13803f6bdfa76fb407fcbda790c329b3ee42fd3d3b03bd5003f0bc432f7ba39631112452dfd12140433ff8980eb6a526ba85ef99477378b4dc76635a5cd5040e43b8c1fe4ee5e158e423bfc0c893c1d5613bed08da719c9073184eeb36fd357380fb1873d8cbd36e2255e985b1b76819743a6584a9b3a580996c9c2eed9bbbfff78a6204b5e5eeae5f4efd2660078b37f0754ab5da862e666b145b5f23f3d0977799929dfa2aedda53d152eda1d0d0e4ea43f6ed889bb965eefe0a7c685bb36770eaa874242c0e229cf6ce56defa5aeae64d0c40dda8aa26eaeb31458f070a3bc72e1619ee9b5f642291c56df5b7e43db6c802fc74f4f3f9b5c0d355c3aae520aa31229d12f3e7cc5d48e691191a36b283765f4133f0ff1fe2f01c6648b2798a74eb5d842a248f524a7e7f8974211297b44f0dd19f386e86be6ba782de77fde887226f37a1c77bc5eddeee5bf46b67fb7478d559865f262caa84d64a8ce59e4df0818e14861526acd3483600f3dae7959d35d8181ca6a81ce791be00752da7759446a2cfbe00b8248b93491debd520220b755416d2fc6b7c8af2ff75e5bcbb8e7537380a5721c77484957a69271d8bafce0f166735ff869232de5d381afbf0e44d69172b79a35191949de09703b94222b13c385c6081e6d2ede1e57fe184ef8f60196b9a3a7b7eff7497191ca8741b5a01e79cb69a61142e6f5d080fbb3e566f79e146f75c8a1097860841b4747df604dba954e4a8d9e0dccc1f609d05cf8d31219ecd60c312de684552f09227cb829291c645732c5f5d4d711639f42a23080aa34fe1420f219bd6bcf4e3b29b9d02293b2da81383e0a51d2bb186c7b0a211a0cd63acbfc0210401e985d436b3803d5601c24136afd1562522e45b457cb439178be4a87cce40346d34ae0f3c39103c8a3ebc9c86c8db8fc5561eb0f3a143d4e9fe93a5cba6f6fcae5650d3f43d2668a5956c922893b816647ded0afc052a6c3d9d01a3d3af0f1ba807ff10491e131dc15e165cfd0650a1f2c313d7956141edcc61cb90e9e7abf2fe35fc9dc1bde88939fa11f7bbe3eb4d8ffa643b074d74f45113586e9bb12060003d71941f2da098dc0e96cad3255cf328ea2d3308c1f4585e89c613c426b7e798e1ec4e98fe6c71e7491f5eca0cd05115861bd160e3fe73a58a026ba538e0e256b92f1d7a2497570594856860ffd06b601ac575592f4ac612b5de7866042123ebc60c55768e3a7600a3260551f2bea22bbf6b6c8246e80f9125c4bb9db354dd64ae695c15f5071f4abb9639207cac7331b310f69a05f54b995de529a023f033b055db95287a14ba30a7cc526bb724c417fba290636a996f286e3e9e939e4fe1c398b5c6599959d0b4445a327ec469a1653cfaea7552cecec085ccaa68938ae4ac3c424f7e480439ebd2c992b5f6f95ec244b657dbdeaa9ae110aaf4d68bf4e27410d43ceef3e88e9c717dd44c9ee
+Secret = 489dd1e9c2be4af3482bdb35bb26ce760e6e414da6ecbe489985748a825f1cd6
 
 Decapsulate = ML-KEM-1024-Wycheproof/Private
 Algorithm = ML-KEM-1024
diff --git a/crypto/evp/test/xwing_tests.txt b/crypto/evp/test/xwing_tests.txt
index 8694408..4f04166 100644
--- a/crypto/evp/test/xwing_tests.txt
+++ b/crypto/evp/test/xwing_tests.txt
@@ -11,9 +11,15 @@
 
 EncapsulateDecapsulate = X-Wing-1/Private
 Algorithm = X-Wing
+Entropy = 3cb1eea988004b93103cfb0aeefd2a686e01fa4a58e8a3639ca8a1e3f9ae57e235b8cc873c23dc62b8d260169afa2f75ab916a58d974918835d25e6a435085b2
+Ciphertext = b83aa828d4d62b9a83ceffe1d3d3bb1ef31264643c070c5798927e41fb07914a273f8f96e7826cd5375a283d7da885304c5de0516a0f0654243dc5b97f8bfeb831f68251219aabdd723bc6512041acbaef8af44265524942b902e68ffd23221cda70b1b55d776a92d1143ea3a0c475f63ee6890157c7116dae3f62bf72f60acd2bb8cc31ce2ba0de364f52b8ed38c79d719715963a5dd3842d8e8b43ab704e4759b5327bf027c63c8fa857c4908d5a8a7b88ac7f2be394d93c3706ddd4e698cc6ce370101f4d0213254238b4a2e8821b6e414a1cf20f6c1244b699046f5a01caa0a1a55516300b40d2048c77cc73afba79afeea9d2c0118bdf2adb8870dc328c5516cc45b1a2058141039e2c90a110a9e16b318dfb53bd49a126d6b73f215787517b8917cc01cabd107d06859854ee8b4f9861c226d3764c87339ab16c3667d2f49384e55456dd40414b70a6af841585f4c90c68725d57704ee8ee7ce6e2f9be582dbee985e038ffc346ebfb4e22158b6c84374a9ab4a44e1f91de5aac5197f89bc5e5442f51f9a5937b102ba3beaebf6e1c58380a4a5fedce4a4e5026f88f528f59ffd2db41752b3a3d90efabe463899b7d40870c530c8841e8712b733668ed033adbfafb2d49d37a44d4064e5863eb0af0a08d47b3cc888373bc05f7a33b841bc2587c57eb69554e8a3767b7506917b6b70498727f16eac1a36ec8d8cfaf751549f2277db277e8a55a9a5106b23a0206b4721fa9b3048552c5bd5b594d6e247f38c18c591aea7f56249c72ce7b117afcc3a8621582f9cf71787e183dee09367976e98409ad9217a497df888042384d7707a6b78f5f7fb8409e3b535175373461b776002d799cbad62860be70573ecbe13b246e0da7e93a52168e0fb6a9756b895ef7f0147a0dc81bfa644b088a9228160c0f9acf1379a2941cd28c06ebc80e44e17aa2f8177010afd78a97ce0868d1629ebb294c5151812c583daeb88685220f4da9118112e07041fcc24d5564a99fdbde28869fe0722387d7a9a4d16e1cc8555917e09944aa5ebaaaec2cf62693afad42a3f518fce67d273cc6c9fb5472b380e8573ec7de06a3ba2fd5f931d725b493026cb0acbd3fe62d00e4c790d965d7a03a3c0b4222ba8c2a9a16e2ac658f572ae0e746eafc4feba023576f08942278a041fb82a70a595d5bacbf297ce2029898a71e5c3b0d1c6228b485b1ade509b35fbca7eca97b2132e7cb6bc465375146b7dceac969308ac0c2ac89e7863eb8943015b24314cafb9c7c0e85fe543d56658c213632599efabfc1ec49dd8c88547bb2cc40c9d38cbd3099b4547840560531d0188cd1e9c23a0ebee0a03d5577d66b1d2bcb4baaf21cc7fef1e03806ca96299df0dfbc56e1b2b43e4fc20c37f834c4af62127e7dae86c3c25a2f696ac8b589dec71d595bfbe94b5ed4bc07d800b330796fda89edb77be0294136139354eb8cd37591578f9c600dd9be8ec6219fdd507adf3397ed4d68707b8d13b24ce4cd8fb22851bfe9d632407f31ed6f7cb1600de56f17576740ce2a32fc5145030145cfb97e63e0e41d354274a079d3e6fb2e15
+Secret = d2df0522128f09dd8e2c92b1e905c793d8f57a54c3da25861f10bf4ca613e384
 
 Encapsulate = X-Wing-1
 Algorithm = X-Wing
+Entropy = 3cb1eea988004b93103cfb0aeefd2a686e01fa4a58e8a3639ca8a1e3f9ae57e235b8cc873c23dc62b8d260169afa2f75ab916a58d974918835d25e6a435085b2
+Ciphertext = b83aa828d4d62b9a83ceffe1d3d3bb1ef31264643c070c5798927e41fb07914a273f8f96e7826cd5375a283d7da885304c5de0516a0f0654243dc5b97f8bfeb831f68251219aabdd723bc6512041acbaef8af44265524942b902e68ffd23221cda70b1b55d776a92d1143ea3a0c475f63ee6890157c7116dae3f62bf72f60acd2bb8cc31ce2ba0de364f52b8ed38c79d719715963a5dd3842d8e8b43ab704e4759b5327bf027c63c8fa857c4908d5a8a7b88ac7f2be394d93c3706ddd4e698cc6ce370101f4d0213254238b4a2e8821b6e414a1cf20f6c1244b699046f5a01caa0a1a55516300b40d2048c77cc73afba79afeea9d2c0118bdf2adb8870dc328c5516cc45b1a2058141039e2c90a110a9e16b318dfb53bd49a126d6b73f215787517b8917cc01cabd107d06859854ee8b4f9861c226d3764c87339ab16c3667d2f49384e55456dd40414b70a6af841585f4c90c68725d57704ee8ee7ce6e2f9be582dbee985e038ffc346ebfb4e22158b6c84374a9ab4a44e1f91de5aac5197f89bc5e5442f51f9a5937b102ba3beaebf6e1c58380a4a5fedce4a4e5026f88f528f59ffd2db41752b3a3d90efabe463899b7d40870c530c8841e8712b733668ed033adbfafb2d49d37a44d4064e5863eb0af0a08d47b3cc888373bc05f7a33b841bc2587c57eb69554e8a3767b7506917b6b70498727f16eac1a36ec8d8cfaf751549f2277db277e8a55a9a5106b23a0206b4721fa9b3048552c5bd5b594d6e247f38c18c591aea7f56249c72ce7b117afcc3a8621582f9cf71787e183dee09367976e98409ad9217a497df888042384d7707a6b78f5f7fb8409e3b535175373461b776002d799cbad62860be70573ecbe13b246e0da7e93a52168e0fb6a9756b895ef7f0147a0dc81bfa644b088a9228160c0f9acf1379a2941cd28c06ebc80e44e17aa2f8177010afd78a97ce0868d1629ebb294c5151812c583daeb88685220f4da9118112e07041fcc24d5564a99fdbde28869fe0722387d7a9a4d16e1cc8555917e09944aa5ebaaaec2cf62693afad42a3f518fce67d273cc6c9fb5472b380e8573ec7de06a3ba2fd5f931d725b493026cb0acbd3fe62d00e4c790d965d7a03a3c0b4222ba8c2a9a16e2ac658f572ae0e746eafc4feba023576f08942278a041fb82a70a595d5bacbf297ce2029898a71e5c3b0d1c6228b485b1ade509b35fbca7eca97b2132e7cb6bc465375146b7dceac969308ac0c2ac89e7863eb8943015b24314cafb9c7c0e85fe543d56658c213632599efabfc1ec49dd8c88547bb2cc40c9d38cbd3099b4547840560531d0188cd1e9c23a0ebee0a03d5577d66b1d2bcb4baaf21cc7fef1e03806ca96299df0dfbc56e1b2b43e4fc20c37f834c4af62127e7dae86c3c25a2f696ac8b589dec71d595bfbe94b5ed4bc07d800b330796fda89edb77be0294136139354eb8cd37591578f9c600dd9be8ec6219fdd507adf3397ed4d68707b8d13b24ce4cd8fb22851bfe9d632407f31ed6f7cb1600de56f17576740ce2a32fc5145030145cfb97e63e0e41d354274a079d3e6fb2e15
+Secret = d2df0522128f09dd8e2c92b1e905c793d8f57a54c3da25861f10bf4ca613e384
 
 Decapsulate = X-Wing-1/Private
 Algorithm = X-Wing
@@ -27,9 +33,15 @@
 
 EncapsulateDecapsulate = X-Wing-2/Private
 Algorithm = X-Wing
+Entropy = 17cda7cfad765f5623474d368ccca8af0007cd9f5e4c849f167a580b14aabdefaee7eef47cb0fca9767be1fda69419dfb927e9df07348b196691abaeb580b32d
+Ciphertext = c93beb22326705699bbc3d1d0aa6339be7a405debe61a7c337e1a91453c097a6f77c130639d1aaeb193175f1a987aa1fd789a63c9cd487ebd6965f5d8389c8d7c8cfacbba4b44d2fbe0ae84de9e96fb11215d9b76acd51887b752329c1a3e0468ccc49392c1e0f1aad61a73c10831e60a9798cb2e7ec07596b5803db3e243ecbb94166feade0c9197378700f8eb65a43502bbac4605992e2de2b906ab30ba401d7e1ff3c98f42cfc4b30b974d3316f331461ac05f43e0db7b41d3da702a4f567b6ee7295199c7be92f6b4a47e7307d34278e03c872fb48647c446a64a3937dccd7c6d8de4d34b9dea45a0b065ef15b9e94d1b6df6dca7174d9bc9d14c6225e3a78a58785c3fe4e2fe6a0706f3365389e4258fbb61ecf1a1957715982b3f1844424e03acd83da7eee50573f6cd3ff396841e9a00ad679da92274129da277833d0524674feea09a98d25b888616f338412d8e65e151e65736c8c6fb448c9260fa20e7b2712148bcd3a0853865f50c1fc9e4f201aee3757120e034fd509d954b7a749ff776561382c4cb64cebcbb6aa82d04cd5c2b40395ecaf231bde8334ecfd955d09efa8c6e7935b1cb0298fb8b6740be4593360eed5f129d59d98822a6cea37c57674e919e84d6b90f695fca58e7d29092bd70f7c97c6dfb021b9f87216a6271d8b144a364d03b6bf084f972dc59800b14a2c008bbd0992b5b82801020978f2bdddb3ca3367d876cffb3548dab695a29882cae2eb5ba7c847c3c71bd0150fa9c33aac8e6240e0c269b8e295ddb7b77e9c17bd310be65e28c0802136d086777be5652d6f1ac879d3263e9c712d1af736eac048fe848a577d6afaea1428dc71db8c430edd7b584ae6e6aeaf7257aff0fd8fe25c30840e30ccfa1d95118ef0f6657367e9070f3d97a2e9a7bae19957bd707b00e31b6b0ebb9d7df4bd22e44c060830a194b5b8288353255b52954ff5905ab2b126d9aa049e44599368c27d6cb033eae5182c2e1504ee4e3745f51488997b8f958f0209064f6f44a7e4de5226d5594d1ad9b42ac59a2d100a2f190df873a2e141552f33c923b4c927e8747c6f830c441a8bd3c5b371f6b3ab8103ebcfb18543aefc1beb6f776bbfd5344779f4aa23daaf395f69ec31dc046b491f0e5cc9c651dfc306bd8f2105be7bc7a4f4e21957f87278c771528a8740a92e2daefa76a3525f1fae17ec4362a2700988001d860011d6ca3a95f79a0205bcf634cef373a8ea273ff0f4250eb8617d0fb92102a6aa09cf0c3ee2cad1ad96438c8e4dfd6ee0fcc85833c3103dd6c1600cd305bc2df4cda89b55ca237a3f9c3f82390074ff30825fc750130ebaf13d0cf7556d2c52a98a4bad39ca5d44aaadeaef775c695e64d06e966acfcd552a14e2df6c63ae541f0fa88fc48263089685704506a21a03856ce65d4f06d54f3157eeabd62491cb4ac7bf029e79f9fbd4c77e2a3588790c710e611da8b2040c76a61507a8020758dcc30894ad018fef98e401cc54106e20d94bd544a8f0e1fd0500342d123f618aa8c91bdf6e0e03200693c9651e469aee6f91c98bea4127ae66312f4ae3ea155b67
+Secret = f2e86241c64d60f6649fbc6c5b7d17180b780a3f34355e64a85749949c45f150
 
 Encapsulate = X-Wing-2
 Algorithm = X-Wing
+Entropy = 17cda7cfad765f5623474d368ccca8af0007cd9f5e4c849f167a580b14aabdefaee7eef47cb0fca9767be1fda69419dfb927e9df07348b196691abaeb580b32d
+Ciphertext = c93beb22326705699bbc3d1d0aa6339be7a405debe61a7c337e1a91453c097a6f77c130639d1aaeb193175f1a987aa1fd789a63c9cd487ebd6965f5d8389c8d7c8cfacbba4b44d2fbe0ae84de9e96fb11215d9b76acd51887b752329c1a3e0468ccc49392c1e0f1aad61a73c10831e60a9798cb2e7ec07596b5803db3e243ecbb94166feade0c9197378700f8eb65a43502bbac4605992e2de2b906ab30ba401d7e1ff3c98f42cfc4b30b974d3316f331461ac05f43e0db7b41d3da702a4f567b6ee7295199c7be92f6b4a47e7307d34278e03c872fb48647c446a64a3937dccd7c6d8de4d34b9dea45a0b065ef15b9e94d1b6df6dca7174d9bc9d14c6225e3a78a58785c3fe4e2fe6a0706f3365389e4258fbb61ecf1a1957715982b3f1844424e03acd83da7eee50573f6cd3ff396841e9a00ad679da92274129da277833d0524674feea09a98d25b888616f338412d8e65e151e65736c8c6fb448c9260fa20e7b2712148bcd3a0853865f50c1fc9e4f201aee3757120e034fd509d954b7a749ff776561382c4cb64cebcbb6aa82d04cd5c2b40395ecaf231bde8334ecfd955d09efa8c6e7935b1cb0298fb8b6740be4593360eed5f129d59d98822a6cea37c57674e919e84d6b90f695fca58e7d29092bd70f7c97c6dfb021b9f87216a6271d8b144a364d03b6bf084f972dc59800b14a2c008bbd0992b5b82801020978f2bdddb3ca3367d876cffb3548dab695a29882cae2eb5ba7c847c3c71bd0150fa9c33aac8e6240e0c269b8e295ddb7b77e9c17bd310be65e28c0802136d086777be5652d6f1ac879d3263e9c712d1af736eac048fe848a577d6afaea1428dc71db8c430edd7b584ae6e6aeaf7257aff0fd8fe25c30840e30ccfa1d95118ef0f6657367e9070f3d97a2e9a7bae19957bd707b00e31b6b0ebb9d7df4bd22e44c060830a194b5b8288353255b52954ff5905ab2b126d9aa049e44599368c27d6cb033eae5182c2e1504ee4e3745f51488997b8f958f0209064f6f44a7e4de5226d5594d1ad9b42ac59a2d100a2f190df873a2e141552f33c923b4c927e8747c6f830c441a8bd3c5b371f6b3ab8103ebcfb18543aefc1beb6f776bbfd5344779f4aa23daaf395f69ec31dc046b491f0e5cc9c651dfc306bd8f2105be7bc7a4f4e21957f87278c771528a8740a92e2daefa76a3525f1fae17ec4362a2700988001d860011d6ca3a95f79a0205bcf634cef373a8ea273ff0f4250eb8617d0fb92102a6aa09cf0c3ee2cad1ad96438c8e4dfd6ee0fcc85833c3103dd6c1600cd305bc2df4cda89b55ca237a3f9c3f82390074ff30825fc750130ebaf13d0cf7556d2c52a98a4bad39ca5d44aaadeaef775c695e64d06e966acfcd552a14e2df6c63ae541f0fa88fc48263089685704506a21a03856ce65d4f06d54f3157eeabd62491cb4ac7bf029e79f9fbd4c77e2a3588790c710e611da8b2040c76a61507a8020758dcc30894ad018fef98e401cc54106e20d94bd544a8f0e1fd0500342d123f618aa8c91bdf6e0e03200693c9651e469aee6f91c98bea4127ae66312f4ae3ea155b67
+Secret = f2e86241c64d60f6649fbc6c5b7d17180b780a3f34355e64a85749949c45f150
 
 Decapsulate = X-Wing-2/Private
 Algorithm = X-Wing
diff --git a/gen/crypto/err_data.cc b/gen/crypto/err_data.cc
index 117618d..9e0884a 100644
--- a/gen/crypto/err_data.cc
+++ b/gen/crypto/err_data.cc
@@ -80,54 +80,54 @@
     0xc3b00f7,
     0xc3b8921,
     0x10320892,
-    0x10329712,
-    0x1033171e,
-    0x10339737,
-    0x1034174a,
+    0x10329729,
+    0x10331735,
+    0x1033974e,
+    0x10341761,
     0x10348fd3,
     0x10350d1f,
-    0x1035975d,
-    0x10361787,
-    0x1036979a,
-    0x103717b9,
-    0x103797d2,
-    0x103817e7,
-    0x10389805,
-    0x10391814,
-    0x10399830,
-    0x103a184b,
-    0x103a985a,
-    0x103b1876,
-    0x103b9891,
-    0x103c18b7,
+    0x10359774,
+    0x1036179e,
+    0x103697b1,
+    0x103717d0,
+    0x103797e9,
+    0x103817fe,
+    0x1038981c,
+    0x1039182b,
+    0x10399847,
+    0x103a1862,
+    0x103a9871,
+    0x103b188d,
+    0x103b98a8,
+    0x103c18ce,
     0x103c80f7,
-    0x103d18c8,
-    0x103d98dc,
-    0x103e18fb,
-    0x103e990a,
-    0x103f1921,
-    0x103f9934,
+    0x103d18df,
+    0x103d98f3,
+    0x103e1912,
+    0x103e9921,
+    0x103f1938,
+    0x103f994b,
     0x10400ce3,
-    0x10409947,
-    0x10411965,
-    0x10419978,
-    0x10421992,
-    0x104299a2,
-    0x104319b6,
-    0x104399cc,
-    0x104419e4,
-    0x104499f9,
-    0x10451a0d,
-    0x10459a1f,
+    0x1040995e,
+    0x1041197c,
+    0x1041998f,
+    0x104219a9,
+    0x104299b9,
+    0x104319cd,
+    0x104399e3,
+    0x104419fb,
+    0x10449a10,
+    0x10451a24,
+    0x10459a36,
     0x10460635,
     0x1046899a,
-    0x10471a34,
-    0x10479a4b,
-    0x10481a60,
-    0x10489a6e,
+    0x10471a4b,
+    0x10479a62,
+    0x10481a77,
+    0x10489a85,
     0x10490f1f,
-    0x104998a8,
-    0x104a1772,
+    0x104998bf,
+    0x104a1789,
     0x14320cb3,
     0x14328cd4,
     0x14330ce3,
@@ -147,56 +147,57 @@
     0x183690c1,
     0x18371113,
     0x18379129,
-    0x1838113d,
-    0x1838914d,
+    0x18381154,
+    0x18389164,
     0x18390ac0,
-    0x1839915d,
-    0x183a1183,
-    0x183a91bf,
+    0x18399174,
+    0x183a119a,
+    0x183a91d6,
     0x183b0d2b,
-    0x183b9221,
-    0x183c1233,
-    0x183c923e,
-    0x183d124e,
-    0x183d925f,
-    0x183e1270,
-    0x183e929c,
-    0x183f1282,
-    0x183f92de,
-    0x184012f6,
+    0x183b9238,
+    0x183c124a,
+    0x183c9255,
+    0x183d1265,
+    0x183d9276,
+    0x183e1287,
+    0x183e92b3,
+    0x183f1299,
+    0x183f92f5,
+    0x1840130d,
     0x1840870d,
-    0x184111f5,
-    0x184191ad,
-    0x184211cc,
+    0x1841120c,
+    0x184191c4,
+    0x184211e3,
     0x18428cc1,
-    0x18431172,
-    0x18439207,
+    0x18431189,
+    0x1843921e,
     0x18441068,
     0x184490e5,
     0x18451087,
-    0x184592c5,
-    0x184611e2,
+    0x184592dc,
+    0x184611f9,
     0x184690f9,
-    0x18471197,
-    0x20321330,
-    0x2032931d,
-    0x2432133c,
+    0x184711ae,
+    0x1847913d,
+    0x20321347,
+    0x20329334,
+    0x24321353,
     0x243289e0,
-    0x2433134e,
-    0x2433935b,
-    0x24341368,
-    0x2434937a,
-    0x24351389,
-    0x243593a6,
-    0x243613b3,
-    0x243693c1,
-    0x243713cf,
-    0x243793dd,
-    0x243813e6,
-    0x243893f3,
-    0x24391406,
-    0x2439941d,
-    0x243a143b,
+    0x24331365,
+    0x24339372,
+    0x2434137f,
+    0x24349391,
+    0x243513a0,
+    0x243593bd,
+    0x243613ca,
+    0x243693d8,
+    0x243713e6,
+    0x243793f4,
+    0x243813fd,
+    0x2438940a,
+    0x2439141d,
+    0x24399434,
+    0x243a1452,
     0x28320d13,
     0x28328d2b,
     0x28330ce3,
@@ -206,53 +207,53 @@
     0x283500f7,
     0x28358cc1,
     0x2836099a,
-    0x2c32350d,
-    0x2c329443,
-    0x2c33351b,
-    0x2c33b52d,
-    0x2c343541,
-    0x2c34b553,
-    0x2c35356e,
-    0x2c35b580,
-    0x2c3635b0,
+    0x2c323524,
+    0x2c32945a,
+    0x2c333532,
+    0x2c33b544,
+    0x2c343558,
+    0x2c34b56a,
+    0x2c353585,
+    0x2c35b597,
+    0x2c3635c7,
     0x2c36833a,
-    0x2c3735bd,
-    0x2c37b5e9,
-    0x2c383648,
-    0x2c38b65f,
-    0x2c39367d,
-    0x2c39b68d,
-    0x2c3a369f,
-    0x2c3ab6b3,
-    0x2c3b36c4,
-    0x2c3bb6e3,
-    0x2c3c1455,
-    0x2c3c946b,
-    0x2c3d3728,
-    0x2c3d9484,
-    0x2c3e3752,
-    0x2c3eb760,
-    0x2c3f3778,
-    0x2c3fb790,
-    0x2c4037ba,
-    0x2c409330,
-    0x2c4137cb,
-    0x2c41b7de,
-    0x2c4212f6,
-    0x2c42b7ef,
+    0x2c3735d4,
+    0x2c37b600,
+    0x2c38365f,
+    0x2c38b676,
+    0x2c393694,
+    0x2c39b6a4,
+    0x2c3a36b6,
+    0x2c3ab6ca,
+    0x2c3b36db,
+    0x2c3bb6fa,
+    0x2c3c146c,
+    0x2c3c9482,
+    0x2c3d373f,
+    0x2c3d949b,
+    0x2c3e3769,
+    0x2c3eb777,
+    0x2c3f378f,
+    0x2c3fb7a7,
+    0x2c4037d1,
+    0x2c409347,
+    0x2c4137e2,
+    0x2c41b7f5,
+    0x2c42130d,
+    0x2c42b806,
     0x2c43076d,
-    0x2c43b6d5,
-    0x2c44361d,
-    0x2c44b79d,
-    0x2c453593,
-    0x2c45b5cf,
-    0x2c46366d,
-    0x2c46b6f7,
-    0x2c47370c,
-    0x2c47b745,
-    0x2c48362f,
-    0x2c48b5fc,
-    0x2c49360b,
+    0x2c43b6ec,
+    0x2c443634,
+    0x2c44b7b4,
+    0x2c4535aa,
+    0x2c45b5e6,
+    0x2c463684,
+    0x2c46b70e,
+    0x2c473723,
+    0x2c47b75c,
+    0x2c483646,
+    0x2c48b613,
+    0x2c493622,
     0x30320000,
     0x30328015,
     0x3033001f,
@@ -392,277 +393,277 @@
     0x3c418e13,
     0x3c420f1f,
     0x3c428ea9,
-    0x40321ada,
-    0x40329af0,
-    0x40331b1e,
-    0x40339b28,
-    0x40341b3f,
-    0x40349b5d,
-    0x40351b6d,
-    0x40359b7f,
-    0x40361b8c,
-    0x40369b98,
-    0x40371bad,
-    0x40379bbf,
-    0x40381bca,
-    0x40389bdc,
+    0x40321af1,
+    0x40329b07,
+    0x40331b35,
+    0x40339b3f,
+    0x40341b56,
+    0x40349b74,
+    0x40351b84,
+    0x40359b96,
+    0x40361ba3,
+    0x40369baf,
+    0x40371bc4,
+    0x40379bd6,
+    0x40381be1,
+    0x40389bf3,
     0x40390fd3,
-    0x40399bec,
-    0x403a1bff,
-    0x403a9c20,
-    0x403b1c31,
-    0x403b9c41,
+    0x40399c03,
+    0x403a1c16,
+    0x403a9c37,
+    0x403b1c48,
+    0x403b9c58,
     0x403c0071,
     0x403c8090,
-    0x403d1ca2,
-    0x403d9cb8,
-    0x403e1cc7,
-    0x403e9cff,
-    0x403f1d19,
-    0x403f9d41,
-    0x40401d56,
-    0x40409d6a,
-    0x40411da5,
-    0x40419dc0,
-    0x40421dd9,
-    0x40429dec,
-    0x40431e00,
-    0x40439e2e,
-    0x40441e45,
+    0x403d1cb9,
+    0x403d9ccf,
+    0x403e1cde,
+    0x403e9d16,
+    0x403f1d30,
+    0x403f9d58,
+    0x40401d6d,
+    0x40409d81,
+    0x40411dbc,
+    0x40419dd7,
+    0x40421df0,
+    0x40429e03,
+    0x40431e17,
+    0x40439e45,
+    0x40441e5c,
     0x404480b9,
-    0x40451e5a,
-    0x40459e6c,
-    0x40461e90,
-    0x40469eb0,
-    0x40471ebe,
-    0x40479ee5,
-    0x40481f77,
-    0x4048a031,
-    0x40492048,
-    0x4049a062,
-    0x404a2079,
-    0x404aa097,
-    0x404b20af,
-    0x404ba0dc,
-    0x404c20f2,
-    0x404ca104,
-    0x404d2125,
-    0x404da15e,
-    0x404e2172,
-    0x404ea17f,
-    0x404f227f,
-    0x404fa2f5,
-    0x405023b1,
-    0x4050a3c5,
-    0x40512412,
-    0x40522422,
-    0x4052a452,
-    0x4053246a,
-    0x4053a47d,
-    0x40542492,
-    0x4054a4b5,
-    0x405524e0,
-    0x4055a51d,
-    0x40562542,
-    0x4056a55b,
-    0x40572573,
-    0x4057a586,
-    0x4058259b,
-    0x4058a5c2,
-    0x405925f1,
-    0x4059a631,
-    0x405aa645,
-    0x405b265d,
-    0x405ba66e,
-    0x405c2681,
-    0x405ca6d6,
-    0x405d26e3,
-    0x405da708,
-    0x405e2746,
+    0x40451e71,
+    0x40459e83,
+    0x40461ea7,
+    0x40469ec7,
+    0x40471ed5,
+    0x40479efc,
+    0x40481f8e,
+    0x4048a048,
+    0x4049205f,
+    0x4049a079,
+    0x404a2090,
+    0x404aa0ae,
+    0x404b20c6,
+    0x404ba0f3,
+    0x404c2109,
+    0x404ca11b,
+    0x404d213c,
+    0x404da175,
+    0x404e2189,
+    0x404ea196,
+    0x404f2296,
+    0x404fa30c,
+    0x405023c8,
+    0x4050a3dc,
+    0x40512429,
+    0x40522439,
+    0x4052a469,
+    0x40532481,
+    0x4053a494,
+    0x405424a9,
+    0x4054a4cc,
+    0x405524f7,
+    0x4055a534,
+    0x40562559,
+    0x4056a572,
+    0x4057258a,
+    0x4057a59d,
+    0x405825b2,
+    0x4058a5d9,
+    0x40592608,
+    0x4059a648,
+    0x405aa65c,
+    0x405b2674,
+    0x405ba685,
+    0x405c2698,
+    0x405ca6ed,
+    0x405d26fa,
+    0x405da71f,
+    0x405e275d,
     0x405e8afe,
-    0x405f2776,
-    0x405fa783,
-    0x40602791,
-    0x4060a7b3,
-    0x40612827,
-    0x4061a85f,
-    0x40622876,
-    0x4062a887,
-    0x406328d4,
-    0x4063a8e9,
-    0x40642900,
-    0x4064a92c,
-    0x40652947,
-    0x4065a95e,
-    0x40662976,
-    0x4066a9a0,
-    0x406729cb,
-    0x4067aa10,
-    0x40682a58,
-    0x4068aa79,
-    0x40692aab,
-    0x4069aad9,
-    0x406a2afa,
-    0x406aab1a,
-    0x406b2ca2,
-    0x406bacc5,
-    0x406c2cdb,
-    0x406cafe5,
-    0x406d3014,
-    0x406db03c,
-    0x406e306a,
-    0x406eb0b7,
-    0x406f3110,
-    0x406fb148,
-    0x4070315b,
-    0x4070b178,
+    0x405f278d,
+    0x405fa79a,
+    0x406027a8,
+    0x4060a7ca,
+    0x4061283e,
+    0x4061a876,
+    0x4062288d,
+    0x4062a89e,
+    0x406328eb,
+    0x4063a900,
+    0x40642917,
+    0x4064a943,
+    0x4065295e,
+    0x4065a975,
+    0x4066298d,
+    0x4066a9b7,
+    0x406729e2,
+    0x4067aa27,
+    0x40682a6f,
+    0x4068aa90,
+    0x40692ac2,
+    0x4069aaf0,
+    0x406a2b11,
+    0x406aab31,
+    0x406b2cb9,
+    0x406bacdc,
+    0x406c2cf2,
+    0x406caffc,
+    0x406d302b,
+    0x406db053,
+    0x406e3081,
+    0x406eb0ce,
+    0x406f3127,
+    0x406fb15f,
+    0x40703172,
+    0x4070b18f,
     0x4071084d,
-    0x4071b18a,
-    0x4072319d,
-    0x4072b1d3,
-    0x407331eb,
-    0x4073966d,
-    0x407431ff,
-    0x4074b219,
-    0x4075322a,
-    0x4075b23e,
-    0x4076324c,
-    0x407693f3,
-    0x40773289,
-    0x4077b2e5,
-    0x40783300,
-    0x4078b352,
-    0x40793369,
-    0x4079b37f,
-    0x407a33ab,
-    0x407ab3be,
-    0x407b33d3,
-    0x407bb3e5,
-    0x407c3416,
-    0x407cb41f,
-    0x407d2a94,
-    0x407da31d,
-    0x407e3315,
-    0x407ea5d2,
-    0x407f1ed2,
-    0x407fa0c6,
-    0x4080228f,
-    0x40809f0b,
-    0x40812440,
-    0x4081a1cd,
-    0x40823055,
-    0x40829c4d,
-    0x408325ad,
-    0x4083a911,
-    0x40841f2f,
-    0x4084a60a,
-    0x40852692,
-    0x4085a7ee,
-    0x40862728,
-    0x4086a369,
-    0x4087309b,
-    0x4087a83c,
-    0x40881c8b,
-    0x4088aa23,
-    0x40891cda,
-    0x40899c67,
-    0x408a2d13,
-    0x408a9a85,
-    0x408b33fa,
-    0x408bb125,
-    0x408c26b8,
-    0x408d2017,
-    0x408d9f61,
-    0x408e2147,
-    0x408ea4fd,
-    0x408f2a37,
-    0x408fa80a,
-    0x409029ec,
-    0x4090a6fa,
-    0x40912cfb,
-    0x40919abd,
-    0x40921d27,
-    0x4092b0d6,
-    0x409331b6,
-    0x4093a37a,
-    0x40941f43,
-    0x4094ad2c,
-    0x40952898,
-    0x4095b38b,
-    0x40963082,
-    0x4096a2a8,
-    0x409723fa,
-    0x4097a196,
-    0x40981d87,
-    0x4098a8ac,
-    0x409930f2,
-    0x4099a52a,
-    0x409a24c3,
-    0x409a9aa1,
-    0x409b1f9d,
-    0x409b9fc8,
-    0x409c32c7,
-    0x409c9ff0,
-    0x409d2264,
-    0x409da1e3,
-    0x409e1e18,
-    0x409ea2dd,
-    0x409f22c5,
-    0x409f9f90,
-    0x40a02305,
-    0x40a0a1b0,
-    0x40a121fe,
-    0x40a1a61e,
-    0x40a22396,
-    0x40a2a767,
-    0x40a327db,
-    0x40a3b2ab,
-    0x40a423e0,
-    0x40a4a215,
-    0x40a51f1f,
-    0x40a5a337,
-    0x40a626a2,
-    0x40a6a237,
-    0x40a73271,
-    0x40a7a434,
-    0x40a82352,
-    0x40a8b339,
-    0x40a9224f,
-    0x40a99efa,
-    0x41f42bcd,
-    0x41f92c5f,
-    0x41fe2b52,
-    0x41feae08,
-    0x41ff2f36,
-    0x42032be6,
-    0x42082c08,
-    0x4208ac44,
-    0x42092b36,
-    0x4209ac7e,
-    0x420a2b8d,
-    0x420aab6d,
-    0x420b2bad,
-    0x420bac26,
-    0x420c2f52,
-    0x420cad3c,
-    0x420d2def,
-    0x420dae26,
-    0x42122e59,
-    0x42172f19,
-    0x4217ae9b,
-    0x421c2ebd,
-    0x421f2e78,
-    0x42212fca,
-    0x42262efc,
-    0x422b2fa8,
-    0x422badca,
-    0x422c2f8a,
-    0x422cad7d,
-    0x422d2d56,
-    0x422daf69,
-    0x422e2da9,
-    0x42302ed8,
-    0x4230ae40,
+    0x4071b1a1,
+    0x407231b4,
+    0x4072b1ea,
+    0x40733202,
+    0x40739684,
+    0x40743216,
+    0x4074b230,
+    0x40753241,
+    0x4075b255,
+    0x40763263,
+    0x4076940a,
+    0x407732a0,
+    0x4077b2fc,
+    0x40783317,
+    0x4078b369,
+    0x40793380,
+    0x4079b396,
+    0x407a33c2,
+    0x407ab3d5,
+    0x407b33ea,
+    0x407bb3fc,
+    0x407c342d,
+    0x407cb436,
+    0x407d2aab,
+    0x407da334,
+    0x407e332c,
+    0x407ea5e9,
+    0x407f1ee9,
+    0x407fa0dd,
+    0x408022a6,
+    0x40809f22,
+    0x40812457,
+    0x4081a1e4,
+    0x4082306c,
+    0x40829c64,
+    0x408325c4,
+    0x4083a928,
+    0x40841f46,
+    0x4084a621,
+    0x408526a9,
+    0x4085a805,
+    0x4086273f,
+    0x4086a380,
+    0x408730b2,
+    0x4087a853,
+    0x40881ca2,
+    0x4088aa3a,
+    0x40891cf1,
+    0x40899c7e,
+    0x408a2d2a,
+    0x408a9a9c,
+    0x408b3411,
+    0x408bb13c,
+    0x408c26cf,
+    0x408d202e,
+    0x408d9f78,
+    0x408e215e,
+    0x408ea514,
+    0x408f2a4e,
+    0x408fa821,
+    0x40902a03,
+    0x4090a711,
+    0x40912d12,
+    0x40919ad4,
+    0x40921d3e,
+    0x4092b0ed,
+    0x409331cd,
+    0x4093a391,
+    0x40941f5a,
+    0x4094ad43,
+    0x409528af,
+    0x4095b3a2,
+    0x40963099,
+    0x4096a2bf,
+    0x40972411,
+    0x4097a1ad,
+    0x40981d9e,
+    0x4098a8c3,
+    0x40993109,
+    0x4099a541,
+    0x409a24da,
+    0x409a9ab8,
+    0x409b1fb4,
+    0x409b9fdf,
+    0x409c32de,
+    0x409ca007,
+    0x409d227b,
+    0x409da1fa,
+    0x409e1e2f,
+    0x409ea2f4,
+    0x409f22dc,
+    0x409f9fa7,
+    0x40a0231c,
+    0x40a0a1c7,
+    0x40a12215,
+    0x40a1a635,
+    0x40a223ad,
+    0x40a2a77e,
+    0x40a327f2,
+    0x40a3b2c2,
+    0x40a423f7,
+    0x40a4a22c,
+    0x40a51f36,
+    0x40a5a34e,
+    0x40a626b9,
+    0x40a6a24e,
+    0x40a73288,
+    0x40a7a44b,
+    0x40a82369,
+    0x40a8b350,
+    0x40a92266,
+    0x40a99f11,
+    0x41f42be4,
+    0x41f92c76,
+    0x41fe2b69,
+    0x41feae1f,
+    0x41ff2f4d,
+    0x42032bfd,
+    0x42082c1f,
+    0x4208ac5b,
+    0x42092b4d,
+    0x4209ac95,
+    0x420a2ba4,
+    0x420aab84,
+    0x420b2bc4,
+    0x420bac3d,
+    0x420c2f69,
+    0x420cad53,
+    0x420d2e06,
+    0x420dae3d,
+    0x42122e70,
+    0x42172f30,
+    0x4217aeb2,
+    0x421c2ed4,
+    0x421f2e8f,
+    0x42212fe1,
+    0x42262f13,
+    0x422b2fbf,
+    0x422bade1,
+    0x422c2fa1,
+    0x422cad94,
+    0x422d2d6d,
+    0x422daf80,
+    0x422e2dc0,
+    0x42302eef,
+    0x4230ae57,
     0x44320778,
     0x44328787,
     0x44330793,
@@ -680,109 +681,109 @@
     0x4439084d,
     0x4439885b,
     0x443a086e,
-    0x48321443,
-    0x48329455,
-    0x4833146b,
-    0x48339484,
-    0x4c3214c1,
-    0x4c3294d1,
-    0x4c3314e4,
-    0x4c339504,
+    0x4832145a,
+    0x4832946c,
+    0x48331482,
+    0x4833949b,
+    0x4c3214d8,
+    0x4c3294e8,
+    0x4c3314fb,
+    0x4c33951b,
     0x4c3400b9,
     0x4c3480f7,
-    0x4c351510,
-    0x4c35951e,
-    0x4c36153a,
-    0x4c369560,
-    0x4c37156f,
-    0x4c37957d,
-    0x4c381592,
-    0x4c38959e,
-    0x4c3915be,
-    0x4c3995e8,
-    0x4c3a1601,
-    0x4c3a961a,
+    0x4c351527,
+    0x4c359535,
+    0x4c361551,
+    0x4c369577,
+    0x4c371586,
+    0x4c379594,
+    0x4c3815a9,
+    0x4c3895b5,
+    0x4c3915d5,
+    0x4c3995ff,
+    0x4c3a1618,
+    0x4c3a9631,
     0x4c3b0635,
-    0x4c3b9633,
-    0x4c3c1645,
-    0x4c3c9654,
-    0x4c3d166d,
+    0x4c3b964a,
+    0x4c3c165c,
+    0x4c3c966b,
+    0x4c3d1684,
     0x4c3d8d06,
-    0x4c3e16da,
-    0x4c3e967c,
-    0x4c3f16fc,
-    0x4c3f93f3,
-    0x4c401692,
-    0x4c4094ad,
-    0x4c4116ca,
-    0x4c41954d,
-    0x4c4216b6,
-    0x4c429495,
-    0x50323801,
-    0x5032b810,
-    0x5033381b,
-    0x5033b82b,
-    0x50343844,
-    0x5034b85e,
-    0x5035386c,
-    0x5035b882,
-    0x50363894,
-    0x5036b8aa,
-    0x503738c3,
-    0x5037b8d6,
-    0x503838ee,
-    0x5038b8ff,
-    0x50393914,
-    0x5039b928,
-    0x503a3948,
-    0x503ab95e,
-    0x503b3976,
-    0x503bb988,
-    0x503c39a4,
-    0x503cb9bb,
-    0x503d39d4,
-    0x503db9ea,
-    0x503e39f7,
-    0x503eba0d,
-    0x503f3a1f,
+    0x4c3e16f1,
+    0x4c3e9693,
+    0x4c3f1713,
+    0x4c3f940a,
+    0x4c4016a9,
+    0x4c4094c4,
+    0x4c4116e1,
+    0x4c419564,
+    0x4c4216cd,
+    0x4c4294ac,
+    0x50323818,
+    0x5032b827,
+    0x50333832,
+    0x5033b842,
+    0x5034385b,
+    0x5034b875,
+    0x50353883,
+    0x5035b899,
+    0x503638ab,
+    0x5036b8c1,
+    0x503738da,
+    0x5037b8ed,
+    0x50383905,
+    0x5038b916,
+    0x5039392b,
+    0x5039b93f,
+    0x503a395f,
+    0x503ab975,
+    0x503b398d,
+    0x503bb99f,
+    0x503c39bb,
+    0x503cb9d2,
+    0x503d39eb,
+    0x503dba01,
+    0x503e3a0e,
+    0x503eba24,
+    0x503f3a36,
     0x503f83b3,
-    0x50403a32,
-    0x5040ba42,
-    0x50413a5c,
-    0x5041ba6b,
-    0x50423a85,
-    0x5042baa2,
-    0x50433ab2,
-    0x5043bac2,
-    0x50443adf,
+    0x50403a49,
+    0x5040ba59,
+    0x50413a73,
+    0x5041ba82,
+    0x50423a9c,
+    0x5042bab9,
+    0x50433ac9,
+    0x5043bad9,
+    0x50443af6,
     0x50448469,
-    0x50453af3,
-    0x5045bb11,
-    0x50463b24,
-    0x5046bb3a,
-    0x50473b4c,
-    0x5047bb61,
-    0x50483b87,
-    0x5048bb95,
-    0x50493ba8,
-    0x5049bbbd,
-    0x504a3bd3,
-    0x504abbe3,
-    0x504b3c03,
-    0x504bbc16,
-    0x504c3c39,
-    0x504cbc67,
-    0x504d3c94,
-    0x504dbcb1,
-    0x504e3ccc,
-    0x504ebce8,
-    0x504f3cfa,
-    0x504fbd11,
-    0x50503d20,
+    0x50453b0a,
+    0x5045bb28,
+    0x50463b3b,
+    0x5046bb51,
+    0x50473b63,
+    0x5047bb78,
+    0x50483b9e,
+    0x5048bbac,
+    0x50493bbf,
+    0x5049bbd4,
+    0x504a3bea,
+    0x504abbfa,
+    0x504b3c1a,
+    0x504bbc2d,
+    0x504c3c50,
+    0x504cbc7e,
+    0x504d3cab,
+    0x504dbcc8,
+    0x504e3ce3,
+    0x504ebcff,
+    0x504f3d11,
+    0x504fbd28,
+    0x50503d37,
     0x50508729,
-    0x50513d33,
-    0x5051bad1,
-    0x50523c79,
+    0x50513d4a,
+    0x5051bae8,
+    0x50523c90,
     0x58321011,
     0x68320fd3,
     0x68328d2b,
@@ -824,22 +825,22 @@
     0x783d8b97,
     0x783e0aed,
     0x783e8a9f,
-    0x7c32130c,
-    0x80321560,
+    0x7c321323,
+    0x80321577,
     0x80328090,
-    0x803334dc,
+    0x803334f3,
     0x803380b9,
-    0x803434eb,
-    0x8034b453,
-    0x80353471,
-    0x8035b4ff,
-    0x803634b3,
-    0x8036b462,
-    0x803734a5,
-    0x8037b440,
-    0x803834c6,
-    0x8038b482,
-    0x80393497,
+    0x80343502,
+    0x8034b46a,
+    0x80353488,
+    0x8035b516,
+    0x803634ca,
+    0x8036b479,
+    0x803734bc,
+    0x8037b457,
+    0x803834dd,
+    0x8038b499,
+    0x803934ae,
     0x84320bb0,
     0x84328bc9,
 };
@@ -1076,6 +1077,7 @@
     "INVALID_CIPHERTEXT_LENGTH\0"
     "INVALID_DIGEST_LENGTH\0"
     "INVALID_DIGEST_TYPE\0"
+    "INVALID_ENTROPY_LENGTH\0"
     "INVALID_KEYBITS\0"
     "INVALID_MGF1_MD\0"
     "INVALID_PADDING_MODE\0"
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index 6e310ec..8f3234f 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -1280,6 +1280,12 @@
 // secret produced and consumed by `kem`.
 OPENSSL_EXPORT size_t EVP_KEM_secret_len(const EVP_KEM *kem);
 
+// EVP_KEM_entropy_len_for_testing returns the fixed length, in bytes, of the
+// external entropy to provide to `EVP_KEM_encap_external_entropy_for_testing`.
+//
+// WARNING: There should be no reason to use this function in production.
+OPENSSL_EXPORT size_t EVP_KEM_entropy_len_for_testing(const EVP_KEM *kem);
+
 // EVP_KEM_encap uses `kem` to encapsulate a `peer_key`. It outputs a
 // ciphertext of length `ciphertext_len` into `*out_ciphertext` and outputs a
 // shared secret of length `secret_len` into `*out_secret`. `peer_key` must be
@@ -1291,6 +1297,20 @@
                                  size_t ciphertext_len, uint8_t *out_secret,
                                  size_t secret_len, const EVP_PKEY *peer_key);
 
+// EVP_KEM_encap_external_entropy_for_testing behaves like `EVP_KEM_encap`, but
+// takes `entropy_len` bytes of external `entropy` to behave deterministically
+// for testing. The format of `entropy` depends on `kem`. `entropy_len` must
+// match the output of `EVP_KEM_entropy_len_for_testing` when called with `kem`.
+//
+// WARNING: This function is only exported to help callers in testing systems
+// against fixed test vectors. It must only be used for this purpose, and not in
+// production. If `entropy` is predictable or known to an attacker, security
+// properties of the KEM will not hold.
+OPENSSL_EXPORT int EVP_KEM_encap_external_entropy_for_testing(
+    const EVP_KEM *kem, uint8_t *out_ciphertext, size_t ciphertext_len,
+    uint8_t *out_secret, size_t secret_len, const EVP_PKEY *peer_key,
+    const uint8_t *entropy, size_t entropy_len);
+
 // EVP_KEM_decap uses `kem` to decapsulate a `ciphertext` of length
 // `ciphertext_len`, using `key` as a decapsulation key. It outputs a shared
 // secret of length `secret_len` into `*out_secret`. `key` must be a private key
diff --git a/include/openssl/evp_errors.h b/include/openssl/evp_errors.h
index 77b7ab5..ebb72cd 100644
--- a/include/openssl/evp_errors.h
+++ b/include/openssl/evp_errors.h
@@ -58,6 +58,7 @@
 #define EVP_R_MISSING_PUBLIC_KEY 140
 #define EVP_R_INVALID_CIPHERTEXT_LENGTH 141
 #define EVP_R_INVALID_SECRET_LENGTH 142
+#define EVP_R_INVALID_ENTROPY_LENGTH 143
 
 // Compatibility defines.
 #define EVP_R_OPERATON_NOT_INITIALIZED EVP_R_OPERATION_NOT_INITIALIZED
diff --git a/include/openssl/prefix_symbols.h b/include/openssl/prefix_symbols.h
index e3211cb..aa82789 100644
--- a/include/openssl/prefix_symbols.h
+++ b/include/openssl/prefix_symbols.h
@@ -1079,6 +1079,8 @@
 #pragma redefine_extname EVP_KEM_ciphertext_len BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_KEM_ciphertext_len)
 #pragma redefine_extname EVP_KEM_decap BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_KEM_decap)
 #pragma redefine_extname EVP_KEM_encap BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_KEM_encap)
+#pragma redefine_extname EVP_KEM_encap_external_entropy_for_testing BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_KEM_encap_external_entropy_for_testing)
+#pragma redefine_extname EVP_KEM_entropy_len_for_testing BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_KEM_entropy_len_for_testing)
 #pragma redefine_extname EVP_KEM_secret_len BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_KEM_secret_len)
 #pragma redefine_extname EVP_MD_CTX_block_size BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_MD_CTX_block_size)
 #pragma redefine_extname EVP_MD_CTX_cleanse BORINGSSL_ADD_USER_LABEL_AND_PREFIX(EVP_MD_CTX_cleanse)
@@ -4222,6 +4224,8 @@
 #define EVP_KEM_ciphertext_len BORINGSSL_ADD_PREFIX(EVP_KEM_ciphertext_len)
 #define EVP_KEM_decap BORINGSSL_ADD_PREFIX(EVP_KEM_decap)
 #define EVP_KEM_encap BORINGSSL_ADD_PREFIX(EVP_KEM_encap)
+#define EVP_KEM_encap_external_entropy_for_testing BORINGSSL_ADD_PREFIX(EVP_KEM_encap_external_entropy_for_testing)
+#define EVP_KEM_entropy_len_for_testing BORINGSSL_ADD_PREFIX(EVP_KEM_entropy_len_for_testing)
 #define EVP_KEM_secret_len BORINGSSL_ADD_PREFIX(EVP_KEM_secret_len)
 #define EVP_MD_CTX_block_size BORINGSSL_ADD_PREFIX(EVP_MD_CTX_block_size)
 #define EVP_MD_CTX_cleanse BORINGSSL_ADD_PREFIX(EVP_MD_CTX_cleanse)