Skip to content

Commit 1d14404

Browse files
committed
Add support for PreHash ML-DSA
This commit adds two functions crypto_sign_signature_pre_hash and crypto_sign_verify_pre_hash implementing the pre-hashing mode of ML-DSA. Instead of receiving a message, they receive a pre-hashed message. Details can be found in Algorithm 4 and 5 in FIPS204. The message to signed is formatted as 0x01 || ctxlen (1 byte) || ctx || oid || ph where ph is the pre-hash and oid is the object identifier of the used hash algorithm. The ACVP client is adjusted to support the pre-hashing test cases. Note that the ACVP testvectors only contain the message, not the pre-hash. I opted for computing the hash in the ACVP Python client as for that we do not have to add implementations for the 12 hash functions. CBMC proofs for the new functions are added. Resolves #39 Signed-off-by: Matthias J. Kannwischer <[email protected]>
1 parent 122dfaa commit 1d14404

File tree

8 files changed

+805
-14
lines changed

8 files changed

+805
-14
lines changed

mldsa/sign.c

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,3 +835,197 @@ int crypto_sign_open(uint8_t *m, size_t *mlen, const uint8_t *sm, size_t smlen,
835835

836836
return -1;
837837
}
838+
839+
/*************************************************
840+
* Name: sha_oid
841+
*
842+
* Description: Writes the OID of a given SHA-2/SHA-3 hash function
843+
* to oid and checks that the given hash length matches
844+
* the given hash function.
845+
*
846+
* Arguments: - uint8_t oid[11]: pointer to output oid
847+
* - mld_hash_alg_t hashAlg: hash algorithm enumeration
848+
* - size_t len: Hash length to be checked
849+
*
850+
* Returns 0 if hash algorithm is known and the hash length matches
851+
* and -1 otherwise.
852+
**************************************************/
853+
static int sha_oid(uint8_t oid[11], mld_hash_alg_t hashAlg, size_t len)
854+
{
855+
const uint8_t sha2_224_oid[11] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
856+
0x65, 0x03, 0x04, 0x02, 0x04};
857+
const uint8_t sha2_256_oid[11] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
858+
0x65, 0x03, 0x04, 0x02, 0x01};
859+
const uint8_t sha2_384_oid[11] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
860+
0x65, 0x03, 0x04, 0x02, 0x02};
861+
const uint8_t sha2_512_oid[11] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
862+
0x65, 0x03, 0x04, 0x02, 0x03};
863+
const uint8_t sha2_512_224_oid[11] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
864+
0x65, 0x03, 0x04, 0x02, 0x05};
865+
const uint8_t sha2_512_256_oid[11] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
866+
0x65, 0x03, 0x04, 0x02, 0x06};
867+
const uint8_t sha3_224_oid[11] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
868+
0x65, 0x03, 0x04, 0x02, 0x07};
869+
const uint8_t sha3_256_oid[11] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
870+
0x65, 0x03, 0x04, 0x02, 0x08};
871+
const uint8_t sha3_384_oid[11] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
872+
0x65, 0x03, 0x04, 0x02, 0x09};
873+
const uint8_t sha3_512_oid[11] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
874+
0x65, 0x03, 0x04, 0x02, 0x0A};
875+
const uint8_t shake_128_oid[11] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
876+
0x65, 0x03, 0x04, 0x02, 0x0B};
877+
const uint8_t shake_256_oid[11] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
878+
0x65, 0x03, 0x04, 0x02, 0x0C};
879+
880+
switch (hashAlg)
881+
{
882+
case MLD_SHA2_224:
883+
if (len == 28)
884+
{
885+
mld_memcpy(oid, sha2_224_oid, 11);
886+
return 0;
887+
}
888+
break;
889+
case MLD_SHA2_256:
890+
if (len == 32)
891+
{
892+
mld_memcpy(oid, sha2_256_oid, 11);
893+
return 0;
894+
}
895+
break;
896+
case MLD_SHA2_384:
897+
if (len == 48)
898+
{
899+
mld_memcpy(oid, sha2_384_oid, 11);
900+
return 0;
901+
}
902+
break;
903+
case MLD_SHA2_512:
904+
if (len == 64)
905+
{
906+
mld_memcpy(oid, sha2_512_oid, 11);
907+
return 0;
908+
}
909+
break;
910+
case MLD_SHA2_512_224:
911+
if (len == 28)
912+
{
913+
mld_memcpy(oid, sha2_512_224_oid, 11);
914+
return 0;
915+
}
916+
break;
917+
case MLD_SHA2_512_256:
918+
if (len == 32)
919+
{
920+
mld_memcpy(oid, sha2_512_256_oid, 11);
921+
return 0;
922+
}
923+
break;
924+
case MLD_SHA3_224:
925+
if (len == 28)
926+
{
927+
mld_memcpy(oid, sha3_224_oid, 11);
928+
return 0;
929+
}
930+
break;
931+
case MLD_SHA3_256:
932+
if (len == 32)
933+
{
934+
mld_memcpy(oid, sha3_256_oid, 11);
935+
return 0;
936+
}
937+
break;
938+
case MLD_SHA3_384:
939+
if (len == 48)
940+
{
941+
mld_memcpy(oid, sha3_384_oid, 11);
942+
return 0;
943+
}
944+
break;
945+
case MLD_SHA3_512:
946+
if (len == 64)
947+
{
948+
mld_memcpy(oid, sha3_512_oid, 11);
949+
return 0;
950+
}
951+
break;
952+
case MLD_SHAKE_128:
953+
if (len == 32)
954+
{
955+
mld_memcpy(oid, shake_128_oid, 11);
956+
return 0;
957+
}
958+
break;
959+
case MLD_SHAKE_256:
960+
if (len == 64)
961+
{
962+
mld_memcpy(oid, shake_256_oid, 11);
963+
return 0;
964+
}
965+
break;
966+
}
967+
return -1;
968+
}
969+
970+
MLD_MUST_CHECK_RETURN_VALUE
971+
MLD_EXTERNAL_API
972+
int crypto_sign_signature_pre_hash(uint8_t *sig, size_t *siglen,
973+
const uint8_t *ph, size_t phlen,
974+
const uint8_t *ctx, size_t ctxlen,
975+
const uint8_t rnd[MLDSA_RNDBYTES],
976+
const uint8_t *sk, mld_hash_alg_t hashAlg)
977+
{
978+
/* formatted message: 0x01 || ctxlen (1 byte) || ctx || oid || ph */
979+
uint8_t fmsg[2 + 255 + 11 + 64];
980+
981+
if (ctxlen > 255)
982+
{
983+
*siglen = 0;
984+
return -1;
985+
}
986+
fmsg[0] = 1;
987+
fmsg[1] = ctxlen;
988+
if (ctx != NULL && ctxlen != 0)
989+
{
990+
mld_memcpy(fmsg + 2, ctx, ctxlen);
991+
}
992+
if (sha_oid(fmsg + 2 + ctxlen, hashAlg, phlen))
993+
{
994+
*siglen = 0;
995+
return -1;
996+
}
997+
mld_memcpy(fmsg + 2 + ctxlen + 11, ph, phlen);
998+
999+
return crypto_sign_signature_internal(sig, siglen, fmsg,
1000+
2 + ctxlen + 11 + phlen, rnd, sk, 0);
1001+
}
1002+
1003+
MLD_MUST_CHECK_RETURN_VALUE
1004+
MLD_EXTERNAL_API
1005+
int crypto_sign_verify_pre_hash(const uint8_t *sig, size_t siglen,
1006+
const uint8_t *ph, size_t phlen,
1007+
const uint8_t *ctx, size_t ctxlen,
1008+
const uint8_t *pk, mld_hash_alg_t hashAlg)
1009+
{
1010+
/* formatted message: 0x01 || ctxlen (1 byte) || ctx || oid || ph */
1011+
uint8_t fmsg[2 + 255 + 11 + 64];
1012+
1013+
if (ctxlen > 255)
1014+
{
1015+
return -1;
1016+
}
1017+
fmsg[0] = 1;
1018+
fmsg[1] = ctxlen;
1019+
if (ctx != NULL && ctxlen != 0)
1020+
{
1021+
mld_memcpy(fmsg + 2, ctx, ctxlen);
1022+
}
1023+
if (sha_oid(fmsg + 2 + ctxlen, hashAlg, phlen))
1024+
{
1025+
return -1;
1026+
}
1027+
mld_memcpy(fmsg + 2 + ctxlen + 11, ph, phlen);
1028+
1029+
return crypto_sign_verify_internal(sig, siglen, fmsg, 2 + ctxlen + 11 + phlen,
1030+
pk, 0);
1031+
}

