Skip to content

Commit fa78d66

Browse files
author
gefeili
committed
Pass Key Pair Generation of Mayo
1 parent 761d964 commit fa78d66

File tree

12 files changed

+1361
-2
lines changed

12 files changed

+1361
-2
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package org.bouncycastle.pqc.crypto.mayo;
2+
3+
public class GF16Utils
4+
{
5+
6+
/**
7+
* Multiplies a 64-bit limb by a GF(16) element (represented as an int, 0–255).
8+
* This emulates gf16v_mul_u64 from C.
9+
*
10+
* @param a a 64-bit limb
11+
* @param b an 8-bit GF(16) element (only the low 4 bits are used)
12+
* @return the product as a 64-bit limb
13+
*/
14+
public static long gf16vMulU64(long a, int b)
15+
{
16+
long maskMsb = 0x8888888888888888L;
17+
long a64 = a;
18+
// In the original code there is a conditional XOR with unsigned_char_blocker;
19+
// here we simply use b directly.
20+
long b32 = b & 0x00000000FFFFFFFFL;
21+
long r64 = a64 * (b32 & 1);
22+
23+
long a_msb = a64 & maskMsb;
24+
a64 ^= a_msb;
25+
a64 = (a64 << 1) ^ ((a_msb >>> 3) * 3);
26+
r64 ^= a64 * ((b32 >> 1) & 1);
27+
28+
a_msb = a64 & maskMsb;
29+
a64 ^= a_msb;
30+
a64 = (a64 << 1) ^ ((a_msb >>> 3) * 3);
31+
r64 ^= a64 * ((b32 >>> 2) & 1);
32+
33+
a_msb = a64 & maskMsb;
34+
a64 ^= a_msb;
35+
a64 = (a64 << 1) ^ ((a_msb >>> 3) * 3);
36+
r64 ^= a64 * ((b32 >> 3) & 1);
37+
38+
return r64;
39+
}
40+
41+
/**
42+
* Multiplies each limb of a GF(16) vector (subarray of 'in') by the GF(16) element 'a'
43+
* and XORs the result into the corresponding subarray of acc.
44+
* <p>
45+
* This version uses explicit array offsets.
46+
*
47+
* @param mVecLimbs the number of limbs in the vector
48+
* @param in the input long array containing the vector; the vector starts at index inOffset
49+
* @param inOffset the starting index in 'in'
50+
* @param a the GF(16) element (0–255) to multiply by
51+
* @param acc the accumulator long array; the target vector starts at index accOffset
52+
* @param accOffset the starting index in 'acc'
53+
*/
54+
public static void mVecMulAdd(int mVecLimbs, long[] in, int inOffset, int a, long[] acc, int accOffset)
55+
{
56+
for (int i = 0; i < mVecLimbs; i++)
57+
{
58+
acc[accOffset + i] ^= gf16vMulU64(in[inOffset + i], a);
59+
}
60+
}
61+
62+
/**
63+
* Convenience overload of mVecMulAdd that assumes zero offsets.
64+
*
65+
* @param mVecLimbs the number of limbs
66+
* @param in the input vector
67+
* @param a the GF(16) element to multiply by
68+
* @param acc the accumulator vector
69+
*/
70+
public static void mVecMulAdd(int mVecLimbs, long[] in, int a, long[] acc)
71+
{
72+
mVecMulAdd(mVecLimbs, in, 0, a, acc, 0);
73+
}
74+
75+
/**
76+
* Performs the multiplication and accumulation of a block of an upper‐triangular matrix
77+
* times a second matrix.
78+
*
79+
* @param mVecLimbs number of limbs per m-vector.
80+
* @param bsMat the “basis” matrix (as a flat long[] array); each entry occupies mVecLimbs elements.
81+
* @param mat the second matrix (as a flat byte[] array) stored row‐major,
82+
* with dimensions (bsMatCols x matCols).
83+
* @param acc the accumulator (as a flat long[] array) with dimensions (bsMatRows x matCols);
84+
* each “entry” is an m‐vector (length mVecLimbs).
85+
* @param bsMatRows number of rows in the bsMat (the “triangular” matrix’s row count).
86+
* @param bsMatCols number of columns in bsMat.
87+
* @param matCols number of columns in the matrix “mat.”
88+
* @param triangular if 1, start column index for each row is (r * triangular); otherwise use 0.
89+
*/
90+
public static void mulAddMUpperTriangularMatXMat(int mVecLimbs, long[] bsMat, byte[] mat, long[] acc,
91+
int bsMatRows, int bsMatCols, int matCols, int triangular)
92+
{
93+
int bsMatEntriesUsed = 0;
94+
for (int r = 0; r < bsMatRows; r++)
95+
{
96+
// For each row r, the inner loop goes from column triangular*r to bsMatCols-1.
97+
for (int c = triangular * r; c < bsMatCols; c++)
98+
{
99+
for (int k = 0; k < matCols; k++)
100+
{
101+
// Calculate the offsets:
102+
// For bsMat: the m-vector starting at index bsMatEntriesUsed * mVecLimbs.
103+
int bsMatOffset = bsMatEntriesUsed * mVecLimbs;
104+
// For mat: element at row c, column k (row-major layout).
105+
int a = mat[c * matCols + k] & 0xFF;
106+
// For acc: add into the m-vector at row r, column k.
107+
int accOffset = (r * matCols + k) * mVecLimbs;
108+
GF16Utils.mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, a, acc, accOffset);
109+
}
110+
bsMatEntriesUsed++;
111+
}
112+
}
113+
}
114+
115+
/**
116+
* Computes P1_times_O.
117+
* <p>
118+
* In C:
119+
* P1_times_O(p, P1, O, acc) calls:
120+
* mul_add_m_upper_triangular_mat_x_mat(PARAM_m_vec_limbs(p), P1, O, acc, PARAM_v(p), PARAM_v(p), PARAM_o(p), 1);
121+
*
122+
* @param p the parameter object.
123+
* @param P1 the P1 matrix as a long[] array.
124+
* @param O the O matrix as a byte[] array.
125+
* @param acc the output accumulator (long[] array).
126+
*/
127+
public static void P1TimesO(MayoParameters p, long[] P1, byte[] O, long[] acc)
128+
{
129+
int mVecLimbs = p.getMVecLimbs();
130+
int paramV = p.getV();
131+
int paramO = p.getO();
132+
// Here, bsMatRows and bsMatCols are both paramV, and matCols is paramO, triangular=1.
133+
mulAddMUpperTriangularMatXMat(mVecLimbs, P1, O, acc, paramV, paramV, paramO, 1);
134+
}
135+
136+
/**
137+
* Multiplies the transpose of a single matrix with m matrices and adds the result into acc.
138+
*
139+
* @param mVecLimbs number of limbs per m-vector.
140+
* @param mat the matrix to be transposed (as a flat byte[] array), dimensions: (matRows x matCols).
141+
* @param bsMat the m-matrix (as a flat long[] array), with each entry of length mVecLimbs.
142+
* Its logical dimensions: (matRows x bsMatCols).
143+
* @param acc the accumulator (as a flat long[] array) with dimensions (matCols x bsMatCols);
144+
* each entry is an m-vector.
145+
* @param matRows number of rows in the matrix “mat.”
146+
* @param matCols number of columns in “mat.”
147+
* @param bsMatCols number of columns in the bsMat matrix.
148+
*/
149+
public static void mulAddMatTransXMMat(int mVecLimbs, byte[] mat, long[] bsMat, long[] acc,
150+
int matRows, int matCols, int bsMatCols)
151+
{
152+
// Loop over each column r of mat (which becomes row of mat^T)
153+
for (int r = 0; r < matCols; r++)
154+
{
155+
for (int c = 0; c < matRows; c++)
156+
{
157+
for (int k = 0; k < bsMatCols; k++)
158+
{
159+
// For bsMat: the m-vector at index (c * bsMatCols + k)
160+
int bsMatOffset = (c * bsMatCols + k) * mVecLimbs;
161+
// For mat: element at row c, column r.
162+
int a = mat[c * matCols + r] & 0xFF;
163+
// For acc: add into the m-vector at index (r * bsMatCols + k)
164+
int accOffset = (r * bsMatCols + k) * mVecLimbs;
165+
GF16Utils.mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, a, acc, accOffset);
166+
}
167+
}
168+
}
169+
}
170+
171+
172+
/**
173+
* Adds (bitwise XOR) mVecLimbs elements from the source array (starting at srcOffset)
174+
* into the destination array (starting at destOffset).
175+
*/
176+
public static void mVecAdd(int mVecLimbs, long[] src, int srcOffset, long[] dest, int destOffset)
177+
{
178+
for (int i = 0; i < mVecLimbs; i++)
179+
{
180+
dest[destOffset + i] ^= src[srcOffset + i];
181+
}
182+
}
183+
184+
}
185+
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
package org.bouncycastle.pqc.crypto.mayo;
2+
3+
import org.bouncycastle.crypto.BlockCipher;
4+
import org.bouncycastle.crypto.engines.AESEngine;
5+
import org.bouncycastle.crypto.modes.CTRModeCipher;
6+
import org.bouncycastle.crypto.modes.SICBlockCipher;
7+
import org.bouncycastle.crypto.params.KeyParameter;
8+
import org.bouncycastle.crypto.params.ParametersWithIV;
9+
import org.bouncycastle.util.Arrays;
10+
import org.bouncycastle.util.Pack;
11+
12+
public class MayoEngine
13+
{
14+
/**
15+
* Expands P1 and P2 using AES_128_CTR as a PRF and then unpacks the resulting bytes
16+
* into an array of 64-bit limbs.
17+
*
18+
* @param p Mayo parameters
19+
* @param P The output long array which will hold the unpacked limbs.
20+
* Its length should be at least ((P1_bytes + P2_bytes) / 8) limbs.
21+
* @param seed_pk The seed (used as the key) for the PRF.
22+
* @return The number of bytes produced, i.e., P1_bytes + P2_bytes.
23+
*/
24+
public static int expandP1P2(MayoParameters p, long[] P, byte[] seed_pk)
25+
{
26+
// Compute total number of bytes to generate: P1_bytes + P2_bytes.
27+
int outLen = p.getP1Bytes() + p.getP2Bytes();
28+
// Temporary byte array to hold the PRF output.
29+
byte[] temp = new byte[outLen];
30+
31+
// Call AES_128_CTR (our previously defined function using BouncyCastle)
32+
// to fill temp with outLen pseudorandom bytes using seed_pk as key.
33+
AES_128_CTR(temp, outLen, seed_pk, p.getPkSeedBytes());
34+
35+
// The number of vectors is the total limbs divided by mVecLimbs.
36+
int numVectors = (p.getP1Limbs() + p.getP2Limbs()) / p.getMVecLimbs();
37+
38+
// Unpack the byte array 'temp' into the long array 'P'
39+
// using our previously defined unpackMVecs method.
40+
Utils.unpackMVecs(temp, P, numVectors, p.getM());
41+
42+
// Return the number of output bytes produced.
43+
return outLen;
44+
}
45+
46+
/**
47+
* AES_128_CTR generates outputByteLen bytes using AES-128 in CTR mode.
48+
* The key (of length keyLen) is used to expand the AES key.
49+
* A 16-byte IV (all zeros) is used.
50+
*
51+
* @param output the output buffer which will be filled with the keystream
52+
* @param outputByteLen the number of bytes to produce
53+
* @param key the AES key (should be 16 bytes for AES-128)
54+
* @param keyLen the length of the key (unused here but kept for similarity)
55+
* @return the number of output bytes produced (i.e. outputByteLen)
56+
*/
57+
public static int AES_128_CTR(byte[] output, int outputByteLen, byte[] key, int keyLen)
58+
{
59+
// Create a 16-byte IV (all zeros)
60+
byte[] iv = new byte[16]; // automatically zero-initialized
61+
62+
// Set up AES engine in CTR (SIC) mode.
63+
BlockCipher aesEngine = AESEngine.newInstance();
64+
// SICBlockCipher implements CTR mode for AES.
65+
CTRModeCipher ctrCipher = SICBlockCipher.newInstance(aesEngine);
66+
// Wrap the key with the IV.
67+
ParametersWithIV params = new ParametersWithIV(new KeyParameter(Arrays.copyOf(key, keyLen)), iv);
68+
ctrCipher.init(true, params);
69+
70+
// CTR mode is a stream cipher: encrypting zero bytes produces the keystream.
71+
int blockSize = ctrCipher.getBlockSize(); // typically 16 bytes
72+
byte[] zeroBlock = new byte[blockSize]; // block of zeros
73+
byte[] blockOut = new byte[blockSize];
74+
75+
int offset = 0;
76+
// Process full blocks
77+
while (offset + blockSize <= outputByteLen)
78+
{
79+
ctrCipher.processBlock(zeroBlock, 0, blockOut, 0);
80+
System.arraycopy(blockOut, 0, output, offset, blockSize);
81+
offset += blockSize;
82+
}
83+
// Process any remaining partial block.
84+
if (offset < outputByteLen)
85+
{
86+
ctrCipher.processBlock(zeroBlock, 0, blockOut, 0);
87+
int remaining = outputByteLen - offset;
88+
System.arraycopy(blockOut, 0, output, offset, remaining);
89+
}
90+
return outputByteLen;
91+
}
92+
93+
public static final int MAYO_OK = 0;
94+
public static final int PK_SEED_BYTES_MAX = 16; // Adjust as needed
95+
public static final int O_BYTES_MAX = 312; // Adjust as needed
96+
97+
/**
98+
* Expands the secret key.
99+
*
100+
* @param p the MayoParameters instance.
101+
* @param csk the input secret key seed (byte array).
102+
* @param sk the Sk object that holds the expanded secret key components.
103+
* @return MAYO_OK on success.
104+
*/
105+
// public static int mayoExpandSk(MayoParameters p, byte[] csk, MayoPrivateKeyParameter sk)
106+
// {
107+
// int ret = MAYO_OK;
108+
// int totalS = PK_SEED_BYTES_MAX + O_BYTES_MAX;
109+
// byte[] S = new byte[totalS];
110+
//
111+
// // sk.p is the long[] array, sk.O is the byte[] array.
112+
//
113+
// long[] P = new long[p.getPkSeedBytes() >> 3];
114+
// Pack.littleEndianToLong(sk.getP(), 0, P);
115+
// byte[] O = sk.getO();
116+
//
117+
// int param_o = p.getO();
118+
// int param_v = p.getV();
119+
// int param_O_bytes = p.getOBytes();
120+
// int param_pk_seed_bytes = p.getPkSeedBytes();
121+
// int param_sk_seed_bytes = p.getSkSeedBytes();
122+
//
123+
// // In C, seed_sk = csk and seed_pk = S (the beginning of S)
124+
// byte[] seed_sk = csk;
125+
// byte[] seed_pk = S; // first param_pk_seed_bytes of S
126+
//
127+
// // Generate S = seed_pk || (additional bytes), using SHAKE256.
128+
// // Output length is param_pk_seed_bytes + param_O_bytes.
129+
// Utils.shake256(S, param_pk_seed_bytes + param_O_bytes, seed_sk, param_sk_seed_bytes);
130+
//
131+
// // Decode the portion of S after the first param_pk_seed_bytes into O.
132+
// // (In C, this is: decode(S + param_pk_seed_bytes, O, param_v * param_o))
133+
// Utils.decode(S, param_pk_seed_bytes, O, param_v * param_o);
134+
//
135+
// // Expand P1 and P2 into the long array P using seed_pk.
136+
// MayoEngine.expandP1P2(p, P, seed_pk);
137+
//
138+
// // Let P2 start at offset = PARAM_P1_limbs(p)
139+
// int p1Limbs = p.getP1Limbs();
140+
// int offsetP2 = p1Limbs;
141+
//
142+
// // Compute L_i = (P1 + P1^t)*O + P2.
143+
// // Here, we assume that P1P1tTimesO writes into the portion of P starting at offsetP2.
144+
// P1P1tTimesO(p, P, O, P, offsetP2);
145+
//
146+
// // Securely clear sensitive temporary data.
147+
// java.util.Arrays.fill(S, (byte)0);
148+
// return ret;
149+
// }
150+
151+
/**
152+
* Multiplies and accumulates the product (P1 + P1^t)*O into the accumulator.
153+
* This version writes into the 'acc' array starting at the specified offset.
154+
*
155+
* @param p the MayoParameters.
156+
* @param P1 the P1 vector as a long[] array.
157+
* @param O the O array (each byte represents a GF(16) element).
158+
* @param acc the accumulator array where results are XORed in.
159+
* @param accOffset the starting index in acc.
160+
*/
161+
public static void P1P1tTimesO(MayoParameters p, long[] P1, byte[] O, long[] acc, int accOffset)
162+
{
163+
int paramO = p.getO();
164+
int paramV = p.getV();
165+
int mVecLimbs = p.getMVecLimbs();
166+
int bsMatEntriesUsed = 0;
167+
for (int r = 0; r < paramV; r++)
168+
{
169+
for (int c = r; c < paramV; c++)
170+
{
171+
if (c == r)
172+
{
173+
bsMatEntriesUsed++;
174+
continue;
175+
}
176+
for (int k = 0; k < paramO; k++)
177+
{
178+
// Multiply the m-vector at P1 for the current matrix entry,
179+
// and accumulate into acc for row r.
180+
GF16Utils.mVecMulAdd(mVecLimbs, P1, bsMatEntriesUsed * mVecLimbs,
181+
O[c * paramO + k] & 0xFF, acc, accOffset + (r * paramO + k) * mVecLimbs);
182+
// Similarly, accumulate into acc for row c.
183+
GF16Utils.mVecMulAdd(mVecLimbs, P1, bsMatEntriesUsed * mVecLimbs,
184+
O[r * paramO + k] & 0xFF, acc, accOffset + (c * paramO + k) * mVecLimbs);
185+
}
186+
bsMatEntriesUsed++;
187+
}
188+
}
189+
}
190+
}

0 commit comments

Comments
 (0)