Skip to content

Commit f240977

Browse files
author
royb
committed
updated HQC to match reference implementation 2024-10-30
1 parent db65337 commit f240977

File tree

5 files changed

+50
-60
lines changed

5 files changed

+50
-60
lines changed

core/src/main/java/org/bouncycastle/pqc/crypto/hqc/FastFourierTransform.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ static void computeRadixBig(int[] f0, int[] f1, int[] f, int mf, int fft)
131131
n <<= (mf - 2);
132132
int fftSize = 1 << (fft - 2);
133133

134-
int Q[] = new int[2 * fftSize];
135-
int R[] = new int[2 * fftSize];
134+
int Q[] = new int[2 * fftSize + 1];
135+
int R[] = new int[2 * fftSize + 1];
136136

137137
int Q0[] = new int[fftSize];
138138
int Q1[] = new int[fftSize];

core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCEngine.java

Lines changed: 42 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.bouncycastle.util.Arrays;
44
import org.bouncycastle.util.Pack;
5+
import org.bouncycastle.util.encoders.Hex;
56

67
class HQCEngine
78
{
@@ -20,8 +21,7 @@ class HQCEngine
2021

2122
private int SEED_SIZE = 40;
2223
private byte G_FCT_DOMAIN = 3;
23-
private byte H_FCT_DOMAIN = 4;
24-
private byte K_FCT_DOMAIN = 5;
24+
private byte K_FCT_DOMAIN = 4;
2525

2626
private int N_BYTE;
2727
private int n1n2;
@@ -87,10 +87,12 @@ public void genKeyPair(byte[] pk, byte[] sk, byte[] seed)
8787
{
8888
// Randomly generate seeds for secret keys and public keys
8989
byte[] secretKeySeed = new byte[SEED_SIZE];
90+
byte[] sigma = new byte[K_BYTE];
9091

9192
KeccakRandomGenerator randomGenerator = new KeccakRandomGenerator(256);
9293
randomGenerator.randomGeneratorInit(seed, null, seed.length, 0);
9394
randomGenerator.squeeze(secretKeySeed, 40);
95+
randomGenerator.squeeze(sigma, K_BYTE);
9496

9597
// 1. Randomly generate secret keys x, y
9698
KeccakRandomGenerator secretKeySeedExpander = new KeccakRandomGenerator(256);
@@ -99,8 +101,8 @@ public void genKeyPair(byte[] pk, byte[] sk, byte[] seed)
99101
long[] xLongBytes = new long[N_BYTE_64];
100102
long[] yLongBytes = new long[N_BYTE_64];
101103

102-
generateRandomFixedWeight(xLongBytes, secretKeySeedExpander, w);
103104
generateRandomFixedWeight(yLongBytes, secretKeySeedExpander, w);
105+
generateRandomFixedWeight(xLongBytes, secretKeySeedExpander, w);
104106

105107
// 2. Randomly generate h
106108
byte[] publicKeySeed = new byte[SEED_SIZE];
@@ -120,7 +122,7 @@ public void genKeyPair(byte[] pk, byte[] sk, byte[] seed)
120122
Utils.fromLongArrayToByteArray(sBytes, s);
121123

122124
byte[] tmpPk = Arrays.concatenate(publicKeySeed, sBytes);
123-
byte[] tmpSk = Arrays.concatenate(secretKeySeed, tmpPk);
125+
byte[] tmpSk = Arrays.concatenate(secretKeySeed, sigma, tmpPk);
124126

125127
System.arraycopy(tmpPk, 0, pk, 0, tmpPk.length);
126128
System.arraycopy(tmpSk, 0, sk, 0, tmpSk.length);
@@ -133,12 +135,11 @@ public void genKeyPair(byte[] pk, byte[] sk, byte[] seed)
133135
*
134136
* @param u u
135137
* @param v v
136-
* @param d d
137138
* @param K session key
138139
* @param pk public key
139140
* @param seed seed
140141
**/
141-
public void encaps(byte[] u, byte[] v, byte[] K, byte[] d, byte[] pk, byte[] seed, byte[] salt)
142+
public void encaps(byte[] u, byte[] v, byte[] K, byte[] pk, byte[] seed, byte[] salt)
142143
{
143144
// 1. Randomly generate m
144145
byte[] m = new byte[K_BYTE];
@@ -148,6 +149,9 @@ public void encaps(byte[] u, byte[] v, byte[] K, byte[] d, byte[] pk, byte[] see
148149
randomGenerator.randomGeneratorInit(seed, null, seed.length, 0);
149150
randomGenerator.squeeze(secretKeySeed, 40);
150151

152+
byte[] sigma = new byte[K_BYTE];
153+
randomGenerator.squeeze(sigma, K_BYTE);
154+
151155
byte[] publicKeySeed = new byte[SEED_SIZE];
152156
randomGenerator.squeeze(publicKeySeed, 40);
153157

@@ -156,12 +160,12 @@ public void encaps(byte[] u, byte[] v, byte[] K, byte[] d, byte[] pk, byte[] see
156160

157161
// 2. Generate theta
158162
byte[] theta = new byte[SHA512_BYTES];
159-
byte[] tmp = new byte[K_BYTE + SEED_SIZE + SALT_SIZE_BYTES];
163+
byte[] tmp = new byte[K_BYTE + (SALT_SIZE_BYTES * 2) + SALT_SIZE_BYTES];
160164
randomGenerator.squeeze(salt, SALT_SIZE_BYTES);
161165

162166
System.arraycopy(m, 0, tmp, 0, m.length);
163-
System.arraycopy(pk, 0, tmp, K_BYTE, SEED_SIZE);
164-
System.arraycopy(salt, 0, tmp, K_BYTE + SEED_SIZE, SALT_SIZE_BYTES);
167+
System.arraycopy(pk, 0, tmp, K_BYTE, SALT_SIZE_BYTES * 2);
168+
System.arraycopy(salt, 0, tmp, K_BYTE + (SALT_SIZE_BYTES * 2), SALT_SIZE_BYTES);
165169
KeccakRandomGenerator shakeDigest = new KeccakRandomGenerator(256);
166170
shakeDigest.SHAKE256_512_ds(theta, tmp, tmp.length, new byte[]{G_FCT_DOMAIN});
167171

@@ -176,13 +180,8 @@ public void encaps(byte[] u, byte[] v, byte[] K, byte[] d, byte[] pk, byte[] see
176180

177181
Utils.fromLongArrayToByteArray(v, vTmp);
178182

179-
// 4. Compute d
180-
shakeDigest.SHAKE256_512_ds(d, m, m.length, new byte[]{H_FCT_DOMAIN});
181-
182183
// 5. Compute session key K
183-
byte[] hashInputK = new byte[K_BYTE + N_BYTE + N1N2_BYTE];
184-
hashInputK = Arrays.concatenate(m, u);
185-
hashInputK = Arrays.concatenate(hashInputK, v);
184+
byte[] hashInputK = Arrays.concatenate(m, u, v);
186185
shakeDigest.SHAKE256_512_ds(K, hashInputK, hashInputK.length, new byte[]{K_FCT_DOMAIN});
187186
}
188187

@@ -194,32 +193,32 @@ public void encaps(byte[] u, byte[] v, byte[] K, byte[] d, byte[] pk, byte[] see
194193
* @param ss session key
195194
* @param ct ciphertext
196195
* @param sk secret key
196+
* @return 0 if decapsulation is successful, -1 otherwise
197197
**/
198-
public void decaps(byte[] ss, byte[] ct, byte[] sk)
198+
public int decaps(byte[] ss, byte[] ct, byte[] sk)
199199
{
200200
//Extract Y and Public Keys from sk
201-
long[] x = new long[N_BYTE_64];
202201
long[] y = new long[N_BYTE_64];
203202
byte[] pk = new byte[40 + N_BYTE];
204-
extractKeysFromSecretKeys(x, y, pk, sk);
203+
byte[] sigma = new byte[K_BYTE];
204+
extractKeysFromSecretKeys(y, sigma, pk, sk);
205205

206206
// Extract u, v, d from ciphertext
207207
byte[] u = new byte[N_BYTE];
208208
byte[] v = new byte[N1N2_BYTE];
209-
byte[] d = new byte[SHA512_BYTES];
210209
byte[] salt = new byte[SALT_SIZE_BYTES];
211-
extractCiphertexts(u, v, d, salt, ct);
210+
extractCiphertexts(u, v, salt, ct);
212211

213212
// 1. Decrypt -> m'
214213
byte[] mPrimeBytes = new byte[k];
215-
decrypt(mPrimeBytes, mPrimeBytes, u, v, y);
214+
int result = decrypt(mPrimeBytes, mPrimeBytes, sigma, u, v, y);
216215

217216
// 2. Compute theta'
218217
byte[] theta = new byte[SHA512_BYTES];
219-
byte[] tmp = new byte[K_BYTE + SALT_SIZE_BYTES + SEED_SIZE];
218+
byte[] tmp = new byte[K_BYTE + (SALT_SIZE_BYTES * 2) + SALT_SIZE_BYTES];
220219
System.arraycopy(mPrimeBytes, 0, tmp, 0, mPrimeBytes.length);
221-
System.arraycopy(pk, 0, tmp, K_BYTE, SEED_SIZE);
222-
System.arraycopy(salt, 0, tmp, K_BYTE + SEED_SIZE, SALT_SIZE_BYTES);
220+
System.arraycopy(pk, 0, tmp, K_BYTE, SALT_SIZE_BYTES * 2);
221+
System.arraycopy(salt, 0, tmp, K_BYTE + (SALT_SIZE_BYTES * 2), SALT_SIZE_BYTES);
223222

224223
KeccakRandomGenerator shakeDigest = new KeccakRandomGenerator(256);
225224
shakeDigest.SHAKE256_512_ds(theta, tmp, tmp.length, new byte[]{G_FCT_DOMAIN});
@@ -236,40 +235,32 @@ public void decaps(byte[] ss, byte[] ct, byte[] sk)
236235
encrypt(u2Bytes, vTmp, h, s, mPrimeBytes, theta);
237236
Utils.fromLongArrayToByteArray(v2Bytes, vTmp);
238237

239-
// 4. Compute d' = H(m')
240-
byte[] dPrime = new byte[SHA512_BYTES];
241-
shakeDigest.SHAKE256_512_ds(dPrime, mPrimeBytes, mPrimeBytes.length, new byte[]{H_FCT_DOMAIN});
242-
243238
// 5. Compute session key KPrime
244239
byte[] hashInputK = new byte[K_BYTE + N_BYTE + N1N2_BYTE];
245-
hashInputK = Arrays.concatenate(mPrimeBytes, u);
246-
hashInputK = Arrays.concatenate(hashInputK, v);
247-
shakeDigest.SHAKE256_512_ds(ss, hashInputK, hashInputK.length, new byte[]{K_FCT_DOMAIN});
248240

249-
int result = 1;
250241
// Compare u, v, d
251242
if (!Arrays.constantTimeAreEqual(u, u2Bytes))
252243
{
253-
result = 0;
244+
result = 1;
254245
}
255246

256247
if (!Arrays.constantTimeAreEqual(v, v2Bytes))
257248
{
258-
result = 0;
249+
result = 1;
259250
}
260251

261-
if (!Arrays.constantTimeAreEqual(d, dPrime))
252+
result -= 1;
253+
254+
for (int i = 0; i < K_BYTE; i++)
262255
{
263-
result = 0;
256+
hashInputK[i] = (byte)(((mPrimeBytes[i] & result) ^ (sigma[i] & ~result)) & 0xff);
264257
}
258+
System.arraycopy(u, 0, hashInputK, K_BYTE, N_BYTE);
259+
System.arraycopy(v, 0, hashInputK, K_BYTE + N_BYTE, N1N2_BYTE);
265260

266-
if (result == 0)
267-
{ //abort
268-
for (int i = 0; i < getSessionKeySize(); i++)
269-
{
270-
ss[i] = 0;
271-
}
272-
}
261+
shakeDigest.SHAKE256_512_ds(ss, hashInputK, hashInputK.length, new byte[]{K_FCT_DOMAIN});
262+
263+
return -result;
273264
}
274265

275266
int getSessionKeySize()
@@ -296,9 +287,9 @@ private void encrypt(byte[] u, long[] v, long[] h, byte[] s, byte[] m, byte[] th
296287
long[] e = new long[N_BYTE_64];
297288
long[] r1 = new long[N_BYTE_64];
298289
long[] r2 = new long[N_BYTE_64];
299-
generateRandomFixedWeight(r1, randomGenerator, wr);
300290
generateRandomFixedWeight(r2, randomGenerator, wr);
301291
generateRandomFixedWeight(e, randomGenerator, we);
292+
generateRandomFixedWeight(r1, randomGenerator, wr);
302293

303294
// Calculate u
304295
long[] uLong = new long[N_BYTE_64];
@@ -327,7 +318,7 @@ private void encrypt(byte[] u, long[] v, long[] h, byte[] s, byte[] m, byte[] th
327318
Utils.resizeArray(v, n1n2, tmpLong, n, N1N2_BYTE_64, N1N2_BYTE_64);
328319
}
329320

330-
private void decrypt(byte[] output, byte[] m, byte[] u, byte[] v, long[] y)
321+
private int decrypt(byte[] output, byte[] m, byte[] sigma, byte[] u, byte[] v, long[] y)
331322
{
332323
long[] uLongs = new long[N_BYTE_64];
333324
Utils.fromByteArrayToLongArray(uLongs, u);
@@ -348,6 +339,7 @@ private void decrypt(byte[] output, byte[] m, byte[] u, byte[] v, long[] y)
348339
ReedSolomon.decode(m, tmp, n1, fft, delta, k, g);
349340

350341
System.arraycopy(m, 0, output, 0, output.length);
342+
return 0;
351343
}
352344

353345
private void generateRandomFixedWeight(long[] output, KeccakRandomGenerator random, int weight)
@@ -427,26 +419,25 @@ private void extractPublicKeys(long[] h, byte[] s, byte[] pk)
427419
System.arraycopy(pk, 40, s, 0, s.length);
428420
}
429421

430-
private void extractKeysFromSecretKeys(long[] x, long[] y, byte[] pk, byte[] sk)
422+
private void extractKeysFromSecretKeys(long[] y, byte[] sigma, byte[] pk, byte[] sk)
431423
{
432424
byte[] secretKeySeed = new byte[SEED_SIZE];
433425
System.arraycopy(sk, 0, secretKeySeed, 0, secretKeySeed.length);
426+
System.arraycopy(sk, SEED_SIZE, sigma, 0, K_BYTE);
434427

435428
// Randomly generate secret keys x, y
436429
KeccakRandomGenerator secretKeySeedExpander = new KeccakRandomGenerator(256);
437430
secretKeySeedExpander.seedExpanderInit(secretKeySeed, secretKeySeed.length);
438431

439-
generateRandomFixedWeight(x, secretKeySeedExpander, w);
440432
generateRandomFixedWeight(y, secretKeySeedExpander, w);
441433

442-
System.arraycopy(sk, SEED_SIZE, pk, 0, pk.length);
434+
System.arraycopy(sk, SEED_SIZE + K_BYTE, pk, 0, pk.length);
443435
}
444436

445-
private void extractCiphertexts(byte[] u, byte[] v, byte[] d, byte[] salt, byte[] ct)
437+
private void extractCiphertexts(byte[] u, byte[] v, byte[] salt, byte[] ct)
446438
{
447439
System.arraycopy(ct, 0, u, 0, u.length);
448440
System.arraycopy(ct, u.length, v, 0, v.length);
449-
System.arraycopy(ct, u.length + v.length, d, 0, d.length);
450-
System.arraycopy(ct, u.length + v.length + d.length, salt, 0, salt.length);
441+
System.arraycopy(ct, u.length + v.length, salt, 0, salt.length);
451442
}
452443
}

core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCKEMGenerator.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,15 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip
2626
byte[] K = new byte[key.getParameters().getSHA512_BYTES()];
2727
byte[] u = new byte[key.getParameters().getN_BYTES()];
2828
byte[] v = new byte[key.getParameters().getN1N2_BYTES()];
29-
byte[] d = new byte[key.getParameters().getSHA512_BYTES()];
3029
byte[] salt = new byte[key.getParameters().getSALT_SIZE_BYTES()];
3130
byte[] pk = key.getPublicKey();
3231
byte[] seed = new byte[48];
3332

3433
sr.nextBytes(seed);
3534

36-
engine.encaps(u, v, K, d, pk, seed, salt);
35+
engine.encaps(u, v, K, pk, seed, salt);
3736

38-
byte[] cipherText = Arrays.concatenate(u, v, d, salt);
37+
byte[] cipherText = Arrays.concatenate(u, v, salt);
3938

4039
return new SecretWithEncapsulationImpl(Arrays.copyOfRange(K, 0, key.getParameters().getK()), cipherText);
4140
}

core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCKeyPairGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ private AsymmetricCipherKeyPair genKeyPair(byte[] seed)
4545
{
4646
HQCEngine engine = hqcKeyGenerationParameters.getParameters().getEngine();
4747
byte[] pk = new byte[40 + N_BYTE];
48-
byte[] sk = new byte[40 + 40 + N_BYTE];
48+
byte[] sk = new byte[40 + 40 + k + N_BYTE];
4949

5050
engine.genKeyPair(pk, sk, seed);
5151

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ public void testVectors()
4242
String[] files;
4343
// test cases
4444
files = new String[]{
45-
"hqc-128_kat.rsp",
46-
"hqc-192_kat.rsp",
47-
"hqc-256_kat.rsp",
45+
"HQC-128.rsp",
46+
"HQC-192.rsp",
47+
"HQC-256.rsp",
4848
};
4949

5050
HQCParameters[] listParams = new HQCParameters[]{

0 commit comments

Comments
 (0)