Repository: randombit/botan
Pull request #2000 (2019-06-21 00:20:38)

Add support for RFC 8032 Ed25519ph
GH #1699


Patch:
diff --git a/src/lib/compat/sodium/sodium_25519.cpp b/src/lib/compat/sodium/sodium_25519.cpp
index 1d520d7a3..e8cce34e5 100644
--- a/src/lib/compat/sodium/sodium_25519.cpp
+++ b/src/lib/compat/sodium/sodium_25519.cpp
@@ -27,7 +27,7 @@ int Sodium::crypto_sign_ed25519_detached(uint8_t sig[],
                                          size_t msg_len,
                                          const uint8_t sk[32])
    {
-   ed25519_sign(sig, msg, msg_len, sk);
+   ed25519_sign(sig, msg, msg_len, sk, nullptr, 0);
 
    if(sig_len)
       *sig_len = 64;
@@ -39,7 +39,7 @@ int Sodium::crypto_sign_ed25519_verify_detached(const uint8_t sig[64],
                                                 size_t msg_len,
                                                 const uint8_t pk[32])
    {
-   const bool ok = ed25519_verify(msg, msg_len, sig, pk);
+   const bool ok = ed25519_verify(msg, msg_len, sig, pk, nullptr, 0);
    return ok ? 0 : -1;
    }
 
diff --git a/src/lib/pubkey/ed25519/ed25519.cpp b/src/lib/pubkey/ed25519/ed25519.cpp
index b75028041..624f82657 100644
--- a/src/lib/pubkey/ed25519/ed25519.cpp
+++ b/src/lib/pubkey/ed25519/ed25519.cpp
@@ -34,8 +34,9 @@ void ed25519_gen_keypair(uint8_t* pk, uint8_t* sk, const uint8_t seed[32])
    }
 
 void ed25519_sign(uint8_t sig[64],
-                  const uint8_t* m, size_t mlen,
-                  const uint8_t* sk)
+                  const uint8_t m[], size_t mlen,
+                  const uint8_t sk[64],
+                  const uint8_t domain_sep[], size_t domain_sep_len)
    {
    uint8_t az[64];
    uint8_t nonce[64];
@@ -49,6 +50,7 @@ void ed25519_sign(uint8_t sig[64],
    az[31] &= 63;
    az[31] |= 64;
 
+   sha.update(domain_sep, domain_sep_len);
    sha.update(az + 32, 32);
    sha.update(m, mlen);
    sha.final(nonce);
@@ -56,6 +58,7 @@ void ed25519_sign(uint8_t sig[64],
    sc_reduce(nonce);
    ge_scalarmult_base(sig, nonce);
 
+   sha.update(domain_sep, domain_sep_len);
    sha.update(sig, 32);
    sha.update(sk + 32, 32);
    sha.update(m, mlen);
@@ -67,7 +70,8 @@ void ed25519_sign(uint8_t sig[64],
 
 bool ed25519_verify(const uint8_t* m, size_t mlen,
                     const uint8_t sig[64],
-                    const uint8_t* pk)
+                    const uint8_t* pk,
+                    const uint8_t domain_sep[], size_t domain_sep_len)
    {
    uint8_t h[64];
    uint8_t rcheck[32];
@@ -83,6 +87,7 @@ bool ed25519_verify(const uint8_t* m, size_t mlen,
       return false;
       }
 
+   sha.update(domain_sep, domain_sep_len);
    sha.update(sig, 32);
    sha.update(pk, 32);
    sha.update(m, mlen);
diff --git a/src/lib/pubkey/ed25519/ed25519.h b/src/lib/pubkey/ed25519/ed25519.h
index 51c75e641..97ed023f2 100644
--- a/src/lib/pubkey/ed25519/ed25519.h
+++ b/src/lib/pubkey/ed25519/ed25519.h
@@ -99,12 +99,14 @@ void ed25519_gen_keypair(uint8_t pk[32], uint8_t sk[64], const uint8_t seed[32])
 void ed25519_sign(uint8_t sig[64],
                   const uint8_t msg[],
                   size_t msg_len,
-                  const uint8_t sk[64]);
+                  const uint8_t sk[64],
+                  const uint8_t domain_sep[], size_t domain_sep_len);
 
 bool ed25519_verify(const uint8_t msg[],
                     size_t msg_len,
                     const uint8_t sig[64],
-                    const uint8_t pk[32]);
+                    const uint8_t pk[32],
+                    const uint8_t domain_sep[], size_t domain_sep_len);
 
 }
 
diff --git a/src/lib/pubkey/ed25519/ed25519_key.cpp b/src/lib/pubkey/ed25519/ed25519_key.cpp
index 1de24aa26..c4b260c4c 100644
--- a/src/lib/pubkey/ed25519/ed25519_key.cpp
+++ b/src/lib/pubkey/ed25519/ed25519_key.cpp
@@ -122,7 +122,7 @@ class Ed25519_Pure_Verify_Operation final : public PK_Ops::Verification
 
          const std::vector<uint8_t>& pub_key = m_key.get_public_key();
          BOTAN_ASSERT_EQUAL(pub_key.size(), 32, "Expected size");
-         const bool ok = ed25519_verify(m_msg.data(), m_msg.size(), sig, pub_key.data());
+         const bool ok = ed25519_verify(m_msg.data(), m_msg.size(), sig, pub_key.data(), nullptr, 0);
          m_msg.clear();
          return ok;
          }
