| 1 | // FIPS 204 s. 5.4: Pre-Hash ML-DSA |
| 2 | // signs PH(M) instead of M directly, prefer using pure ml-dsa when possible |
| 3 | |
| 4 | module mldsa |
| 5 | |
| 6 | import crypto.sha256 |
| 7 | import crypto.sha512 |
| 8 | import crypto.sha3 |
| 9 | |
| 10 | // algo. 4/5: DER-encoded OID for each pre-hash function |
| 11 | // all under arc 2.16.840.1.101.3.4.2 |
| 12 | // joint-iso-itu-t(2) country(16) us(840) organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2) |
| 13 | fn prehash_oid(ph PreHash) []u8 { |
| 14 | suffix := match ph { |
| 15 | .sha2_256 { u8(0x01) } |
| 16 | .sha2_384 { u8(0x02) } |
| 17 | .sha2_512 { u8(0x03) } |
| 18 | .sha2_224 { u8(0x04) } |
| 19 | .sha2_512_224 { u8(0x05) } |
| 20 | .sha2_512_256 { u8(0x06) } |
| 21 | .sha3_224 { u8(0x07) } |
| 22 | .sha3_256 { u8(0x08) } |
| 23 | .sha3_384 { u8(0x09) } |
| 24 | .sha3_512 { u8(0x0a) } |
| 25 | .shake_128 { u8(0x0b) } |
| 26 | .shake_256 { u8(0x0c) } |
| 27 | // unreachable, called from mu_prehash, which is called from sign when ph != .none |
| 28 | .none { panic('mldsa: prehash_oid called with .none') } |
| 29 | } |
| 30 | |
| 31 | return [u8(0x06), 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix] |
| 32 | } |
| 33 | |
| 34 | // algo. 4, lines 10-22: PH(M) for the given hash function |
| 35 | fn prehash_message(msg []u8, ph PreHash) []u8 { |
| 36 | return match ph { |
| 37 | .sha2_224 { sha256.sum224(msg) } |
| 38 | .sha2_256 { sha256.sum256(msg) } |
| 39 | .sha2_384 { sha512.sum384(msg) } |
| 40 | .sha2_512 { sha512.sum512(msg) } |
| 41 | .sha2_512_224 { sha512.sum512_224(msg) } |
| 42 | .sha2_512_256 { sha512.sum512_256(msg) } |
| 43 | .sha3_224 { sha3.sum224(msg) } |
| 44 | .sha3_256 { sha3.sum256(msg) } |
| 45 | .sha3_384 { sha3.sum384(msg) } |
| 46 | .sha3_512 { sha3.sum512(msg) } |
| 47 | .shake_128 { sha3.shake128(msg, 32) } |
| 48 | .shake_256 { sha3.shake256(msg, 64) } |
| 49 | // unreachable, called from mu_prehash, which is called from sign when ph != .none |
| 50 | .none { panic('mldsa: prehash_message called with .none') } |
| 51 | } |
| 52 | } |
| 53 | |
| 54 | // algo. 4, line 23: M' = 0x01 || |ctx| || ctx || OID || PH(M) |
| 55 | // algo. 7, line 6: mu = H(tr || M') |
| 56 | // compute_mu_prehash computes mu for prehash mode: H(tr || 0x01 || |ctx| || ctx || OID || PH(msg), 64). |
| 57 | pub fn compute_mu_prehash(tr []u8, msg []u8, context string, ph PreHash) [64]u8 { |
| 58 | mut h := sha3.new_shake256() |
| 59 | h.write(tr) |
| 60 | h.write([u8(0x01)]) // domain sep |
| 61 | h.write([u8(context.len)]) |
| 62 | h.write(context.bytes()) |
| 63 | h.write(prehash_oid(ph)) |
| 64 | h.write(prehash_message(msg, ph)) |
| 65 | return slice_to_64(h.read(64)) |
| 66 | } |
| 67 | |