static void hash_reduce(u8 h[32],
const u8 *a, size_t a_size,
const u8 *b, size_t b_size,
- const u8 *c, size_t c_size)
+ const u8 *c, size_t c_size,
+ const u8 *d, size_t d_size)
{
u8 hash[64];
crypto_sha512_ctx ctx;
crypto_sha512_update(&ctx, a, a_size);
crypto_sha512_update(&ctx, b, b_size);
crypto_sha512_update(&ctx, c, c_size);
+ crypto_sha512_update(&ctx, d, d_size);
crypto_sha512_final (&ctx, hash);
crypto_eddsa_reduce(h, hash);
}
-void crypto_ed25519_sign(u8 signature [64], const u8 secret_key[32],
- const u8 *message, size_t message_size)
+static void ed25519_dom_sign(u8 signature [64], const u8 secret_key[32],
+ const u8 *dom, size_t dom_size,
+ const u8 *message, size_t message_size)
{
u8 a[64]; // secret scalar and prefix
u8 r[32]; // secret deterministic "random" nonce
u8 h[32]; // publically verifiable hash of the message (not wiped)
u8 R[32]; // first half of the signature (allows overlapping inputs)
+ const u8 *pk = secret_key + 32;
crypto_sha512(a, secret_key, 32);
crypto_eddsa_trim_scalar(a, a);
- hash_reduce(r, a + 32, 32, message, message_size, 0, 0);
+ hash_reduce(r, dom, dom_size, a + 32, 32, message, message_size, 0, 0);
crypto_eddsa_scalarbase(R, r);
- hash_reduce(h, R, 32, secret_key + 32, 32, message, message_size);
+ hash_reduce(h, dom, dom_size, R, 32, pk, 32, message, message_size);
COPY(signature, R, 32);
crypto_eddsa_mul_add(signature + 32, h, a, r);
WIPE_BUFFER(r);
}
-int crypto_ed25519_check(const u8 signature[64], const u8 public_key[32],
+void crypto_ed25519_sign(u8 signature [64], const u8 secret_key[32],
const u8 *message, size_t message_size)
{
- u8 h_ram [32];
- hash_reduce(h_ram, signature, 32, public_key, 32, message, message_size);
+ ed25519_dom_sign(signature, secret_key, 0, 0, message, message_size);
+}
+
+int crypto_ed25519_check(const u8 signature[64], const u8 public_key[32],
+ const u8 *msg, size_t msg_size)
+{
+ u8 h_ram[32];
+ hash_reduce(h_ram, signature, 32, public_key, 32, msg, msg_size, 0, 0);
return crypto_eddsa_check_equation(signature, public_key, h_ram);
}
+static const u8 domain[34] = "SigEd25519 no Ed25519 collisions\1\0";
+
+void crypto_ed25519_ph_sign(uint8_t signature[64], const uint8_t secret_key[32],
+ const uint8_t message_hash[64])
+{
+ ed25519_dom_sign(signature, secret_key, domain, sizeof(domain),
+ message_hash, 64);
+}
+
+int crypto_ed25519_ph_check(const uint8_t sig[64], const uint8_t pk[32],
+ const uint8_t msg_hash[64])
+{
+ u8 h_ram[32];
+ hash_reduce(h_ram, domain, sizeof(domain), sig, 32, pk, 32, msg_hash, 64);
+ return crypto_eddsa_check_equation(sig, pk, h_ram);
+}
+
+
#ifdef MONOCYPHER_CPP_NAMESPACE
}
#endif
msg.buf, msg.size);
}
+static void ed_25519ph(vector_reader *reader)
+{
+ vector sk = next_input(reader);
+ vector pk = next_input(reader);
+ vector msg = next_input(reader);
+ vector out = next_output(reader);
+
+ // Test that we generate the correct public key
+ uint8_t secret_key[64];
+ uint8_t public_key[32];
+ crypto_ed25519_key_pair(secret_key, public_key, sk.buf);
+ ASSERT_EQUAL(public_key, pk.buf, 32);
+ ASSERT_EQUAL(public_key, secret_key + 32, 32);
+
+ // Generate output signature for comparison
+ uint8_t digest[64];
+ crypto_sha512(digest, msg.buf, msg.size);
+ crypto_ed25519_ph_sign(out.buf, secret_key, digest);
+
+ // Test that the correct signature is accepted
+ ASSERT_OK(crypto_ed25519_ph_check(out.buf, pk.buf, digest));
+
+ // Test that corrupted signatures are rejected
+ for (size_t i = 0; i < 64; i++) {
+ uint8_t corrupt_signature[64];
+ memcpy(corrupt_signature, out.buf, 64);
+ corrupt_signature[i] ^= 1; // corrupt one bit
+ ASSERT_KO(crypto_ed25519_ph_check(corrupt_signature, pk.buf, digest));
+ }
+}
+
static void test_ed25519()
{
VECTORS(ed_25519);
VECTORS(ed_25519_pk);
VECTORS(ed_25519_check);
+ VECTORS(ed_25519ph);
}
/////////////////