|
4 | 4 |
|
5 | 5 | import org.bouncycastle.crypto.AsymmetricCipherKeyPair; |
6 | 6 | import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; |
7 | | -import org.bouncycastle.crypto.BlockCipher; |
8 | 7 | import org.bouncycastle.crypto.KeyGenerationParameters; |
9 | | -import org.bouncycastle.crypto.digests.SHAKEDigest; |
10 | | -import org.bouncycastle.crypto.engines.AESEngine; |
11 | | -import org.bouncycastle.crypto.modes.CTRModeCipher; |
12 | | -import org.bouncycastle.crypto.modes.SICBlockCipher; |
13 | | -import org.bouncycastle.crypto.params.KeyParameter; |
14 | | -import org.bouncycastle.crypto.params.ParametersWithIV; |
15 | 8 | import org.bouncycastle.util.Arrays; |
16 | 9 |
|
17 | 10 | public class SnovaKeyPairGenerator |
@@ -79,181 +72,15 @@ public AsymmetricCipherKeyPair generateKeyPair() |
79 | 72 | private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[] skSeed) |
80 | 73 | { |
81 | 74 | // Generate T12 matrix |
82 | | - genSeedsAndT12(keyElements.T12, skSeed); |
| 75 | + engine.genSeedsAndT12(keyElements.T12, skSeed); |
83 | 76 |
|
84 | 77 | // Generate map components |
85 | | - genABQP(keyElements.map1, pkSeed, keyElements.fixedAbq); |
| 78 | + engine.genABQP(keyElements.map1, pkSeed, keyElements.fixedAbq); |
86 | 79 |
|
87 | 80 | // Generate F matrices |
88 | 81 | engine.genF(keyElements.map2, keyElements.map1, keyElements.T12); |
89 | 82 |
|
90 | 83 | // Generate P22 matrix |
91 | 84 | engine.genP22(keyElements.publicKey.P22, keyElements.T12, keyElements.map1.p21, keyElements.map2.f12); |
92 | 85 | } |
93 | | - |
94 | | - private void genSeedsAndT12(byte[][][] T12, byte[] skSeed) |
95 | | - { |
96 | | - int bytesPrngPrivate = (params.getV() * params.getO() * params.getL() + 1) >>> 1; |
97 | | - int gf16sPrngPrivate = params.getV() * params.getO() * params.getL(); |
98 | | - byte[] prngOutput = new byte[bytesPrngPrivate]; |
99 | | - |
100 | | - // Generate PRNG output using SHAKE-256 |
101 | | - SHAKEDigest shake = new SHAKEDigest(256); |
102 | | - shake.update(skSeed, 0, skSeed.length); |
103 | | - shake.doFinal(prngOutput, 0, prngOutput.length); |
104 | | - |
105 | | - // Convert bytes to GF16 array |
106 | | - byte[] gf16PrngOutput = new byte[gf16sPrngPrivate]; |
107 | | - GF16Utils.decode(prngOutput, gf16PrngOutput, gf16sPrngPrivate); |
108 | | - |
109 | | - // Generate T12 matrices |
110 | | - int ptArray = 0; |
111 | | - int l = params.getL(); |
112 | | - for (int j = 0; j < params.getV(); j++) |
113 | | - { |
114 | | - for (int k = 0; k < params.getO(); k++) |
115 | | - { |
116 | | - //gen_a_FqS_ct |
117 | | - engine.genAFqSCT(gf16PrngOutput, ptArray, T12[j][k]); |
118 | | - ptArray += l; |
119 | | - } |
120 | | - } |
121 | | - } |
122 | | - |
123 | | - private void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) |
124 | | - { |
125 | | - int l = params.getL(); |
126 | | - int lsq = l * l; |
127 | | - int m = params.getM(); |
128 | | - int alpha = params.getAlpha(); |
129 | | - int v = params.getV(); |
130 | | - int o = params.getO(); |
131 | | - int n = v + o; |
132 | | - |
133 | | - int gf16sPrngPublic = lsq * (2 * m * alpha + m * (n * n - m * m)) + l * 2 * m * alpha; |
134 | | - byte[] qTemp = new byte[(m * alpha * lsq + m * alpha * lsq) / l]; |
135 | | - byte[] prngOutput = new byte[(gf16sPrngPublic + 1) >> 1]; |
136 | | - |
137 | | - if (params.isPkExpandShake()) |
138 | | - { |
139 | | - snovaShake(pkSeed, prngOutput.length, prngOutput); |
140 | | - } |
141 | | - else |
142 | | - { |
143 | | - // Create a 16-byte IV (all zeros) |
144 | | - byte[] iv = new byte[16]; // automatically zero-initialized |
145 | | - // AES-CTR-based expansion |
146 | | - // Set up AES engine in CTR (SIC) mode. |
147 | | - BlockCipher aesEngine = AESEngine.newInstance(); |
148 | | - // SICBlockCipher implements CTR mode for AES. |
149 | | - CTRModeCipher ctrCipher = SICBlockCipher.newInstance(aesEngine); |
150 | | - ParametersWithIV params = new ParametersWithIV(new KeyParameter(pkSeed), iv); |
151 | | - ctrCipher.init(true, params); |
152 | | - int blockSize = ctrCipher.getBlockSize(); // typically 16 bytes |
153 | | - byte[] zeroBlock = new byte[blockSize]; // block of zeros |
154 | | - byte[] blockOut = new byte[blockSize]; |
155 | | - |
156 | | - int offset = 0; |
157 | | - // Process full blocks |
158 | | - while (offset + blockSize <= prngOutput.length) |
159 | | - { |
160 | | - ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); |
161 | | - System.arraycopy(blockOut, 0, prngOutput, offset, blockSize); |
162 | | - offset += blockSize; |
163 | | - } |
164 | | - // Process any remaining partial block. |
165 | | - if (offset < prngOutput.length) |
166 | | - { |
167 | | - ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); |
168 | | - int remaining = prngOutput.length - offset; |
169 | | - System.arraycopy(blockOut, 0, prngOutput, offset, remaining); |
170 | | - } |
171 | | - } |
172 | | - byte[] temp = new byte[gf16sPrngPublic - qTemp.length]; |
173 | | - GF16Utils.decode(prngOutput, temp, temp.length); |
174 | | - map1.fill(temp); |
175 | | - if (l >= 4) |
176 | | - { |
177 | | - GF16Utils.decode(prngOutput, temp.length >> 1, qTemp, 0, qTemp.length); |
178 | | - |
179 | | - // Post-processing for invertible matrices |
180 | | - for (int pi = 0; pi < m; ++pi) |
181 | | - { |
182 | | - for (int a = 0; a < alpha; ++a) |
183 | | - { |
184 | | - engine.makeInvertibleByAddingAS(map1.aAlpha[pi][a], 0); |
185 | | - } |
186 | | - } |
187 | | - for (int pi = 0; pi < m; ++pi) |
188 | | - { |
189 | | - for (int a = 0; a < alpha; ++a) |
190 | | - { |
191 | | - engine.makeInvertibleByAddingAS(map1.bAlpha[pi][a], 0); |
192 | | - } |
193 | | - } |
194 | | - |
195 | | - int ptArray = 0; |
196 | | - for (int pi = 0; pi < m; ++pi) |
197 | | - { |
198 | | - for (int a = 0; a < alpha; ++a) |
199 | | - { |
200 | | - engine.genAFqS(qTemp, ptArray, map1.qAlpha1[pi][a], 0); |
201 | | - ptArray += l; |
202 | | - } |
203 | | - } |
204 | | - for (int pi = 0; pi < m; ++pi) |
205 | | - { |
206 | | - for (int a = 0; a < alpha; ++a) |
207 | | - { |
208 | | - engine.genAFqS(qTemp, ptArray, map1.qAlpha2[pi][a], 0); |
209 | | - ptArray += l; |
210 | | - } |
211 | | - } |
212 | | - } |
213 | | - else |
214 | | - { |
215 | | - MapGroup1.fillAlpha(fixedAbq, 0, map1.aAlpha, m * o * alpha * lsq); |
216 | | - MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq, map1.bAlpha, (m - 1) * o * alpha * lsq); |
217 | | - MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq * 2, map1.qAlpha1, (m - 2) * o * alpha * lsq); |
218 | | - MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq * 3, map1.qAlpha2, (m - 3) * o * alpha * lsq); |
219 | | - } |
220 | | - } |
221 | | - |
222 | | - public static void snovaShake(byte[] ptSeed, int outputBytes, byte[] out) |
223 | | - { |
224 | | - final int SHAKE128_RATE = 168; // 1344-bit rate = 168 bytes |
225 | | - long blockCounter = 0; |
226 | | - int offset = 0; |
227 | | - int remaining = outputBytes; |
228 | | - |
229 | | - while (remaining > 0) |
230 | | - { |
231 | | - SHAKEDigest shake = new SHAKEDigest(128); |
232 | | - |
233 | | - // Process seed + counter |
234 | | - shake.update(ptSeed, 0, ptSeed.length); |
235 | | - updateWithCounter(shake, blockCounter); |
236 | | - |
237 | | - // Calculate bytes to generate in this iteration |
238 | | - int bytesToGenerate = Math.min(remaining, SHAKE128_RATE); |
239 | | - |
240 | | - // Generate output (XOF mode) |
241 | | - shake.doFinal(out, offset, bytesToGenerate); |
242 | | - |
243 | | - offset += bytesToGenerate; |
244 | | - remaining -= bytesToGenerate; |
245 | | - blockCounter++; |
246 | | - } |
247 | | - } |
248 | | - |
249 | | - private static void updateWithCounter(SHAKEDigest shake, long counter) |
250 | | - { |
251 | | - byte[] counterBytes = new byte[8]; |
252 | | - // Little-endian conversion |
253 | | - for (int i = 0; i < 8; i++) |
254 | | - { |
255 | | - counterBytes[i] = (byte)(counter >> (i * 8)); |
256 | | - } |
257 | | - shake.update(counterBytes, 0, 8); |
258 | | - } |
259 | 86 | } |
0 commit comments