Skip to content

Commit 6cbd530

Browse files
author
gefeili
committed
TODO: SnovaSigner
1 parent 9c24366 commit 6cbd530

File tree

7 files changed

+328
-6
lines changed

7 files changed

+328
-6
lines changed

core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ public class MayoSigner
5050
@Override
5151
public void init(boolean forSigning, CipherParameters param)
5252
{
53-
5453
if (forSigning)
5554
{
5655
pubKey = null;

core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ public AsymmetricCipherKeyPair generateKeyPair()
7171
}
7272

7373
return new AsymmetricCipherKeyPair(
74-
new SnovaPublicKeyParameters(pk),
75-
new SnovaPrivateKeyParameters(sk)
74+
new SnovaPublicKeyParameters(params, pk),
75+
new SnovaPrivateKeyParameters(params, sk)
7676
);
7777
}
7878

core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,14 @@ public int getPrivateKeyLength()
177177
return ((l * l * (4 * o * alpha + o * (v * v + v * o + o * v) + v * o) + 1) >> 1)
178178
+ SnovaKeyPairGenerator.privateSeedLength + SnovaKeyPairGenerator.publicSeedLength;
179179
}
180+
181+
public int getN()
182+
{
183+
return v + o;
184+
}
185+
186+
public int getLsq()
187+
{
188+
return l * l;
189+
}
180190
}

core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPrivateKeyParameters.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ public class SnovaPrivateKeyParameters
77
extends AsymmetricKeyParameter
88
{
99
private final byte[] privateKey;
10+
private final SnovaParameters parameters;
1011

11-
public SnovaPrivateKeyParameters(byte[] privateKey)
12+
public SnovaPrivateKeyParameters(SnovaParameters parameters, byte[] privateKey)
1213
{
1314
super(true);
1415
this.privateKey = Arrays.clone(privateKey);
16+
this.parameters = parameters;
1517
}
1618

1719
public byte[] getPrivateKey()
@@ -23,4 +25,9 @@ public byte[] getEncoded()
2325
{
2426
return Arrays.clone(privateKey);
2527
}
28+
29+
public SnovaParameters getParameters()
30+
{
31+
return parameters;
32+
}
2633
}

