44using System . Text ;
55
66using NUnit . Framework ;
7-
7+ using Org . BouncyCastle . Asn1 ;
8+ using Org . BouncyCastle . Asn1 . Gnu ;
9+ using Org . BouncyCastle . Asn1 . Misc ;
810using Org . BouncyCastle . Asn1 . Sec ;
911using Org . BouncyCastle . Asn1 . X9 ;
1012using Org . BouncyCastle . Crypto ;
1315using Org . BouncyCastle . Security ;
1416using Org . BouncyCastle . Utilities ;
1517using Org . BouncyCastle . Utilities . Encoders ;
18+ using Org . BouncyCastle . Utilities . IO ;
1619using Org . BouncyCastle . Utilities . Test ;
1720
1821namespace Org . BouncyCastle . Bcpg . OpenPgp . Tests
@@ -53,6 +56,40 @@ public class PgpECDHTest
5356 "6HiuFH7VKWcxPUBjXwf5+Z3uOKEp28tBgNyDrdbr1BbqlgYzIKq/pe9zUbUXfitn" +
5457 "vFc6HcGhvmRQreQ+Yw1x3x0HJeoPwg==" ) ;
5558
59+ private static readonly byte [ ] testX25519PubKey =
60+ Base64 . Decode (
61+ "mDMEX9XwXhYJKwYBBAHaRw8BAQdAR5ZghmMHL8wldNlOkmbaiAOdyF5V5bgZdKq7" +
62+ "L+yb4A20HEVDREggPHRlc3QuZWNkaEBleGFtcGxlLmNvbT6IkAQTFggAOBYhBGoy" +
63+ "UrxNv7c3S2JjGzewWiN8tfzXBQJf1fBeAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4B" +
64+ "AheAAAoJEDewWiN8tfzX0ZMA/AhEvrIgu+29eMQeuHOwX1ZY/UssU5TdVROQzGTL" +
65+ "n5cgAP9hIKtt/mZ112HiAHDuWk2JskdtsuopnrEccz4PSEkSDLg4BF/V8F4SCisG" +
66+ "AQQBl1UBBQEBB0DLPhNt/6GHDbb7vZW/iMsbXTZpgJNQiT6QA/4EzgYQLwMBCAeI" +
67+ "eAQYFggAIBYhBGoyUrxNv7c3S2JjGzewWiN8tfzXBQJf1fBeAhsMAAoJEDewWiN8" +
68+ "tfzXU34BAKJJLDee+qJCmUI20sMy/YoKfWmMnH2RBBHmLV8FAJ7vAP0e2wGixEfs" +
69+ "oPqe8fHmvjQGxSByOyQGn7yD+oq9nVzTAA==" ) ;
70+
71+ private static readonly byte [ ] testX25519PrivKey =
72+ Base64 . Decode (
73+ "lIYEX9XwXhYJKwYBBAHaRw8BAQdAR5ZghmMHL8wldNlOkmbaiAOdyF5V5bgZdKq7" +
74+ "L+yb4A3+BwMCMscozrXr93fOFmtxu/BJjEJrwRl20Jrv9lryfM+SF4UHgVMmJUpJ" +
75+ "1RuTbSnM2KaqHwOgmdrvf2FJnpg1vMafBk1CmopqkRzzrbJ6xQhiPrQcRUNESCA8" +
76+ "dGVzdC5lY2RoQGV4YW1wbGUuY29tPoiQBBMWCAA4FiEEajJSvE2/tzdLYmMbN7Ba" +
77+ "I3y1/NcFAl/V8F4CGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQN7BaI3y1" +
78+ "/NfRkwD8CES+siC77b14xB64c7BfVlj9SyxTlN1VE5DMZMuflyAA/2Egq23+ZnXX" +
79+ "YeIAcO5aTYmyR22y6imesRxzPg9ISRIMnIsEX9XwXhIKKwYBBAGXVQEFAQEHQMs+" +
80+ "E23/oYcNtvu9lb+IyxtdNmmAk1CJPpAD/gTOBhAvAwEIB/4HAwJ7ShSBrUuUAM5r" +
81+ "G4I/gJKo+eBmbNC4NM81eALAF1vcovZPsGsiZ8IgXT64XiC1bpeAoINn6vM4vVbi" +
82+ "LqNKqu6ll3ZgQ4po6vCW9GkhuEMmiHgEGBYIACAWIQRqMlK8Tb+3N0tiYxs3sFoj" +
83+ "fLX81wUCX9XwXgIbDAAKCRA3sFojfLX811N+AQCiSSw3nvqiQplCNtLDMv2KCn1p" +
84+ "jJx9kQQR5i1fBQCe7wD9HtsBosRH7KD6nvHx5r40BsUgcjskBp+8g/qKvZ1c0wA=" ) ;
85+
86+ private static readonly byte [ ] testX25519Message =
87+ Base64 . Decode (
88+ "hF4DbDc2fNL0VcUSAQdAqdV0v1D4X9cuGrT7+oQBpMFnw1wdfAcxH9xdO00s2HUw" +
89+ "qB+XkIRETH7yesynLOKajmYftMWZRyTnW2tJUc1w5NFPjPxcbvd2bYmqkY57uAFg" +
90+ "0kcBKhFklH2LRbBNThtQr3jn2YEFbNnhiGfOpoHfCn0oFh5RbXDwm+P3Q3tksvpZ" +
91+ "wEGe2VkxLLe7BWnv/sRINQ2YpuaYshe8hw==" ) ;
92+
5693 private void Generate ( )
5794 {
5895 SecureRandom random = SecureRandom . GetInstance ( "SHA1PRNG" ) ;
@@ -107,6 +144,71 @@ private void Generate()
107144 PgpPrivateKey pgpPrivKey = secRing . GetSecretKey ( ) . ExtractPrivateKey ( passPhrase ) ;
108145 }
109146
147+ private void Generate25519 ( )
148+ {
149+ SecureRandom random = SecureRandom . GetInstance ( "SHA1PRNG" ) ;
150+
151+ //
152+ // Generate a master key
153+ //
154+ IAsymmetricCipherKeyPairGenerator keyGen = GeneratorUtilities . GetKeyPairGenerator ( "Ed25519" ) ;
155+ keyGen . Init ( new ECKeyGenerationParameters ( GnuObjectIdentifiers . Ed25519 , random ) ) ;
156+
157+ AsymmetricCipherKeyPair kpSign = keyGen . GenerateKeyPair ( ) ;
158+
159+ PgpKeyPair ecdsaKeyPair = new PgpKeyPair ( PublicKeyAlgorithmTag . EdDsa , kpSign , DateTime . UtcNow ) ;
160+
161+ //
162+ // Generate an encryption key
163+ //
164+ keyGen = GeneratorUtilities . GetKeyPairGenerator ( "X25519" ) ;
165+ keyGen . Init ( new ECKeyGenerationParameters ( MiscObjectIdentifiers . Curve25519 , random ) ) ;
166+
167+ AsymmetricCipherKeyPair kpEnc = keyGen . GenerateKeyPair ( ) ;
168+
169+ PgpKeyPair ecdhKeyPair = new PgpKeyPair ( PublicKeyAlgorithmTag . ECDH , kpEnc , DateTime . UtcNow ) ;
170+
171+ //
172+ // Generate a key ring
173+ //
174+ char [ ] passPhrase = "test" . ToCharArray ( ) ;
175+ PgpKeyRingGenerator keyRingGen = new PgpKeyRingGenerator ( PgpSignature . PositiveCertification , ecdsaKeyPair ,
176+ "test@bouncycastle.org" , SymmetricKeyAlgorithmTag . Aes256 , passPhrase , true , null , null , random ) ;
177+ keyRingGen . AddSubKey ( ecdhKeyPair ) ;
178+
179+ PgpPublicKeyRing pubRing = keyRingGen . GeneratePublicKeyRing ( ) ;
180+
181+ // TODO: add check of KdfParameters
182+ DoBasicKeyRingCheck ( pubRing ) ;
183+
184+ PgpSecretKeyRing secRing = keyRingGen . GenerateSecretKeyRing ( ) ;
185+
186+ PgpPublicKeyRing pubRingEnc = new PgpPublicKeyRing ( pubRing . GetEncoded ( ) ) ;
187+ if ( ! Arrays . AreEqual ( pubRing . GetEncoded ( ) , pubRingEnc . GetEncoded ( ) ) )
188+ {
189+ Fail ( "public key ring encoding failed" ) ;
190+ }
191+
192+ PgpSecretKeyRing secRingEnc = new PgpSecretKeyRing ( secRing . GetEncoded ( ) ) ;
193+ if ( ! Arrays . AreEqual ( secRing . GetEncoded ( ) , secRingEnc . GetEncoded ( ) ) )
194+ {
195+ Fail ( "secret key ring encoding failed" ) ;
196+ }
197+
198+ // Extract back the ECDH key and verify the encoded values to ensure correct endianness
199+ PgpSecretKey pgpSecretKey = secRing . GetSecretKey ( ecdhKeyPair . KeyId ) ;
200+ PgpPrivateKey pgpPrivKey = pgpSecretKey . ExtractPrivateKey ( passPhrase ) ;
201+
202+ if ( ! Arrays . AreEqual ( ( ( X25519PrivateKeyParameters ) kpEnc . Private ) . GetEncoded ( ) , ( ( X25519PrivateKeyParameters ) pgpPrivKey . Key ) . GetEncoded ( ) ) )
203+ {
204+ Fail ( "private key round trip failed" ) ;
205+ }
206+ if ( ! Arrays . AreEqual ( ( ( X25519PublicKeyParameters ) kpEnc . Public ) . GetEncoded ( ) , ( ( X25519PublicKeyParameters ) pgpSecretKey . PublicKey . GetKey ( ) ) . GetEncoded ( ) ) )
207+ {
208+ Fail ( "private key round trip failed" ) ;
209+ }
210+ }
211+
110212 private void TestDecrypt ( PgpSecretKeyRing secretKeyRing )
111213 {
112214 PgpObjectFactory pgpF = new PgpObjectFactory ( testMessage ) ;
@@ -136,14 +238,14 @@ private void TestDecrypt(PgpSecretKeyRing secretKeyRing)
136238 // }
137239 }
138240
139- private void EncryptDecryptTest ( )
241+ private void EncryptDecryptTest ( string algorithm , DerObjectIdentifier curve )
140242 {
141243 SecureRandom random = SecureRandom . GetInstance ( "SHA1PRNG" ) ;
142244
143245 byte [ ] text = Encoding . ASCII . GetBytes ( "hello world!" ) ;
144246
145- IAsymmetricCipherKeyPairGenerator keyGen = GeneratorUtilities . GetKeyPairGenerator ( "ECDH" ) ;
146- keyGen . Init ( new ECKeyGenerationParameters ( SecObjectIdentifiers . SecP256r1 , random ) ) ;
247+ IAsymmetricCipherKeyPairGenerator keyGen = GeneratorUtilities . GetKeyPairGenerator ( algorithm ) ;
248+ keyGen . Init ( new ECKeyGenerationParameters ( curve , random ) ) ;
147249
148250 AsymmetricCipherKeyPair kpEnc = keyGen . GenerateKeyPair ( ) ;
149251
@@ -199,6 +301,39 @@ private void EncryptDecryptTest()
199301 }
200302 }
201303
304+ private void GnuPGCrossCheck ( )
305+ {
306+ PgpSecretKeyRing secretKeyRing = new PgpSecretKeyRing ( testX25519PrivKey ) ;
307+
308+ PgpObjectFactory pgpF = new PgpObjectFactory ( testX25519Message ) ;
309+
310+ PgpEncryptedDataList encList = ( PgpEncryptedDataList ) pgpF . NextPgpObject ( ) ;
311+
312+ PgpPublicKeyEncryptedData encP = ( PgpPublicKeyEncryptedData ) encList [ 0 ] ;
313+
314+ PgpSecretKey secretKey = secretKeyRing . GetSecretKey ( 0x6c37367cd2f455c5 ) ;
315+
316+ PgpPrivateKey pgpPrivKey = secretKey . ExtractPrivateKey ( "test" . ToCharArray ( ) ) ;
317+
318+ Stream clear = encP . GetDataStream ( pgpPrivKey ) ;
319+
320+ pgpF = new PgpObjectFactory ( clear ) ;
321+
322+ PgpCompressedData c1 = ( PgpCompressedData ) pgpF . NextPgpObject ( ) ;
323+
324+ pgpF = new PgpObjectFactory ( c1 . GetDataStream ( ) ) ;
325+
326+ PgpLiteralData ld = ( PgpLiteralData ) pgpF . NextPgpObject ( ) ;
327+
328+ Stream inLd = ld . GetDataStream ( ) ;
329+ byte [ ] bytes = Streams . ReadAll ( inLd ) ;
330+
331+ if ( ! Arrays . AreEqual ( bytes , Encoding . ASCII . GetBytes ( "hello world!" ) ) )
332+ {
333+ Fail ( "wrong plain text in decrypted packet" ) ;
334+ }
335+ }
336+
202337 public override void PerformTest ( )
203338 {
204339 //
@@ -215,9 +350,15 @@ public override void PerformTest()
215350
216351 TestDecrypt ( secretKeyRing ) ;
217352
218- EncryptDecryptTest ( ) ;
353+ EncryptDecryptTest ( "ECDH" , SecObjectIdentifiers . SecP256r1 ) ;
354+
355+ EncryptDecryptTest ( "X25519" , MiscObjectIdentifiers . Curve25519 ) ;
356+
357+ GnuPGCrossCheck ( ) ;
219358
220359 Generate ( ) ;
360+
361+ Generate25519 ( ) ;
221362 }
222363
223364 private void DoBasicKeyRingCheck ( PgpPublicKeyRing pubKeyRing )
0 commit comments