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 . Crypto ;
1012using Org . BouncyCastle . Crypto . Parameters ;
1113using Org . BouncyCastle . Security ;
1214using Org . BouncyCastle . Utilities ;
1315using Org . BouncyCastle . Utilities . Encoders ;
16+ using Org . BouncyCastle . Utilities . IO ;
1417using Org . BouncyCastle . Utilities . Test ;
1518
1619namespace Org . BouncyCastle . Bcpg . OpenPgp . Tests
@@ -51,6 +54,40 @@ public class PgpECDHTest
5154 "6HiuFH7VKWcxPUBjXwf5+Z3uOKEp28tBgNyDrdbr1BbqlgYzIKq/pe9zUbUXfitn" +
5255 "vFc6HcGhvmRQreQ+Yw1x3x0HJeoPwg==" ) ;
5356
57+ private static readonly byte [ ] testX25519PubKey =
58+ Base64 . Decode (
59+ "mDMEX9XwXhYJKwYBBAHaRw8BAQdAR5ZghmMHL8wldNlOkmbaiAOdyF5V5bgZdKq7" +
60+ "L+yb4A20HEVDREggPHRlc3QuZWNkaEBleGFtcGxlLmNvbT6IkAQTFggAOBYhBGoy" +
61+ "UrxNv7c3S2JjGzewWiN8tfzXBQJf1fBeAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4B" +
62+ "AheAAAoJEDewWiN8tfzX0ZMA/AhEvrIgu+29eMQeuHOwX1ZY/UssU5TdVROQzGTL" +
63+ "n5cgAP9hIKtt/mZ112HiAHDuWk2JskdtsuopnrEccz4PSEkSDLg4BF/V8F4SCisG" +
64+ "AQQBl1UBBQEBB0DLPhNt/6GHDbb7vZW/iMsbXTZpgJNQiT6QA/4EzgYQLwMBCAeI" +
65+ "eAQYFggAIBYhBGoyUrxNv7c3S2JjGzewWiN8tfzXBQJf1fBeAhsMAAoJEDewWiN8" +
66+ "tfzXU34BAKJJLDee+qJCmUI20sMy/YoKfWmMnH2RBBHmLV8FAJ7vAP0e2wGixEfs" +
67+ "oPqe8fHmvjQGxSByOyQGn7yD+oq9nVzTAA==" ) ;
68+
69+ private static readonly byte [ ] testX25519PrivKey =
70+ Base64 . Decode (
71+ "lIYEX9XwXhYJKwYBBAHaRw8BAQdAR5ZghmMHL8wldNlOkmbaiAOdyF5V5bgZdKq7" +
72+ "L+yb4A3+BwMCMscozrXr93fOFmtxu/BJjEJrwRl20Jrv9lryfM+SF4UHgVMmJUpJ" +
73+ "1RuTbSnM2KaqHwOgmdrvf2FJnpg1vMafBk1CmopqkRzzrbJ6xQhiPrQcRUNESCA8" +
74+ "dGVzdC5lY2RoQGV4YW1wbGUuY29tPoiQBBMWCAA4FiEEajJSvE2/tzdLYmMbN7Ba" +
75+ "I3y1/NcFAl/V8F4CGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQN7BaI3y1" +
76+ "/NfRkwD8CES+siC77b14xB64c7BfVlj9SyxTlN1VE5DMZMuflyAA/2Egq23+ZnXX" +
77+ "YeIAcO5aTYmyR22y6imesRxzPg9ISRIMnIsEX9XwXhIKKwYBBAGXVQEFAQEHQMs+" +
78+ "E23/oYcNtvu9lb+IyxtdNmmAk1CJPpAD/gTOBhAvAwEIB/4HAwJ7ShSBrUuUAM5r" +
79+ "G4I/gJKo+eBmbNC4NM81eALAF1vcovZPsGsiZ8IgXT64XiC1bpeAoINn6vM4vVbi" +
80+ "LqNKqu6ll3ZgQ4po6vCW9GkhuEMmiHgEGBYIACAWIQRqMlK8Tb+3N0tiYxs3sFoj" +
81+ "fLX81wUCX9XwXgIbDAAKCRA3sFojfLX811N+AQCiSSw3nvqiQplCNtLDMv2KCn1p" +
82+ "jJx9kQQR5i1fBQCe7wD9HtsBosRH7KD6nvHx5r40BsUgcjskBp+8g/qKvZ1c0wA=" ) ;
83+
84+ private static readonly byte [ ] testX25519Message =
85+ Base64 . Decode (
86+ "hF4DbDc2fNL0VcUSAQdAqdV0v1D4X9cuGrT7+oQBpMFnw1wdfAcxH9xdO00s2HUw" +
87+ "qB+XkIRETH7yesynLOKajmYftMWZRyTnW2tJUc1w5NFPjPxcbvd2bYmqkY57uAFg" +
88+ "0kcBKhFklH2LRbBNThtQr3jn2YEFbNnhiGfOpoHfCn0oFh5RbXDwm+P3Q3tksvpZ" +
89+ "wEGe2VkxLLe7BWnv/sRINQ2YpuaYshe8hw==" ) ;
90+
5491 private void Generate ( )
5592 {
5693 SecureRandom random = SecureRandom . GetInstance ( "SHA1PRNG" ) ;
@@ -105,6 +142,71 @@ private void Generate()
105142 PgpPrivateKey pgpPrivKey = secRing . GetSecretKey ( ) . ExtractPrivateKey ( passPhrase ) ;
106143 }
107144
145+ private void Generate25519 ( )
146+ {
147+ SecureRandom random = SecureRandom . GetInstance ( "SHA1PRNG" ) ;
148+
149+ //
150+ // Generate a master key
151+ //
152+ IAsymmetricCipherKeyPairGenerator keyGen = GeneratorUtilities . GetKeyPairGenerator ( "Ed25519" ) ;
153+ keyGen . Init ( new ECKeyGenerationParameters ( GnuObjectIdentifiers . Ed25519 , random ) ) ;
154+
155+ AsymmetricCipherKeyPair kpSign = keyGen . GenerateKeyPair ( ) ;
156+
157+ PgpKeyPair ecdsaKeyPair = new PgpKeyPair ( PublicKeyAlgorithmTag . EdDsa , kpSign , DateTime . UtcNow ) ;
158+
159+ //
160+ // Generate an encryption key
161+ //
162+ keyGen = GeneratorUtilities . GetKeyPairGenerator ( "X25519" ) ;
163+ keyGen . Init ( new ECKeyGenerationParameters ( MiscObjectIdentifiers . Curve25519 , random ) ) ;
164+
165+ AsymmetricCipherKeyPair kpEnc = keyGen . GenerateKeyPair ( ) ;
166+
167+ PgpKeyPair ecdhKeyPair = new PgpKeyPair ( PublicKeyAlgorithmTag . ECDH , kpEnc , DateTime . UtcNow ) ;
168+
169+ //
170+ // Generate a key ring
171+ //
172+ char [ ] passPhrase = "test" . ToCharArray ( ) ;
173+ PgpKeyRingGenerator keyRingGen = new PgpKeyRingGenerator ( PgpSignature . PositiveCertification , ecdsaKeyPair ,
174+ "test@bouncycastle.org" , SymmetricKeyAlgorithmTag . Aes256 , passPhrase , true , null , null , random ) ;
175+ keyRingGen . AddSubKey ( ecdhKeyPair ) ;
176+
177+ PgpPublicKeyRing pubRing = keyRingGen . GeneratePublicKeyRing ( ) ;
178+
179+ // TODO: add check of KdfParameters
180+ DoBasicKeyRingCheck ( pubRing ) ;
181+
182+ PgpSecretKeyRing secRing = keyRingGen . GenerateSecretKeyRing ( ) ;
183+
184+ PgpPublicKeyRing pubRingEnc = new PgpPublicKeyRing ( pubRing . GetEncoded ( ) ) ;
185+ if ( ! Arrays . AreEqual ( pubRing . GetEncoded ( ) , pubRingEnc . GetEncoded ( ) ) )
186+ {
187+ Fail ( "public key ring encoding failed" ) ;
188+ }
189+
190+ PgpSecretKeyRing secRingEnc = new PgpSecretKeyRing ( secRing . GetEncoded ( ) ) ;
191+ if ( ! Arrays . AreEqual ( secRing . GetEncoded ( ) , secRingEnc . GetEncoded ( ) ) )
192+ {
193+ Fail ( "secret key ring encoding failed" ) ;
194+ }
195+
196+ // Extract back the ECDH key and verify the encoded values to ensure correct endianness
197+ PgpSecretKey pgpSecretKey = secRing . GetSecretKey ( ecdhKeyPair . KeyId ) ;
198+ PgpPrivateKey pgpPrivKey = pgpSecretKey . ExtractPrivateKey ( passPhrase ) ;
199+
200+ if ( ! Arrays . AreEqual ( ( ( X25519PrivateKeyParameters ) kpEnc . Private ) . GetEncoded ( ) , ( ( X25519PrivateKeyParameters ) pgpPrivKey . Key ) . GetEncoded ( ) ) )
201+ {
202+ Fail ( "private key round trip failed" ) ;
203+ }
204+ if ( ! Arrays . AreEqual ( ( ( X25519PublicKeyParameters ) kpEnc . Public ) . GetEncoded ( ) , ( ( X25519PublicKeyParameters ) pgpSecretKey . PublicKey . GetKey ( ) ) . GetEncoded ( ) ) )
205+ {
206+ Fail ( "private key round trip failed" ) ;
207+ }
208+ }
209+
108210 private void TestDecrypt ( PgpSecretKeyRing secretKeyRing )
109211 {
110212 PgpObjectFactory pgpF = new PgpObjectFactory ( testMessage ) ;
@@ -134,14 +236,14 @@ private void TestDecrypt(PgpSecretKeyRing secretKeyRing)
134236 // }
135237 }
136238
137- private void EncryptDecryptTest ( )
239+ private void EncryptDecryptTest ( string algorithm , DerObjectIdentifier curve )
138240 {
139241 SecureRandom random = SecureRandom . GetInstance ( "SHA1PRNG" ) ;
140242
141243 byte [ ] text = Encoding . ASCII . GetBytes ( "hello world!" ) ;
142244
143- IAsymmetricCipherKeyPairGenerator keyGen = GeneratorUtilities . GetKeyPairGenerator ( "ECDH" ) ;
144- keyGen . Init ( new ECKeyGenerationParameters ( SecObjectIdentifiers . SecP256r1 , random ) ) ;
245+ IAsymmetricCipherKeyPairGenerator keyGen = GeneratorUtilities . GetKeyPairGenerator ( algorithm ) ;
246+ keyGen . Init ( new ECKeyGenerationParameters ( curve , random ) ) ;
145247
146248 AsymmetricCipherKeyPair kpEnc = keyGen . GenerateKeyPair ( ) ;
147249
@@ -197,6 +299,39 @@ private void EncryptDecryptTest()
197299 }
198300 }
199301
302+ private void GnuPGCrossCheck ( )
303+ {
304+ PgpSecretKeyRing secretKeyRing = new PgpSecretKeyRing ( testX25519PrivKey ) ;
305+
306+ PgpObjectFactory pgpF = new PgpObjectFactory ( testX25519Message ) ;
307+
308+ PgpEncryptedDataList encList = ( PgpEncryptedDataList ) pgpF . NextPgpObject ( ) ;
309+
310+ PgpPublicKeyEncryptedData encP = ( PgpPublicKeyEncryptedData ) encList [ 0 ] ;
311+
312+ PgpSecretKey secretKey = secretKeyRing . GetSecretKey ( 0x6c37367cd2f455c5 ) ;
313+
314+ PgpPrivateKey pgpPrivKey = secretKey . ExtractPrivateKey ( "test" . ToCharArray ( ) ) ;
315+
316+ Stream clear = encP . GetDataStream ( pgpPrivKey ) ;
317+
318+ pgpF = new PgpObjectFactory ( clear ) ;
319+
320+ PgpCompressedData c1 = ( PgpCompressedData ) pgpF . NextPgpObject ( ) ;
321+
322+ pgpF = new PgpObjectFactory ( c1 . GetDataStream ( ) ) ;
323+
324+ PgpLiteralData ld = ( PgpLiteralData ) pgpF . NextPgpObject ( ) ;
325+
326+ Stream inLd = ld . GetDataStream ( ) ;
327+ byte [ ] bytes = Streams . ReadAll ( inLd ) ;
328+
329+ if ( ! Arrays . AreEqual ( bytes , Encoding . ASCII . GetBytes ( "hello world!" ) ) )
330+ {
331+ Fail ( "wrong plain text in decrypted packet" ) ;
332+ }
333+ }
334+
200335 public override void PerformTest ( )
201336 {
202337 //
@@ -213,9 +348,15 @@ public override void PerformTest()
213348
214349 TestDecrypt ( secretKeyRing ) ;
215350
216- EncryptDecryptTest ( ) ;
351+ EncryptDecryptTest ( "ECDH" , SecObjectIdentifiers . SecP256r1 ) ;
352+
353+ EncryptDecryptTest ( "X25519" , MiscObjectIdentifiers . Curve25519 ) ;
354+
355+ GnuPGCrossCheck ( ) ;
217356
218357 Generate ( ) ;
358+
359+ Generate25519 ( ) ;
219360 }
220361
221362 private void DoBasicKeyRingCheck ( PgpPublicKeyRing pubKeyRing )
0 commit comments