@@ -138,9 +138,18 @@ class Ed25519_Pure_Verify_Operation final : public PK_Ops::Verification
 class Ed25519_Hashed_Verify_Operation final : public PK_Ops::Verification
    {
    public:
-      Ed25519_Hashed_Verify_Operation(const Ed25519_PublicKey& key, const std::string& hash) : m_key(key)
+      Ed25519_Hashed_Verify_Operation(const Ed25519_PublicKey& key, const std::string& hash, bool rfc8032) :
+         m_key(key)
          {
          m_hash = HashFunction::create_or_throw(hash);
+
+         if(rfc8032)
+            {
+            m_domain_sep = {
+               0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6E, 0x6F, 0x20, 0x45, 0x64,
+               0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6F, 0x6C, 0x6C, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x73,
+               0x01, 0x00 };
+            }
          }
 
       void update(const uint8_t msg[], size_t msg_len) override
@@ -157,12 +166,13 @@ class Ed25519_Hashed_Verify_Operation final : public PK_Ops::Verification
 
          const std::vector<uint8_t>& pub_key = m_key.get_public_key();
          BOTAN_ASSERT_EQUAL(pub_key.size(), 32, "Expected size");
-         return ed25519_verify(msg_hash.data(), msg_hash.size(), sig, pub_key.data());
+         return ed25519_verify(msg_hash.data(), msg_hash.size(), sig, pub_key.data(), m_domain_sep.data(), m_domain_sep.size());
          }
 
    private:
       std::unique_ptr<HashFunction> m_hash;
       const Ed25519_PublicKey& m_key;
