Support all five EdDSA instances from RFC 8032
[openssl.git] / crypto / ec / curve25519.c
index 286d6bff80749b15217ef833f6120b226fad16f4..4f033d74d0e5e5b10186a400d23786cbe332e2d7 100644 (file)
@@ -5434,9 +5434,47 @@ static void sc_muladd(uint8_t *s, const uint8_t *a, const uint8_t *b,
     s[31] = (uint8_t) (s11 >> 17);
 }
 
+static int hash_init_with_dom(EVP_MD_CTX *hash_ctx,
+                              EVP_MD *sha512,
+                              const uint8_t dom2flag,
+                              const uint8_t phflag,
+                              const uint8_t *context,
+                              const size_t context_len)
+{
+    /* ASCII: "SigEd25519 no Ed25519 collisions", in hex for EBCDIC compatibility */
+    const char dom_s[] =
+            "\x53\x69\x67\x45\x64\x32\x35\x35\x31\x39\x20\x6e"
+            "\x6f\x20\x45\x64\x32\x35\x35\x31\x39\x20\x63\x6f"
+            "\x6c\x6c\x69\x73\x69\x6f\x6e\x73";
+    uint8_t dom[2];
+
+    if (!EVP_DigestInit_ex(hash_ctx, sha512, NULL))
+        return 0;
+
+    /* return early if dom2flag is not set */
+    if (!dom2flag)
+        return 1;
+
+    if (context_len > UINT8_MAX)
+        return 0;
+
+    dom[0] = (uint8_t)(phflag >= 1 ? 1 : 0);
+    dom[1] = (uint8_t)context_len;
+
+    if (!EVP_DigestUpdate(hash_ctx, dom_s, sizeof(dom_s)-1)
+        || !EVP_DigestUpdate(hash_ctx, dom, sizeof(dom))
+        || !EVP_DigestUpdate(hash_ctx, context, context_len)) {
+        return 0;
+    }
+
+    return 1;
+}
+
 int
-ossl_ed25519_sign(uint8_t *out_sig, const uint8_t *message, size_t message_len,
+ossl_ed25519_sign(uint8_t *out_sig, const uint8_t *tbs, size_t tbs_len,
                   const uint8_t public_key[32], const uint8_t private_key[32],
+                  const uint8_t dom2flag, const uint8_t phflag, const uint8_t csflag,
+                  const uint8_t *context, size_t context_len,
                   OSSL_LIB_CTX *libctx, const char *propq)
 {
     uint8_t az[SHA512_DIGEST_LENGTH];
@@ -5448,6 +5486,17 @@ ossl_ed25519_sign(uint8_t *out_sig, const uint8_t *message, size_t message_len,
     unsigned int sz;
     int res = 0;
 
+    if (context == NULL)
+        context_len = 0;
+
+    /* if csflag is set, then a non-empty context-string is required */
+    if (csflag && context_len == 0)
+        goto err;
+
+    /* if dom2flag is not set, then an empty context-string is required */
+    if (!dom2flag && context_len > 0)
+        goto err;
+
     if (sha512 == NULL || hash_ctx == NULL)
         goto err;
 
@@ -5460,9 +5509,9 @@ ossl_ed25519_sign(uint8_t *out_sig, const uint8_t *message, size_t message_len,
     az[31] &= 63;
     az[31] |= 64;
 
-    if (!EVP_DigestInit_ex(hash_ctx, sha512, NULL)
+    if (!hash_init_with_dom(hash_ctx, sha512, dom2flag, phflag, context, context_len)
         || !EVP_DigestUpdate(hash_ctx, az + 32, 32)
-        || !EVP_DigestUpdate(hash_ctx, message, message_len)
+        || !EVP_DigestUpdate(hash_ctx, tbs, tbs_len)
         || !EVP_DigestFinal_ex(hash_ctx, nonce, &sz))
         goto err;
 
@@ -5470,10 +5519,10 @@ ossl_ed25519_sign(uint8_t *out_sig, const uint8_t *message, size_t message_len,
     ge_scalarmult_base(&R, nonce);
     ge_p3_tobytes(out_sig, &R);
 
-    if (!EVP_DigestInit_ex(hash_ctx, sha512, NULL)
+    if (!hash_init_with_dom(hash_ctx, sha512, dom2flag, phflag, context, context_len)
         || !EVP_DigestUpdate(hash_ctx, out_sig, 32)
         || !EVP_DigestUpdate(hash_ctx, public_key, 32)
-        || !EVP_DigestUpdate(hash_ctx, message, message_len)
+        || !EVP_DigestUpdate(hash_ctx, tbs, tbs_len)
         || !EVP_DigestFinal_ex(hash_ctx, hram, &sz))
         goto err;
 
@@ -5492,8 +5541,10 @@ err:
 static const char allzeroes[15];
 
 int
-ossl_ed25519_verify(const uint8_t *message, size_t message_len,
+ossl_ed25519_verify(const uint8_t *tbs, size_t tbs_len,
                     const uint8_t signature[64], const uint8_t public_key[32],
+                    const uint8_t dom2flag, const uint8_t phflag, const uint8_t csflag,
+                    const uint8_t *context, size_t context_len,
                     OSSL_LIB_CTX *libctx, const char *propq)
 {
     int i;
@@ -5512,6 +5563,17 @@ ossl_ed25519_verify(const uint8_t *message, size_t message_len,
         0xDE, 0xF9, 0xDE, 0x14
     };
 
+    if (context == NULL)
+        context_len = 0;
+
+    /* if csflag is set, then a non-empty context-string is required */
+    if (csflag && context_len == 0)
+        return 0;
+
+    /* if dom2flag is not set, then an empty context-string is required */
+    if (!dom2flag && context_len > 0)
+        return 0;
+
     r = signature;
     s = signature + 32;
 
@@ -5556,10 +5618,10 @@ ossl_ed25519_verify(const uint8_t *message, size_t message_len,
     if (hash_ctx == NULL)
         goto err;
 
-    if (!EVP_DigestInit_ex(hash_ctx, sha512, NULL)
+    if (!hash_init_with_dom(hash_ctx, sha512, dom2flag, phflag, context, context_len)
         || !EVP_DigestUpdate(hash_ctx, r, 32)
         || !EVP_DigestUpdate(hash_ctx, public_key, 32)
-        || !EVP_DigestUpdate(hash_ctx, message, message_len)
+        || !EVP_DigestUpdate(hash_ctx, tbs, tbs_len)
         || !EVP_DigestFinal_ex(hash_ctx, h, &sz))
         goto err;
 
@@ -5570,6 +5632,14 @@ ossl_ed25519_verify(const uint8_t *message, size_t message_len,
     ge_tobytes(rcheck, &R);
 
     res = CRYPTO_memcmp(rcheck, r, sizeof(rcheck)) == 0;
+
+    /* note that we have used the strict verification equation here.
+     * we checked that  ENC( [h](-A) + [s]B ) == r
+     * B is the base point.
+     *
+     * the less strict verification equation uses the curve cofactor:
+     *          [h*8](-A) + [s*8]B == [8]R
+     */
 err:
     EVP_MD_free(sha512);
     EVP_MD_CTX_free(hash_ctx);