blob: e9b9f9f257a69ceca5677d6d8abbee8edb652288 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
//
// Domain-Oriented Masking GF(2) Multiplier with 2-shares
// ref: Higher-Order Side-Channel Protected Implementations of Keccak
// https://eprint.iacr.org/2017/395.pdf
//
// q0 = a0 & b0 + (a0 & b1 + z)
// q1 = a1 & b1 + (a1 & b0 + z)
// () ==> registered
//
// all input should be stable for two clocks
// as the output is valid after a clock
// For z, it can use other slice from the state
// as it is fairly random w.r.t the current inputs.
// General formula of Q in the paper
// Qi = t{i,i} + Sig(j>i,d)(t{i,j}+Z{i+j*(j-1)/2}) + Sig(j<i,d)(t{i,j}+Z{j+i*(i-1)/2})
// for d=1 (NumShare 2 for first order protection)
// Q0 = t{0,0} + Sig(j>0,1)(t{0,j}+Z{j(j-1)/2}) + Sig(j<0,d)(..)
// = a0&b0 + (a0&b1 + z0 + 0)
// Q1 = t{1,1} + sig(j>1,1)(...) + sig(j<1,1)(t{1,j} + Z{j})
// = a1&b1 + (0 + a1&b0 + z0)
`include "prim_assert.sv"
module prim_dom_and_2share #(
parameter int DW = 64 // Input width
) (
input clk_i,
input rst_ni,
input [DW-1:0] a0_i, // share0 of a
input [DW-1:0] a1_i, // share1 of a
input [DW-1:0] b0_i, // share0 of b
input [DW-1:0] b1_i, // share1 of b
input z_valid_i, // random number input validity
input [DW-1:0] z_i, // random number
output logic [DW-1:0] q0_o, // share0 of q
output logic [DW-1:0] q1_o // share1 of q
);
logic [DW-1:0] t0_d, t0_q, t1_d, t1_q;
logic [DW-1:0] t_a0b1, t_a1b0, t_a0b0, t_a1b1;
/////////////////
// Calculation //
/////////////////
// Inner-domain terms
assign t_a0b0 = a0_i & b0_i;
assign t_a1b1 = a1_i & b1_i;
// Cross-domain terms
assign t_a0b1 = a0_i & b1_i;
assign t_a1b0 = a1_i & b0_i;
///////////////
// Resharing //
///////////////
// Resharing of cross-domain terms
// Preserve the logic sequence for XOR not to proceed cross-domain AND.
prim_xor2 #(
.Width ( DW*2 )
) u_prim_xor_t01 (
.in0_i ( {t_a0b1, t_a1b0} ),
.in1_i ( {z_i, z_i} ),
.out_o ( {t0_d, t1_d} )
);
// Register stage
prim_flop_en #(
.Width ( DW*2 ),
.ResetValue ( '0 )
) u_prim_flop_t01 (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.en_i ( z_valid_i ),
.d_i ( {t0_d, t1_d} ),
.q_o ( {t0_q, t1_q} )
);
/////////////////
// Integration //
/////////////////
// Preserve the logic sequence for XOR not to proceed the inner-domain AND.
prim_xor2 #(
.Width ( DW*2 )
) u_prim_xor_q01 (
.in0_i ( {t_a0b0, t_a1b1} ),
.in1_i ( {t0_q, t1_q} ),
.out_o ( {q0_o, q1_o} )
);
// DOM AND should be same as unmasked computation
// TODO: Put assumption that input need to be stable for at least two cycles
// The correct test sequence will be:
// 1. inputs are changed
// 2. check if z_valid_i,
// 3. at the next cycle, inputs are still stable (assumption)
// 4. and results Q == A & B (assertion)
// To speed up the FPV process, random value is ready in less than or
// equal to two cycles.
`ASSUME_FPV(RandomReadyInShortTime_A,
$changed(a0_i) || $changed(a1_i) || $changed(b0_i) || $changed(b1_i)
|-> ##[0:2] z_valid_i,
clk_i, !rst_ni)
`ASSERT(UnmaskedAndMatched_A,
$changed(a0_i) || $changed(a1_i) || $changed(b0_i) || $changed(b1_i)
|-> ##[0:$] z_valid_i
|=> $stable(a0_i) && $stable(a1_i) && $stable(b0_i) && $stable(b1_i)
|-> (q0_o ^ q1_o) == ((a0_i ^ a1_i) & (b0_i ^ b1_i)),
clk_i, !rst_ni)
endmodule