22
33import java .math .BigInteger ;
44
5+ import org .bouncycastle .crypto .Digest ;
56import org .bouncycastle .crypto .EncapsulatedSecretExtractor ;
67import org .bouncycastle .crypto .params .SAKKEPrivateKeyParameters ;
78import org .bouncycastle .crypto .params .SAKKEPublicKeyParameters ;
1011import org .bouncycastle .util .Arrays ;
1112import org .bouncycastle .util .BigIntegers ;
1213
13-
14+ /**
15+ * Implements the receiver side of the SAKKE (Sakai-Kasahara Key Encryption) protocol
16+ * as defined in RFC 6508. This class extracts the shared secret value (SSV) from
17+ * encapsulated data using the receiver's private key.
18+ * <p>
19+ * The extraction process follows these steps (RFC 6508, Section 6.2.2):
20+ * <ol>
21+ * <li>Parse encapsulated data into R_(b,S) and H</li>
22+ * <li>Compute pairing result w = <R_(b,S), K_(b,S)></li>
23+ * <li>Recover SSV via SSV = H XOR HashToIntegerRange(w, 2^n)</li>
24+ * <li>Validate R_(b,S) by recomputing it with derived parameters</li>
25+ * </ol>
26+ * </p>
27+ *
28+ * @see <a href="https://datatracker.ietf.org/doc/html/rfc6508">Sakai-Kasahara Key Encryption (SAKKE)</a>
29+ */
1430public class SAKKEKEMExtractor
1531 implements EncapsulatedSecretExtractor
1632{
@@ -22,65 +38,88 @@ public class SAKKEKEMExtractor
2238 private final ECPoint K_bs ;
2339 private final int n ; // Security parameter
2440 private final BigInteger identifier ;
25-
41+ private final Digest digest ;
42+
43+ /**
44+ * Initializes the extractor with cryptographic parameters from the receiver's private key.
45+ *
46+ * @param privateKey The receiver's private key containing public parameters
47+ * (curve, prime, generator, etc.) and the Receiver Secret Key (RSK).
48+ * Must not be {@code null}.
49+ */
2650 public SAKKEKEMExtractor (SAKKEPrivateKeyParameters privateKey )
2751 {
2852 SAKKEPublicKeyParameters publicKey = privateKey .getPublicParams ();
2953 this .curve = publicKey .getCurve ();
3054 this .q = publicKey .getQ ();
31- this .P = publicKey .getP ();
55+ this .P = publicKey .getPoint ();
3256 this .p = publicKey .getPrime ();
3357 this .Z_S = publicKey .getZ ();
34- this .K_bs = privateKey .getRSK ();
35- this .n = publicKey .getN ();
3658 this .identifier = publicKey .getIdentifier ();
59+ this .K_bs = P .multiply (this .identifier .add (privateKey .getMasterSecret ()).modInverse (q )).normalize ();
60+ this .n = publicKey .getN ();
61+
62+ this .digest = publicKey .getDigest ();
3763 }
3864
65+ /**
66+ * Extracts the shared secret value (SSV) from encapsulated data as per RFC 6508.
67+ *
68+ * @param encapsulation The encapsulated data containing:
69+ * <ul>
70+ * <li>R_(b,S): Elliptic curve point (uncompressed format, 257 bytes)</li>
71+ * <li>H: Integer value (n/8 bytes)</li>
72+ * </ul>
73+ * @return The extracted SSV as a byte array.
74+ * @throws IllegalStateException If: Validation of R_(b,S) fails
75+ */
3976 @ Override
4077 public byte [] extractSecret (byte [] encapsulation )
4178 {
42- try
43- {
44- // Step 1: Parse Encapsulated Data (R_bS, H)
45- ECPoint R_bS = curve .decodePoint (Arrays .copyOfRange (encapsulation , 0 , 257 ));
46- BigInteger H = new BigInteger (Arrays .copyOfRange (encapsulation , 257 , 274 ));
47-
48- // Step 2: Compute w = <R_bS, K_bS> using pairing
49- BigInteger w = computePairing (R_bS , K_bs , p , q );
50-
51- // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n)
52- BigInteger twoToN = BigInteger .ONE .shiftLeft (n );
53- BigInteger mask = SAKKEUtils .hashToIntegerRange (w .toByteArray (), twoToN );
54- BigInteger ssv = H .xor (mask );
55-
56- // Step 4: Compute r = HashToIntegerRange(SSV || b)
57- BigInteger b = identifier ;
58- BigInteger r = SAKKEUtils .hashToIntegerRange (Arrays .concatenate (ssv .toByteArray (), b .toByteArray ()), q );
59-
60- // Step 5: Validate R_bS
61- ECPoint bP = P .multiply (b ).normalize ();
62- ECPoint Test = bP .add (Z_S ).multiply (r ).normalize ();
63- if (!R_bS .equals (Test ))
64- {
65- throw new IllegalStateException ("Validation of R_bS failed" );
66- }
67-
68- return BigIntegers .asUnsignedByteArray (n / 8 , ssv );
69- }
70- catch (Exception e )
79+ // Step 1: Parse Encapsulated Data (R_bS, H)
80+ ECPoint R_bS = curve .decodePoint (Arrays .copyOfRange (encapsulation , 0 , 257 ));
81+ BigInteger H = BigIntegers .fromUnsignedByteArray (encapsulation , 257 , 16 );
82+
83+ // Step 2: Compute w = <R_bS, K_bS> using pairing
84+ BigInteger w = computePairing (R_bS , K_bs , p , q );
85+
86+ // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n)
87+ BigInteger twoToN = BigInteger .ONE .shiftLeft (n );
88+ BigInteger mask = SAKKEKEMSGenerator .hashToIntegerRange (w .toByteArray (), twoToN , digest );
89+ BigInteger ssv = H .xor (mask ).mod (p );
90+
91+ // Step 4: Compute r = HashToIntegerRange(SSV || b)
92+ BigInteger b = identifier ;
93+ BigInteger r = SAKKEKEMSGenerator .hashToIntegerRange (Arrays .concatenate (ssv .toByteArray (), b .toByteArray ()), q , digest );
94+
95+ // Step 5: Validate R_bS
96+ ECPoint bP = P .multiply (b ).normalize ();
97+ ECPoint Test = bP .add (Z_S ).multiply (r ).normalize ();
98+ if (!R_bS .equals (Test ))
7199 {
72- throw new IllegalStateException ("SAKKE extraction failed: " + e . getMessage () );
100+ throw new IllegalStateException ("Validation of R_bS failed" );
73101 }
102+
103+ return BigIntegers .asUnsignedByteArray (n / 8 , ssv );
74104 }
75105
76106 @ Override
77107 public int getEncapsulationLength ()
78108 {
79- return 0 ;
109+ return 273 ; //257 (length of ECPoint) + 16 (length of Hash)
80110 }
81111
82-
83- public static BigInteger computePairing (ECPoint R , ECPoint Q , BigInteger p , BigInteger q )
112+ /**
113+ * Computes the Tate-Lichtenbaum pairing <R, Q> for SAKKE validation.
114+ * Follows the pairing algorithm described in RFC 6508, Section 3.2.
115+ *
116+ * @param R First pairing input (elliptic curve point)
117+ * @param Q Second pairing input (elliptic curve point)
118+ * @param p Prime field characteristic
119+ * @param q Subgroup order
120+ * @return Pairing result in PF_p[q], represented as a field element
121+ */
122+ static BigInteger computePairing (ECPoint R , ECPoint Q , BigInteger p , BigInteger q )
84123 {
85124 // v = (1,0) in F_p^2
86125 BigInteger [] v = new BigInteger []{BigInteger .ONE , BigInteger .ZERO };
@@ -133,6 +172,16 @@ public static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigI
133172 return v [1 ].multiply (v [0 ].modInverse (p )).mod (p );
134173 }
135174
175+ /**
176+ * Performs multiplication in F_p^2 field.
177+ *
178+ * @param x_real Real component of first operand
179+ * @param x_imag Imaginary component of first operand
180+ * @param y_real Real component of second operand
181+ * @param y_imag Imaginary component of second operand
182+ * @param p Prime field characteristic
183+ * @return Result of multiplication in F_p^2 as [real, imaginary] array
184+ */
136185 static BigInteger [] fp2Multiply (BigInteger x_real , BigInteger x_imag , BigInteger y_real , BigInteger y_imag , BigInteger p )
137186 {
138187 return new BigInteger []{
@@ -141,6 +190,14 @@ static BigInteger[] fp2Multiply(BigInteger x_real, BigInteger x_imag, BigInteger
141190 };
142191 }
143192
193+ /**
194+ * Computes squaring operation in F_p^2 field.
195+ *
196+ * @param currentX Real component of input
197+ * @param currentY Imaginary component of input
198+ * @param p Prime field characteristic
199+ * @return Squared result in F_p^2 as [newX, newY] array
200+ */
144201 static BigInteger [] fp2PointSquare (BigInteger currentX , BigInteger currentY , BigInteger p )
145202 {
146203 BigInteger xPlusY = currentX .add (currentY ).mod (p );
0 commit comments