diff --git a/sw/otbn/crypto/mldsa87/mldsa87_gadgets.s b/sw/otbn/crypto/mldsa87/mldsa87_gadgets.s new file mode 100644 index 0000000000000..4d82ff4774d53 --- /dev/null +++ b/sw/otbn/crypto/mldsa87/mldsa87_gadgets.s @@ -0,0 +1,161 @@ +/* Copyright lowRISC contributors (OpenTitan project). */ +/* Licensed under the Apache License, Version 2.0, see LICENSE for details. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* Secure masked gadgets. */ + +.globl sec_a2b_8x32 +.globl sec_b2a_8x32 +.globl sec_add_8x32 +.globl sec_unmask_8x32 + +/* + +The masking accelerator supports three functions that operate in a vectroized +fashion on 8 coefficients in a single WDR. The interface consists of four input +WSRs to pass two shared vectors to the accelerator, MAI_IN0_S0 and MAI_IN0_S1 +for the first input and MAI_IN1_S0 and MAI_IN1_S1 for the second input which is +only used for the second summand in the secure addition. The shared result of +the operation can be read in the MAI_RES_S0 and MAI_RES_S1 WSRs. The interface +has a single control register MAI_CTRL that instruments the accelerator and +immediately triggers a computation upon seeing a valid configuration value. + +Note that some routines of this modules draw randomness from URND for masking +purposes, hence while an MAI operation is ongoing, URND bits shall not be +shared between different routines. + +Most routines of this module are a direct implementation of the gadgets in the +work of Azouaoui et al. [1]. + +[1] https://tches.iacr.org/index.php/TCHES/article/view/11158/10597 + +*/ + +/* + * Configuration values for the MAI_CTRL register. + */ +.set MAI_CTRL_A2B, 0x1 +.set MAI_CTRL_B2A, 0x3 +.set MAI_CTRL_ADD, 0x5 + +.text + +/* MAI interface polling routine. */ +_mai_poll: + csrrs x20, MAI_STATUS, x0 + andi x20, x20, 0x1 + bne x20, x0, _mai_poll + ret + +/** + * Convert the arithmetic sharing of a vector of 8 coefficients (x0_A, x1_A) to + * a Boolean sharing (x0_B, x1_B). + * + * @param[in] w0: x0_A, first arithmetic share. + * @param[in] w1: x1_A, second arithmetic share. + * @param[out] w0: x0_B, first Boolean share. + * @param[out] w1: x1_B, second Boolean share + */ +sec_a2b_8x32: + /* Write the two shares to the input WSRs (intersperse with configuration of + MAI_CTRL to not access both shares in subsequent instructions). */ + bn.wsrw MAI_IN0_S0, w0 + addi x20, x0, MAI_CTRL_A2B + bn.wsrw MAI_IN0_S1, w1 + + /* Trigger the conversion. */ + csrrw x0, MAI_CTRL, x20 + + /* TODO: Replace with deterministic wait, once exact latency is known. */ + jal x1, _mai_poll + + /* Read back the result. */ + bn.wsrr w0, MAI_RES_S0 + bn.xor w31, w31, w31 /* dummy */ + bn.wsrr w1, MAI_RES_S1 + + ret + +/** + * Convert the Boolean sharing of a vector of 8 coefficients (x0_B, x1_B) to a + * arithmetic sharing (x0_A, x1_A). + * + * @param[in] w0: x0_B, first arithmetic share. + * @param[in] w1: x1_B, second arithmetic share. + * @param[out] w0: x0_A, first Boolean share. + * @param[out] w1: x1_A, second Boolean share + */ +sec_b2a_8x32: + /* Write the two shares to the input WSRs (intersperse with configuration of + MAI_CTRL to not access both shares in subsequent instructions). */ + bn.wsrw MAI_IN0_S0, w0 + addi x20, x0, MAI_CTRL_B2A + bn.wsrw MAI_IN0_S1, w1 + + /* Trigger the conversion. */ + csrrw x0, MAI_CTRL, x20 + + /* TODO: Replace with deterministic wait, once latency is known. */ + jal x1, _mai_poll + + /* Read back the result. */ + bn.wsrr w0, MAI_RES_S0 + bn.xor w31, w31, w31 /* dummy */ + bn.wsrr w1, MAI_RES_S1 + + ret + +/** + * Calculate a vectorized addition modulo 2^32 of 8 Boolean-shared coefficients. + * + * @param[in] w0: x0_B, first Boolean share of x + * @param[in] w1: x1_B, second Boolean share of x. + * @param[in] w2: y0_B, first Boolean share of y + * @param[in] w3: y1_B, second Boolean share of y. + * @param[out] w0: z0_B, first Boolean share of the result z = x + y. + * @param[out] w1: z1_B, second Boolean share of the result z = x + y. + */ +sec_add_8x32: + /* Write the two summands to the input WSRs (intersperse with configuration of + MAI_CTRL to not access both shares in subsequent instructions). */ + bn.wsrw MAI_IN0_S0, w0 + bn.wsrw MAI_IN1_S0, w2 + + addi x20, x0, MAI_CTRL_ADD + + bn.wsrw MAI_IN0_S1, w1 + bn.wsrw MAI_IN1_S1, w3 + + /* Trigger the conversion. */ + csrrw x0, MAI_CTRL, x20 + + /* TODO: Replace with deterministic wait, once latency is known. */ + jal x1, _mai_poll + + /* Read back the result. */ + bn.wsrr w0, MAI_RES_S0 + bn.xor w31, w31, w31 /* dummy */ + bn.wsrr w1, MAI_RES_S1 + + ret + +/** + * Securely unmask a vector of 8 Boolean-shared coefficients. + * + * This is an implementation of the `SecUnMask` function (Algorithm 3 in [1]). + * + * @param[in] w0: x0_B, first Boolean share of x + * @param[in] w1: x1_B, second Boolean share of x. + * @param[out] w0: x, unmasked value x. + */ +sec_unmask_8x32: + /* Sample a fresh random mask and XOR it to the shares before unmasking. */ + bn.wsrr w20, URND + + bn.xor w0, w0, w20 + bn.xor w31, w31, w31 /* dummy */ + bn.xor w1, w1, w20 + + bn.xor w0, w0, w1 + + ret diff --git a/sw/otbn/crypto/mldsa87/tests/BUILD b/sw/otbn/crypto/mldsa87/tests/BUILD index 5f1f4d7784f27..adb727dcde470 100644 --- a/sw/otbn/crypto/mldsa87/tests/BUILD +++ b/sw/otbn/crypto/mldsa87/tests/BUILD @@ -13,6 +13,7 @@ srcs = [ "//sw/otbn/crypto/mldsa87:mldsa87_ntt.s", "//sw/otbn/crypto/mldsa87:mldsa87_sample.s", "//sw/otbn/crypto/mldsa87:mldsa87_encoding.s", + "//sw/otbn/crypto/mldsa87:mldsa87_gadgets.s", "//sw/otbn/crypto/mldsa87:mldsa87_xof.s", "//sw/otbn/crypto/mldsa87:mldsa87_expand.s", ] @@ -32,6 +33,10 @@ unit_tests = [ "mldsa87_encode_w1_test", "mldsa87_xof_shake128_test", "mldsa87_xof_shake256_test", + "mldsa87_sec_a2b_test", + "mldsa87_sec_b2a_test", + "mldsa87_sec_add_test", + "mldsa87_sec_unmask_test", ] [ diff --git a/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_a2b_test.hjson b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_a2b_test.hjson new file mode 100644 index 0000000000000..00397d9660c80 --- /dev/null +++ b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_a2b_test.hjson @@ -0,0 +1,12 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +{ + "entrypoint": "main", + "output": { + "regs": { + "w2": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + } + } +} diff --git a/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_a2b_test.s b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_a2b_test.s new file mode 100644 index 0000000000000..751bcb3e60ece --- /dev/null +++ b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_a2b_test.s @@ -0,0 +1,60 @@ +/* Copyright lowRISC contributors (OpenTitan project). */ +/* Licensed under the Apache License, Version 2.0, see LICENSE for details. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* Randomized test to verify the A2B conversion. */ + +.section .text.start + +main: + la x31, _stack + bn.xor w31, w31, w31 + + la x2, _params + bn.lid x0, 0(x2) + bn.wsrw MOD, w0 + + bn.not w2, w31 /* flag */ + bn.shv.8s w3, w2 >> 10 /* mask */ + + /* Generate 100 random arithmetically shared vectors and verify that they can + be correctly converted to Boolean shares. */ + loopi 100, 10 + /* Random vector of coefficients < q. */ + bn.wsrr w4, URND + bn.and w4, w4, w3 + + /* Random coefficient masks < q. */ + bn.wsrr w5, URND + bn.and w5, w5, w3 + + /* Create the two arithmetic shares and trigger the conversion. */ + bn.subvm.8s w0, w4, w5 + bn.mov w1, w5 + jal x1, sec_a2b_8x32 + + /* Unmask the result. */ + bn.xor w0, w0, w1 + + /* Check that the unmask result is equal to the initial vector. */ + bn.cmp w0, w4, FG0 + bn.sel w2, w2, w31, FG0.Z + /* End of loop */ + + ecall + +.data +.balign 32 + +_params: +.word 0x007fe001 /* q */ +.word 0xfc7fdfff /* mu */ +.word 0x0000a3fa /* n^-1 * R^3 mod q */ +.word 0x00000000 +.word 0x00000000 +.word 0x00000000 +.word 0x00000000 +.word 0x00000000 + +_stack: +.zero 4 diff --git a/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_add_test.hjson b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_add_test.hjson new file mode 100644 index 0000000000000..d4e69dc2b60d0 --- /dev/null +++ b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_add_test.hjson @@ -0,0 +1,12 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +{ + "entrypoint": "main", + "output": { + "regs": { + "w4": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + } + } +} diff --git a/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_add_test.s b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_add_test.s new file mode 100644 index 0000000000000..158349e653588 --- /dev/null +++ b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_add_test.s @@ -0,0 +1,64 @@ +/* Copyright lowRISC contributors (OpenTitan project). */ +/* Licensed under the Apache License, Version 2.0, see LICENSE for details. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* Randomized test to verify the secure addition. */ + +.section .text.start + +main: + la x31, _stack + bn.xor w31, w31, w31 + + la x2, _params + bn.lid x0, 0(x2) + bn.wsrw MOD, w0 + + bn.not w4, w31 /* flag */ + + /* Generate 100 random Boolean-shared summands and verify that they can be + correctly added together. */ + loopi 100, 13 + /* Random vectors x and y < 2^32. */ + bn.wsrr w5, URND + bn.wsrr w6, URND + + /* Expected result x + y mod 2^32. */ + bn.addv.8s w7, w5, w6 + + /* Random masks */ + bn.wsrr w8, URND + bn.wsrr w9, URND + + /* Create the two Boolean shares and trigger the conversion. */ + bn.xor w0, w5, w8 + bn.mov w1, w8 + bn.xor w2, w6, w9 + bn.mov w3, w9 + jal x1, sec_add_8x32 + + /* Unmask the result. */ + bn.xor w0, w0, w1 + + /* Check that the unmask result is equal to the initial vector. */ + bn.cmp w0, w7, FG0 + bn.sel w4, w4, w31, FG0.Z + /* End of loop */ + + ecall + +.data +.balign 32 + +_params: +.word 0x007fe001 /* q */ +.word 0xfc7fdfff /* mu */ +.word 0x0000a3fa /* n^-1 * R^3 mod q */ +.word 0x00000000 +.word 0x00000000 +.word 0x00000000 +.word 0x00000000 +.word 0x00000000 + +_stack: +.zero 4 diff --git a/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_b2a_test.hjson b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_b2a_test.hjson new file mode 100644 index 0000000000000..00397d9660c80 --- /dev/null +++ b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_b2a_test.hjson @@ -0,0 +1,12 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +{ + "entrypoint": "main", + "output": { + "regs": { + "w2": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + } + } +} diff --git a/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_b2a_test.s b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_b2a_test.s new file mode 100644 index 0000000000000..f751421a4c2e5 --- /dev/null +++ b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_b2a_test.s @@ -0,0 +1,59 @@ +/* Copyright lowRISC contributors (OpenTitan project). */ +/* Licensed under the Apache License, Version 2.0, see LICENSE for details. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* Randomized test to verify the B2A conversion. */ + +.section .text.start + +main: + la x31, _stack + bn.xor w31, w31, w31 + + la x2, _params + bn.lid x0, 0(x2) + bn.wsrw MOD, w0 + + bn.not w2, w31 /* flag */ + bn.shv.8s w3, w2 >> 10 /* mask */ + + /* Generate 100 random Boolean-shared vectors and verify that they can be + correctly converted to arithmetic shares. */ + loopi 100, 9 + /* Random vector of coefficients < q. */ + bn.wsrr w4, URND + bn.and w4, w4, w3 + + /* Random coefficient masks. */ + bn.wsrr w5, URND + + /* Create the two Boolean shares and trigger the conversion. */ + bn.xor w0, w4, w5 + bn.mov w1, w5 + jal x1, sec_b2a_8x32 + + /* Unmask the result. */ + bn.addvm.8s w0, w0, w1 + + /* Check that the unmask result is equal to the initial vector. */ + bn.cmp w0, w4, FG0 + bn.sel w2, w2, w31, FG0.Z + /* End of loop */ + + ecall + +.data +.balign 32 + +_params: +.word 0x007fe001 /* q */ +.word 0xfc7fdfff /* mu */ +.word 0x0000a3fa /* n^-1 * R^3 mod q */ +.word 0x00000000 +.word 0x00000000 +.word 0x00000000 +.word 0x00000000 +.word 0x00000000 + +_stack: +.zero 4 diff --git a/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_unmask_test.hjson b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_unmask_test.hjson new file mode 100644 index 0000000000000..00397d9660c80 --- /dev/null +++ b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_unmask_test.hjson @@ -0,0 +1,12 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +{ + "entrypoint": "main", + "output": { + "regs": { + "w2": "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + } + } +} diff --git a/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_unmask_test.s b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_unmask_test.s new file mode 100644 index 0000000000000..d650f711101aa --- /dev/null +++ b/sw/otbn/crypto/mldsa87/tests/mldsa87_sec_unmask_test.s @@ -0,0 +1,54 @@ +/* Copyright lowRISC contributors (OpenTitan project). */ +/* Licensed under the Apache License, Version 2.0, see LICENSE for details. */ +/* SPDX-License-Identifier: Apache-2.0 */ + +/* Randomized test to verify the secure Boolean unmasking. */ + +.section .text.start + +main: + la x31, _stack + bn.xor w31, w31, w31 + + la x2, _params + bn.lid x0, 0(x2) + bn.wsrw MOD, w0 + + bn.not w2, w31 /* flag */ + + /* Generate 100 random Boolean-shared vectors and verify that they can be + correctly unmasked. */ + loopi 100, 7 + /* Random vector. */ + bn.wsrr w3, URND + + /* Random masks. */ + bn.wsrr w4, URND + + /* Create the two Boolean shares and trigger the conversion. */ + bn.xor w0, w3, w4 + bn.mov w1, w4 + jal x1, sec_unmask_8x32 + + /* Check that the unmask result is equal to the initial vector. */ + bn.cmp w0, w3, FG0 + bn.sel w2, w2, w31, FG0.Z + /* End of loop */ + + ecall + +.data +.balign 32 + +_params: +.word 0x007fe001 /* q */ +.word 0xfc7fdfff /* mu */ +.word 0x0000a3fa /* n^-1 * R^3 mod q */ +.word 0x00000000 +.word 0x00000000 +.word 0x00000000 +.word 0x00000000 +.word 0x00000000 + +_stack: +.zero 4