|
| 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 | +} |
0 commit comments