Skip to content

Commit f4a2618

Browse files
gefeilidghgit
authored andcommitted
TODO: try to get the correct q
1 parent 5989d52 commit f4a2618

File tree

5 files changed

+386
-0
lines changed

5 files changed

+386
-0
lines changed
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package org.bouncycastle.crypto.kems;
2+
3+
import org.bouncycastle.crypto.EncapsulatedSecretGenerator;
4+
import org.bouncycastle.crypto.SecretWithEncapsulation;
5+
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
6+
import org.bouncycastle.crypto.params.SAKKEPublicKey;
7+
import org.bouncycastle.math.ec.ECCurve;
8+
import org.bouncycastle.math.ec.ECFieldElement;
9+
import org.bouncycastle.math.ec.ECPoint;
10+
import org.bouncycastle.util.Arrays;
11+
import org.bouncycastle.util.BigIntegers;
12+
13+
import java.math.BigInteger;
14+
import java.nio.ByteBuffer;
15+
import java.security.SecureRandom;
16+
17+
public class SAKKEKEMSGenerator
18+
implements EncapsulatedSecretGenerator
19+
{
20+
private final SAKKEPublicKey publicParams;
21+
private final SecureRandom random;
22+
23+
public SAKKEKEMSGenerator(SAKKEPublicKey params, SecureRandom random)
24+
{
25+
this.publicParams = params;
26+
this.random = random;
27+
}
28+
29+
@Override
30+
public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey)
31+
{
32+
// 1. Generate random SSV in range [0, 2^n - 1]
33+
BigInteger ssv = new BigInteger(publicParams.getN(), random);
34+
35+
// 2. Compute r = HashToIntegerRange(SSV || b, q)
36+
BigInteger b = getRecipientId((SAKKEPublicKey)recipientKey);
37+
BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), publicParams.getQ());
38+
39+
// 3. Compute R_(b,S) = [r]([b]P + Z_S)
40+
ECPoint bP = publicParams.getP().multiply(b); // [b]P
41+
ECPoint Z_S = publicParams.getZ(); // Z_S
42+
ECPoint R_bS = bP.add(Z_S).multiply(r); // [r]([b]P + Z_S)
43+
44+
// 4. Compute H = SSV XOR HashToIntegerRange( g^r, 2^n )
45+
BigInteger g_r = pairing(R_bS, publicParams.getP(), publicParams.getQ(), publicParams.getP().getCurve().getField().getCharacteristic());
46+
BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(publicParams.getN())); // 2^n
47+
48+
BigInteger H = ssv.xor(mask);
49+
50+
// 5. Encode encapsulated data (R_bS, H)
51+
byte[] encapsulated = encodeData(R_bS, H);
52+
53+
return new SecretWithEncapsulationImpl(
54+
BigIntegers.asUnsignedByteArray(publicParams.getN() / 8, ssv), // Output SSV as key material
55+
encapsulated
56+
);
57+
}
58+
59+
private BigInteger getRecipientId(SAKKEPublicKey pubKey)
60+
{
61+
byte[] hashedId = SAKKEUtils.hash(pubKey.getZ().getEncoded(false)); // Hash Z_S
62+
return new BigInteger(1, hashedId).mod(pubKey.getQ().subtract(BigInteger.ONE)).add(BigIntegers.TWO);
63+
}
64+
65+
/**
66+
* Computes the Tate-Lichtenbaum pairing ⟨P, Q⟩ as per RFC 6508.
67+
* <p>
68+
* //* @param P First point (on E(F_p)).
69+
*
70+
* @param Q Second point (on E(F_p)).
71+
* @return Result of the pairing in the field F_p^2.
72+
*/
73+
public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q)
74+
{
75+
ECCurve curve = R.getCurve();
76+
ECFieldElement i = curve.fromBigInteger(BigInteger.ONE.negate()); // i = -1 in F_p^2
77+
78+
ECPoint C = R;
79+
BigInteger c = p.add(BigInteger.ONE).divide(q);
80+
ECFieldElement v = curve.fromBigInteger(BigInteger.ONE); // v = 1 in F_p
81+
82+
String qBits = q.subtract(BigInteger.ONE).toString(2); // Binary representation of q-1
83+
84+
for (int j = 1; j < qBits.length(); j++)
85+
{ // Skip MSB
86+
// l = (3 * (C_x^2 - 1)) / (2 * C_y)
87+
ECFieldElement Cx = C.getAffineXCoord();
88+
ECFieldElement Cy = C.getAffineYCoord();
89+
ECFieldElement l = Cx.square().multiply(curve.fromBigInteger(ECFieldElement.THREE)).subtract(curve.fromBigInteger(BigInteger.ONE))
90+
.divide(Cy.multiply(curve.fromBigInteger(BigIntegers.TWO)));
91+
92+
// v = v^2 * (l * (Q_x + C_x) + (i * Q_y - C_y))
93+
ECFieldElement Qx = Q.getAffineXCoord();
94+
ECFieldElement Qy = Q.getAffineYCoord();
95+
v = v.square().multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy)));
96+
97+
// Double the point
98+
C = C.twice();
99+
100+
// If the bit is 1, perform additional step
101+
if (qBits.charAt(j) == '1')
102+
{
103+
// l = (C_y - R_y) / (C_x - R_x)
104+
ECFieldElement Rx = R.getAffineXCoord();
105+
ECFieldElement Ry = R.getAffineYCoord();
106+
l = Cy.subtract(Ry).divide(Cx.subtract(Rx));
107+
108+
// v = v * (l * (Q_x + C_x) + (i * Q_y - C_y))
109+
v = v.multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy)));
110+
111+
// C = C + R
112+
C = C.add(R);
113+
}
114+
}
115+
116+
// Compute v^c
117+
v = curve.fromBigInteger(v.toBigInteger().pow(c.intValue()));
118+
119+
// Convert to F_p representative
120+
return computeFpRepresentative(v, curve);
121+
}
122+
123+
private static BigInteger computeFpRepresentative(ECFieldElement t, ECCurve curve)
124+
{
125+
// Characteristic of F_p
126+
BigInteger p = ((ECCurve.Fp) curve).getQ();
127+
128+
// Assume t = a + i * b in F_p² → extract a, b
129+
ECFieldElement a = t; // In F_p², a is the real part
130+
ECFieldElement b = t.multiply(curve.fromBigInteger(BigInteger.ONE.negate())); // Imaginary part
131+
132+
// Compute b/a mod p
133+
return b.toBigInteger().multiply(a.toBigInteger().modInverse(p)).mod(p);
134+
}
135+
136+
public static byte[] encodeData(ECPoint R_bS, BigInteger H) {
137+
// 1. Serialize EC Point (use compressed format for efficiency)
138+
byte[] R_bS_bytes = R_bS.getEncoded(true);
139+
140+
// 2. Serialize H (convert to a fixed-length byte array)
141+
byte[] H_bytes = H.toByteArray();
142+
143+
// 3. Combine both into a single byte array
144+
ByteBuffer buffer = ByteBuffer.allocate(R_bS_bytes.length + H_bytes.length);
145+
buffer.put(R_bS_bytes);
146+
buffer.put(H_bytes);
147+
148+
return buffer.array();
149+
}
150+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package org.bouncycastle.crypto.kems;
2+
3+
import java.math.BigInteger;
4+
5+
import org.bouncycastle.crypto.Digest;
6+
import org.bouncycastle.crypto.digests.SHA256Digest;
7+
import org.bouncycastle.math.ec.ECPoint;
8+
import org.bouncycastle.util.Strings;
9+
import org.bouncycastle.util.encoders.Hex;
10+
11+
public class SAKKEUtils
12+
{
13+
public static BigInteger hashToIntegerRange(byte[] input, BigInteger q)
14+
{
15+
// RFC 6508 Section 5.1: Hashing to an Integer Range
16+
SHA256Digest digest = new SHA256Digest();
17+
byte[] hash = new byte[digest.getDigestSize()];
18+
19+
// Step 1: Compute A = hashfn(s)
20+
digest.update(input, 0, input.length);
21+
digest.doFinal(hash, 0);
22+
byte[] A = hash.clone();
23+
24+
// Step 2: Initialize h_0 to all-zero bytes of hashlen size
25+
byte[] h = new byte[digest.getDigestSize()];
26+
27+
// Step 3: Compute l = Ceiling(lg(n)/hashlen)
28+
int l = q.bitLength() >> 8;
29+
30+
BigInteger v = BigInteger.ZERO;
31+
32+
// Step 4: Compute h_i and v_i
33+
for (int i = 1; i <= l; i++)
34+
{
35+
// h_i = hashfn(h_{i-1})
36+
digest.update(h, 0, h.length);
37+
digest.doFinal(h, 0);
38+
System.out.println("h_"+i+":" +new String(Hex.encode(h)));
39+
// v_i = hashfn(h_i || A)
40+
digest.update(h, 0, h.length);
41+
digest.update(A, 0, A.length);
42+
byte[] v_i = new byte[digest.getDigestSize()];
43+
digest.doFinal(v_i, 0);
44+
System.out.println("v_"+i+":" +new String(Hex.encode(v_i)));
45+
// Append v_i to v'
46+
v = v.shiftLeft(v_i.length * 8).add(new BigInteger(1, v_i));
47+
}
48+
System.out.println("v:" +new String(Hex.encode(v.toByteArray())));
49+
// Step 6: v = v' mod n
50+
return v.mod(q);
51+
}
52+
53+
public static byte[] hash(byte[] data)
54+
{
55+
Digest digest = new SHA256Digest();
56+
byte[] rlt = new byte[digest.getDigestSize()];
57+
digest.update(data, 0, data.length);
58+
digest.doFinal(rlt, 0);
59+
return rlt;
60+
}
61+
62+
public static byte[] hash(ECPoint point)
63+
{
64+
return hash(point.getEncoded(false)); // Use uncompressed encoding
65+
}
66+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package org.bouncycastle.crypto.params;
2+
3+
import java.math.BigInteger;
4+
5+
import org.bouncycastle.math.ec.ECPoint;
6+
7+
public class SAKKEPrivateKey
8+
extends AsymmetricKeyParameter
9+
{
10+
private final BigInteger b; // User's identity
11+
private final ECPoint K; // Private key K_a
12+
private final SAKKEPublicKey publicParams;
13+
14+
public SAKKEPrivateKey(BigInteger b, ECPoint K, SAKKEPublicKey publicParams)
15+
{
16+
super(true);
17+
this.b = b;
18+
this.K = K;
19+
this.publicParams = publicParams;
20+
}
21+
22+
// Getters
23+
public ECPoint getK()
24+
{
25+
return K;
26+
}
27+
28+
public BigInteger getB()
29+
{
30+
return b;
31+
}
32+
33+
public SAKKEPublicKey getPublicParams()
34+
{
35+
return publicParams;
36+
}
37+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package org.bouncycastle.crypto.params;
2+
3+
import java.math.BigInteger;
4+
5+
import org.bouncycastle.math.ec.ECCurve;
6+
import org.bouncycastle.math.ec.ECPoint;
7+
8+
public class SAKKEPublicKey
9+
extends AsymmetricKeyParameter
10+
{
11+
private final ECCurve curve;
12+
private final ECPoint P; // Base point
13+
private final ECPoint Z; // KMS Public Key: Z = [z]P
14+
private final BigInteger q; // Subgroup order
15+
private final int n; // SSV bit length
16+
17+
public SAKKEPublicKey(ECCurve curve, ECPoint P, ECPoint Z, BigInteger q, int n)
18+
{
19+
super(false);
20+
this.curve = curve;
21+
this.P = P;
22+
this.Z = Z;
23+
this.q = q;
24+
this.n = n;
25+
}
26+
27+
// Getters
28+
public ECCurve getCurve()
29+
{
30+
return curve;
31+
}
32+
33+
public ECPoint getP()
34+
{
35+
return P;
36+
}
37+
38+
public ECPoint getZ()
39+
{
40+
return Z;
41+
}
42+
43+
public BigInteger getQ()
44+
{
45+
return q;
46+
}
47+
48+
public int getN()
49+
{
50+
return n;
51+
}
52+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package org.bouncycastle.crypto.kems.test;
2+
3+
import java.math.BigInteger;
4+
5+
6+
import org.bouncycastle.crypto.kems.SAKKEKEMSGenerator;
7+
import org.bouncycastle.crypto.kems.SAKKEUtils;
8+
import org.bouncycastle.util.Arrays;
9+
import org.bouncycastle.util.Strings;
10+
import org.bouncycastle.util.encoders.Hex;
11+
import org.bouncycastle.util.test.SimpleTest;
12+
import org.junit.Assert;
13+
14+
public class SAKKEKEMSTest
15+
extends SimpleTest
16+
{
17+
public static void main(String[] args)
18+
throws Exception
19+
{
20+
SAKKEKEMSTest test = new SAKKEKEMSTest();
21+
test.performTest();
22+
// Expected Rb values
23+
// BigInteger expectedRbx = new BigInteger("44E8AD44AB8592A6A5A3DDCA5CF896C718043606A01D650DEF37A01F37C228C332FC317354E2C274D4DAF8AD001054C7...
24+
// BigInteger expectedRby = new BigInteger("557E134AD85BB1D4B9CE4F8BE4B08A12BABF55B1D6F1D7A638019EA28E15AB1C9F76375FDD1210D4F4351B9A009486B7...
25+
//
26+
// // Instantiate SAKKE KEM Generator
27+
// SAKKEKEMSGenerator kem = new SAKKEKEMSGenerator();
28+
// EncapsulatedData encapsulatedData = kem.encapsulate(SSV);
29+
//
30+
// // Validate results
31+
// boolean testPassed = expectedRbx.equals(encapsulatedData.getRbx()) && expectedRby.equals(encapsulatedData.getRby());
32+
33+
//System.out.println("SAKKE KEM Test " + (testPassed ? "PASSED" : "FAILED"));
34+
}
35+
36+
private static byte[] hexStringToByteArray(String s)
37+
{
38+
int len = s.length();
39+
byte[] data = new byte[len / 2];
40+
for (int i = 0; i < len; i += 2)
41+
{
42+
data[i / 2] = (byte)((Character.digit(s.charAt(i), 16) << 4)
43+
+ Character.digit(s.charAt(i + 1), 16));
44+
}
45+
return data;
46+
}
47+
48+
@Override
49+
public String getName()
50+
{
51+
return null;
52+
}
53+
54+
@Override
55+
public void performTest()
56+
throws Exception
57+
{
58+
// BigInteger z = new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F");
59+
// BigInteger Zx = new BigInteger("5958EF1B1679BF099B3A030DF255AA6A23C1D8F143D4D23F753E69BD27A832F38CB4AD53DDEF"
60+
// + "4260B0FE8BB45C4C1FF510EFFE300367A37B61F701D914AEF09724825FA0707D61A6DFF4FBD7273566CDDE352A0B04B7C16A78309BE"
61+
// + "640697DE747613A5FC195E8B9F328852A579DB8F99B1D0034479EA9C5595F47C4B2F54FF2");
62+
// BigInteger Zy = new BigInteger("1508D37514DCF7A8E143A6058C09A6BF2C9858CA37C258065AE6BF7532BC8B5B63383866E075"
63+
// + "3C5AC0E72709F8445F2E6178E065857E0EDA10F68206B63505ED87E534FB2831FF957FB7DC619DAE61301EEACC2FDA3680EA499925" +
64+
// "8A833CEA8FC67C6D19487FB449059F26CC8AAB655AB58B7CC796E24E9A394095754F5F8BAE");
65+
//
66+
byte[] b = Hex.decode("323031312D30320074656C3A2B34343737303039303031323300");
67+
68+
byte[] SSV = Hex.decode("123456789ABCDEF0123456789ABCDEF0");
69+
byte[] expectedR = Hex.decode("13EE3E1B8DAC5DB168B1CEB32F0566A4C273693F78BAFFA2A2EE6A686E6BD90F8206CCAB84E7F"
70+
+ "42ED39BD4FB131012ECCA2ECD2119414560C17CAB46B956A80F58A3302EB3E2C9A228FBA7ED34D8ACA2392DA1FFB0B17B2320AE09AAEDF"
71+
+ "D0235F6FE0EB65337A63F9CC97728B8E5AD0460FADE144369AA5B2166213247712096");
72+
73+
BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(SSV, b), BigInteger.valueOf(1024));
74+
75+
System.out.println("r:" +new String(Hex.encode(r.toByteArray())));
76+
77+
System.out.println("r:" +new String(Hex.encode(expectedR)));
78+
79+
Assert.assertTrue(Arrays.areEqual(r.toByteArray(), expectedR));
80+
}
81+
}

0 commit comments

Comments
 (0)