+      std::vector<uint8_t> m_domain_sep;
    };
 
 /**
@@ -183,7 +193,7 @@ class Ed25519_Pure_Sign_Operation final : public PK_Ops::Signature
       secure_vector<uint8_t> sign(RandomNumberGenerator&) override
          {
          secure_vector<uint8_t> sig(64);
-         ed25519_sign(sig.data(), m_msg.data(), m_msg.size(), m_key.get_private_key().data());
+         ed25519_sign(sig.data(), m_msg.data(), m_msg.size(), m_key.get_private_key().data(), nullptr, 0);
          m_msg.clear();
          return sig;
          }
@@ -201,9 +211,18 @@ class Ed25519_Pure_Sign_Operation final : public PK_Ops::Signature
 class Ed25519_Hashed_Sign_Operation final : public PK_Ops::Signature
    {
    public:
-      Ed25519_Hashed_Sign_Operation(const Ed25519_PrivateKey& key, const std::string& hash) : m_key(key)
+      Ed25519_Hashed_Sign_Operation(const Ed25519_PrivateKey& key, const std::string& hash, bool rfc8032) :
+         m_key(key)
          {
          m_hash = HashFunction::create_or_throw(hash);
+
+         if(rfc8032)
+            {
+            m_domain_sep = std::vector<uint8_t>{
+               0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6E, 0x6F, 0x20, 0x45, 0x64,
+               0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6F, 0x6C, 0x6C, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x73,
+               0x01, 0x00 };
+            }
          }
 
       size_t signature_length() const override { return 64; }
@@ -218,13 +237,17 @@ class Ed25519_Hashed_Sign_Operation final : public PK_Ops::Signature
          secure_vector<uint8_t> sig(64);
          std::vector<uint8_t> msg_hash(m_hash->output_length());
          m_hash->final(msg_hash.data());
-         ed25519_sign(sig.data(), msg_hash.data(), msg_hash.size(), m_key.get_private_key().data());
+         ed25519_sign(sig.data(),
+                      msg_hash.data(), msg_hash.size(),
+                      m_key.get_private_key().data(),
+                      m_domain_sep.data(), m_domain_sep.size());
          return sig;
          }
 
    private:
       std::unique_ptr<HashFunction> m_hash;
       const Ed25519_PrivateKey& m_key;
+      std::vector<uint8_t> m_domain_sep;
    };
 
 }
@@ -237,8 +260,10 @@ Ed25519_PublicKey::create_verification_op(const std::string& params,
       {
       if(params == "" || params == "Identity" || params == "Pure")
          return std::unique_ptr<PK_Ops::Verification>(new Ed25519_Pure_Verify_Operation(*this));
+      else if(params == "Ed25519ph")
+         return std::unique_ptr<PK_Ops::Verification>(new Ed25519_Hashed_Verify_Operation(*this, "SHA-512", true));
       else
-         return std::unique_ptr<PK_Ops::Verification>(new Ed25519_Hashed_Verify_Operation(*this, params));
+         return std::unique_ptr<PK_Ops::Verification>(new Ed25519_Hashed_Verify_Operation(*this, params, false));
       }
    throw Provider_Not_Found(algo_name(), provider);
    }
@@ -252,8 +277,10 @@ Ed25519_PrivateKey::create_signature_op(RandomNumberGenerator&,
       {
       if(params == "" || params == "Identity" || params == "Pure")
          return std::unique_ptr<PK_Ops::Signature>(new Ed25519_Pure_Sign_Operation(*this));
+      else if(params == "Ed25519ph")
+         return std::unique_ptr<PK_Ops::Signature>(new Ed25519_Hashed_Sign_Operation(*this, "SHA-512", true));
       else
-         return std::unique_ptr<PK_Ops::Signature>(new Ed25519_Hashed_Sign_Operation(*this, params));
+         return std::unique_ptr<PK_Ops::Signature>(new Ed25519_Hashed_Sign_Operation(*this, params, false));
       }
    throw Provider_Not_Found(algo_name(), provider);
    }


Test patch:
diff --git a/src/tests/data/pubkey/ed25519.vec b/src/tests/data/pubkey/ed25519.vec
index da328259b..aa56015f5 100644
--- a/src/tests/data/pubkey/ed25519.vec
+++ b/src/tests/data/pubkey/ed25519.vec
@@ -3306,8 +3306,16 @@ Pubkey = 3f098994bdd916ed4053197934e4a87c80733a1280d62f8010992e43ee3b2406
 Msg = f6220a3f757814f4c2176ffbb68b00249cd4ccdc059c4b34ad871f30b1740280
 Signature = 56f90cca98e2102637bd983fdb16c131dfd27ed82bf4dde5606e0d756aed3366d09c4fa11527f038e0f57f2201d82f2ea2c9033265fa6ceb489e854bae61b404
 
+[Ed25519ph]
+# From RFC 8032 section 7.3
+Privkey = 833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42
+Pubkey = ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf
+Msg = 616263
+Signature = 98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406
+
 # From Wycheproof
 
+[Pure]
 # Test 1
 Privkey = add4bb8103785baf9ac534258e8aaf65f5f1adb5ef5f3df19bb80ab989c4d64b
 Pubkey = 7d4d0e7f6153a69b6242b522abbee685fda4420f8834b108c3bdae369ef549fa
diff --git a/src/tests/test_pubkey.cpp b/src/tests/test_pubkey.cpp
index 80b95b70b..3d2526a78 100644
--- a/src/tests/test_pubkey.cpp
+++ b/src/tests/test_pubkey.cpp
@@ -142,12 +142,6 @@ PK_Signature_Generation_Test::run_one_test(const std::string& pad_hdr, const Var
 
    for(auto const& sign_provider : possible_providers(algo_name()))
       {
-      std::unique_ptr<Botan::RandomNumberGenerator> rng;
-      if(vars.has_key("Nonce"))
-         {
-         rng.reset(test_rng(vars.get_req_bin("Nonce")));
-         }
-
       std::unique_ptr<Botan::PK_Signer> signer;
 
       std::vector<uint8_t> generated_signature;
@@ -156,7 +150,15 @@ PK_Signature_Generation_Test::run_one_test(const std::string& pad_hdr, const Var
          {
          signer.reset(new Botan::PK_Signer(*privkey, Test::rng(), padding, Botan::IEEE_1363, sign_provider));
 
-         generated_signature = signer->sign_message(message, rng ? *rng : Test::rng());
+         if(vars.has_key("Nonce"))
+            {
+            std::unique_ptr<Botan::RandomNumberGenerator> rng(test_rng(vars.get_req_bin("Nonce")));
+            generated_signature = signer->sign_message(message, *rng);
+            }
+         else
+            {
+            generated_signature = signer->sign_message(message, Test::rng());
+            }
 
          result.test_lte("Generated signature within announced bound",
                          generated_signature.size(), signer->signature_length());