mldsa/sign.h

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,25 @@
1313
#include "polyvec.h"
1414
#include "sys.h"
1515

16+
/*************************************************
17+
* Hash algorithm enumeration for pre-hash functions
18+
**************************************************/
19+
typedef enum
20+
{
21+
MLD_SHA2_224,
22+
MLD_SHA2_256,
23+
MLD_SHA2_384,
24+
MLD_SHA2_512,
25+
MLD_SHA2_512_224,
26+
MLD_SHA2_512_256,
27+
MLD_SHA3_224,
28+
MLD_SHA3_256,
29+
MLD_SHA3_384,
30+
MLD_SHA3_512,
31+
MLD_SHAKE_128,
32+
MLD_SHAKE_256
33+
} mld_hash_alg_t;
34+
1635
#define crypto_sign_keypair_internal MLD_NAMESPACE(keypair_internal)
1736
/*************************************************
1837
* Name: crypto_sign_keypair_internal
@@ -346,4 +365,94 @@ __contract__(
346365
ensures(return_value == 0 || return_value == -1)
347366
);
348367

368+
369+
#define crypto_sign_signature_pre_hash MLD_NAMESPACE(signature_pre_hash)
370+
/*************************************************
371+
* Name: crypto_sign_signature_pre_hash
372+
*
373+
* Description: FIPS 204: Algorithm 4 HashML-DSA.Sign.
374+
* Computes signature with pre-hashed message.
375+
*
376+
* Arguments: - uint8_t *sig: pointer to output signature (of length
377+
* CRYPTO_BYTES)
378+
* - size_t *siglen: pointer to output length of signature
379+
* - const uint8_t *ph: pointer to pre-hashed message
380+
* - size_t phlen: length of pre-hashed message
381+
* - const uint8_t *ctx: pointer to context string
382+
* - size_t ctxlen: length of context string
383+
* - const uint8_t *rnd: pointer to random seed
384+
* - const uint8_t *sk: pointer to bit-packed secret key
385+
* - mld_hash_alg_t hashAlg: hash algorithm enumeration
386+
*
387+
* The supported hash functions are: "SHA2-224", "SHA2-256", "SHA2-384",
388+
* "SHA2-512", "SHA2-512/224", "SHA2-512/256",
389+
* "SHA3-224", "SHA3-256", "SHA3-384",
390+
* "SHA3-512", "SHAKE-128", "SHAKE-256"
391+
*
392+
* Returns 0 (success) or -1 (context string too long OR nonce exhaustion)
393+
**************************************************/
394+
MLD_MUST_CHECK_RETURN_VALUE
395+
MLD_EXTERNAL_API
396+
int crypto_sign_signature_pre_hash(uint8_t *sig, size_t *siglen,
397+
const uint8_t *ph, size_t phlen,
398+
const uint8_t *ctx, size_t ctxlen,
399+
const uint8_t rnd[MLDSA_RNDBYTES],
400+
const uint8_t *sk, mld_hash_alg_t hashAlg)
401+
__contract__(
402+
requires(phlen <= 64)
403+
requires(ctxlen <= MLD_MAX_BUFFER_SIZE)
404+
requires(memory_no_alias(sig, CRYPTO_BYTES))
405+
requires(memory_no_alias(siglen, sizeof(size_t)))
406+
requires(memory_no_alias(ph, phlen))
407+
requires((ctx == NULL && ctxlen == 0) || memory_no_alias(ctx, ctxlen))
408+
requires(memory_no_alias(rnd, MLDSA_RNDBYTES))
409+
requires(memory_no_alias(sk, CRYPTO_SECRETKEYBYTES))
410+
requires(hashAlg >= MLD_SHA2_224 && hashAlg <= MLD_SHAKE_256)
411+
assigns(memory_slice(sig, CRYPTO_BYTES))
412+
assigns(object_whole(siglen))
413+
ensures((return_value == 0 && *siglen == CRYPTO_BYTES) ||
414+
(return_value == -1 && *siglen == 0))
415+
);
416+
417+
#define crypto_sign_verify_pre_hash MLD_NAMESPACE(verify_pre_hash)
418+
/*************************************************
419+
* Name: crypto_sign_verify_pre_hash
420+
*
421+
* Description: FIPS 204: Algorithm 5 HashML-DSA.Verify.
422+
* Verifies signature with pre-hashed message.
423+
*
424+
* Arguments: - const uint8_t *sig: pointer to input signature
425+
* - size_t siglen: length of signature
426+
* - const uint8_t *ph: pointer to pre-hashed message
427+
* - size_t phlen: length of pre-hashed message
428+
* - const uint8_t *ctx: pointer to context string
429+
* - size_t ctxlen: length of context string
430+
* - const uint8_t *pk: pointer to bit-packed public key
431+
* - mld_hash_alg_t hashAlg: hash algorithm enumeration
432+
*
433+
* The supported hash functions are: "SHA2-224", "SHA2-256", "SHA2-384",
434+
* "SHA2-512", "SHA2-512/224", "SHA2-512/256",
435+
* "SHA3-224", "SHA3-256", "SHA3-384",
436+
* "SHA3-512", "SHAKE-128", "SHAKE-256"
437+
*
438+
* Returns 0 if signature could be verified correctly and -1 otherwise
439+
**************************************************/
440+
MLD_MUST_CHECK_RETURN_VALUE
441+
MLD_EXTERNAL_API
442+
int crypto_sign_verify_pre_hash(const uint8_t *sig, size_t siglen,
443+
const uint8_t *ph, size_t phlen,
444+
const uint8_t *ctx, size_t ctxlen,
445+
const uint8_t *pk, mld_hash_alg_t hashAlg)
446+
__contract__(
447+
requires(phlen <= 64)
448+
requires(ctxlen <= MLD_MAX_BUFFER_SIZE - 77)
449+
requires(siglen <= MLD_MAX_BUFFER_SIZE)
450+
requires(memory_no_alias(sig, siglen))
451+
requires(memory_no_alias(ph, phlen))
452+
requires((ctx == NULL && ctxlen == 0) || memory_no_alias(ctx, ctxlen))
453+
requires(memory_no_alias(pk, CRYPTO_PUBLICKEYBYTES))
454+
requires(hashAlg >= MLD_SHA2_224 && hashAlg <= MLD_SHAKE_256)
455+
ensures(return_value == 0 || return_value == -1)
456+
);
457+
349458
#endif /* !MLD_SIGN_H */
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright (c) The mldsa-native project authors
2+
# SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT
3+
4+
include ../Makefile_params.common
5+
6+
HARNESS_ENTRY = harness
7+
HARNESS_FILE = crypto_sign_signature_pre_hash_harness
8+
9+
# This should be a unique identifier for this proof, and will appear on the
10+
# Litani dashboard. It can be human-readable and contain spaces if you wish.
11+
PROOF_UID = crypto_sign_signature_pre_hash
12+
13+
DEFINES +=
14+
INCLUDES +=
15+
16+
REMOVE_FUNCTION_BODY +=
17+
UNWINDSET +=
18+
19+
PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
20+
PROJECT_SOURCES += $(SRCDIR)/mldsa/sign.c
21+
22+
CHECK_FUNCTION_CONTRACTS=$(MLD_NAMESPACE)signature_pre_hash
23+
USE_FUNCTION_CONTRACTS=$(MLD_NAMESPACE)signature_internal
24+
25+
APPLY_LOOP_CONTRACTS=on
26+
USE_DYNAMIC_FRAMES=1
27+
28+
# Disable any setting of EXTERNAL_SAT_SOLVER, and choose SMT backend instead
29+
EXTERNAL_SAT_SOLVER=
30+
CBMCFLAGS=--smt2
31+
CBMCFLAGS += --slice-formula
32+
CBMCFLAGS += --no-array-field-sensitivity
33+
34+
FUNCTION_NAME = crypto_sign_signature_pre_hash
35+
36+
# If this proof is found to consume huge amounts of RAM, you can set the
37+
# EXPENSIVE variable. With new enough versions of the proof tools, this will
38+
# restrict the number of EXPENSIVE CBMC jobs running at once. See the
39+
# documentation in Makefile.common under the "Job Pools" heading for details.
40+
# EXPENSIVE = true
41+
42+
# This function is large enough to need...
43+
CBMC_OBJECT_BITS = 8
44+
45+
# If you require access to a file-local ("static") function or object to conduct
46+
# your proof, set the following (and do not include the original source file
47+
# ("mldsa/poly.c") in PROJECT_SOURCES).
48+
# REWRITTEN_SOURCES = $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i
49+
# include ../Makefile.common
50+
# $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i_SOURCE = $(SRCDIR)/mldsa/poly.c
51+
# $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i_FUNCTIONS = foo bar
52+
# $(PROOFDIR)/<__SOURCE_FILE_BASENAME__>.i_OBJECTS = baz
53+
# Care is required with variables on the left-hand side: REWRITTEN_SOURCES must
54+
# be set before including Makefile.common, but any use of variables on the
55+
# left-hand side requires those variables to be defined. Hence, _SOURCE,
56+
# _FUNCTIONS, _OBJECTS is set after including Makefile.common.
57+
58+
include ../Makefile.common
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) The mldsa-native project authors
2+
// SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT
3+
4+
#include "sign.h"
5+
6+
void harness(void)
7+
{
8+
uint8_t *sig;
9+
size_t *siglen;
10+
const uint8_t *ph;
11+
size_t phlen;
12+
const uint8_t *ctx;
13+
size_t ctxlen;
14+
const uint8_t *rnd;
15+
const uint8_t *sk;
16+
mld_hash_alg_t hashAlg;
17+
int r;
18+
r = crypto_sign_signature_pre_hash(sig, siglen, ph, phlen, ctx, ctxlen, rnd,
19+
sk, hashAlg);
20+
}

0 commit comments

Comments
 (0)