@@ -117,17 +117,119 @@ public void generatePublic_fromRawPublicKey_validatesSize() throws Exception {
117117 () -> keyFactory .generatePublic (new RawKeySpec (new byte [rawPublicKey .length + 1 ])));
118118 }
119119
120+ /** Helper class to test KeyFactory.translateKey. */
121+ static class TestPublicKey implements PublicKey {
122+ public TestPublicKey (byte [] x509encoded ) {
123+ this .x509encoded = x509encoded ;
124+ }
125+
126+ private final byte [] x509encoded ;
127+
128+ @ Override
129+ public String getAlgorithm () {
130+ return "XWING" ;
131+ }
132+
133+ @ Override
134+ public String getFormat () {
135+ return "X.509" ;
136+ }
137+
138+ @ Override
139+ public byte [] getEncoded () {
140+ return x509encoded ;
141+ }
142+ }
143+
144+ /** Helper class to test KeyFactory.translateKey. */
145+ static class TestPrivateKey implements PrivateKey {
146+ public TestPrivateKey (byte [] pkcs8encoded ) {
147+ this .pkcs8encoded = pkcs8encoded ;
148+ }
149+
150+ private final byte [] pkcs8encoded ;
151+
152+ @ Override
153+ public String getAlgorithm () {
154+ return "XWING" ;
155+ }
156+
157+ @ Override
158+ public String getFormat () {
159+ return "PKCS#8" ;
160+ }
161+
162+ @ Override
163+ public byte [] getEncoded () {
164+ return pkcs8encoded ;
165+ }
166+ }
167+
120168 @ Test
121- public void x509AndPkcs8_areNotSupported () throws Exception {
122- KeyPairGenerator keyGen = KeyPairGenerator .getInstance ("XWING" , conscryptProvider );
123- KeyPair keyPair = keyGen .generateKeyPair ();
169+ public void toAndFromX509AndPkcs8_works () throws Exception {
170+ // from https://datatracker.ietf.org/doc/draft-connolly-cfrg-xwing-kem/, Appendix D
171+ byte [] rawPrivateKey = TestUtils .decodeHex (
172+ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" );
173+ byte [] rawPublicKey = NativeCrypto .XWING_public_key_from_seed (rawPrivateKey );
174+
175+ byte [] encodedPrivateKey = TestUtils .decodeBase64 (
176+ "MDQCAQAwDQYLKwYBBAGD5i2ByHoEIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4f" );
177+ byte [] encodedPublicKey = TestUtils .decodeBase64 (
178+ "MIIE1DANBgsrBgEEAYPmLYHIegOCBMEAb1QJigoOZBFGYUtpYLpg2GA9YvRH+atJ"
179+ + "m0e9aQbMQLBh2GNKPoiQbyhJWOdEHKbHJcu5cJW3ZxpGK2aByeZYC7yNYLFJ+mAm"
180+ + "EEOvu6UvIFpgKDhIUVlq3zcavqmNM0c4PSu2c0OPZ4NhK/hwFPe5Gol0AmU0XfZ5"
181+ + "NARz0cTBdohuXim48Fi7fHNTFmhs/1w764wmHLAJcKacGvzFS5TLhuHOY7pjbjlc"
182+ + "pFEB4hx70EwxPqGa8kFB79KtREFqJbpPZZEO99iAnDCT8EqvAOPNluNcSqPIAsGK"
183+ + "1vOdpLS42YyL15Atg6B7pFOWZ0pgJDyrk+gP2bHId3N2qcwNb6EV4mOTgLnGvnhI"
184+ + "vRNYjGRwOgU10ZoPgWM6l2oKEFtm7ihdD9JV6CwDMZJfQ4O278dh72CZI1oLmHJj"
185+ + "WKqdAbi4llGfkhR0u3wUuyIlK1wvENQSRsmyPnZEhJNn9UGhX2O8koo5u3vHPwe2"
186+ + "ZcSWu2VYyPRUiacuxLrNNOnFlMM4cbcj8DSV6ItDkasm5DBD3rYRezkZ5FxMGxar"
187+ + "KOR93XI2Y4VHZhkvwYBspwq7eGy9swky5oyKNwvPsHmDoBLDJmuT76YmV/S4ODdM"
188+ + "sLuV4OwGVBsHZdmc8VO8a5YTXKeApVs2R3ieMZFeRig8+ce7boRT+2aCEFFB8dwN"
189+ + "ANhe7XA7bGyWH3nIRSdrQkiUnAZ4LlE+spkbldlgQuOMvto1JEmytQhOvaUiamIG"
190+ + "QAeJEwowlkSYSLYp/upKLCp0PEoN3Jyz89Z2/FY3MbJsShpm3IRZFwBW1XaX8UQ7"
191+ + "gamjRBK7e/BfMydXWlkR3TAdYFOGfzwwgHEfG/EVh7C7KYQnayaF53ViEOSz+JVT"
192+ + "hCMeVYxvUQyR4PxWtdGIX/KUnpWka8G+4fpx9QJ+EMRDsOkdD9dED0Z6JyISEuiP"
193+ + "XGumQpbK4NIHv8YPiMfPtcRaoYOdGMs3xFhD5UJqSpDIArZCj5U8NZxKwGA0UvrA"
194+ + "tzYeL9NdzIhakhRdT8oBWPG31wtLzRGOSipBVEON8xDESpobmepBWQcmeoiwYkJB"
195+ + "V5wXIvRu1hwuPspUXJlwUXF1OZuADbJdo5WT0GSQ1xQsAOiNLbBH6YmL23rLftkH"
196+ + "9uMEFswN5UokLAohJjAvXVTIW8Zqwvg8eXlFtQZ8qkK9LgwZypdQblB6sKXJ9WM3"
197+ + "CEmcGfJK7FE705A6XXO27EmR98cuuZHBw3iJgFyx6jigzAIXayfFjWOM5aMmaEV8"
198+ + "+bm+AnygIUBXlxcl1UEC6JlnFusq2CNFO2BbhVNwsbIbOTLN7UFgqplzx+uuWsR2"
199+ + "TZTPfMlQbwd7rXMBLbtKyBQKOHRkEuszyVFFliBfcHY1hiIX2bYJGMYmjZNEkVuE"
200+ + "eiR2waJw8VSlyEI0FlrPyGk5hwLOqemgfnsOmeqb3LeEH+nA+iXIM4CSVho+3dxw"
201+ + "AfR4rWV4GmAkqtFl2baXmtrESKRGL1ZGhVJ/diQ0/ppCWoRDe0VzkuyoDJE1BhUe"
202+ + "OhMjnzQvynZVtuquhFoiHOs+Z/VjnGGT9v3u9X45m4CLfzqitXQKre2QFj3F13XJ"
203+ + "+vfx+9B12rNE6dfRRmRygfu6ezxWyv1YM7epMOxCBufDptd2T+gdeg==" );
124204
125205 KeyFactory keyFactory = KeyFactory .getInstance ("XWING" , conscryptProvider );
126206
127- assertThrows (UnsupportedOperationException .class ,
128- () -> keyFactory .getKeySpec (keyPair .getPrivate (), PKCS8EncodedKeySpec .class ));
129- assertThrows (UnsupportedOperationException .class ,
130- () -> keyFactory .getKeySpec (keyPair .getPublic (), X509EncodedKeySpec .class ));
207+ // Check generatePrivate from PKCS8EncodedKeySpec works.
208+ PrivateKey privateKey =
209+ keyFactory .generatePrivate (new PKCS8EncodedKeySpec (encodedPrivateKey ));
210+ assertArrayEquals (encodedPrivateKey , privateKey .getEncoded ());
211+ assertArrayEquals (rawPrivateKey , ((OpenSslXwingPrivateKey ) privateKey ).getRaw ());
212+
213+ // Check generatePublic from X509EncodedKeySpec works.
214+ PublicKey publicKey = keyFactory .generatePublic (new X509EncodedKeySpec (encodedPublicKey ));
215+ assertArrayEquals (encodedPublicKey , publicKey .getEncoded ());
216+ assertArrayEquals (rawPublicKey , ((OpenSslXwingPublicKey ) publicKey ).getRaw ());
217+
218+ // Check getKeySpec with works for both private and public keys.
219+ EncodedKeySpec privateKeySpec =
220+ keyFactory .getKeySpec (privateKey , PKCS8EncodedKeySpec .class );
221+ assertEquals ("PKCS#8" , privateKeySpec .getFormat ());
222+ assertArrayEquals (encodedPrivateKey , privateKeySpec .getEncoded ());
223+
224+ EncodedKeySpec publicKeySpec = keyFactory .getKeySpec (publicKey , X509EncodedKeySpec .class );
225+ assertEquals ("X.509" , publicKeySpec .getFormat ());
226+ assertArrayEquals (encodedPublicKey , publicKeySpec .getEncoded ());
227+
228+ assertEquals (privateKey , keyFactory .translateKey (privateKey ));
229+ assertEquals (
230+ privateKey , keyFactory .translateKey (new TestPrivateKey (privateKey .getEncoded ())));
231+ assertEquals (publicKey , keyFactory .translateKey (publicKey ));
232+ assertEquals (publicKey , keyFactory .translateKey (new TestPublicKey (publicKey .getEncoded ())));
131233 }
132234
133235 @ Test
@@ -168,6 +270,37 @@ public void sealAndOpen_works() throws Exception {
168270 }
169271 }
170272
273+ @ Test
274+ public void sealAndOpenWithForeignKeys_works () throws Exception {
275+ byte [] info = TestUtils .decodeHex ("aa" );
276+ byte [] plaintext = TestUtils .decodeHex ("bb" );
277+ byte [] aad = TestUtils .decodeHex ("cc" );
278+ for (int aead : new int [] {AEAD_AES_128_GCM , AEAD_AES_256_GCM , AEAD_CHACHA20POLY1305 }) {
279+ HpkeSuite suite = new HpkeSuite (KEM_XWING , KDF_HKDF_SHA256 , aead );
280+ KeyPairGenerator keyGen = KeyPairGenerator .getInstance ("XWING" , conscryptProvider );
281+ KeyPair keyPairRecipient = keyGen .generateKeyPair ();
282+ PublicKey foreignPublicKey =
283+ new TestPublicKey (keyPairRecipient .getPublic ().getEncoded ());
284+ PrivateKey foreignPrivateKey =
285+ new TestPrivateKey (keyPairRecipient .getPrivate ().getEncoded ());
286+
287+ HpkeContextSender ctxSender =
288+ HpkeContextSender .getInstance (suite .name (), conscryptProvider );
289+ ctxSender .init (foreignPublicKey , info );
290+
291+ byte [] encapsulated = ctxSender .getEncapsulated ();
292+ byte [] ciphertext = ctxSender .seal (plaintext , aad );
293+
294+ HpkeContextRecipient foreignContextRecipient =
295+ HpkeContextRecipient .getInstance (suite .name (), conscryptProvider );
296+ foreignContextRecipient .init (encapsulated , foreignPrivateKey , info );
297+
298+ byte [] foreignOutput = foreignContextRecipient .open (ciphertext , aad );
299+
300+ assertArrayEquals (plaintext , foreignOutput );
301+ }
302+ }
303+
171304 @ Test
172305 public void kemTestVectors_encapsulatedIsCorrect () throws Exception {
173306 HpkeSuite suite = new HpkeSuite (KEM_XWING , KDF_HKDF_SHA256 , AEAD_AES_128_GCM );
0 commit comments