core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPublicKeyParameters.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ public class SnovaPublicKeyParameters
77
extends AsymmetricKeyParameter
88
{
99
private final byte[] publicKey;
10+
private final SnovaParameters parameters;
1011

11-
public SnovaPublicKeyParameters(byte[] publicKey)
12+
public SnovaPublicKeyParameters(SnovaParameters parameters, byte[] publicKey)
1213
{
1314
super(false);
1415
this.publicKey = Arrays.clone(publicKey);
16+
this.parameters = parameters;
1517
}
1618

1719
public byte[] getPublicKey()
@@ -23,4 +25,9 @@ public byte[] getEncoded()
2325
{
2426
return Arrays.clone(publicKey);
2527
}
28+
29+
public SnovaParameters getParameters()
30+
{
31+
return parameters;
32+
}
2633
}
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
package org.bouncycastle.pqc.crypto.snova;
2+
3+
import java.security.SecureRandom;
4+
import java.util.Arrays;
5+
6+
import org.bouncycastle.crypto.CipherParameters;
7+
import org.bouncycastle.crypto.CryptoException;
8+
import org.bouncycastle.crypto.CryptoServicesRegistrar;
9+
import org.bouncycastle.crypto.DataLengthException;
10+
import org.bouncycastle.crypto.Signer;
11+
import org.bouncycastle.crypto.digests.SHAKEDigest;
12+
import org.bouncycastle.crypto.params.ParametersWithRandom;
13+
14+
public class SnovaSigner
15+
implements Signer
16+
{
17+
private SnovaParameters params;
18+
private SnovaEngine engine;
19+
private SecureRandom random;
20+
private final SHAKEDigest digest = new SHAKEDigest(256);
21+
22+
private SnovaPublicKeyParameters pubKey;
23+
private SnovaPrivateKeyParameters privKey;
24+
25+
@Override
26+
public void init(boolean forSigning, CipherParameters param)
27+
{
28+
if (forSigning)
29+
{
30+
pubKey = null;
31+
32+
if (param instanceof ParametersWithRandom)
33+
{
34+
ParametersWithRandom withRandom = (ParametersWithRandom)param;
35+
privKey = (SnovaPrivateKeyParameters)withRandom.getParameters();
36+
random = withRandom.getRandom();
37+
}
38+
else
39+
{
40+
privKey = (SnovaPrivateKeyParameters)param;
41+
random = CryptoServicesRegistrar.getSecureRandom();
42+
}
43+
params = privKey.getParameters();
44+
}
45+
else
46+
{
47+
pubKey = (SnovaPublicKeyParameters)param;
48+
params = pubKey.getParameters();
49+
privKey = null;
50+
random = null;
51+
}
52+
}
53+
54+
@Override
55+
public void update(byte b)
56+
{
57+
digest.update(b);
58+
}
59+
60+
@Override
61+
public void update(byte[] in, int off, int len)
62+
{
63+
digest.update(in, off, len);
64+
}
65+
66+
@Override
67+
public byte[] generateSignature()
68+
throws CryptoException, DataLengthException
69+
{
70+
return new byte[0];
71+
}
72+
73+
@Override
74+
public boolean verifySignature(byte[] signature)
75+
{
76+
return false;
77+
}
78+
79+
@Override
80+
public void reset()
81+
{
82+
83+
}
84+
85+
public static void createSignedHash(
86+
byte[] digest, int bytesDigest,
87+
byte[] ptPublicKeySeed, int seedLengthPublic,
88+
byte[] arraySalt, int bytesSalt,
89+
byte[] signedHashOut, int bytesHash
90+
)
91+
{
92+
// Initialize SHAKE256 XOF
93+
SHAKEDigest shake = new SHAKEDigest(256);
94+
95+
// 1. Absorb public key seed
96+
shake.update(ptPublicKeySeed, 0, seedLengthPublic);
97+
98+
// 2. Absorb message digest
99+
shake.update(digest, 0, bytesDigest);
100+
101+
// 3. Absorb salt
102+
shake.update(arraySalt, 0, bytesSalt);
103+
104+
// 4. Finalize absorption and squeeze output
105+
shake.doFinal(signedHashOut, 0, bytesHash);
106+
}
107+
108+
public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt,
109+
byte[][][][] Aalpha, byte[][][][] Balpha,
110+
byte[][][][] Qalpha1, byte[][][][] Qalpha2,
111+
byte[][][][] T12, byte[][][][] F11,
112+
byte[][][][] F12, byte[][][][] F21,
113+
byte[] ptPublicKeySeed, byte[] ptPrivateKeySeed)
114+
{
115+
// Initialize constants from parameters
116+
final int m = params.getM();
117+
final int lsq = params.getLsq();
118+
final int alpha = params.getAlpha();
119+
final int v = params.getV();
120+
final int o = params.getO();
121+
final int n = params.getN();
122+
final int bytesHash = (o * lsq + 1) >>> 1;
123+
final int bytesSalt = 16;
124+
125+
// Initialize matrices and arrays
126+
byte[][] Gauss = new byte[m * lsq][m * lsq + 1];
127+
byte[][] Temp = new byte[lsq][lsq];
128+
byte[] solution = new byte[m * lsq];
129+
130+
byte[][][] Left = new byte[m][alpha][v];
131+
byte[][][] Right = new byte[m][alpha][v];
132+
byte[][] XInGF16Matrix = new byte[n][lsq];
133+
byte[][] FvvGF16Matrix = new byte[m][lsq];
134+
byte[] hashInGF16 = new byte[m * lsq];
135+
byte[][] signatureGF16Matrix = new byte[n][lsq];
136+
137+
byte[] signedHash = new byte[bytesHash];
138+
byte[] vinegarBytes = new byte[(v * lsq + 1) / 2];
139+
140+
// Temporary matrices
141+
byte[] gf16mTemp0 = new byte[lsq];
142+
byte[] gf16mTemp1 = new byte[lsq];
143+
byte[] gf16mSecretTemp0 = new byte[lsq];
144+
145+
int flagRedo;
146+
byte numSign = 0;
147+
148+
// Step 1: Create signed hash
149+
createSignedHash(digest, digest.length, ptPublicKeySeed, ptPublicKeySeed.length,
150+
arraySalt, arraySalt.length, signedHash, bytesHash);
151+
GF16Utils.decode(signedHash, 0, hashInGF16, 0, hashInGF16.length);
152+
153+
do
154+
{
155+
// Initialize Gauss matrix
156+
Arrays.stream(Gauss).forEach(row -> Arrays.fill(row, (byte)0));
157+
numSign++;
158+
flagRedo = 0;
159+
160+
// Fill last column of Gauss matrix
161+
for (int i = 0; i < m * lsq; i++)
162+
{
163+
Gauss[i][m * lsq] = hashInGF16[i];
164+
}
165+
166+
// Generate vinegar values
167+
SHAKEDigest shake = new SHAKEDigest(256);
168+
shake.update(ptPrivateKeySeed, 0, ptPrivateKeySeed.length);
169+
shake.update(digest, 0, digest.length);
170+
shake.update(arraySalt, 0, arraySalt.length);
171+
shake.update(new byte[]{numSign}, 0, 1);
172+
shake.doFinal(vinegarBytes, 0, vinegarBytes.length);
173+
//GF16Utils.decode(vinegarBytes, 0, XInGF16Matrix, 0, v * lsq);
174+
175+
// Evaluate vinegar part of central map
176+
for (int mi = 0; mi < m; mi++)
177+
{
178+
for (int a = 0; a < alpha; a++)
179+
{
180+
for (int idx = 0; idx < v; idx++)
181+
{
182+
transposeGF16Matrix(XInGF16Matrix[idx], gf16mTemp0);
183+
// multiplyGF16Matrices(gf16mTemp0, Qalpha1[mi][a], gf16mTemp1);
184+
// multiplyGF16Matrices(Aalpha[mi][a], gf16mTemp1, Left[mi][a][idx]);
185+
//
186+
// multiplyGF16Matrices(Qalpha2[mi][a], XInGF16Matrix[idx], gf16mTemp1);
187+
// multiplyGF16Matrices(gf16mTemp1, Balpha[mi][a], Right[mi][a][idx]);
188+
}
189+
}
190+
}
191+
192+
// Matrix operations for Fvv
193+
Arrays.stream(FvvGF16Matrix).forEach(row -> Arrays.fill(row, (byte)0));
194+
for (int mi = 0; mi < m; mi++)
195+
{
196+
for (int a = 0; a < alpha; a++)
197+
{
198+
int miPrime = iPrime(mi, a);
199+
for (int j = 0; j < v; j++)
200+
{
201+
for (int k = 0; k < v; k++)
202+
{
203+
// multiplyGF16Matrices(Left[mi][a][j], F11[miPrime][j][k], gf16mTemp0);
204+
// multiplyGF16Matrices(gf16mTemp0, Right[mi][a][k], gf16mTemp1);
205+
//GF16Utils.addGF16Matrices(FvvGF16Matrix[mi], gf16mTemp1, FvvGF16Matrix[mi]);
206+
}
207+
}
208+
}
209+
}
210+
211+
// Gaussian elimination setup
212+
for (int i = 0; i < m; i++)
213+
{
214+
for (int j = 0; j < params.getL(); j++)
215+
{
216+
for (int k = 0; k < params.getL(); k++)
217+
{
218+
int idx1 = i * lsq + j * params.getL() + k;
219+
Gauss[idx1][m * lsq] = GF16Utils.add(
220+
Gauss[idx1][m * lsq],
221+
engine.getGF16m(FvvGF16Matrix[i], j, k)
222+
);
223+
}
224+
}
225+
}
226+
227+
// Gaussian elimination implementation
228+
flagRedo = performGaussianElimination(Gauss, solution, m * lsq);
229+
230+
}
231+
while (flagRedo != 0);
232+
233+
// Build final signature
234+
buildSignature(XInGF16Matrix, signatureGF16Matrix, T12, v, o, lsq);
235+
convertGF16MatrixToBytes(ptSignature, signatureGF16Matrix, n * lsq);
236+
//System.arraycopy(arraySalt, 0, ptSignature, params.getBytesSignature(), bytesSalt);
237+
238+
// Clear sensitive data
239+
Arrays.fill(gf16mSecretTemp0, (byte)0);
240+
}
241+
242+
private void transposeGF16Matrix(byte[] src, byte[] dest)
243+
{
244+
for (int i = 0; i < params.getL(); i++)
245+
{
246+
for (int j = 0; j < params.getL(); j++)
247+
{
248+
engine.setGF16m(dest, i, j, engine.getGF16m(src, j, i));
249+
}
250+
}
251+
}
252+
253+
private void multiplyGF16Matrices(byte[] a, byte[] b, byte[] result)
254+
{
255+
Arrays.fill(result, (byte)0);
256+
for (int i = 0; i < params.getL(); i++)
257+
{
258+
for (int j = 0; j < params.getL(); j++)
259+
{
260+
byte sum = 0;
261+
for (int k = 0; k < params.getL(); k++)
262+
{
263+
sum = GF16Utils.add(sum, GF16Utils.mul(
264+
engine.getGF16m(a, i, k),
265+
engine.getGF16m(b, k, j)
266+
));
267+
}
268+
engine.setGF16m(result, i, j, sum);
269+
}
270+
}
271+
}
272+
273+
private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size)
274+
{
275+
// Implementation of Gaussian elimination with GF16 arithmetic
276+
// ... (similar structure to C code's elimination steps)
277+
return 0; // Return 0 if successful, 1 if needs redo
278+
}
279+
280+
private void buildSignature(byte[][] XIn, byte[][] signature,
281+
byte[][][][] T12, int v, int o, int lsq)
282+
{
283+
// Implementation of signature construction
284+
// ... (similar to C code's final matrix operations)
285+
}
286+
287+
private void convertGF16MatrixToBytes(byte[] output, byte[][] matrix, int totalElements)
288+
{
289+
// Conversion implementation using GF16Utils.encode
290+
}
291+
292+
private int iPrime(int mi, int alpha)
293+
{
294+
// Implement index calculation based on SNOVA specification
295+
return (mi + alpha) % params.getM();
296+
}
297+
298+
}

core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.bouncycastle.pqc.crypto.snova.SnovaParameters;
1414
import org.bouncycastle.pqc.crypto.snova.SnovaPrivateKeyParameters;
1515
import org.bouncycastle.pqc.crypto.snova.SnovaPublicKeyParameters;
16+
import org.bouncycastle.pqc.crypto.snova.SnovaSigner;
1617

1718

1819
public class SnovaTest
@@ -158,7 +159,7 @@ public byte[] getPrivateKeyEncoded(CipherParameters privParams)
158159
@Override
159160
public Signer getSigner()
160161
{
161-
return null;
162+
return new SnovaSigner();
162163
}
163164

164165
@Override

0 commit comments

Comments
 (0)