| ... | @@ -443,14 +443,14 @@ fn PolyVec(comptime len: u8) type { | ... | @@ -443,14 +443,14 @@ fn PolyVec(comptime len: u8) type { |
| 443 | } | 443 | } |
| 444 | | 444 | |
| 445 | /// Unpack vector with coefficients in [-eta, eta] | 445 | /// Unpack vector with coefficients in [-eta, eta] |
| 446 | fn unpackLeqEta(comptime eta: u8, buf: []const u8) Self { | 446 | fn unpackLeqEta(comptime eta: u8, buf: []const u8) EncodingError!Self { |
| 447 | const poly_size = if (eta == 2) 96 else 128; | 447 | const poly_size = if (eta == 2) 96 else 128; |
| 448 | const unpack_fn = struct { | 448 | var result: Self = undefined; |
| 449 | fn unpack(b: []const u8) Poly { | 449 | inline for (0..len) |i| { |
| 450 | return polyUnpackLeqEta(eta, b); | 450 | const offset = i * poly_size; |
| 451 | } | 451 | result.ps[i] = try polyUnpackLeqEta(eta, buf[offset..][0..poly_size]); |
| 452 | }.unpack; | 452 | } |
| 453 | return unpackWith(poly_size, unpack_fn, buf); | 453 | return result; |
| 454 | } | 454 | } |
| 455 | | 455 | |
| 456 | /// Pack vector of polynomials with coefficients < gamma1 | 456 | /// Pack vector of polynomials with coefficients < gamma1 |
| ... | @@ -996,7 +996,7 @@ fn polyPackLeqEta(p: Poly, comptime eta: u8, buf: []u8) void { | ... | @@ -996,7 +996,7 @@ fn polyPackLeqEta(p: Poly, comptime eta: u8, buf: []u8) void { |
| 996 | | 996 | |
| 997 | /// Unpack polynomial with coefficients in [Q-eta, Q+eta] from bytes. | 997 | /// Unpack polynomial with coefficients in [Q-eta, Q+eta] from bytes. |
| 998 | /// Output coefficients will not be normalized, but in [q-η, q+η]. | 998 | /// Output coefficients will not be normalized, but in [q-η, q+η]. |
| 999 | fn polyUnpackLeqEta(comptime eta: u8, buf: []const u8) Poly { | 999 | fn polyUnpackLeqEta(comptime eta: u8, buf: []const u8) EncodingError!Poly { |
| 1000 | comptime { | 1000 | comptime { |
| 1001 | if (eta != 2 and eta != 4) { | 1001 | if (eta != 2 and eta != 4) { |
| 1002 | @compileError("eta must be 2 or 4"); | 1002 | @compileError("eta must be 2 or 4"); |
| ... | @@ -1010,22 +1010,31 @@ fn polyUnpackLeqEta(comptime eta: u8, buf: []const u8) Poly { | ... | @@ -1010,22 +1010,31 @@ fn polyUnpackLeqEta(comptime eta: u8, buf: []const u8) Poly { |
| 1010 | var j: usize = 0; | 1010 | var j: usize = 0; |
| 1011 | var i: usize = 0; | 1011 | var i: usize = 0; |
| 1012 | while (i < buf.len) : (i += 3) { | 1012 | while (i < buf.len) : (i += 3) { |
| 1013 | p.cs[j] = Q + eta - (buf[i] & 7); | 1013 | const values = [_]u8{ |
| 1014 | p.cs[j + 1] = Q + eta - ((buf[i] >> 3) & 7); | 1014 | buf[i] & 7, |
| 1015 | p.cs[j + 2] = Q + eta - ((buf[i] >> 6) | ((buf[i + 1] << 2) & 7)); | 1015 | (buf[i] >> 3) & 7, |
| 1016 | p.cs[j + 3] = Q + eta - ((buf[i + 1] >> 1) & 7); | 1016 | (buf[i] >> 6) | ((buf[i + 1] << 2) & 7), |
| 1017 | p.cs[j + 4] = Q + eta - ((buf[i + 1] >> 4) & 7); | 1017 | (buf[i + 1] >> 1) & 7, |
| 1018 | p.cs[j + 5] = Q + eta - ((buf[i + 1] >> 7) | ((buf[i + 2] << 1) & 7)); | 1018 | (buf[i + 1] >> 4) & 7, |
| 1019 | p.cs[j + 6] = Q + eta - ((buf[i + 2] >> 2) & 7); | 1019 | (buf[i + 1] >> 7) | ((buf[i + 2] << 1) & 7), |
| 1020 | p.cs[j + 7] = Q + eta - ((buf[i + 2] >> 5) & 7); | 1020 | (buf[i + 2] >> 2) & 7, |
| | 1021 | (buf[i + 2] >> 5) & 7, |
| | 1022 | }; |
| | 1023 | for (p.cs[j..][0..values.len], values) |*coefficient, value| { |
| | 1024 | if (value > 2 * eta) return error.InvalidEncoding; |
| | 1025 | coefficient.* = Q + eta - value; |
| | 1026 | } |
| 1021 | j += 8; | 1027 | j += 8; |
| 1022 | } | 1028 | } |
| 1023 | } else { // eta == 4 | 1029 | } else { // eta == 4 |
| 1024 | // 4 bits per coefficient: unpack 2 coefficients from 1 byte | 1030 | // 4 bits per coefficient: unpack 2 coefficients from 1 byte |
| 1025 | var j: usize = 0; | 1031 | var j: usize = 0; |
| 1026 | for (0..buf.len) |i| { | 1032 | for (0..buf.len) |i| { |
| 1027 | p.cs[j] = Q + eta - (buf[i] & 15); | 1033 | const values = [_]u8{ buf[i] & 15, buf[i] >> 4 }; |
| 1028 | p.cs[j + 1] = Q + eta - (buf[i] >> 4); | 1034 | for (p.cs[j..][0..values.len], values) |*coefficient, value| { |
| | 1035 | if (value > 2 * eta) return error.InvalidEncoding; |
| | 1036 | coefficient.* = Q + eta - value; |
| | 1037 | } |
| 1029 | j += 2; | 1038 | j += 2; |
| 1030 | } | 1039 | } |
| 1031 | } | 1040 | } |
| ... | @@ -1589,15 +1598,15 @@ fn MLDSAImpl(comptime p: Params) type { | ... | @@ -1589,15 +1598,15 @@ fn MLDSAImpl(comptime p: Params) type { |
| 1589 | offset += p.tr_size; | 1598 | offset += p.tr_size; |
| 1590 | | 1599 | |
| 1591 | sk.s1 = if (p.eta == 2) | 1600 | sk.s1 = if (p.eta == 2) |
| 1592 | PolyVecL.unpackLeqEta(2, bytes[offset..][0 .. p.l * polyLeqEtaPackedSize()]) | 1601 | try PolyVecL.unpackLeqEta(2, bytes[offset..][0 .. p.l * polyLeqEtaPackedSize()]) |
| 1593 | else | 1602 | else |
| 1594 | PolyVecL.unpackLeqEta(4, bytes[offset..][0 .. p.l * polyLeqEtaPackedSize()]); | 1603 | try PolyVecL.unpackLeqEta(4, bytes[offset..][0 .. p.l * polyLeqEtaPackedSize()]); |
| 1595 | offset += p.l * polyLeqEtaPackedSize(); | 1604 | offset += p.l * polyLeqEtaPackedSize(); |
| 1596 | | 1605 | |
| 1597 | sk.s2 = if (p.eta == 2) | 1606 | sk.s2 = if (p.eta == 2) |
| 1598 | PolyVecK.unpackLeqEta(2, bytes[offset..][0 .. p.k * polyLeqEtaPackedSize()]) | 1607 | try PolyVecK.unpackLeqEta(2, bytes[offset..][0 .. p.k * polyLeqEtaPackedSize()]) |
| 1599 | else | 1608 | else |
| 1600 | PolyVecK.unpackLeqEta(4, bytes[offset..][0 .. p.k * polyLeqEtaPackedSize()]); | 1609 | try PolyVecK.unpackLeqEta(4, bytes[offset..][0 .. p.k * polyLeqEtaPackedSize()]); |
| 1601 | offset += p.k * polyLeqEtaPackedSize(); | 1610 | offset += p.k * polyLeqEtaPackedSize(); |
| 1602 | | 1611 | |
| 1603 | sk.t0 = PolyVecK.unpackT0(bytes[offset..][0 .. p.k * polyT0PackedSize()]); | 1612 | sk.t0 = PolyVecK.unpackT0(bytes[offset..][0 .. p.k * polyT0PackedSize()]); |
| ... | @@ -2443,7 +2452,7 @@ test "polyPackLeqEta / polyUnpackLeqEta roundtrip for eta=2" { | ... | @@ -2443,7 +2452,7 @@ test "polyPackLeqEta / polyUnpackLeqEta roundtrip for eta=2" { |
| 2443 | polyPackLeqEta(p, eta, &buf); | 2452 | polyPackLeqEta(p, eta, &buf); |
| 2444 | | 2453 | |
| 2445 | // Unpack it | 2454 | // Unpack it |
| 2446 | const p2 = polyUnpackLeqEta(eta, &buf); | 2455 | const p2 = try polyUnpackLeqEta(eta, &buf); |
| 2447 | | 2456 | |
| 2448 | // Should be identical | 2457 | // Should be identical |
| 2449 | for (0..N) |i| { | 2458 | for (0..N) |i| { |
| ... | @@ -2468,7 +2477,7 @@ test "polyPackLeqEta / polyUnpackLeqEta roundtrip for eta=4" { | ... | @@ -2468,7 +2477,7 @@ test "polyPackLeqEta / polyUnpackLeqEta roundtrip for eta=4" { |
| 2468 | polyPackLeqEta(p, eta, &buf); | 2477 | polyPackLeqEta(p, eta, &buf); |
| 2469 | | 2478 | |
| 2470 | // Unpack it | 2479 | // Unpack it |
| 2471 | const p2 = polyUnpackLeqEta(eta, &buf); | 2480 | const p2 = try polyUnpackLeqEta(eta, &buf); |
| 2472 | | 2481 | |
| 2473 | // Should be identical | 2482 | // Should be identical |
| 2474 | for (0..N) |i| { | 2483 | for (0..N) |i| { |
| ... | @@ -2860,6 +2869,19 @@ test "Key generation basic - all variants" { | ... | @@ -2860,6 +2869,19 @@ test "Key generation basic - all variants" { |
| 2860 | } | 2869 | } |
| 2861 | } | 2870 | } |
| 2862 | | 2871 | |
| | 2872 | test "Reject reserved eta coefficients encodings" { |
| | 2873 | inline for (.{ |
| | 2874 | .{ .variant = MLDSA44, .reserved = 7 }, |
| | 2875 | .{ .variant = MLDSA65, .reserved = 15 }, |
| | 2876 | }) |config| { |
| | 2877 | var encoded: [config.variant.SecretKey.encoded_length]u8 = @splat(0); |
| | 2878 | _ = try config.variant.SecretKey.fromBytes(encoded); |
| | 2879 | const s1_offset = 32 + 32 + config.variant.params.tr_size; |
| | 2880 | encoded[s1_offset] = config.reserved; |
| | 2881 | try testing.expectError(error.InvalidEncoding, config.variant.SecretKey.fromBytes(encoded)); |
| | 2882 | } |
| | 2883 | } |
| | 2884 | |
| 2863 | test "Key generation determinism" { | 2885 | test "Key generation determinism" { |
| 2864 | if (builtin.zig_backend == .stage2_llvm and builtin.target.cpu.arch.isPowerpc64()) return error.SkipZigTest; // https://github.com/llvm/llvm-project/issues/229620 | 2886 | if (builtin.zig_backend == .stage2_llvm and builtin.target.cpu.arch.isPowerpc64()) return error.SkipZigTest; // https://github.com/llvm/llvm-project/issues/229620 |
| 2865 | | 2887 | |