| 1 | // Copyright 2025 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | // |
| 5 | // Ported to V from Go's crypto/internal/fips140/mldsa. |
| 6 | module mldsa |
| 7 | |
| 8 | import crypto.internal.subtle |
| 9 | import crypto.sha3 |
| 10 | |
| 11 | // algo. 22: pkEncode (s. 7.2) |
| 12 | @[direct_array_access] |
| 13 | fn pk_encode(rho []u8, t1 [][]u16, p Params) []u8 { |
| 14 | mut pk := rho.clone() |
| 15 | for i in 0 .. p.k { |
| 16 | w := t1[i] |
| 17 | mut j := 0 |
| 18 | for j < n { |
| 19 | c0 := w[j] |
| 20 | c1 := w[j + 1] |
| 21 | c2 := w[j + 2] |
| 22 | c3 := w[j + 3] |
| 23 | pk << u8(c0) |
| 24 | pk << u8((c0 >> 8) | (c1 << 2)) |
| 25 | pk << u8((c1 >> 6) | (c2 << 4)) |
| 26 | pk << u8((c2 >> 4) | (c3 << 6)) |
| 27 | pk << u8(c3 >> 2) |
| 28 | j += 4 |
| 29 | } |
| 30 | } |
| 31 | return pk |
| 32 | } |
| 33 | |
| 34 | // algo. 23: pkDecode (s. 7.2) |
| 35 | @[direct_array_access] |
| 36 | fn pk_decode(pk []u8, p Params) !([]u8, [][]u16) { |
| 37 | expected := pub_key_size(p) |
| 38 | if pk.len != expected { |
| 39 | return error('invalid public key length') |
| 40 | } |
| 41 | rho := pk[..32].clone() |
| 42 | // avoid cloning on each iteration |
| 43 | mut data := unsafe { pk[32..] } |
| 44 | mut t1 := [][]u16{len: p.k, init: []u16{len: n}} |
| 45 | for r in 0 .. p.k { |
| 46 | mut j := 0 |
| 47 | for j < n { |
| 48 | b0 := data[0] |
| 49 | b1 := data[1] |
| 50 | b2 := data[2] |
| 51 | b3 := data[3] |
| 52 | b4 := data[4] |
| 53 | t1[r][j] = u16(b0) | (u16(b1 & 0x03) << 8) |
| 54 | t1[r][j + 1] = u16(b1 >> 2) | (u16(b2 & 0x0f) << 6) |
| 55 | t1[r][j + 2] = u16(b2 >> 4) | (u16(b3 & 0x3f) << 4) |
| 56 | t1[r][j + 3] = u16(b3 >> 6) | (u16(b4) << 2) |
| 57 | data = unsafe { data[5..] } |
| 58 | j += 4 |
| 59 | } |
| 60 | } |
| 61 | return rho, t1 |
| 62 | } |
| 63 | |
| 64 | fn compute_pk_hash(pk []u8) [64]u8 { |
| 65 | return slice_to_64(sha3.shake256(pk, 64)) |
| 66 | } |
| 67 | |
| 68 | @[direct_array_access] |
| 69 | fn compute_t1_hat(t1 [][]u16) []NttElement { |
| 70 | mut result := []NttElement{len: t1.len} |
| 71 | for i in 0 .. t1.len { |
| 72 | mut w := RingElement{} |
| 73 | for j in 0 .. n { |
| 74 | // panics if (t1[i][j] << d) >= q, c.f field.v |
| 75 | // only called from pk_decode, which produces 10bit vals (<= 1023) |
| 76 | // so 1023 << 13 = 8_380_416 = q - 1 |
| 77 | z := field_to_montgomery(u32(t1[i][j]) << d) or { panic(err) } |
| 78 | w[j] = z |
| 79 | } |
| 80 | result[i] = ntt(w) |
| 81 | } |
| 82 | return result |
| 83 | } |
| 84 | |
| 85 | // algo. 35: Power2Round (s. 7.4) |
| 86 | fn power2_round(r FieldElement) (u16, FieldElement) { |
| 87 | rr_ := field_from_montgomery(r) |
| 88 | r1 := (rr_ + (1 << 12) - 1) >> d |
| 89 | r0 := field_sub_to_montgomery(rr_, r1 << d) |
| 90 | return u16(r1), r0 |
| 91 | } |
| 92 | |
| 93 | // algo. 37: HighBits (s. 7.4) |
| 94 | @[direct_array_access] |
| 95 | fn high_bits(r RingElement, p Params) [256]u8 { |
| 96 | mut w := [256]u8{} |
| 97 | match p.gamma2 { |
| 98 | 32 { |
| 99 | for i in 0 .. n { |
| 100 | w[i] = high_bits_32(field_from_montgomery(r[i])) |
| 101 | } |
| 102 | } |
| 103 | 88 { |
| 104 | for i in 0 .. n { |
| 105 | w[i] = high_bits_88(field_from_montgomery(r[i])) |
| 106 | } |
| 107 | } |
| 108 | else { |
| 109 | panic('mldsa: unsupported gamma2') // unreachable |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | return w |
| 114 | } |
| 115 | |
| 116 | fn high_bits_32(x u32) u8 { |
| 117 | mut r1 := (x + 127) >> 7 // approx div by 2*gamma2 |
| 118 | r1 = (r1 * 1025 + (1 << 21)) >> 22 |
| 119 | r1 &= 0xf |
| 120 | return u8(r1) |
| 121 | } |
| 122 | |
| 123 | fn high_bits_88(x u32) u8 { |
| 124 | mut r1 := (x + 127) >> 7 // approx div by 2*gamma2 |
| 125 | r1 = (r1 * 11275 + (1 << 23)) >> 24 |
| 126 | r1 = ct_select_eq(r1, 44, 0, r1) |
| 127 | return u8(r1) |
| 128 | } |
| 129 | |
| 130 | // algo. 36: Decompose, gamma2 = (q-1)/32 (s. 7.4) |
| 131 | fn decompose_32(r FieldElement) (u8, i32) { |
| 132 | x := field_from_montgomery(r) |
| 133 | r1 := high_bits_32(x) |
| 134 | r0 := i32(x) - i32(r1) * 2 * i32((q - 1) / 32) |
| 135 | r0_adj := ct_select_leq(i32(q / 2 + 1), r0, r0 - i32(q), r0) |
| 136 | return r1, r0_adj |
| 137 | } |
| 138 | |
| 139 | // algo. 36: Decompose, gamma2 = (q-1)/88 (s. 7.4) |
| 140 | fn decompose_88(r FieldElement) (u8, i32) { |
| 141 | x := field_from_montgomery(r) |
| 142 | r1 := high_bits_88(x) |
| 143 | r0 := i32(x) - i32(r1) * 2 * i32((q - 1) / 88) |
| 144 | r0_adj := ct_select_leq(i32(q / 2 + 1), r0, r0 - i32(q), r0) |
| 145 | return r1, r0_adj |
| 146 | } |
| 147 | |
| 148 | @[direct_array_access] |
| 149 | fn low_bits_exceed_bound(w RingElement, bound u32, p Params) bool { |
| 150 | match p.gamma2 { |
| 151 | 32 { |
| 152 | for i in 0 .. n { |
| 153 | _, r0 := decompose_32(w[i]) |
| 154 | if ct_abs(r0) >= bound { |
| 155 | return true |
| 156 | } |
| 157 | } |
| 158 | } |
| 159 | 88 { |
| 160 | for i in 0 .. n { |
| 161 | _, r0 := decompose_88(w[i]) |
| 162 | if ct_abs(r0) >= bound { |
| 163 | return true |
| 164 | } |
| 165 | } |
| 166 | } |
| 167 | else { |
| 168 | panic('mldsa: unsupported gamma2') // unreachable |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | return false |
| 173 | } |
| 174 | |
| 175 | // algo. 40: UseHint (s. 7.4) |
| 176 | @[direct_array_access] |
| 177 | fn use_hint(r RingElement, h [256]u8, p Params) [256]u8 { |
| 178 | mut w := [256]u8{} |
| 179 | match p.gamma2 { |
| 180 | 32 { |
| 181 | for i in 0 .. n { |
| 182 | w[i] = use_hint_32(r[i], h[i]) |
| 183 | } |
| 184 | } |
| 185 | 88 { |
| 186 | for i in 0 .. n { |
| 187 | w[i] = use_hint_88(r[i], h[i]) |
| 188 | } |
| 189 | } |
| 190 | else { |
| 191 | panic('mldsa: unsupported gamma2') // unreachable |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | return w |
| 196 | } |
| 197 | |
| 198 | fn use_hint_32(r FieldElement, hint u8) u8 { |
| 199 | mut r1, r0 := decompose_32(r) |
| 200 | if hint == 1 { |
| 201 | if r0 > 0 { |
| 202 | r1 = (r1 + 1) % 16 |
| 203 | } else { |
| 204 | r1 = (r1 - 1) % 16 |
| 205 | } |
| 206 | } |
| 207 | return r1 |
| 208 | } |
| 209 | |
| 210 | fn use_hint_88(r FieldElement, hint u8) u8 { |
| 211 | mut r1, r0 := decompose_88(r) |
| 212 | if hint == 1 { |
| 213 | if r0 > 0 { |
| 214 | if r1 == 43 { |
| 215 | r1 = 0 |
| 216 | } else { |
| 217 | r1++ |
| 218 | } |
| 219 | } else { |
| 220 | if r1 == 0 { |
| 221 | r1 = 43 |
| 222 | } else { |
| 223 | r1-- |
| 224 | } |
| 225 | } |
| 226 | } |
| 227 | return r1 |
| 228 | } |
| 229 | |
| 230 | // algo. 39: MakeHint (s. 7.4) |
| 231 | @[direct_array_access] |
| 232 | fn make_hint(ct0 RingElement, w RingElement, cs2 RingElement, p Params) ([256]u8, int) { |
| 233 | mut h := [256]u8{} |
| 234 | mut count := 0 |
| 235 | match p.gamma2 { |
| 236 | 32 { |
| 237 | for i in 0 .. n { |
| 238 | h[i] = make_hint_32(ct0[i], w[i], cs2[i]) |
| 239 | count += int(h[i]) |
| 240 | } |
| 241 | } |
| 242 | 88 { |
| 243 | for i in 0 .. n { |
| 244 | h[i] = make_hint_88(ct0[i], w[i], cs2[i]) |
| 245 | count += int(h[i]) |
| 246 | } |
| 247 | } |
| 248 | else { |
| 249 | panic('mldsa: unsupported gamma2') // unreachable |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | return h, count |
| 254 | } |
| 255 | |
| 256 | fn make_hint_32(ct0 FieldElement, w FieldElement, cs2 FieldElement) u8 { |
| 257 | r_plus_z := field_sub(w, cs2) |
| 258 | v1 := high_bits_32(field_from_montgomery(r_plus_z)) |
| 259 | r1 := high_bits_32(field_from_montgomery(field_add(r_plus_z, ct0))) |
| 260 | return u8(1 - subtle.constant_time_byte_eq(v1, r1)) |
| 261 | } |
| 262 | |
| 263 | fn make_hint_88(ct0 FieldElement, w FieldElement, cs2 FieldElement) u8 { |
| 264 | r_plus_z := field_sub(w, cs2) |
| 265 | v1 := high_bits_88(field_from_montgomery(r_plus_z)) |
| 266 | r1 := high_bits_88(field_from_montgomery(field_add(r_plus_z, ct0))) |
| 267 | return u8(1 - subtle.constant_time_byte_eq(v1, r1)) |
| 268 | } |
| 269 | |
| 270 | fn w1_encode_len(p Params) int { |
| 271 | return match p.gamma2 { |
| 272 | 32 { 4 * n / 8 } |
| 273 | 88 { 6 * n / 8 } |
| 274 | else { panic('mldsa: unsupported gamma2') } // unreachable |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | // algo. 28: w1Encode (s. 7.2) |
| 279 | @[direct_array_access] |
| 280 | fn w1_encode(w [256]u8, p Params, mut buf []u8) { |
| 281 | match p.gamma2 { |
| 282 | 32 { |
| 283 | for i := 0; i < n; i += 2 { |
| 284 | buf[i / 2] = w[i] | (w[i + 1] << 4) |
| 285 | } |
| 286 | } |
| 287 | 88 { |
| 288 | for i := 0; i < n; i += 4 { |
| 289 | buf[3 * i / 4] = w[i] | (w[i + 1] << 6) |
| 290 | buf[3 * i / 4 + 1] = (w[i + 1] >> 2) | (w[i + 2] << 4) |
| 291 | buf[3 * i / 4 + 2] = (w[i + 2] >> 4) | (w[i + 3] << 2) |
| 292 | } |
| 293 | } |
| 294 | else { |
| 295 | panic('mldsa: unsupported gamma2') // unreachable |
| 296 | } |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | // algo. 26: sigEncode (s. 7.2) |
| 301 | fn sig_encode(ch []u8, z []RingElement, h [][256]u8, p Params) []u8 { |
| 302 | mut sig := ch.clone() |
| 303 | for i in 0 .. z.len { |
| 304 | sig << bit_pack(z[i], p) |
| 305 | } |
| 306 | sig << hint_encode(h, p) |
| 307 | return sig |
| 308 | } |
| 309 | |
| 310 | // algo. 27: sigDecode (s. 7.2) |
| 311 | @[direct_array_access] |
| 312 | fn sig_decode(sig []u8, p Params) !([]u8, []RingElement, [][256]u8) { |
| 313 | expected := sig_size(p) |
| 314 | if sig.len != expected { |
| 315 | return error('invalid signature length') |
| 316 | } |
| 317 | ch_len := p.lambda / 4 |
| 318 | ch := sig[..ch_len].clone() |
| 319 | mut offset := ch_len |
| 320 | mut z := []RingElement{len: p.l} |
| 321 | for i in 0 .. p.l { |
| 322 | length := (p.gamma1 + 1) * n / 8 |
| 323 | z[i] = bit_unpack(sig[offset..offset + length], p) |
| 324 | offset += length |
| 325 | } |
| 326 | h := hint_decode(sig[offset..], p)! |
| 327 | return ch, z, h |
| 328 | } |
| 329 | |
| 330 | // algo. 17: BitPack (s. 7.1) |
| 331 | fn bit_pack(r RingElement, p Params) []u8 { |
| 332 | match p.gamma1 { |
| 333 | 17 { return bit_pack_18(r) } |
| 334 | 19 { return bit_pack_20(r) } |
| 335 | else { panic('mldsa: unsupported gamma1') } // unreachable |
| 336 | } |
| 337 | } |
| 338 | |
| 339 | @[direct_array_access] |
| 340 | fn bit_pack_18(r RingElement) []u8 { |
| 341 | mut v := []u8{len: 18 * n / 8} |
| 342 | mut pos := 0 |
| 343 | for i := 0; i < n; i += 4 { |
| 344 | w0 := u32(1 << 17) - u32(field_centered_mod(r[i])) |
| 345 | w1 := u32(1 << 17) - u32(field_centered_mod(r[i + 1])) |
| 346 | w2 := u32(1 << 17) - u32(field_centered_mod(r[i + 2])) |
| 347 | w3 := u32(1 << 17) - u32(field_centered_mod(r[i + 3])) |
| 348 | v[pos] = u8(w0) |
| 349 | v[pos + 1] = u8(w0 >> 8) |
| 350 | v[pos + 2] = u8(w0 >> 16) |
| 351 | v[pos + 2] |= u8(w1 << 2) |
| 352 | v[pos + 3] = u8(w1 >> 6) |
| 353 | v[pos + 4] = u8(w1 >> 14) |
| 354 | v[pos + 4] |= u8(w2 << 4) |
| 355 | v[pos + 5] = u8(w2 >> 4) |
| 356 | v[pos + 6] = u8(w2 >> 12) |
| 357 | v[pos + 6] |= u8(w3 << 6) |
| 358 | v[pos + 7] = u8(w3 >> 2) |
| 359 | v[pos + 8] = u8(w3 >> 10) |
| 360 | pos += 9 |
| 361 | } |
| 362 | return v |
| 363 | } |
| 364 | |
| 365 | @[direct_array_access] |
| 366 | fn bit_pack_20(r RingElement) []u8 { |
| 367 | mut v := []u8{len: 20 * n / 8} |
| 368 | mut pos := 0 |
| 369 | for i := 0; i < n; i += 2 { |
| 370 | w0 := u32(1 << 19) - u32(field_centered_mod(r[i])) |
| 371 | w1 := u32(1 << 19) - u32(field_centered_mod(r[i + 1])) |
| 372 | v[pos] = u8(w0) |
| 373 | v[pos + 1] = u8(w0 >> 8) |
| 374 | v[pos + 2] = u8(w0 >> 16) |
| 375 | v[pos + 2] |= u8(w1 << 4) |
| 376 | v[pos + 3] = u8(w1 >> 4) |
| 377 | v[pos + 4] = u8(w1 >> 12) |
| 378 | pos += 5 |
| 379 | } |
| 380 | return v |
| 381 | } |
| 382 | |
| 383 | // algo. 19: BitUnpack (s. 7.1) |
| 384 | fn bit_unpack(v []u8, p Params) RingElement { |
| 385 | match p.gamma1 { |
| 386 | 17 { return bit_unpack_18(v) } |
| 387 | 19 { return bit_unpack_20(v) } |
| 388 | else { panic('mldsa: unsupported gamma1') } // unreachable |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | @[direct_array_access] |
| 393 | fn bit_unpack_18(v []u8) RingElement { |
| 394 | mut r := RingElement{} |
| 395 | mut pos := 0 |
| 396 | for i := 0; i < n; i += 4 { |
| 397 | w0 := u32(v[pos]) | (u32(v[pos + 1]) << 8) | (u32(v[pos + 2]) << 16) |
| 398 | r[i] = field_sub_to_montgomery(u32(1 << 17), w0 & 0x3ffff) |
| 399 | w1 := (u32(v[pos + 2]) >> 2) | (u32(v[pos + 3]) << 6) | (u32(v[pos + 4]) << 14) |
| 400 | r[i + 1] = field_sub_to_montgomery(u32(1 << 17), w1 & 0x3ffff) |
| 401 | w2 := (u32(v[pos + 4]) >> 4) | (u32(v[pos + 5]) << 4) | (u32(v[pos + 6]) << 12) |
| 402 | r[i + 2] = field_sub_to_montgomery(u32(1 << 17), w2 & 0x3ffff) |
| 403 | w3 := (u32(v[pos + 6]) >> 6) | (u32(v[pos + 7]) << 2) | (u32(v[pos + 8]) << 10) |
| 404 | r[i + 3] = field_sub_to_montgomery(u32(1 << 17), w3 & 0x3ffff) |
| 405 | pos += 9 |
| 406 | } |
| 407 | return r |
| 408 | } |
| 409 | |
| 410 | @[direct_array_access] |
| 411 | fn bit_unpack_20(v []u8) RingElement { |
| 412 | mut r := RingElement{} |
| 413 | mut pos := 0 |
| 414 | for i := 0; i < n; i += 2 { |
| 415 | w0 := u32(v[pos]) | (u32(v[pos + 1]) << 8) | (u32(v[pos + 2]) << 16) |
| 416 | r[i] = field_sub_to_montgomery(u32(1 << 19), w0 & 0xfffff) |
| 417 | w1 := (u32(v[pos + 2]) >> 4) | (u32(v[pos + 3]) << 4) | (u32(v[pos + 4]) << 12) |
| 418 | r[i + 1] = field_sub_to_montgomery(u32(1 << 19), w1 & 0xfffff) |
| 419 | pos += 5 |
| 420 | } |
| 421 | return r |
| 422 | } |
| 423 | |
| 424 | // algo. 20: HintBitPack (s. 7.2) |
| 425 | @[direct_array_access] |
| 426 | fn hint_encode(h [][256]u8, p Params) []u8 { |
| 427 | mut out := []u8{len: p.omega + p.k} |
| 428 | mut idx := 0 |
| 429 | for i in 0 .. p.k { |
| 430 | for j in 0 .. n { |
| 431 | if h[i][j] != 0 { |
| 432 | out[idx] = u8(j) |
| 433 | idx++ |
| 434 | } |
| 435 | } |
| 436 | out[p.omega + i] = u8(idx) |
| 437 | } |
| 438 | return out |
| 439 | } |
| 440 | |
| 441 | // algo. 21: HintBitUnpack (s. 7.2) |
| 442 | @[direct_array_access] |
| 443 | fn hint_decode(y []u8, p Params) ![][256]u8 { |
| 444 | if y.len != p.omega + p.k { |
| 445 | return error('invalid hint length') |
| 446 | } |
| 447 | mut h := [][256]u8{len: p.k, init: [256]u8{}} |
| 448 | mut idx := u8(0) |
| 449 | for i in 0 .. p.k { |
| 450 | limit := y[p.omega + i] |
| 451 | if limit < idx || limit > u8(p.omega) { |
| 452 | return error('invalid hint limits') |
| 453 | } |
| 454 | first := idx |
| 455 | for idx < limit { |
| 456 | if idx > first && y[idx - 1] >= y[idx] { |
| 457 | return error('invalid hint index order') |
| 458 | } |
| 459 | h[i][y[idx]] = 1 |
| 460 | idx++ |
| 461 | } |
| 462 | } |
| 463 | for i := int(idx); i < p.omega; i++ { |
| 464 | if y[i] != 0 { |
| 465 | return error('invalid hint padding') |
| 466 | } |
| 467 | } |
| 468 | return h |
| 469 | } |
| 470 | |
| 471 | @[direct_array_access] |
| 472 | fn bit_pack_slow(r RingElement, a int, b int) []u8 { |
| 473 | bitlen := bits_len(u32(a + b)) |
| 474 | mut out := []u8{len: n * bitlen / 8} |
| 475 | mut acc := u32(0) |
| 476 | mut acc_bits := u32(0) |
| 477 | mut vi := 0 |
| 478 | for i in 0 .. n { |
| 479 | w := u32(int(b) - int(field_centered_mod(r[i]))) |
| 480 | acc |= w << acc_bits |
| 481 | acc_bits += u32(bitlen) |
| 482 | for acc_bits >= 8 { |
| 483 | out[vi] = u8(acc) |
| 484 | vi++ |
| 485 | acc >>= 8 |
| 486 | acc_bits -= 8 |
| 487 | } |
| 488 | } |
| 489 | if acc_bits > 0 { |
| 490 | out[vi] = u8(acc) |
| 491 | } |
| 492 | return out |
| 493 | } |
| 494 | |
| 495 | @[direct_array_access] |
| 496 | fn bit_unpack_slow(v []u8, a int, b int) !RingElement { |
| 497 | bitlen := bits_len(u32(a + b)) |
| 498 | if v.len != n * bitlen / 8 { |
| 499 | return error('mldsa: invalid input length for bit_unpack_slow') |
| 500 | } |
| 501 | |
| 502 | mask := u32((1 << bitlen) - 1) |
| 503 | max_value := u32(a + b) |
| 504 | |
| 505 | mut r := RingElement{} |
| 506 | mut acc := u32(0) |
| 507 | mut acc_bits := u32(0) |
| 508 | mut vi := 0 |
| 509 | |
| 510 | for i in 0 .. n { |
| 511 | for acc_bits < u32(bitlen) { |
| 512 | if vi < v.len { |
| 513 | acc |= u32(v[vi]) << acc_bits |
| 514 | vi++ |
| 515 | acc_bits += 8 |
| 516 | } |
| 517 | } |
| 518 | w := acc & mask |
| 519 | if w > max_value { |
| 520 | return error('mldsa: coefficient out of range') |
| 521 | } |
| 522 | r[i] = field_sub_to_montgomery(u32(b), w) |
| 523 | acc >>= u32(bitlen) |
| 524 | acc_bits -= u32(bitlen) |
| 525 | } |
| 526 | |
| 527 | return r |
| 528 | } |
| 529 | |
| 530 | fn bits_len(x u32) int { |
| 531 | if x == 0 { |
| 532 | return 0 |
| 533 | } |
| 534 | mut v := x |
| 535 | mut n_ := 0 |
| 536 | for v > 0 { |
| 537 | v >>= 1 |
| 538 | n_++ |
| 539 | } |
| 540 | return n_ |
| 541 | } |
| 542 | |
| 543 | // algo. 24: skEncode (s. 7.2) |
| 544 | fn sk_encode(rho []u8, capital_k [32]u8, tr [64]u8, s1 []NttElement, s2 []NttElement, t0 []NttElement, p Params) []u8 { |
| 545 | mut out := []u8{} |
| 546 | out << rho[..32] |
| 547 | out << capital_k[..] |
| 548 | out << tr[..] |
| 549 | eta := int(p.eta) |
| 550 | for i in 0 .. p.l { |
| 551 | out << bit_pack_slow(inverse_ntt(s1[i]), eta, eta) |
| 552 | } |
| 553 | for i in 0 .. p.k { |
| 554 | out << bit_pack_slow(inverse_ntt(s2[i]), eta, eta) |
| 555 | } |
| 556 | for i in 0 .. p.k { |
| 557 | out << bit_pack_slow(inverse_ntt(t0[i]), 4095, 4096) |
| 558 | } |
| 559 | return out |
| 560 | } |
| 561 | |
| 562 | // algo. 25: skDecode (s. 7.2) |
| 563 | fn sk_decode(sk []u8, p Params) !([]u8, [32]u8, [64]u8, []RingElement, []RingElement, []RingElement) { |
| 564 | k, l, eta := p.k, p.l, p.eta |
| 565 | if sk.len != priv_key_size(p) { |
| 566 | return error('mldsa: invalid private key size') |
| 567 | } |
| 568 | rho := sk[..32].clone() |
| 569 | capital_k := slice_to_32(sk[32..64]) |
| 570 | tr := slice_to_64(sk[64..128]) |
| 571 | mut offset := 128 |
| 572 | |
| 573 | eta_len := n * bits_len(u32(eta * 2)) / 8 |
| 574 | mut s1 := []RingElement{len: l} |
| 575 | for i in 0 .. l { |
| 576 | s1[i] = bit_unpack_slow(sk[offset..offset + eta_len], eta, eta)! |
| 577 | offset += eta_len |
| 578 | } |
| 579 | |
| 580 | mut s2 := []RingElement{len: k} |
| 581 | for i in 0 .. k { |
| 582 | s2[i] = bit_unpack_slow(sk[offset..offset + eta_len], eta, eta)! |
| 583 | offset += eta_len |
| 584 | } |
| 585 | |
| 586 | t0_len := n * 13 / 8 |
| 587 | mut t0 := []RingElement{len: k} |
| 588 | for i in 0 .. k { |
| 589 | t0[i] = bit_unpack_slow(sk[offset..offset + t0_len], 4095, 4096)! |
| 590 | offset += t0_len |
| 591 | } |
| 592 | |
| 593 | return rho, capital_k, tr, s1, s2, t0 |
| 594 | } |
| 595 | |