From 72f0d68a8a17cdb2e304f9ce591933873de990a9 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 15 Oct 2024 11:41:34 +0700 Subject: [PATCH 001/202] WIP Signed-off-by: Nguyen Van Nguyen --- pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index fcf21e3c..2bfc599e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,9 +12,9 @@ dependencies: pointycastle: ^3.9.1 dev_dependencies: - lints: ^2.0.0 + lints: ^3.0.0 faker: ^2.0.0 - test: ^1.16.0 + test: ^1.24.0 false_secrets: - /test/data/key_data.dart From f7edd7599cef78e0ab7e018df7fa72dede0993be Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 18 Nov 2024 11:54:17 +0700 Subject: [PATCH 002/202] Refactor AEAD crypt. Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/aead_encrypted_data.dart | 103 ++++++++++++++---------- 1 file changed, 60 insertions(+), 43 deletions(-) diff --git a/lib/src/packet/aead_encrypted_data.dart b/lib/src/packet/aead_encrypted_data.dart index 9614f7e0..3ae70285 100644 --- a/lib/src/packet/aead_encrypted_data.dart +++ b/lib/src/packet/aead_encrypted_data.dart @@ -85,19 +85,20 @@ class AeadEncryptedData extends ContainedPacket { final int chunkSize = 12, }) async { final iv = Helper.secureRandom().nextBytes(aead.ivLength); - final encryptor = AeadEncryptedData( - symmetric, - aead, - chunkSize, - iv, - Uint8List(0), - ); return AeadEncryptedData( symmetric, aead, chunkSize, iv, - encryptor._crypt(true, key, packets.encode()), + _crypt( + true, + key, + packets.encode(), + symmetric: symmetric, + aead: aead, + chunkSizeByte: chunkSize, + iv: iv, + ), packets: packets, ); } @@ -150,40 +151,64 @@ class AeadEncryptedData extends ContainedPacket { key, data, finalChunk: authTag, + symmetric: symmetric, + aead: aead, + chunkSizeByte: chunkSize, + iv: iv, )), ); } /// En/decrypt the payload. - Uint8List _crypt( + static Uint8List _crypt( bool forEncryption, Uint8List key, Uint8List data, { + final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + AeadAlgorithm aead = AeadAlgorithm.gcm, + final chunkSizeByte = 0, + final Uint8List? iv, Uint8List? finalChunk, }) { final cipher = aead.cipherEngine(key, symmetric); final dataLength = data.length; final tagLength = forEncryption ? 0 : aead.tagLength; - final chunkSize = (1 << (this.chunkSize + 6)) + tagLength; + final chunkSize = (1 << (chunkSizeByte + 6)) + tagLength; final adataBuffer = Uint8List(13); - adataBuffer.setAll(0, _getAAData()); - - final List crypted = List.empty(growable: true); + adataBuffer.setAll( + 0, + Uint8List.fromList([ + 0xc0 | PacketTag.aeadEncryptedData.value, + version, + symmetric.value, + aead.value, + chunkSize, + ])); + + final processed = dataLength - tagLength * (dataLength / chunkSize).ceil(); + final crypted = Uint8List(processed); for (var chunkIndex = 0; chunkIndex == 0 || data.isNotEmpty;) { final chunkIndexData = adataBuffer.sublist(5, 13); final size = chunkSize < data.length ? chunkSize : data.length; - crypted.add( + crypted.setAll( + chunkIndex * size, forEncryption ? cipher.encrypt( data.sublist(0, size), - cipher.getNonce(iv, chunkIndexData), + cipher.getNonce( + iv ?? Uint8List(aead.ivLength), + chunkIndexData, + ), adataBuffer, ) : cipher.decrypt( data.sublist(0, size), - cipher.getNonce(iv, chunkIndexData), + cipher.getNonce( + iv ?? Uint8List(aead.ivLength), + chunkIndexData, + ), adataBuffer, ), ); @@ -199,36 +224,28 @@ class AeadEncryptedData extends ContainedPacket { final chunkIndexData = adataBuffer.sublist(5, 13); final adataTagBuffer = Uint8List(21); adataTagBuffer.setAll(0, adataBuffer); - adataTagBuffer.setAll( - 17, - (dataLength - tagLength * (dataLength / chunkSize).ceil()).pack32(), - ); - crypted.add( - forEncryption - ? cipher.encrypt( - finalChunk ?? Uint8List(0), - cipher.getNonce(iv, chunkIndexData), - adataTagBuffer, - ) - : cipher.decrypt( - finalChunk ?? Uint8List(0), - cipher.getNonce(iv, chunkIndexData), - adataTagBuffer, + adataTagBuffer.setAll(17, processed.pack32()); + final finalCrypted = forEncryption + ? cipher.encrypt( + finalChunk ?? Uint8List(0), + cipher.getNonce( + iv ?? Uint8List(aead.ivLength), + chunkIndexData, ), - ); - - return Uint8List.fromList([ - ...crypted.expand((element) => element), - ]); - } + adataTagBuffer, + ) + : cipher.decrypt( + finalChunk ?? Uint8List(0), + cipher.getNonce( + iv ?? Uint8List(aead.ivLength), + chunkIndexData, + ), + adataTagBuffer, + ); - Uint8List _getAAData() { return Uint8List.fromList([ - 0xc0 | tag.value, - version, - symmetric.value, - aead.value, - chunkSize, + ...crypted, + ...finalCrypted, ]); } } From 80994d0ef0d171911d929da23fe421499857d86f Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 19 Nov 2024 10:41:07 +0700 Subject: [PATCH 003/202] WIP Signed-off-by: Nguyen Van Nguyen --- example/README.md | 5 - example/decrypt_example.dart | 19 - example/encrypt_example.dart | 21 - lib/dart_pg.dart | 11 +- lib/src/common/argon2_s2k.dart | 83 ++ lib/src/{armor => common}/armor.dart | 23 +- lib/src/common/config.dart | 36 + lib/src/common/extensions.dart | 250 ++++++ .../key/s2k.dart => common/generic_s2k.dart} | 127 ++- lib/src/common/helpers.dart | 124 +++ lib/src/crypto/aead/ocb.dart | 78 -- lib/src/crypto/math/big_int.dart | 244 ----- lib/src/crypto/math/byte_ext.dart | 127 --- lib/src/crypto/math/int_ext.dart | 62 -- lib/src/crypto/symmetric/base_cipher.dart | 26 - lib/src/crypto/symmetric/camellia_light.dart | 565 ------------ lib/src/{crypto => cryptor}/aead/eax.dart | 26 +- lib/src/{crypto => cryptor}/aead/gcm.dart | 24 +- .../ocb_cipher.dart => cryptor/aead/ocb.dart} | 80 +- .../signer => cryptor/asymmetric}/dsa.dart | 106 +-- .../asymmetric/elgamal.dart | 122 +-- lib/src/cryptor/ecc/x448.dart | 835 ++++++++++++++++++ lib/src/cryptor/symmetric/base_engine.dart | 22 + .../symmetric/blowfish.dart | 35 +- .../symmetric/buffered_cipher.dart | 12 +- .../symmetric/camellia.dart | 96 +- .../{crypto => cryptor}/symmetric/cast5.dart | 469 ++-------- .../{crypto => cryptor}/symmetric/idea.dart | 67 +- .../symmetric/twofish.dart | 182 ++-- lib/src/enum/aead_algorithm.dart | 75 +- lib/src/enum/armor_type.dart | 8 +- lib/src/enum/compression_algorithm.dart | 12 +- lib/src/enum/curve_info.dart | 106 --- lib/src/enum/dh_key_size.dart | 17 - lib/src/enum/ecc.dart | 45 + lib/src/enum/eddsa_curve.dart | 24 + lib/src/enum/hash_algorithm.dart | 93 +- lib/src/enum/kek_size.dart | 17 + lib/src/enum/key_algorithm.dart | 46 +- lib/src/enum/key_flag.dart | 32 - lib/src/enum/key_generation_type.dart | 6 - lib/src/enum/key_version.dart | 16 + lib/src/enum/literal_format.dart | 9 +- lib/src/enum/montgomery_curve.dart | 44 + .../{packet_tag.dart => packet_type.dart} | 19 +- lib/src/enum/revocation_key_tag.dart | 13 - lib/src/enum/revocation_reason_tag.dart | 27 - lib/src/enum/rsa_key_size.dart | 19 +- lib/src/enum/s2k_type.dart | 31 +- lib/src/enum/s2k_usage.dart | 17 +- lib/src/enum/signature_subpacket_type.dart | 40 - lib/src/enum/signature_type.dart | 125 --- lib/src/enum/support_feature.dart | 20 - lib/src/enum/symmetric_algorithm.dart | 132 +-- lib/src/helpers.dart | 107 --- lib/src/openpgp.dart | 233 +---- lib/src/packet/aead_encrypted_data.dart | 115 ++- .../{contained_packet.dart => base.dart} | 49 +- lib/src/packet/compressed_data.dart | 100 +-- lib/src/packet/image_attribute.dart | 37 - lib/src/packet/image_user_attribute.dart | 42 + lib/src/packet/key/aes_key_wrap.dart | 13 - lib/src/packet/key/aes_key_wrapper.dart | 15 + lib/src/packet/key/camellia_key_wrap.dart | 15 - lib/src/packet/key/camellia_key_wrapper.dart | 20 + ...c_params.dart => dsa_public_material.dart} | 43 +- lib/src/packet/key/dsa_secret_material.dart | 105 +++ lib/src/packet/key/dsa_secret_params.dart | 93 -- lib/src/packet/key/ec_public_material.dart | 42 + lib/src/packet/key/ec_public_params.dart | 44 - lib/src/packet/key/ec_secret_material.dart | 39 + lib/src/packet/key/ec_secret_params.dart | 83 -- ..._params.dart => ecdh_public_material.dart} | 45 +- lib/src/packet/key/ecdh_secret_material.dart | 111 +++ ...ams.dart => ecdh_session_key_cryptor.dart} | 212 +++-- ...params.dart => ecdsa_public_material.dart} | 30 +- lib/src/packet/key/ecdsa_secret_material.dart | 91 ++ lib/src/packet/key/ed_secret_params.dart | 56 -- ...dart => eddsa_legacy_public_material.dart} | 28 +- .../key/eddsa_legacy_secret_material.dart | 83 ++ lib/src/packet/key/eddsa_public_material.dart | 63 ++ lib/src/packet/key/eddsa_secret_material.dart | 97 ++ ...rams.dart => elgamal_public_material.dart} | 30 +- .../packet/key/elgamal_secret_material.dart | 85 ++ lib/src/packet/key/elgamal_secret_params.dart | 77 -- .../key/elgamal_session_key_cryptor.dart | 70 ++ .../key/elgamal_session_key_params.dart | 89 -- lib/src/packet/key/key_id.dart | 37 - lib/src/packet/key/key_pair_params.dart | 254 ------ lib/src/packet/key/key_params.dart | 24 - .../key/{key_wrap.dart => key_wrapper.dart} | 27 +- .../key/montgomery_public_material.dart | 36 + .../key/montgomery_secret_material.dart | 101 +++ .../key/montgomery_session_key_cryptor.dart | 94 ++ lib/src/packet/key/public_material.dart | 14 + ...c_params.dart => rsa_public_material.dart} | 33 +- lib/src/packet/key/rsa_secret_material.dart | 154 ++++ lib/src/packet/key/rsa_secret_params.dart | 114 --- .../packet/key/rsa_session_key_cryptor.dart | 66 ++ .../packet/key/rsa_session_key_params.dart | 66 -- lib/src/packet/key/secret_material.dart | 14 + lib/src/packet/key/session_key.dart | 70 +- lib/src/packet/key/session_key_cryptor.dart | 56 ++ lib/src/packet/key/session_key_params.dart | 68 -- lib/src/packet/key/verification_params.dart | 17 - lib/src/packet/key_packet.dart | 72 -- lib/src/packet/literal_data.dart | 85 +- lib/src/packet/marker.dart | 22 + lib/src/packet/marker_packet.dart | 22 - .../packet/modification_detection_code.dart | 31 - lib/src/packet/one_pass_signature.dart | 84 -- lib/src/packet/packet_list.dart | 130 +-- lib/src/packet/packet_reader.dart | 101 +-- lib/src/packet/padding.dart | 30 + lib/src/packet/public_key.dart | 230 ++--- .../public_key_encrypted_session_key.dart | 316 ++++--- lib/src/packet/public_subkey.dart | 33 +- lib/src/packet/secret_key.dart | 558 +++++++----- lib/src/packet/secret_subkey.dart | 123 ++- .../packet/signature/embedded_signature.dart | 20 - .../signature/exportable_certification.dart | 31 - lib/src/packet/signature/features.dart | 39 - .../packet/signature/issuer_fingerprint.dart | 28 - lib/src/packet/signature/issuer_key_id.dart | 30 - .../packet/signature/key_expiration_time.dart | 33 - lib/src/packet/signature/key_flags.dart | 61 -- .../signature/key_server_preferences.dart | 34 - lib/src/packet/signature/notation_data.dart | 106 --- lib/src/packet/signature/policy_uri.dart | 30 - .../signature/preferred_aead_algorithms.dart | 25 - .../preferred_compression_algorithms.dart | 29 - .../signature/preferred_hash_algorithms.dart | 26 - .../signature/preferred_key_server.dart | 29 - .../preferred_symmetric_algorithms.dart | 26 - lib/src/packet/signature/primary_user_id.dart | 26 - .../packet/signature/regular_expression.dart | 27 - lib/src/packet/signature/revocable.dart | 31 - lib/src/packet/signature/revocation_key.dart | 51 -- .../packet/signature/revocation_reason.dart | 44 - .../signature/signature_creation_time.dart | 30 - .../signature/signature_expiration_time.dart | 31 - .../packet/signature/signature_target.dart | 50 -- lib/src/packet/signature/signer_user_id.dart | 26 - lib/src/packet/signature/trust_signature.dart | 31 - lib/src/packet/signature_packet.dart | 814 ----------------- lib/src/packet/signature_subpacket.dart | 74 -- lib/src/packet/subkey_packet.dart | 8 - lib/src/packet/subpacket_reader.dart | 16 +- lib/src/packet/sym_encrypted_data.dart | 72 +- ...ym_encrypted_integrity_protected_data.dart | 374 ++++++-- lib/src/packet/sym_encrypted_session_key.dart | 157 ++-- lib/src/packet/trust.dart | 30 + lib/src/packet/trust_packet.dart | 36 - lib/src/packet/user_attribute.dart | 68 +- lib/src/packet/user_attribute_subpacket.dart | 11 +- lib/src/packet/user_id.dart | 49 +- .../{crypto/aead/base.dart => type/aead.dart} | 20 +- lib/src/type/armorable.dart | 12 + lib/src/type/cleartext_message.dart | 50 -- lib/src/type/encrypted_data_packet.dart | 32 + lib/src/type/for_signing.dart | 14 + lib/src/type/key.dart | 362 -------- lib/src/type/key_material.dart | 17 + lib/src/type/key_packet.dart | 48 + lib/src/type/literal_data.dart | 27 + lib/src/type/message.dart | 454 ---------- lib/src/type/packet.dart | 22 + lib/src/type/packet_list.dart | 19 + lib/src/type/private_key.dart | 386 -------- lib/src/type/public_key.dart | 121 --- lib/src/type/public_key_material.dart | 20 + lib/src/type/s2k.dart | 28 + lib/src/type/secret_key_material.dart | 17 + lib/src/type/secret_key_packet.dart | 37 + lib/src/type/session_key.dart | 26 + lib/src/type/session_key_cryptor.dart | 17 + lib/src/type/signature.dart | 33 - lib/src/type/signed_message.dart | 96 -- lib/src/type/signing_key_material.dart | 20 + lib/src/type/subkey.dart | 166 ---- lib/src/type/subkey_packet.dart | 11 + lib/src/type/subpacket.dart | 23 + lib/src/type/user.dart | 117 --- lib/src/type/user_id_packet.dart | 16 + lib/src/type/verification.dart | 56 -- licenses/LICENSE-BouncyCastle.html | 22 - licenses/LICENSE-PineNaCl | 21 - licenses/LICENSE-PointyCastle | 20 - pubspec.yaml | 15 +- test/armor/armor_test.dart | 158 ---- test/common/s2k_test.dart | 7 + test/crypto/aead_test.dart | 116 +-- test/crypto/asymmetric_test.dart | 156 +--- test/crypto/ecc_test.dart | 324 +++++++ test/crypto/symmetric_test.dart | 586 ++++++------ test/dart_pg_test.dart | 16 + test/data/key_data.dart | 613 ------------- test/data/s2k_data.dart | 10 - test/packet/compression_test.dart | 57 -- test/packet/encryption_test.dart | 304 ------- test/packet/key_packet_test.dart | 248 ------ test/packet/key_wrap_test.dart | 382 -------- test/packet/s2k_test.dart | 200 ----- test/packet/signing_test.dart | 202 ----- test/packet/user_packet_test.dart | 51 -- test/type/key_test.dart | 534 ----------- test/type/message_test.dart | 394 --------- 207 files changed, 6469 insertions(+), 13151 deletions(-) delete mode 100644 example/README.md delete mode 100644 example/decrypt_example.dart delete mode 100644 example/encrypt_example.dart create mode 100644 lib/src/common/argon2_s2k.dart rename lib/src/{armor => common}/armor.dart (91%) create mode 100644 lib/src/common/config.dart create mode 100644 lib/src/common/extensions.dart rename lib/src/{packet/key/s2k.dart => common/generic_s2k.dart} (55%) create mode 100644 lib/src/common/helpers.dart delete mode 100644 lib/src/crypto/aead/ocb.dart delete mode 100644 lib/src/crypto/math/big_int.dart delete mode 100644 lib/src/crypto/math/byte_ext.dart delete mode 100644 lib/src/crypto/math/int_ext.dart delete mode 100644 lib/src/crypto/symmetric/base_cipher.dart delete mode 100644 lib/src/crypto/symmetric/camellia_light.dart rename lib/src/{crypto => cryptor}/aead/eax.dart (81%) rename lib/src/{crypto => cryptor}/aead/gcm.dart (77%) rename lib/src/{crypto/modes/ocb_cipher.dart => cryptor/aead/ocb.dart} (88%) rename lib/src/{crypto/signer => cryptor/asymmetric}/dsa.dart (64%) rename lib/src/{crypto => cryptor}/asymmetric/elgamal.dart (54%) create mode 100644 lib/src/cryptor/ecc/x448.dart create mode 100644 lib/src/cryptor/symmetric/base_engine.dart rename lib/src/{crypto => cryptor}/symmetric/blowfish.dart (95%) rename lib/src/{crypto => cryptor}/symmetric/buffered_cipher.dart (93%) rename lib/src/{crypto => cryptor}/symmetric/camellia.dart (90%) rename lib/src/{crypto => cryptor}/symmetric/cast5.dart (77%) rename lib/src/{crypto => cryptor}/symmetric/idea.dart (74%) rename lib/src/{crypto => cryptor}/symmetric/twofish.dart (84%) delete mode 100644 lib/src/enum/curve_info.dart delete mode 100644 lib/src/enum/dh_key_size.dart create mode 100644 lib/src/enum/ecc.dart create mode 100644 lib/src/enum/eddsa_curve.dart create mode 100644 lib/src/enum/kek_size.dart delete mode 100644 lib/src/enum/key_flag.dart delete mode 100644 lib/src/enum/key_generation_type.dart create mode 100644 lib/src/enum/key_version.dart create mode 100644 lib/src/enum/montgomery_curve.dart rename lib/src/enum/{packet_tag.dart => packet_type.dart} (56%) delete mode 100644 lib/src/enum/revocation_key_tag.dart delete mode 100644 lib/src/enum/revocation_reason_tag.dart delete mode 100644 lib/src/enum/signature_subpacket_type.dart delete mode 100644 lib/src/enum/signature_type.dart delete mode 100644 lib/src/enum/support_feature.dart delete mode 100644 lib/src/helpers.dart rename lib/src/packet/{contained_packet.dart => base.dart} (57%) delete mode 100644 lib/src/packet/image_attribute.dart create mode 100644 lib/src/packet/image_user_attribute.dart delete mode 100644 lib/src/packet/key/aes_key_wrap.dart create mode 100644 lib/src/packet/key/aes_key_wrapper.dart delete mode 100644 lib/src/packet/key/camellia_key_wrap.dart create mode 100644 lib/src/packet/key/camellia_key_wrapper.dart rename lib/src/packet/key/{dsa_public_params.dart => dsa_public_material.dart} (71%) create mode 100644 lib/src/packet/key/dsa_secret_material.dart delete mode 100644 lib/src/packet/key/dsa_secret_params.dart create mode 100644 lib/src/packet/key/ec_public_material.dart delete mode 100644 lib/src/packet/key/ec_public_params.dart create mode 100644 lib/src/packet/key/ec_secret_material.dart delete mode 100644 lib/src/packet/key/ec_secret_params.dart rename lib/src/packet/key/{ecdh_public_params.dart => ecdh_public_material.dart} (63%) create mode 100644 lib/src/packet/key/ecdh_secret_material.dart rename lib/src/packet/key/{ecdh_session_key_params.dart => ecdh_session_key_cryptor.dart} (51%) rename lib/src/packet/key/{ecdsa_public_params.dart => ecdsa_public_material.dart} (61%) create mode 100644 lib/src/packet/key/ecdsa_secret_material.dart delete mode 100644 lib/src/packet/key/ed_secret_params.dart rename lib/src/packet/key/{eddsa_public_params.dart => eddsa_legacy_public_material.dart} (57%) create mode 100644 lib/src/packet/key/eddsa_legacy_secret_material.dart create mode 100644 lib/src/packet/key/eddsa_public_material.dart create mode 100644 lib/src/packet/key/eddsa_secret_material.dart rename lib/src/packet/key/{elgamal_public_params.dart => elgamal_public_material.dart} (54%) create mode 100644 lib/src/packet/key/elgamal_secret_material.dart delete mode 100644 lib/src/packet/key/elgamal_secret_params.dart create mode 100644 lib/src/packet/key/elgamal_session_key_cryptor.dart delete mode 100644 lib/src/packet/key/elgamal_session_key_params.dart delete mode 100644 lib/src/packet/key/key_id.dart delete mode 100644 lib/src/packet/key/key_pair_params.dart delete mode 100644 lib/src/packet/key/key_params.dart rename lib/src/packet/key/{key_wrap.dart => key_wrapper.dart} (84%) create mode 100644 lib/src/packet/key/montgomery_public_material.dart create mode 100644 lib/src/packet/key/montgomery_secret_material.dart create mode 100644 lib/src/packet/key/montgomery_session_key_cryptor.dart create mode 100644 lib/src/packet/key/public_material.dart rename lib/src/packet/key/{rsa_public_params.dart => rsa_public_material.dart} (61%) create mode 100644 lib/src/packet/key/rsa_secret_material.dart delete mode 100644 lib/src/packet/key/rsa_secret_params.dart create mode 100644 lib/src/packet/key/rsa_session_key_cryptor.dart delete mode 100644 lib/src/packet/key/rsa_session_key_params.dart create mode 100644 lib/src/packet/key/secret_material.dart create mode 100644 lib/src/packet/key/session_key_cryptor.dart delete mode 100644 lib/src/packet/key/session_key_params.dart delete mode 100644 lib/src/packet/key/verification_params.dart delete mode 100644 lib/src/packet/key_packet.dart create mode 100644 lib/src/packet/marker.dart delete mode 100644 lib/src/packet/marker_packet.dart delete mode 100644 lib/src/packet/modification_detection_code.dart delete mode 100644 lib/src/packet/one_pass_signature.dart create mode 100644 lib/src/packet/padding.dart delete mode 100644 lib/src/packet/signature/embedded_signature.dart delete mode 100644 lib/src/packet/signature/exportable_certification.dart delete mode 100644 lib/src/packet/signature/features.dart delete mode 100644 lib/src/packet/signature/issuer_fingerprint.dart delete mode 100644 lib/src/packet/signature/issuer_key_id.dart delete mode 100644 lib/src/packet/signature/key_expiration_time.dart delete mode 100644 lib/src/packet/signature/key_flags.dart delete mode 100644 lib/src/packet/signature/key_server_preferences.dart delete mode 100644 lib/src/packet/signature/notation_data.dart delete mode 100644 lib/src/packet/signature/policy_uri.dart delete mode 100644 lib/src/packet/signature/preferred_aead_algorithms.dart delete mode 100644 lib/src/packet/signature/preferred_compression_algorithms.dart delete mode 100644 lib/src/packet/signature/preferred_hash_algorithms.dart delete mode 100644 lib/src/packet/signature/preferred_key_server.dart delete mode 100644 lib/src/packet/signature/preferred_symmetric_algorithms.dart delete mode 100644 lib/src/packet/signature/primary_user_id.dart delete mode 100644 lib/src/packet/signature/regular_expression.dart delete mode 100644 lib/src/packet/signature/revocable.dart delete mode 100644 lib/src/packet/signature/revocation_key.dart delete mode 100644 lib/src/packet/signature/revocation_reason.dart delete mode 100644 lib/src/packet/signature/signature_creation_time.dart delete mode 100644 lib/src/packet/signature/signature_expiration_time.dart delete mode 100644 lib/src/packet/signature/signature_target.dart delete mode 100644 lib/src/packet/signature/signer_user_id.dart delete mode 100644 lib/src/packet/signature/trust_signature.dart delete mode 100644 lib/src/packet/signature_packet.dart delete mode 100644 lib/src/packet/signature_subpacket.dart delete mode 100644 lib/src/packet/subkey_packet.dart create mode 100644 lib/src/packet/trust.dart delete mode 100644 lib/src/packet/trust_packet.dart rename lib/src/{crypto/aead/base.dart => type/aead.dart} (62%) create mode 100644 lib/src/type/armorable.dart delete mode 100644 lib/src/type/cleartext_message.dart create mode 100644 lib/src/type/encrypted_data_packet.dart create mode 100644 lib/src/type/for_signing.dart delete mode 100644 lib/src/type/key.dart create mode 100644 lib/src/type/key_material.dart create mode 100644 lib/src/type/key_packet.dart create mode 100644 lib/src/type/literal_data.dart delete mode 100644 lib/src/type/message.dart create mode 100644 lib/src/type/packet.dart create mode 100644 lib/src/type/packet_list.dart delete mode 100644 lib/src/type/private_key.dart delete mode 100644 lib/src/type/public_key.dart create mode 100644 lib/src/type/public_key_material.dart create mode 100644 lib/src/type/s2k.dart create mode 100644 lib/src/type/secret_key_material.dart create mode 100644 lib/src/type/secret_key_packet.dart create mode 100644 lib/src/type/session_key.dart create mode 100644 lib/src/type/session_key_cryptor.dart delete mode 100644 lib/src/type/signature.dart delete mode 100644 lib/src/type/signed_message.dart create mode 100644 lib/src/type/signing_key_material.dart delete mode 100644 lib/src/type/subkey.dart create mode 100644 lib/src/type/subkey_packet.dart create mode 100644 lib/src/type/subpacket.dart delete mode 100644 lib/src/type/user.dart create mode 100644 lib/src/type/user_id_packet.dart delete mode 100644 lib/src/type/verification.dart delete mode 100644 licenses/LICENSE-BouncyCastle.html delete mode 100644 licenses/LICENSE-PineNaCl delete mode 100644 licenses/LICENSE-PointyCastle delete mode 100644 test/armor/armor_test.dart create mode 100644 test/common/s2k_test.dart create mode 100644 test/crypto/ecc_test.dart create mode 100644 test/dart_pg_test.dart delete mode 100644 test/data/key_data.dart delete mode 100644 test/data/s2k_data.dart delete mode 100644 test/packet/compression_test.dart delete mode 100644 test/packet/encryption_test.dart delete mode 100644 test/packet/key_packet_test.dart delete mode 100644 test/packet/key_wrap_test.dart delete mode 100644 test/packet/s2k_test.dart delete mode 100644 test/packet/signing_test.dart delete mode 100644 test/packet/user_packet_test.dart delete mode 100644 test/type/key_test.dart delete mode 100644 test/type/message_test.dart diff --git a/example/README.md b/example/README.md deleted file mode 100644 index f60e29f2..00000000 --- a/example/README.md +++ /dev/null @@ -1,5 +0,0 @@ -Dart PG Examples -================== - -* [Encrypt example](encrypt_example.dart): a basic Dart PG encrypt & sign example. -* [Decrypt example](encrypt_example.dart): a basic Dart PG decrypt & verify example. diff --git a/example/decrypt_example.dart b/example/decrypt_example.dart deleted file mode 100644 index c8ac6b7e..00000000 --- a/example/decrypt_example.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:dart_pg/dart_pg.dart'; - -Future main() async { - const passphrase = 'secret stuff'; - const armoredPublicKey = '-----BEGIN PGP PUBLIC KEY BLOCK-----'; - const armoredPrivateKey = '-----BEGIN PGP PRIVATE KEY BLOCK-----'; - const armoredMessage = ''; - - final publicKey = await OpenPGP.readPublicKey(armoredPublicKey); - final privateKey = await OpenPGP.decryptPrivateKey(armoredPrivateKey, passphrase); - - final decryptedMessage = await OpenPGP.decrypt( - await OpenPGP.readMessage(armoredMessage), - decryptionKeys: [privateKey], - verificationKeys: [publicKey], - ); - final verifications = decryptedMessage.verifications; - print(verifications); -} diff --git a/example/encrypt_example.dart b/example/encrypt_example.dart deleted file mode 100644 index 6354c760..00000000 --- a/example/encrypt_example.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:dart_pg/dart_pg.dart'; - -Future main() async { - const text = 'Hello Dart PG!'; - const passphrase = 'secret stuff'; - const armoredPublicKeys = ['-----BEGIN PGP PUBLIC KEY BLOCK-----']; - const armoredPrivateKey = '-----BEGIN PGP PRIVATE KEY BLOCK-----'; - - final publicKeys = await Future.wait( - armoredPublicKeys.map((armored) => OpenPGP.readPublicKey(armored)), - ); - final privateKey = await OpenPGP.decryptPrivateKey(armoredPrivateKey, passphrase); - - final encryptedMessage = await OpenPGP.encrypt( - Message.createTextMessage(text), - encryptionKeys: publicKeys, - signingKeys: [privateKey], - ); - final encrypted = encryptedMessage.armor(); - print(encrypted); -} diff --git a/lib/dart_pg.dart b/lib/dart_pg.dart index 716f6f39..2d38aef3 100644 --- a/lib/dart_pg.dart +++ b/lib/dart_pg.dart @@ -1,7 +1,8 @@ -// Copyright 2022-present by Nguyen Van Nguyen . All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -library dart_pg; +/// Support for doing something awesome. +/// +/// More dartdocs go here. +library; export 'src/openpgp.dart'; + +// TODO: Export any libraries intended for clients of this package. diff --git a/lib/src/common/argon2_s2k.dart b/lib/src/common/argon2_s2k.dart new file mode 100644 index 00000000..ed179987 --- /dev/null +++ b/lib/src/common/argon2_s2k.dart @@ -0,0 +1,83 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; +import 'package:pointycastle/export.dart'; + +import 'helpers.dart'; +import '../enum/s2k_type.dart'; +import '../type/s2k.dart'; + +/// Implementation of the Argon2 string-to-key specifier +/// See https://www.rfc-editor.org/rfc/rfc9580#section-3.7 +class Argon2S2k implements S2kInterface { + /// Default salt length + static const saltLength = 16; + + @override + final Uint8List salt; + + /// Number of iterations + final int iteration; + + /// Number of parallel threads + final int parallelism; + + /// The exponent of the memory size + final int memoryExponent; + + Argon2S2k( + this.salt, { + this.iteration = Argon2Parameters.DEFAULT_ITERATIONS, + this.parallelism = Argon2Parameters.DEFAULT_LANES, + this.memoryExponent = Argon2Parameters.DEFAULT_MEMORY_COST, + }); + + /// Parsing function for a string-to-key specifier + factory Argon2S2k.fromBytes(final Uint8List bytes) { + var pos = 1; + final salt = bytes.sublist(pos, pos + saltLength); + pos += saltLength; + final iteration = bytes[pos++]; + final parallelism = bytes[pos++]; + final memoryExponent = bytes[pos++]; + return Argon2S2k( + salt, + iteration: iteration, + parallelism: parallelism, + memoryExponent: memoryExponent, + ); + } + + @override + Uint8List produceKey(String passphrase, int length) { + final gen = Argon2BytesGenerator() + ..init(Argon2Parameters( + Argon2Parameters.ARGON2_id, + salt, + desiredKeyLength: length, + iterations: iteration, + lanes: parallelism, + memoryPowerOf2: memoryExponent, + )); + return gen.process(passphrase.toBytes()); + } + + @override + int get length => type.length; + + @override + Uint8List get toBytes => Uint8List.fromList([ + type.value, + ...salt, + iteration, + parallelism, + memoryExponent, + ]); + + @override + S2kType get type => S2kType.argon2; +} diff --git a/lib/src/armor/armor.dart b/lib/src/common/armor.dart similarity index 91% rename from lib/src/armor/armor.dart rename to lib/src/common/armor.dart index 8f15f64b..95143ed1 100644 --- a/lib/src/armor/armor.dart +++ b/lib/src/common/armor.dart @@ -1,23 +1,24 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:convert'; import 'dart:typed_data'; -import '../crypto/math/int_ext.dart'; +import 'helpers.dart'; import '../enum/armor_type.dart'; -import '../helpers.dart'; /// ASCII Armor class /// OpenPGP's Radix-64 encoding. /// It is composed of two parts: a base64 /// encoding of the binary data and a checksum. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-6 +/// See https://www.rfc-editor.org/rfc/rfc9580#section-6 /// Author Nguyen Van Nguyen class Armor { - static const version = 'Dart PG v1.5'; - static const comment = 'https://github.com/web-of-trust/dart-pg'; + static const version = 'Dart PG v2'; + static const comment = 'The Dart OpenPGP library'; static const messageBegin = '-----BEGIN PGP MESSAGE'; static const signedMessageBegin = '-----BEGIN PGP SIGNED MESSAGE'; @@ -35,8 +36,7 @@ class Armor { static const endOfLine = '-----\n'; static const splitPattern = r'^-----[^-]+-----$'; - static const emptyLinePattern = - r'^[ \f\r\t\u00a0\u2000-\u200a\u202f\u205f\u3000]*$'; + static const emptyLinePattern = r'^[ \f\r\t\u00a0\u2000-\u200a\u202f\u205f\u3000]*$'; static const headerPattern = r'^([^\s:]|[^\s:][^:]*[^\s:]): .+$'; static const beginPattern = r'^-----BEGIN PGP (MESSAGE, PART \d+\/\d+|MESSAGE, PART \d+|SIGNED MESSAGE|MESSAGE|PUBLIC KEY BLOCK|PRIVATE KEY BLOCK|SIGNATURE)-----$'; @@ -101,8 +101,7 @@ class Armor { final text = textLines.join('\r\n').trim(); final data = base64.decode(dataLines.join().trim()); - if ((checksum != _crc24Checksum(data)) && - (checksum.isNotEmpty || checksumRequired)) { + if ((checksum != _crc24Checksum(data)) && (checksum.isNotEmpty || checksumRequired)) { throw StateError('Ascii armor integrity check failed'); } diff --git a/lib/src/common/config.dart b/lib/src/common/config.dart new file mode 100644 index 00000000..151b69c5 --- /dev/null +++ b/lib/src/common/config.dart @@ -0,0 +1,36 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import '../enum/aead_algorithm.dart'; +import '../enum/compression_algorithm.dart'; +import '../enum/hash_algorithm.dart'; +import '../enum/symmetric_algorithm.dart'; + +/// Configuration class +/// Author Nguyen Van Nguyen +final class Config { + static const aeadSupported = true; + static const aeadChunkSizeMin = 10; + static const aeadChunkSizeMax = 16; + + static int aeadChunkSize = 12; + + static bool allowUnauthenticated = false; + + static bool checksumRequired = false; + + static bool aeadProtect = false; + + static bool useV6Key = false; + + static HashAlgorithm preferredHash = HashAlgorithm.sha256; + + static SymmetricAlgorithm preferredSymmetric = SymmetricAlgorithm.aes128; + + static CompressionAlgorithm preferredCompression = CompressionAlgorithm.uncompressed; + + static AeadAlgorithm preferredAead = AeadAlgorithm.gcm; +} diff --git a/lib/src/common/extensions.dart b/lib/src/common/extensions.dart new file mode 100644 index 00000000..f6599a60 --- /dev/null +++ b/lib/src/common/extensions.dart @@ -0,0 +1,250 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:convert'; +import 'dart:typed_data'; + +/// BigInt extension +/// Author Nguyen Van Nguyen +extension BigIntExt on BigInt { + int get byteLength => (bitLength + 7) >> 3; + + Uint8List toBytes() { + if (sign == 0) { + return Uint8List.fromList([0]); + } + + final byteMask = BigInt.from(0xff); + final negativeFlag = BigInt.from(0x80); + + final int needsPaddingByte; + final int rawSize; + + if (sign > 0) { + rawSize = (bitLength + 7) >> 3; + needsPaddingByte = ((this >> (rawSize - 1) * 8) & negativeFlag) == negativeFlag ? 1 : 0; + } else { + needsPaddingByte = 0; + rawSize = (bitLength + 8) >> 3; + } + + final size = rawSize + needsPaddingByte; + final result = Uint8List(size); + var number = this; + for (var i = 0; i < rawSize; i++) { + result[size - i - 1] = (number & byteMask).toInt(); + number = number >> 8; + } + return result; + } + + Uint8List toUnsignedBytes() { + if (sign == 0) { + return Uint8List.fromList([0]); + } + final byteMask = BigInt.from(0xff); + final size = (bitLength + (isNegative ? 8 : 7)) >> 3; + var result = Uint8List(size); + var number = this; + for (var i = 0; i < size; i++) { + result[size - i - 1] = (number & byteMask).toInt(); + number = number >> 8; + } + return result; + } +} + +/// DateTime extension +/// Author Nguyen Van Nguyen +extension DateTimeExt on DateTime { + Uint8List toBytes() => (millisecondsSinceEpoch ~/ 1000).pack32(); +} + +/// Int extension +/// Author Nguyen Van Nguyen +extension IntExt on int { + Uint8List pack16([Endian endian = Endian.big]) => Uint8List(2) + ..buffer.asByteData().setUint16( + 0, + this, + endian, + ); + + Uint8List pack16Le() => pack16(Endian.little); + + Uint8List pack32([Endian endian = Endian.big]) => Uint8List(4) + ..buffer.asByteData().setUint32( + 0, + this, + endian, + ); + + Uint8List pack32Le() => pack32(Endian.little); + + Uint8List pack64([Endian endian = Endian.big]) => Uint8List(8) + ..buffer.asByteData().setUint64( + 0, + this, + endian, + ); + + Uint8List pack64Le() => pack64(Endian.little); + + int shiftLeft32(final int n) { + return toUnsigned(32) << n; + } + + int shiftRight32(final int n) { + return toUnsigned(32) >> n; + } + + int rotateLeft32(final int n) { + final x = toUnsigned(32); + return (x << n) | (x >> 32 - n); + } + + int rotateRight32(final int n) { + final x = toUnsigned(32); + return (x >> n) | (x << 32 - n); + } +} + + +/// String extension +/// Author Nguyen Van Nguyen +extension StringExt on String { + List chunk(final int chunkSize) { + assert(chunkSize > 0); + final chunkCount = (length / chunkSize).ceil(); + return List.generate(chunkCount, (index) { + final sliceStart = index * chunkSize; + final sliceEnd = sliceStart + chunkSize; + return substring( + sliceStart, + (sliceEnd < length) ? sliceEnd : length, + ); + }); + } + + Uint8List hexToBytes() { + final hex = replaceAll(RegExp(r'\s'), ''); + final result = Uint8List(hex.length ~/ 2); + + for (var i = 0; i < hex.length; i += 2) { + final num = hex.substring(i, i + 2); + final byte = int.parse(num, radix: 16); + result[i ~/ 2] = byte; + } + + return result; + } + + Uint8List toBytes() => utf8.encoder.convert(this); + + bool hasMatch(final String text) => RegExp(this).hasMatch(text); +} + +/// Uint8List extension +/// Author Nguyen Van Nguyen +extension Uint8ListExt on Uint8List { + int unpack16([ + Endian endian = Endian.big, + ]) => + buffer.asByteData().getUint16(0, endian); + + int unpack16Le() => unpack16(Endian.little); + + int unpack32([ + Endian endian = Endian.big, + ]) => + buffer.asByteData().getUint32(0, endian); + + int unpack32Le() => unpack32(Endian.little); + + int unpack64([ + Endian endian = Endian.big, + ]) => + buffer.asByteData().getUint64(0, endian); + + int unpack64Le() => unpack64(Endian.little); + + BigInt toBigInt() { + final negative = isNotEmpty && this[0] & 0x80 == 0x80; + BigInt result; + if (length == 1) { + result = BigInt.from(this[0]); + } else { + result = BigInt.zero; + for (var i = 0; i < length; i++) { + final item = this[length - i - 1]; + result |= (BigInt.from(item) << (8 * i)); + } + } + return result.sign != 0 + ? negative + ? result.toSigned(result.bitLength) + : result + : BigInt.zero; + } + + BigInt toBigIntWithSign(final int sign) { + if (sign == 0) { + return BigInt.zero; + } + + BigInt result; + + if (length == 1) { + result = BigInt.from(this[0]); + } else { + result = BigInt.from(0); + for (var i = 0; i < length; i++) { + var item = this[length - i - 1]; + result |= (BigInt.from(item) << (8 * i)); + } + } + + if (result.sign != 0) { + if (sign < 0) { + result = result.toSigned(result.bitLength); + } else { + result = result.toUnsigned(result.bitLength); + } + } + return result; + } + + DateTime toDateTime() => DateTime.fromMillisecondsSinceEpoch( + unpack32() * 1000, + ); + + String toHexadecimal() { + final result = StringBuffer(); + for (var i = 0; i < lengthInBytes; i++) { + final part = this[i]; + result.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}'); + } + return result.toString(); + } + + bool equals(final Uint8List expected) { + if (expected == this) { + return true; + } + + final len = (expected.length < length) ? expected.length : length; + var nonEqual = expected.length ^ length; + + for (var i = 0; i != len; i++) { + nonEqual |= (expected[i] ^ this[i]); + } + for (var i = len; i < length; i++) { + nonEqual |= (this[i] ^ ~this[i]); + } + + return nonEqual == 0; + } +} diff --git a/lib/src/packet/key/s2k.dart b/lib/src/common/generic_s2k.dart similarity index 55% rename from lib/src/packet/key/s2k.dart rename to lib/src/common/generic_s2k.dart index c7a38239..c9cc46c1 100644 --- a/lib/src/packet/key/s2k.dart +++ b/lib/src/common/generic_s2k.dart @@ -1,56 +1,54 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:convert'; import 'dart:typed_data'; -import '../../enum/hash_algorithm.dart'; -import '../../enum/s2k_type.dart'; -import '../../helpers.dart'; - -/// Implementation of the String-to-key specifier -/// -/// String-to-key (S2K) specifiers are used to convert passphrase strings into symmetric-key encryption/decryption keys. -/// They are used in two places, currently: to encrypt the secret part of private keys in the private keyring, -/// and to convert passphrases to encryption keys for symmetrically encrypted messages. -/// Author Nguyen Van Nguyen -class S2K { +import 'helpers.dart'; +import '../enum/hash_algorithm.dart'; +import '../enum/s2k_type.dart'; +import '../type/s2k.dart'; + +/// Implementation of the string-to-key specifier +/// See https://www.rfc-editor.org/rfc/rfc9580#section-3.7 +class GenericS2k implements S2kInterface { /// Default salt length static const saltLength = 8; - /// Exponent bias, defined in RFC4880 + /// Exponent bias static const _expbias = 6; - /// Default Iteration Count Byte + /// Default iteration count byte static const _defaultItCount = 224; - /// s2k identifier or 'gnu-dummy' - final S2kType type; - - /// Hash function identifier, or 0 for gnu-dummy keys + /// Hash function identifier final HashAlgorithm hash; /// s2k iteration count final int itCount; - /// Eight bytes of salt in a binary string. + // s2k iteration count byte + final int count; + + @override final Uint8List salt; - /// s2k iteration count byte - final int count; + @override + final S2kType type; - S2K( + GenericS2k( this.salt, { this.type = S2kType.iterated, - this.hash = HashAlgorithm.sha1, + this.hash = HashAlgorithm.sha256, this.itCount = _defaultItCount, }) : count = (16 + (itCount & 15)) << ((itCount >> 4) + _expbias); /// Parsing function for a string-to-key specifier - factory S2K.fromByteData(final Uint8List bytes) { + factory GenericS2k.fromBytes(final Uint8List bytes) { var pos = 0; - var itCount = _defaultItCount; final type = S2kType.values.firstWhere( (type) => type.value == bytes[pos], ); @@ -60,20 +58,21 @@ class S2K { ); pos++; + var itCount = 0; final Uint8List salt; switch (type) { case S2kType.salted: - salt = bytes.sublist(pos, pos + 8); + salt = bytes.sublist(pos, pos + saltLength); break; case S2kType.iterated: - salt = bytes.sublist(pos, pos + 8); - itCount = bytes[pos + 8]; + salt = bytes.sublist(pos, pos + saltLength); + itCount = bytes[pos + saltLength]; break; default: salt = Uint8List(0); break; } - return S2K( + return GenericS2k( salt, type: type, hash: hash, @@ -81,49 +80,49 @@ class S2K { ); } - int get length => type.length; - - /// Serializes s2k information - Uint8List encode() { - final bytes = [type.value, hash.value]; - switch (type) { - case S2kType.simple: - return Uint8List.fromList(bytes); - case S2kType.salted: - return Uint8List.fromList([...bytes, ...salt]); - case S2kType.iterated: - return Uint8List.fromList([...bytes, ...salt, itCount]); - case S2kType.gnu: - return Uint8List.fromList([...bytes, ...utf8.encode('GNU'), 1]); - } - } - - /// Produces a key using the specified passphrase and the defined hashAlgorithm - Future produceKey( - final String passphrase, - final int keyLen, - ) async { + @override + Uint8List produceKey(final String passphrase, final int length) { switch (type) { case S2kType.simple: - return _hash(passphrase.stringToBytes(), keyLen); + return _hashDigest(passphrase.toBytes(), length); case S2kType.salted: - return _hash( + return _hashDigest( Uint8List.fromList([ ...salt, - ...passphrase.stringToBytes(), + ...passphrase.toBytes(), ]), - keyLen, + length, ); case S2kType.iterated: - return _hash( + return _hashDigest( _iterate(Uint8List.fromList([ ...salt, - ...passphrase.stringToBytes(), + ...passphrase.toBytes(), ])), - keyLen, + length, ); default: - throw UnsupportedError('s2k type not supported.'); + throw UnsupportedError('S2k type not supported.'); + } + } + + @override + int get length => type.length; + + @override + Uint8List get toBytes { + final bytes = [type.value, hash.value]; + switch (type) { + case S2kType.simple: + return Uint8List.fromList(bytes); + case S2kType.salted: + return Uint8List.fromList([...bytes, ...salt]); + case S2kType.iterated: + return Uint8List.fromList([...bytes, ...salt, itCount]); + case S2kType.gnu: + return Uint8List.fromList([...bytes, ...utf8.encode('GNU'), 1]); + case S2kType.argon2: + throw UnsupportedError('Argon2 s2k type not supported.'); } } @@ -141,9 +140,9 @@ class S2K { return result.sublist(0, count); } - Uint8List _hash(final Uint8List data, final int size) { + Uint8List _hashDigest(final Uint8List data, final int length) { var result = Helper.hashDigest(data, hash); - while (result.length < size) { + while (result.length < length) { result = Uint8List.fromList([ ...result, ...Helper.hashDigest( @@ -152,6 +151,6 @@ class S2K { ), ]); } - return result.sublist(0, size); + return result.sublist(0, length); } } diff --git a/lib/src/common/helpers.dart b/lib/src/common/helpers.dart new file mode 100644 index 00000000..b31359b1 --- /dev/null +++ b/lib/src/common/helpers.dart @@ -0,0 +1,124 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:math'; +import 'dart:typed_data'; +import 'package:pointycastle/export.dart'; + +import 'argon2_s2k.dart'; +import 'extensions.dart'; +import 'generic_s2k.dart'; +import '../type/s2k.dart'; +import '../enum/s2k_type.dart'; +import '../enum/hash_algorithm.dart'; +import '../enum/symmetric_algorithm.dart'; + +export 'extensions.dart'; + +/// Helper class +/// Author Nguyen Van Nguyen +final class Helper { + static final _random = Random.secure(); + + static final _secureRandom = SecureRandom('Fortuna') + ..seed( + KeyParameter( + Uint8List.fromList( + List.generate( + 32, + ((_) => _random.nextInt(0xffffffff)), + ), + ), + ), + ); + + static BigInt readMPI(final Uint8List bytes) { + final bitLength = bytes.sublist(0, 2).unpack16(); + return bytes.sublist(2, ((bitLength + 7) >> 3) + 2).toBigIntWithSign(1); + } + + static SecureRandom secureRandom() => _secureRandom; + + static Uint8List generatePrefix([ + final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + ]) { + final prefix = _secureRandom.nextBytes(symmetric.blockSize); + return Uint8List.fromList([ + ...prefix, + prefix[prefix.length - 2], + prefix[prefix.length - 1], + ]); + } + + static Uint8List generateEncryptionKey([ + final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + ]) => + _secureRandom.nextBytes((symmetric.keySize + 7) >> 3); + + static Uint8List hashDigest( + final Uint8List input, [ + final HashAlgorithm hash = HashAlgorithm.sha256, + ]) { + return Digest(hash.digestName).process(input); + } + + static BigInt randomBigInt( + final BigInt min, + final BigInt max, { + SecureRandom? random, + }) { + random = random ?? secureRandom(); + BigInt k; + do { + k = random.nextBigInteger(max.bitLength); + } while (k.compareTo(min) <= 0 || k.compareTo(max) >= 0); + return k; + } + + static S2kInterface stringToKey(final S2kType type) { + assert(type != S2kType.simple); + return switch (type) { + S2kType.argon2 => Argon2S2k( + _secureRandom.nextBytes(Argon2S2k.saltLength), + ), + _ => GenericS2k( + _secureRandom.nextBytes(Argon2S2k.saltLength), + ), + }; + } + + /// Generate a HKDF key derivation of a supplied key input + static Uint8List hkdf( + final Uint8List key, + final int length, { + final HashAlgorithm hash = HashAlgorithm.sha256, + final Uint8List? salt, + final Uint8List? info, + }) { + final params = HkdfParameters( + key, + length, + salt, + info, + ); + final hkdf = HKDFKeyDerivator(Digest(hash.digestName)); + hkdf.init(params); + final derivedKey = Uint8List(length); + hkdf.deriveKey(null, 0, derivedKey, 0); + return derivedKey; + } + + static assertHash(final HashAlgorithm hash) { + assert(hash != HashAlgorithm.md5 && hash != HashAlgorithm.sha1 && hash != HashAlgorithm.ripemd160); + } + + static assertSymmetric(final SymmetricAlgorithm symmetric) { + assert(symmetric != SymmetricAlgorithm.plaintext && + symmetric != SymmetricAlgorithm.cast5 && + symmetric != SymmetricAlgorithm.idea && + symmetric != SymmetricAlgorithm.tripledes); + } +} diff --git a/lib/src/crypto/aead/ocb.dart b/lib/src/crypto/aead/ocb.dart deleted file mode 100644 index e37e23f7..00000000 --- a/lib/src/crypto/aead/ocb.dart +++ /dev/null @@ -1,78 +0,0 @@ -/// Copyright 2023-present by Dart Privacy Guard project. All rights reserved. -/// For the full copyright and license information, please view the LICENSE -/// file that was distributed with this source code. - -import 'dart:typed_data'; - -import 'package:pointycastle/api.dart'; - -import '../../enum/symmetric_algorithm.dart'; -import '../modes/ocb_cipher.dart'; -import 'base.dart'; - -/// OCB Authenticated-Encryption class -/// Author Nguyen Van Nguyen -class Ocb implements Base { - final Uint8List _key; - final SymmetricAlgorithm _symmetric; - - Ocb(this._key, this._symmetric); - - @override - Uint8List encrypt( - final Uint8List plaintext, - final Uint8List nonce, - final Uint8List adata, - ) { - final cipher = OCBCipher( - _symmetric.cipherEngine, - _symmetric.cipherEngine, - )..init( - true, - AEADParameters( - KeyParameter(_key), - _symmetric.blockSize * 8, - nonce, - adata, - ), - ); - - return cipher.process(plaintext); - } - - @override - Uint8List decrypt( - final Uint8List ciphertext, - final Uint8List nonce, - final Uint8List adata, - ) { - final cipher = OCBCipher( - _symmetric.cipherEngine, - _symmetric.cipherEngine, - )..init( - false, - AEADParameters( - KeyParameter(_key), - _symmetric.blockSize * 8, - nonce, - adata, - ), - ); - - return cipher.process(ciphertext); - } - - @override - Uint8List getNonce( - final Uint8List iv, - final Uint8List chunkIndex, - ) { - final nonce = iv.sublist(0); - - for (var i = 0; i < chunkIndex.length; i++) { - nonce[7 + i] ^= chunkIndex[i]; - } - - return nonce; - } -} diff --git a/lib/src/crypto/math/big_int.dart b/lib/src/crypto/math/big_int.dart deleted file mode 100644 index 7bbf773d..00000000 --- a/lib/src/crypto/math/big_int.dart +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -/// Author Nguyen Van Nguyen -extension BigIntExt on BigInt { - int get byteLength => (bitLength + 7) >> 3; - - Uint8List toBytes() { - if (sign == 0) { - return Uint8List.fromList([0]); - } - - final byteMask = BigInt.from(0xff); - final negativeFlag = BigInt.from(0x80); - - final int needsPaddingByte; - final int rawSize; - - if (sign > 0) { - rawSize = (bitLength + 7) >> 3; - needsPaddingByte = - ((this >> (rawSize - 1) * 8) & negativeFlag) == negativeFlag ? 1 : 0; - } else { - needsPaddingByte = 0; - rawSize = (bitLength + 8) >> 3; - } - - final size = rawSize + needsPaddingByte; - final result = Uint8List(size); - var number = this; - for (var i = 0; i < rawSize; i++) { - result[size - i - 1] = (number & byteMask).toInt(); - number = number >> 8; - } - return result; - } - - Uint8List toUnsignedBytes() { - if (sign == 0) { - return Uint8List.fromList([0]); - } - final byteMask = BigInt.from(0xff); - final size = (bitLength + (isNegative ? 8 : 7)) >> 3; - var result = Uint8List(size); - var number = this; - for (var i = 0; i < size; i++) { - result[size - i - 1] = (number & byteMask).toInt(); - number = number >> 8; - } - return result; - } - - /// test primality with certainty >= 1-.5^t - bool isProbablePrime(final int t) { - final x = abs(); - if (this <= _lowprimes.last) { - for (var i = 0; i < _lowprimes.length; ++i) { - if (this == _lowprimes[i]) return true; - } - return false; - } - if (x.isEven) return false; - var i = 1; - while (i < _lowprimes.length) { - var m = _lowprimes[i], j = i + 1; - while (j < _lowprimes.length && m < _lplim) { - m *= _lowprimes[j++]; - } - m = x % m; - while (i < j) { - if ((m % _lowprimes[i++]).sign == 0) { - return false; - } - } - } - return x._millerRabin(t); - } - - int get nafWeight { - if (sign == 0) { - return 0; - } - return (((this << 1) + this) ^ this).bitLength; - } - - /// true if probably prime (HAC 4.24, Miller-Rabin) - bool _millerRabin(int t) { - // Implementation borrowed from bignum.BigIntegerDartvm. - final n1 = this - BigInt.one; - final k = n1._lbit(); - if (k <= 0) return false; - final r = n1 >> k; - t = (t + 1) >> 1; - if (t > _lowprimes.length) t = _lowprimes.length; - BigInt a; - for (var i = 0; i < t; ++i) { - a = _lowprimes[i]; - var y = a.modPow(r, this); - if (y.compareTo(BigInt.one) != 0 && y.compareTo(n1) != 0) { - var j = 1; - while (j++ < k && y.compareTo(n1) != 0) { - y = y.modPow(BigInt.two, this); - if (y.compareTo(BigInt.one) == 0) return false; - } - if (y.compareTo(n1) != 0) return false; - } - } - return true; - } - - /// return index of lowest 1-bit in x, x < 2^31 - int _lbit() { - var x = this; - if (x.sign == 0) return -1; - var r = 0; - while ((x & BigInt.from(0xffffffff)).sign == 0) { - x >>= 32; - r += 32; - } - if ((x & BigInt.from(0xffff)).sign == 0) { - x >>= 16; - r += 16; - } - if ((x & BigInt.from(0xff)).sign == 0) { - x >>= 8; - r += 8; - } - if ((x & BigInt.from(0xf)).sign == 0) { - x >>= 4; - r += 4; - } - if ((x & BigInt.from(3)).sign == 0) { - x >>= 2; - r += 2; - } - if ((x & BigInt.one).sign == 0) ++r; - return r; - } - - static final List _lowprimes = [ - BigInt.from(2), - BigInt.from(3), - BigInt.from(5), - BigInt.from(7), - BigInt.from(11), - BigInt.from(13), - BigInt.from(17), - BigInt.from(19), - BigInt.from(23), - BigInt.from(29), - BigInt.from(31), - BigInt.from(37), - BigInt.from(41), - BigInt.from(43), - BigInt.from(47), - BigInt.from(53), - BigInt.from(59), - BigInt.from(61), - BigInt.from(67), - BigInt.from(71), - BigInt.from(73), - BigInt.from(79), - BigInt.from(83), - BigInt.from(89), - BigInt.from(97), - BigInt.from(101), - BigInt.from(103), - BigInt.from(107), - BigInt.from(109), - BigInt.from(113), - BigInt.from(127), - BigInt.from(131), - BigInt.from(137), - BigInt.from(139), - BigInt.from(149), - BigInt.from(151), - BigInt.from(157), - BigInt.from(163), - BigInt.from(167), - BigInt.from(173), - BigInt.from(179), - BigInt.from(181), - BigInt.from(191), - BigInt.from(193), - BigInt.from(197), - BigInt.from(199), - BigInt.from(211), - BigInt.from(223), - BigInt.from(227), - BigInt.from(229), - BigInt.from(233), - BigInt.from(239), - BigInt.from(241), - BigInt.from(251), - BigInt.from(257), - BigInt.from(263), - BigInt.from(269), - BigInt.from(271), - BigInt.from(277), - BigInt.from(281), - BigInt.from(283), - BigInt.from(293), - BigInt.from(307), - BigInt.from(311), - BigInt.from(313), - BigInt.from(317), - BigInt.from(331), - BigInt.from(337), - BigInt.from(347), - BigInt.from(349), - BigInt.from(353), - BigInt.from(359), - BigInt.from(367), - BigInt.from(373), - BigInt.from(379), - BigInt.from(383), - BigInt.from(389), - BigInt.from(397), - BigInt.from(401), - BigInt.from(409), - BigInt.from(419), - BigInt.from(421), - BigInt.from(431), - BigInt.from(433), - BigInt.from(439), - BigInt.from(443), - BigInt.from(449), - BigInt.from(457), - BigInt.from(461), - BigInt.from(463), - BigInt.from(467), - BigInt.from(479), - BigInt.from(487), - BigInt.from(491), - BigInt.from(499), - BigInt.from(503), - BigInt.from(509) - ]; - - static final BigInt _lplim = (BigInt.one << 26) ~/ _lowprimes.last; -} diff --git a/lib/src/crypto/math/byte_ext.dart b/lib/src/crypto/math/byte_ext.dart deleted file mode 100644 index 4c3a8422..00000000 --- a/lib/src/crypto/math/byte_ext.dart +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -/// Author Nguyen Van Nguyen -extension Uint8ListExt on Uint8List { - int toIn16([ - Endian endian = Endian.big, - ]) => - buffer.asByteData().getInt16(0, endian); - - int toUint16([ - Endian endian = Endian.big, - ]) => - buffer.asByteData().getUint16(0, endian); - - int toLeIn16() => toIn16(Endian.little); - - int toLeUint16() => toUint16(Endian.little); - - int toInt32([ - Endian endian = Endian.big, - ]) => - buffer.asByteData().getInt32(0, endian); - - int toUint32([ - Endian endian = Endian.big, - ]) => - buffer.asByteData().getUint32(0, endian); - - int toLeInt32() => toInt32(Endian.little); - - int toLeUint32() => toUint32(Endian.little); - - int toInt64([ - Endian endian = Endian.big, - ]) => - buffer.asByteData().getInt64(0, endian); - - int toUint64([ - Endian endian = Endian.big, - ]) => - buffer.asByteData().getUint64(0, endian); - - int toLeInt64() => toInt64(Endian.little); - - int toLeUint64() => toUint64(Endian.little); - - BigInt toBigInt() { - final negative = isNotEmpty && this[0] & 0x80 == 0x80; - BigInt result; - if (length == 1) { - result = BigInt.from(this[0]); - } else { - result = BigInt.zero; - for (var i = 0; i < length; i++) { - final item = this[length - i - 1]; - result |= (BigInt.from(item) << (8 * i)); - } - } - return result.sign != 0 - ? negative - ? result.toSigned(result.bitLength) - : result - : BigInt.zero; - } - - BigInt toBigIntWithSign(final int sign) { - if (sign == 0) { - return BigInt.zero; - } - - BigInt result; - - if (length == 1) { - result = BigInt.from(this[0]); - } else { - result = BigInt.from(0); - for (var i = 0; i < length; i++) { - var item = this[length - i - 1]; - result |= (BigInt.from(item) << (8 * i)); - } - } - - if (result.sign != 0) { - if (sign < 0) { - result = result.toSigned(result.bitLength); - } else { - result = result.toUnsigned(result.bitLength); - } - } - return result; - } - - DateTime toDateTime() => DateTime.fromMillisecondsSinceEpoch( - toInt32() * 1000, - ); - - String toHexadecimal() { - final result = StringBuffer(); - for (var i = 0; i < lengthInBytes; i++) { - final part = this[i]; - result.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}'); - } - return result.toString(); - } - - bool equals(final Uint8List expected) { - if (expected == this) { - return true; - } - - final len = (expected.length < length) ? expected.length : length; - var nonEqual = expected.length ^ length; - - for (var i = 0; i != len; i++) { - nonEqual |= (expected[i] ^ this[i]); - } - for (var i = len; i < length; i++) { - nonEqual |= (this[i] ^ ~this[i]); - } - - return nonEqual == 0; - } -} diff --git a/lib/src/crypto/math/int_ext.dart b/lib/src/crypto/math/int_ext.dart deleted file mode 100644 index 91fb09b7..00000000 --- a/lib/src/crypto/math/int_ext.dart +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -const _mask32 = 0xffffffff; - -/// Author Nguyen Van Nguyen -extension IntExt on int { - Uint8List pack16([Endian endian = Endian.big]) => Uint8List(2) - ..buffer.asByteData().setInt16( - 0, - this, - endian, - ); - - Uint8List pack16Le() => pack16(Endian.little); - - Uint8List pack32([Endian endian = Endian.big]) => Uint8List(4) - ..buffer.asByteData().setInt32( - 0, - this, - endian, - ); - - Uint8List pack32Le() => pack32(Endian.little); - - Uint8List pack64([Endian endian = Endian.big]) => Uint8List(8) - ..buffer.asByteData().setInt64( - 0, - this, - endian, - ); - - Uint8List pack64Le() => pack64(Endian.little); - - int rotateLeft8(int n) { - assert(n >= 0); - assert((this >= 0) && (this <= 0xff)); - n &= 0x07; - return ((this << n) & 0xff) | (this >> (8 - n)); - } - - int shiftLeft32(final int n) { - return (this & _mask32) << n; - } - - int shiftRight32(final int n) { - return (this & _mask32) >> n; - } - - int rotateLeft32(final int n) { - final num = this & _mask32; - return (num << n) | (num >> 32 - n); - } - - int rotateRight32(final int n) { - final num = this & _mask32; - return (num >> n) | (num << 32 - n); - } -} diff --git a/lib/src/crypto/symmetric/base_cipher.dart b/lib/src/crypto/symmetric/base_cipher.dart deleted file mode 100644 index f73a1e94..00000000 --- a/lib/src/crypto/symmetric/base_cipher.dart +++ /dev/null @@ -1,26 +0,0 @@ -/// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -/// For the full copyright and license information, please view the LICENSE -/// file that was distributed with this source code. - -import 'dart:typed_data'; - -import 'package:pointycastle/api.dart'; - -export 'blowfish.dart'; -export 'buffered_cipher.dart'; -export 'camellia.dart'; -export 'cast5.dart'; -export 'idea.dart'; -export 'twofish.dart'; - -/// Base implementation of [BlockCipher] which provides shared methods. -/// Ported from Bouncy Castle project -/// Author Nguyen Van Nguyen -abstract class BaseCipher implements BlockCipher { - @override - Uint8List process(final Uint8List data) { - final out = Uint8List(blockSize); - final len = processBlock(data, 0, out, 0); - return out.sublist(0, len); - } -} diff --git a/lib/src/crypto/symmetric/camellia_light.dart b/lib/src/crypto/symmetric/camellia_light.dart deleted file mode 100644 index a1cb2f22..00000000 --- a/lib/src/crypto/symmetric/camellia_light.dart +++ /dev/null @@ -1,565 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import 'package:pointycastle/api.dart'; - -import '../math/byte_ext.dart'; -import '../math/int_ext.dart'; -import 'base_cipher.dart'; - -/// Camellia - based on RFC 3713 -/// Ported from Bouncy Castle project -/// Author Nguyen Van Nguyen -class CamelliaLightEngine extends BaseCipher { - static const _blockSize = 16; - - static const _mask8 = 0xff; - - static const _sigma = [ - 0xa09e667f, 0x3bcc908b, 0xb67ae858, 0x4caa73b2, // 0 - 3 - 0xc6ef372f, 0xe94f82be, 0x54ff53a5, 0xf1d36f1c, - 0x10e527fa, 0xde682d1d, 0xb05688c2, 0xb3e6c1fd - ]; - - /// S-box data - static const _sbox = [ - 112, 130, 44, 236, // 0 - 3 - 179, 39, 192, 229, - 228, 133, 87, 53, - 234, 12, 174, 65, - 35, 239, 107, 147, - 69, 25, 165, 33, - 237, 14, 79, 78, - 29, 101, 146, 189, - 134, 184, 175, 143, - 124, 235, 31, 206, - 62, 48, 220, 95, - 94, 197, 11, 26, - 166, 225, 57, 202, - 213, 71, 93, 61, - 217, 1, 90, 214, - 81, 86, 108, 77, - 139, 13, 154, 102, - 251, 204, 176, 45, - 116, 18, 43, 32, - 240, 177, 132, 153, - 223, 76, 203, 194, - 52, 126, 118, 5, - 109, 183, 169, 49, - 209, 23, 4, 215, - 20, 88, 58, 97, - 222, 27, 17, 28, - 50, 15, 156, 22, - 83, 24, 242, 34, - 254, 68, 207, 178, - 195, 181, 122, 145, - 36, 8, 232, 168, - 96, 252, 105, 80, - 170, 208, 160, 125, - 161, 137, 98, 151, - 84, 91, 30, 149, - 224, 255, 100, 210, - 16, 196, 0, 72, - 163, 247, 117, 219, - 138, 3, 230, 218, - 9, 63, 221, 148, - 135, 92, 131, 2, - 205, 74, 144, 51, - 115, 103, 246, 243, - 157, 127, 191, 226, - 82, 155, 216, 38, - 200, 55, 198, 59, - 129, 150, 111, 75, - 19, 190, 99, 46, - 233, 121, 167, 140, - 159, 110, 188, 142, - 41, 245, 249, 182, - 47, 253, 180, 89, - 120, 152, 6, 106, - 231, 70, 113, 186, - 212, 37, 171, 66, - 136, 162, 141, 250, - 114, 7, 185, 85, - 248, 238, 172, 10, - 54, 73, 42, 104, - 60, 56, 241, 164, - 64, 40, 211, 123, - 187, 201, 67, 193, - 21, 227, 173, 244, - 119, 199, 128, 158 - ]; - - late bool _initialised; - - late bool _keyIs128; - - final _subkey = List.filled(24 * 4, 0); - - /// for whitening - final _kw = List.filled(4 * 2, 0); - - /// for FL and FL^(-1) - final _ke = List.filled(6 * 2, 0); - - /// for encryption and decryption - final _state = List.filled(4, 0); - - @override - String get algorithmName => 'Camellia'; - - @override - int get blockSize => _blockSize; - - @override - void init(bool forEncryption, CipherParameters? params) { - if (params is! KeyParameter) { - throw Exception('only simple KeyParameter expected.'); - } - - _setKey(forEncryption, params.key); - _initialised = true; - } - - @override - int processBlock( - Uint8List input, - int inOff, - Uint8List output, - int outOff, - ) { - if (!_initialised) { - throw Exception('Camellia is not initialized'); - } - if ((inOff + _blockSize) > input.length) { - throw Exception('input buffer too short'); - } - if ((outOff + _blockSize) > output.length) { - throw Exception('output buffer too short'); - } - - if (_keyIs128) { - return _processBlock128(input, inOff, output, outOff); - } else { - return _processBlock256(input, inOff, output, outOff); - } - } - - @override - void reset() {} - - static void _roldq( - int rot, - List ki, - int inOff, - List ko, - int outOff, - ) { - ko[outOff] = - ki[inOff].shiftLeft32(rot) | ki[1 + inOff].shiftRight32(32 - rot); - ko[1 + outOff] = - ki[1 + inOff].shiftLeft32(rot) | ki[2 + inOff].shiftRight32(32 - rot); - ko[2 + outOff] = - ki[2 + inOff].shiftLeft32(rot) | ki[3 + inOff].shiftRight32(32 - rot); - ko[3 + outOff] = - ki[3 + inOff].shiftLeft32(rot) | ki[inOff].shiftRight32(32 - rot); - ki[inOff] = ko[outOff]; - ki[1 + inOff] = ko[1 + outOff]; - ki[2 + inOff] = ko[2 + outOff]; - ki[3 + inOff] = ko[3 + outOff]; - } - - static void _decroldq( - int rot, - List ki, - int inOff, - List ko, - int outOff, - ) { - ko[2 + outOff] = - ki[inOff].shiftLeft32(rot) | ki[1 + inOff].shiftRight32(32 - rot); - ko[3 + outOff] = - ki[1 + inOff].shiftLeft32(rot) | ki[2 + inOff].shiftRight32(32 - rot); - ko[outOff] = - ki[2 + inOff].shiftLeft32(rot) | ki[3 + inOff].shiftRight32(32 - rot); - ko[1 + outOff] = - ki[3 + inOff].shiftLeft32(rot) | ki[inOff].shiftRight32(32 - rot); - ki[inOff] = ko[2 + outOff]; - ki[1 + inOff] = ko[3 + outOff]; - ki[2 + inOff] = ko[outOff]; - ki[3 + inOff] = ko[1 + outOff]; - } - - static void _roldqo32( - int rot, - List ki, - int inOff, - List ko, - int outOff, - ) { - ko[outOff] = ki[1 + inOff].shiftLeft32(rot - 32) | - ki[2 + inOff].shiftRight32(64 - rot); - ko[1 + outOff] = ki[2 + inOff].shiftLeft32(rot - 32) | - ki[3 + inOff].shiftRight32(64 - rot); - ko[2 + outOff] = - ki[3 + inOff].shiftLeft32(rot - 32) | ki[inOff].shiftRight32(64 - rot); - ko[3 + outOff] = - ki[inOff].shiftLeft32(rot - 32) | ki[1 + inOff].shiftRight32(64 - rot); - ki[inOff] = ko[outOff]; - ki[1 + inOff] = ko[1 + outOff]; - ki[2 + inOff] = ko[2 + outOff]; - ki[3 + inOff] = ko[3 + outOff]; - } - - static void _decroldqo32( - int rot, - List ki, - int inOff, - List ko, - int outOff, - ) { - ko[2 + outOff] = ki[1 + inOff].shiftLeft32(rot - 32) | - ki[2 + inOff].shiftRight32(64 - rot); - ko[3 + outOff] = ki[2 + inOff].shiftLeft32(rot - 32) | - ki[3 + inOff].shiftRight32(64 - rot); - ko[outOff] = - ki[3 + inOff].shiftLeft32(rot - 32) | ki[inOff].shiftRight32(64 - rot); - ko[1 + outOff] = - ki[inOff].shiftLeft32(rot - 32) | ki[1 + inOff].shiftRight32(64 - rot); - ki[inOff] = ko[2 + outOff]; - ki[1 + inOff] = ko[3 + outOff]; - ki[2 + inOff] = ko[outOff]; - ki[3 + inOff] = ko[1 + outOff]; - } - - static int _bytes2uint(Uint8List src, int offset) { - return src.sublist(offset, offset + 4).toUint32(); - } - - static void _uint2bytes(int word, Uint8List dst, int offset) { - dst.setRange(offset, offset + 4, word.toUnsigned(32).pack32()); - } - - static int _sbox2(int x) { - return _sbox[x].rotateLeft8(1); - } - - static int _sbox3(int x) { - return _sbox[x].rotateLeft8(7); - } - - static int _sbox4(int x) { - return _sbox[x.rotateLeft8(1)]; - } - - static void _camelliaF2(List s, List skey, int keyoff) { - var t1 = s[0] ^ skey[0 + keyoff]; - var u = _sbox4((t1 & _mask8)); - u |= (_sbox3(((t1 >> 8) & _mask8)) << 8); - u |= (_sbox2(((t1 >> 16) & _mask8)) << 16); - u |= ((_sbox[((t1 >> 24) & _mask8)] & _mask8) << 24); - - var t2 = s[1] ^ skey[1 + keyoff]; - var v = _sbox[(t2 & _mask8)] & _mask8; - v |= (_sbox4(((t2 >> 8) & _mask8)) << 8); - v |= (_sbox3(((t2 >> 16) & _mask8)) << 16); - v |= (_sbox2(((t2 >> 24) & _mask8)) << 24); - - v = v.rotateLeft32(8); - u ^= v; - v = v.rotateLeft32(8) ^ u; - u = u.rotateRight32(8) ^ v; - s[2] ^= v.rotateLeft32(16) ^ u; - s[3] ^= u.rotateLeft32(8); - - t1 = s[2] ^ skey[2 + keyoff]; - u = _sbox4((t1 & _mask8)); - u |= _sbox3(((t1 >> 8) & _mask8)) << 8; - u |= _sbox2(((t1 >> 16) & _mask8)) << 16; - u |= (_sbox[((t1 >> 24) & _mask8)] & _mask8) << 24; - - t2 = s[3] ^ skey[3 + keyoff]; - v = (_sbox[(t2 & _mask8)] & _mask8); - v |= _sbox4(((t2 >> 8) & _mask8)) << 8; - v |= _sbox3(((t2 >> 16) & _mask8)) << 16; - v |= _sbox2(((t2 >> 24) & _mask8)) << 24; - - v = v.rotateLeft32(8); - u ^= v; - v = v.rotateLeft32(8) ^ u; - u = u.rotateRight32(8) ^ v; - s[0] ^= v.rotateLeft32(16) ^ u; - s[1] ^= u.rotateLeft32(8); - } - - static void _camelliaFLs(List s, List fkey, int keyoff) { - s[1] ^= (s[0] & fkey[0 + keyoff]).rotateLeft32(1); - s[0] ^= fkey[1 + keyoff] | s[1]; - - s[2] ^= fkey[3 + keyoff] | s[3]; - s[3] ^= (fkey[2 + keyoff] & s[2]).rotateLeft32(1); - } - - void _setKey(bool forEncryption, Uint8List key) { - final k = List.filled(8, 0); - final ka = List.filled(4, 0); - final kb = List.filled(4, 0); - final t = List.filled(4, 0); - - switch (key.length) { - case 16: - _keyIs128 = true; - k[0] = _bytes2uint(key, 0); - k[1] = _bytes2uint(key, 4); - k[2] = _bytes2uint(key, 8); - k[3] = _bytes2uint(key, 12); - break; - case 24: - k[0] = _bytes2uint(key, 0); - k[1] = _bytes2uint(key, 4); - k[2] = _bytes2uint(key, 8); - k[3] = _bytes2uint(key, 12); - k[4] = _bytes2uint(key, 16); - k[5] = _bytes2uint(key, 20); - k[6] = ~k[4]; - k[7] = ~k[5]; - _keyIs128 = false; - break; - case 32: - k[0] = _bytes2uint(key, 0); - k[1] = _bytes2uint(key, 4); - k[2] = _bytes2uint(key, 8); - k[3] = _bytes2uint(key, 12); - k[4] = _bytes2uint(key, 16); - k[5] = _bytes2uint(key, 20); - k[6] = _bytes2uint(key, 24); - k[7] = _bytes2uint(key, 28); - _keyIs128 = false; - break; - default: - throw Exception('key sizes are only 16/24/32 bytes.'); - } - - for (var i = 0; i < 4; i++) { - ka[i] = k[i] ^ k[i + 4]; - } - /* compute KA */ - _camelliaF2(ka, _sigma, 0); - for (var i = 0; i < 4; i++) { - ka[i] ^= k[i]; - } - _camelliaF2(ka, _sigma, 4); - - if (_keyIs128) { - if (forEncryption) { - /* KL dependant keys */ - _kw[0] = k[0]; - _kw[1] = k[1]; - _kw[2] = k[2]; - _kw[3] = k[3]; - _roldq(15, k, 0, _subkey, 4); - _roldq(30, k, 0, _subkey, 12); - _roldq(15, k, 0, t, 0); - _subkey[18] = t[2]; - _subkey[19] = t[3]; - _roldq(17, k, 0, _ke, 4); - _roldq(17, k, 0, _subkey, 24); - _roldq(17, k, 0, _subkey, 32); - /* KA dependant keys */ - _subkey[0] = ka[0]; - _subkey[1] = ka[1]; - _subkey[2] = ka[2]; - _subkey[3] = ka[3]; - _roldq(15, ka, 0, _subkey, 8); - _roldq(15, ka, 0, _ke, 0); - _roldq(15, ka, 0, t, 0); - _subkey[16] = t[0]; - _subkey[17] = t[1]; - _roldq(15, ka, 0, _subkey, 20); - _roldqo32(34, ka, 0, _subkey, 28); - _roldq(17, ka, 0, _kw, 4); - } else { - // decryption - /* KL dependant keys */ - _kw[4] = k[0]; - _kw[5] = k[1]; - _kw[6] = k[2]; - _kw[7] = k[3]; - _decroldq(15, k, 0, _subkey, 28); - _decroldq(30, k, 0, _subkey, 20); - _decroldq(15, k, 0, t, 0); - _subkey[16] = t[0]; - _subkey[17] = t[1]; - _decroldq(17, k, 0, _ke, 0); - _decroldq(17, k, 0, _subkey, 8); - _decroldq(17, k, 0, _subkey, 0); - /* KA dependant keys */ - _subkey[34] = ka[0]; - _subkey[35] = ka[1]; - _subkey[32] = ka[2]; - _subkey[33] = ka[3]; - _decroldq(15, ka, 0, _subkey, 24); - _decroldq(15, ka, 0, _ke, 4); - _decroldq(15, ka, 0, t, 0); - _subkey[18] = t[2]; - _subkey[19] = t[3]; - _decroldq(15, ka, 0, _subkey, 12); - _decroldqo32(34, ka, 0, _subkey, 4); - _roldq(17, ka, 0, _kw, 0); - } - } else { - // 192bit or 256bit - /* compute KB */ - for (var i = 0; i < 4; i++) { - kb[i] = ka[i] ^ k[i + 4]; - } - _camelliaF2(kb, _sigma, 8); - - if (forEncryption) { - /* KL dependant keys */ - _kw[0] = k[0]; - _kw[1] = k[1]; - _kw[2] = k[2]; - _kw[3] = k[3]; - _roldqo32(45, k, 0, _subkey, 16); - _roldq(15, k, 0, _ke, 4); - _roldq(17, k, 0, _subkey, 32); - _roldqo32(34, k, 0, _subkey, 44); - /* KR dependant keys */ - _roldq(15, k, 4, _subkey, 4); - _roldq(15, k, 4, _ke, 0); - _roldq(30, k, 4, _subkey, 24); - _roldqo32(34, k, 4, _subkey, 36); - /* KA dependant keys */ - _roldq(15, ka, 0, _subkey, 8); - _roldq(30, ka, 0, _subkey, 20); - /* 32bit rotation */ - _ke[8] = ka[1]; - _ke[9] = ka[2]; - _ke[10] = ka[3]; - _ke[11] = ka[0]; - _roldqo32(49, ka, 0, _subkey, 40); - - /* KB dependant keys */ - _subkey[0] = kb[0]; - _subkey[1] = kb[1]; - _subkey[2] = kb[2]; - _subkey[3] = kb[3]; - _roldq(30, kb, 0, _subkey, 12); - _roldq(30, kb, 0, _subkey, 28); - _roldqo32(51, kb, 0, _kw, 4); - } else { - // decryption - /* KL dependant keys */ - _kw[4] = k[0]; - _kw[5] = k[1]; - _kw[6] = k[2]; - _kw[7] = k[3]; - _decroldqo32(45, k, 0, _subkey, 28); - _decroldq(15, k, 0, _ke, 4); - _decroldq(17, k, 0, _subkey, 12); - _decroldqo32(34, k, 0, _subkey, 0); - /* KR dependant keys */ - _decroldq(15, k, 4, _subkey, 40); - _decroldq(15, k, 4, _ke, 8); - _decroldq(30, k, 4, _subkey, 20); - _decroldqo32(34, k, 4, _subkey, 8); - /* KA dependant keys */ - _decroldq(15, ka, 0, _subkey, 36); - _decroldq(30, ka, 0, _subkey, 24); - /* 32bit rotation */ - _ke[2] = ka[1]; - _ke[3] = ka[2]; - _ke[0] = ka[3]; - _ke[1] = ka[0]; - _decroldqo32(49, ka, 0, _subkey, 4); - - /* KB dependant keys */ - _subkey[46] = kb[0]; - _subkey[47] = kb[1]; - _subkey[44] = kb[2]; - _subkey[45] = kb[3]; - _decroldq(30, kb, 0, _subkey, 32); - _decroldq(30, kb, 0, _subkey, 16); - _roldqo32(51, kb, 0, _kw, 0); - } - } - } - - int _processBlock128( - Uint8List input, - int inOff, - Uint8List output, - int outOff, - ) { - for (var i = 0; i < 4; i++) { - _state[i] = _bytes2uint(input, inOff + (i * 4)); - _state[i] ^= _kw[i]; - } - - _camelliaF2(_state, _subkey, 0); - _camelliaF2(_state, _subkey, 4); - _camelliaF2(_state, _subkey, 8); - _camelliaFLs(_state, _ke, 0); - _camelliaF2(_state, _subkey, 12); - _camelliaF2(_state, _subkey, 16); - _camelliaF2(_state, _subkey, 20); - _camelliaFLs(_state, _ke, 4); - _camelliaF2(_state, _subkey, 24); - _camelliaF2(_state, _subkey, 28); - _camelliaF2(_state, _subkey, 32); - - _state[2] ^= _kw[4]; - _state[3] ^= _kw[5]; - _state[0] ^= _kw[6]; - _state[1] ^= _kw[7]; - - _uint2bytes(_state[2], output, outOff); - _uint2bytes(_state[3], output, outOff + 4); - _uint2bytes(_state[0], output, outOff + 8); - _uint2bytes(_state[1], output, outOff + 12); - - return _blockSize; - } - - int _processBlock256( - Uint8List input, - int inOff, - Uint8List output, - int outOff, - ) { - for (var i = 0; i < 4; i++) { - _state[i] = _bytes2uint(input, inOff + (i * 4)); - _state[i] ^= _kw[i]; - } - - _camelliaF2(_state, _subkey, 0); - _camelliaF2(_state, _subkey, 4); - _camelliaF2(_state, _subkey, 8); - _camelliaFLs(_state, _ke, 0); - _camelliaF2(_state, _subkey, 12); - _camelliaF2(_state, _subkey, 16); - _camelliaF2(_state, _subkey, 20); - _camelliaFLs(_state, _ke, 4); - _camelliaF2(_state, _subkey, 24); - _camelliaF2(_state, _subkey, 28); - _camelliaF2(_state, _subkey, 32); - _camelliaFLs(_state, _ke, 8); - _camelliaF2(_state, _subkey, 36); - _camelliaF2(_state, _subkey, 40); - _camelliaF2(_state, _subkey, 44); - - _state[2] ^= _kw[4]; - _state[3] ^= _kw[5]; - _state[0] ^= _kw[6]; - _state[1] ^= _kw[7]; - - _uint2bytes(_state[2], output, outOff); - _uint2bytes(_state[3], output, outOff + 4); - _uint2bytes(_state[0], output, outOff + 8); - _uint2bytes(_state[1], output, outOff + 12); - return _blockSize; - } -} diff --git a/lib/src/crypto/aead/eax.dart b/lib/src/cryptor/aead/eax.dart similarity index 81% rename from lib/src/crypto/aead/eax.dart rename to lib/src/cryptor/aead/eax.dart index 17d70c84..36af543a 100644 --- a/lib/src/crypto/aead/eax.dart +++ b/lib/src/cryptor/aead/eax.dart @@ -1,17 +1,19 @@ -/// Copyright 2023-present by Dart Privacy Guard project. All rights reserved. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. /// For the full copyright and license information, please view the LICENSE /// file that was distributed with this source code. +library; + import 'dart:typed_data'; + import 'package:pointycastle/export.dart'; import '../../enum/symmetric_algorithm.dart'; - -import 'base.dart'; +import '../../type/aead.dart'; /// EAX Authenticated-Encryption class /// Author Nguyen Van Nguyen -class Eax implements Base { +class Eax implements AeadInterface { final Uint8List _key; final SymmetricAlgorithm _symmetric; @@ -19,9 +21,9 @@ class Eax implements Base { @override Uint8List encrypt( - final Uint8List plaintext, + final Uint8List plainText, final Uint8List nonce, - final Uint8List adata, + final Uint8List aData, ) { final cipher = EAX( _symmetric.cipherEngine, @@ -31,18 +33,18 @@ class Eax implements Base { KeyParameter(_key), _symmetric.blockSize * 8, nonce, - adata, + aData, ), ); - return _process(cipher, plaintext); + return _process(cipher, plainText); } @override Uint8List decrypt( - final Uint8List ciphertext, + final Uint8List cipherText, final Uint8List nonce, - final Uint8List adata, + final Uint8List aData, ) { final cipher = EAX( _symmetric.cipherEngine, @@ -52,11 +54,11 @@ class Eax implements Base { KeyParameter(_key), _symmetric.blockSize * 8, nonce, - adata, + aData, ), ); - return _process(cipher, ciphertext); + return _process(cipher, cipherText); } @override diff --git a/lib/src/crypto/aead/gcm.dart b/lib/src/cryptor/aead/gcm.dart similarity index 77% rename from lib/src/crypto/aead/gcm.dart rename to lib/src/cryptor/aead/gcm.dart index 8b1a1a5d..ca8053d2 100644 --- a/lib/src/crypto/aead/gcm.dart +++ b/lib/src/cryptor/aead/gcm.dart @@ -1,17 +1,19 @@ -/// Copyright 2023-present by Dart Privacy Guard project. All rights reserved. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. /// For the full copyright and license information, please view the LICENSE /// file that was distributed with this source code. +library; + import 'dart:typed_data'; import 'package:pointycastle/export.dart'; import '../../enum/symmetric_algorithm.dart'; -import 'base.dart'; +import '../../type/aead.dart'; /// GCM Authenticated-Encryption class /// Author Nguyen Van Nguyen -class Gcm implements Base { +class Gcm implements AeadInterface { final Uint8List _key; final SymmetricAlgorithm _symmetric; @@ -19,9 +21,9 @@ class Gcm implements Base { @override Uint8List encrypt( - final Uint8List ciphertext, + final Uint8List plainText, final Uint8List nonce, - final Uint8List adata, + final Uint8List aData, ) { final cipher = GCMBlockCipher( _symmetric.cipherEngine, @@ -31,17 +33,17 @@ class Gcm implements Base { KeyParameter(_key), _symmetric.blockSize * 8, nonce, - adata, + aData, ), ); - return cipher.process(ciphertext); + return cipher.process(plainText); } @override Uint8List decrypt( - final Uint8List plaintext, + final Uint8List cipherText, final Uint8List nonce, - final Uint8List adata, + final Uint8List aData, ) { final cipher = GCMBlockCipher( _symmetric.cipherEngine, @@ -51,10 +53,10 @@ class Gcm implements Base { KeyParameter(_key), _symmetric.blockSize * 8, nonce, - adata, + aData, ), ); - return cipher.process(plaintext); + return cipher.process(cipherText); } @override diff --git a/lib/src/crypto/modes/ocb_cipher.dart b/lib/src/cryptor/aead/ocb.dart similarity index 88% rename from lib/src/crypto/modes/ocb_cipher.dart rename to lib/src/cryptor/aead/ocb.dart index 5a5b6e86..af27c8a5 100644 --- a/lib/src/crypto/modes/ocb_cipher.dart +++ b/lib/src/cryptor/aead/ocb.dart @@ -1,14 +1,84 @@ -/// Copyright 2023-present by Dart Privacy Guard project. All rights reserved. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. /// For the full copyright and license information, please view the LICENSE /// file that was distributed with this source code. +library; + import 'dart:typed_data'; +import 'package:pointycastle/export.dart'; + +import '../../common/extensions.dart'; +import '../../enum/symmetric_algorithm.dart'; +import '../../type/aead.dart'; + +/// OCB Authenticated-Encryption class +/// Author Nguyen Van Nguyen +class Ocb implements AeadInterface { + final Uint8List _key; + final SymmetricAlgorithm _symmetric; + + Ocb(this._key, this._symmetric); + + @override + Uint8List encrypt( + final Uint8List plainText, + final Uint8List nonce, + final Uint8List aData, + ) { + final cipher = OCBCipher( + _symmetric.cipherEngine, + _symmetric.cipherEngine, + )..init( + true, + AEADParameters( + KeyParameter(_key), + _symmetric.blockSize * 8, + nonce, + aData, + ), + ); -import 'package:dart_pg/src/crypto/math/byte_ext.dart'; -import 'package:pointycastle/api.dart'; + return cipher.process(plainText); + } + + @override + Uint8List decrypt( + final Uint8List cipherText, + final Uint8List nonce, + final Uint8List aData, + ) { + final cipher = OCBCipher( + _symmetric.cipherEngine, + _symmetric.cipherEngine, + )..init( + false, + AEADParameters( + KeyParameter(_key), + _symmetric.blockSize * 8, + nonce, + aData, + ), + ); + + return cipher.process(cipherText); + } + + @override + Uint8List getNonce( + final Uint8List iv, + final Uint8List chunkIndex, + ) { + final nonce = iv.sublist(0); + + for (var i = 0; i < chunkIndex.length; i++) { + nonce[7 + i] ^= chunkIndex[i]; + } + + return nonce; + } +} /// An implementation of RFC 7253 on The OCB Authenticated-Encryption Algorithm. -/// Ported and modified from Bouncy Castle project /// See https://tools.ietf.org/html/rfc7253 /// Author Nguyen Van Nguyen class OCBCipher implements AEADCipher { @@ -109,7 +179,7 @@ class OCBCipher implements AEADCipher { _macSize = macSizeBits ~/ 8; keyParam = param.parameters as KeyParameter; } else if (params is ParametersWithIV) { - var param = params; + final param = params; newNonce = param.iv; _initialAssociatedText = Uint8List(0); keyParam = param.parameters as KeyParameter; diff --git a/lib/src/crypto/signer/dsa.dart b/lib/src/cryptor/asymmetric/dsa.dart similarity index 64% rename from lib/src/crypto/signer/dsa.dart rename to lib/src/cryptor/asymmetric/dsa.dart index c2baebcd..408a4371 100644 --- a/lib/src/crypto/signer/dsa.dart +++ b/lib/src/cryptor/asymmetric/dsa.dart @@ -1,19 +1,16 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:math'; import 'dart:typed_data'; +import 'package:pointycastle/api.dart'; -import 'package:pointycastle/export.dart'; - -import '../../helpers.dart'; -import '../math/big_int.dart'; -import '../math/byte_ext.dart'; -import '../math/int_ext.dart'; +import '../../common/helpers.dart'; /// Implementation of DSA (digital signature algorithm) -/// Ported and modified from Bouncy Castle project /// Author Nguyen Van Nguyen class DSASigner implements Signer { final Digest? _digest; @@ -63,7 +60,7 @@ class DSASigner implements Signer { @override DSASignature generateSignature(final Uint8List message) { if (!_forSigning) { - throw StateError('DSASigner not initialised for signature generation'); + throw StateError('DSA signer not initialised for signature generation'); } final pri = _key as DSAPrivateKey; @@ -217,90 +214,3 @@ class DSAPrivateKey extends DSAAsymmetricKey implements PrivateKey { BigInt get y => publicKey.y; } - -class DSAKeyGeneratorParameters extends KeyGeneratorParameters { - final int size; - - final int certainty; - - DSAKeyGeneratorParameters(super.bitStrength, this.size, this.certainty); - - Map generateParameters(final SecureRandom random) { - final order = generateProbablePrime(size, 1, random); - final divisor = order * BigInt.two; - BigInt prime, generator; - do { - final x = random.nextBigInteger(bitStrength); - final c = x % divisor; - prime = x - (c - BigInt.one); - } while ( - !prime.isProbablePrime(certainty) || prime.bitLength != bitStrength); - - final p1 = prime - BigInt.one; - final e = p1 ~/ order; - var h = BigInt.two; - do { - generator = h.modPow(e, prime); - h += BigInt.one; - } while (generator.compareTo(BigInt.one) == 0); - - return { - 'prime': prime, - 'order': order, - 'generator': generator, - }; - } -} - -class DSAKeyGenerator implements KeyGenerator { - late SecureRandom _random; - - late DSAKeyGeneratorParameters _params; - - @override - String get algorithmName => 'DSA'; - - @override - AsymmetricKeyPair generateKeyPair() { - final params = _params.generateParameters(_random); - final prime = params['prime']!; - final order = params['order']!; - final generator = params['generator']!; - final privateKey = DSAPrivateKey( - _generateSecretExponent(order), - prime, - order, - generator, - ); - - return AsymmetricKeyPair( - privateKey.publicKey, - privateKey, - ); - } - - @override - void init(final CipherParameters params) { - if (params is ParametersWithRandom) { - _random = params.random; - _params = params.parameters as DSAKeyGeneratorParameters; - } else { - _random = Helper.secureRandom(); - _params = params as DSAKeyGeneratorParameters; - } - } - - BigInt _generateSecretExponent(final BigInt order) { - int minWeight = order.bitLength >> 2; - for (;;) { - BigInt x = Helper.randomBigInt( - BigInt.one, - order - BigInt.one, - random: _random, - ); - if (x.nafWeight > minWeight) { - return x; - } - } - } -} diff --git a/lib/src/crypto/asymmetric/elgamal.dart b/lib/src/cryptor/asymmetric/elgamal.dart similarity index 54% rename from lib/src/crypto/asymmetric/elgamal.dart rename to lib/src/cryptor/asymmetric/elgamal.dart index 15f8a715..3abd18a9 100644 --- a/lib/src/crypto/asymmetric/elgamal.dart +++ b/lib/src/cryptor/asymmetric/elgamal.dart @@ -1,17 +1,15 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. -import 'dart:typed_data'; +library; -import 'package:pointycastle/export.dart'; +import 'dart:typed_data'; +import 'package:pointycastle/api.dart'; -import '../../helpers.dart'; -import '../math/big_int.dart'; -import '../math/byte_ext.dart'; +import '../../common/helpers.dart'; /// Asymmetric block cipher using basic ElGamal algorithm. -/// Ported and modified from Bouncy Castle project /// Author Nguyen Van Nguyen class ElGamalEngine implements AsymmetricBlockCipher { late ElGamalAsymmetricKey? _key; @@ -54,13 +52,11 @@ class ElGamalEngine implements AsymmetricBlockCipher { /// Return the maximum size for an input block to this engine. @override - int get inputBlockSize => - _forEncryption ? (_bitSize - 1) ~/ 8 : 2 * ((_bitSize + 7) >> 3); + int get inputBlockSize => _forEncryption ? (_bitSize - 1) ~/ 8 : 2 * ((_bitSize + 7) >> 3); /// Return the maximum size for an output block to this engine. @override - int get outputBlockSize => - _forEncryption ? 2 * ((_bitSize + 7) >> 3) : (_bitSize - 1) ~/ 8; + int get outputBlockSize => _forEncryption ? 2 * ((_bitSize + 7) >> 3) : (_bitSize - 1) ~/ 8; @override Uint8List process(final Uint8List data) { @@ -83,7 +79,7 @@ class ElGamalEngine implements AsymmetricBlockCipher { } if (inLength > inputBlockSize) { - throw ArgumentError('input too large for $algorithmName cipher.'); + throw ArgumentError('Input too large for $algorithmName cipher.'); } final prime = _key!.prime; @@ -93,21 +89,18 @@ class ElGamalEngine implements AsymmetricBlockCipher { final phi = input.sublist(inLength ~/ 2).toBigIntWithSign(1); final priv = _key as ElGamalPrivateKey; - final m = - (gamma.modPow(prime - (BigInt.one + priv.x), prime) * phi) % prime; + final m = (gamma.modPow(prime - (BigInt.one + priv.x), prime) * phi) % prime; output.setAll( outOff, m.toUnsignedBytes().sublist(0, output.length - outOff), ); } else { /// encryption - final block = (inOff != 0 || inLength != input.length) - ? input.sublist(0, inLength) - : input; + final block = (inOff != 0 || inLength != input.length) ? input.sublist(0, inLength) : input; final inp = block.toBigIntWithSign(1); if (inp > prime) { - throw ArgumentError('input too large for $algorithmName cipher.'); + throw ArgumentError('Input too large for $algorithmName cipher.'); } final byteLength = outputBlockSize ~/ 2; @@ -174,92 +167,3 @@ class ElGamalPrivateKey extends ElGamalAsymmetricKey implements PrivateKey { BigInt get y => publicKey.y; } - -class ElGamalKeyGeneratorParameters extends KeyGeneratorParameters { - final int size; - - final int certainty; - - ElGamalKeyGeneratorParameters(super.bitStrength, this.size, this.certainty); - - Map generateParameters(final SecureRandom random) { - BigInt prime, generator; - - final order = generateProbablePrime(size, 1, random); - final divisor = order * BigInt.two; - do { - final x = random.nextBigInteger(bitStrength); - final c = x % divisor; - prime = x - (c - BigInt.one); - } while ( - !prime.isProbablePrime(certainty) || prime.bitLength != bitStrength); - - final p2 = prime - BigInt.two; - do { - final h = Helper.randomBigInt(BigInt.two, p2, random: random); - generator = h.modPow(BigInt.two, prime); - } while (generator.compareTo(BigInt.one) == 0); - - return { - 'prime': prime, - 'generator': generator, - }; - } -} - -class ElGamalKeyGenerator implements KeyGenerator { - late SecureRandom _random; - - late ElGamalKeyGeneratorParameters _params; - - @override - String get algorithmName => 'ElGamal'; - - @override - AsymmetricKeyPair generateKeyPair() { - final params = _params.generateParameters(_random); - final prime = params['prime']!; - final generator = params['generator']!; - final privateKey = ElGamalPrivateKey( - _generateSecretExponent(_params.size, prime), - prime, - generator, - ); - - return AsymmetricKeyPair( - privateKey.publicKey, - privateKey, - ); - } - - @override - void init(final CipherParameters params) { - if (params is ParametersWithRandom) { - _random = params.random; - _params = params.parameters as ElGamalKeyGeneratorParameters; - } else { - _random = Helper.secureRandom(); - _params = params as ElGamalKeyGeneratorParameters; - } - } - - BigInt _generateSecretExponent(final int size, final BigInt prime) { - if (size != 0) { - final minWeight = size >> 2; - for (;;) { - BigInt x = _random.nextBigInteger(size - 1); - if (x.nafWeight > minWeight) { - return x; - } - } - } - final max = prime - BigInt.two; - final minWeight = max.bitLength >> 2; - for (;;) { - BigInt x = Helper.randomBigInt(BigInt.two, max, random: _random); - if (x.nafWeight > minWeight) { - return x; - } - } - } -} diff --git a/lib/src/cryptor/ecc/x448.dart b/lib/src/cryptor/ecc/x448.dart new file mode 100644 index 00000000..2545cfbc --- /dev/null +++ b/lib/src/cryptor/ecc/x448.dart @@ -0,0 +1,835 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +/// Implementation of rfc-7748 x448 +/// Author Nguyen Van Nguyen +final class X448 { + static const payloadSize = 56; + static const a24 = 39082; // (156326 + 2) % 4; + static const mask_8 = 0xff; + + /// Calculate a scalar point multiplication from base scalar + static Uint8List scalarMultBase( + final Uint8List k, [ + final int kOff = 0, + ]) { + final u = Uint8List(payloadSize); + u[0] = 5; + return X448.scalarMult(k, u, kOff); + } + + /// Calculate a generic scalar point multiplication + static Uint8List scalarMult( + final Uint8List k, + final Uint8List u, [ + final int kOff = 0, + final int uOff = 0, + ]) { + final n = _decodeScalar(k, kOff); + + final x1 = decode(u, uOff); + final x2 = X448Field.create(); + x2.setAll(0, x1); + final z2 = X448Field.create(); + z2[0] = 1; + final x3 = X448Field.create(); + x3[0] = 1; + final z3 = X448Field.create(); + + final t1 = X448Field.create(); + final t2 = X448Field.create(); + + var bit = 447, swap = 1; + do { + X448Field.add(x3, z3, t1); + X448Field.sub(x3, z3, x3); + + X448Field.add(x2, z2, z3); + X448Field.sub(x2, z2, x2); + + X448Field.mul(t1, x2, t1); + X448Field.mul(x3, z3, x3); + + X448Field.sqr(z3, z3); + X448Field.sqr(x2, x2); + + X448Field.sub(z3, x2, t2); + X448Field.mulA24(t2, a24, z2); + X448Field.add(z2, x2, z2); + X448Field.mul(z2, t2, z2); + X448Field.mul(x2, z3, x2); + + X448Field.sub(t1, x3, z3); + X448Field.add(t1, x3, x3); + X448Field.sqr(x3, x3); + X448Field.sqr(z3, z3); + X448Field.mul(z3, x1, z3); + + --bit; + + final word = bit >>> 5; + final shift = bit & 0x1F; + final kt = (n[word] >>> shift) & 1; + swap ^= kt; + X448Field.cswap(swap, x2, x3); + X448Field.cswap(swap, z2, z3); + swap = kt; + } while (bit >= 2); + + for (var i = 0; i < 2; ++i) { + _pointDouble(x2, z2); + } + + X448Field.inv(z2, z2); + X448Field.mul(x2, z2, x2); + X448Field.normalize(x2); + + return encode(x2); + } + + static Uint32List decode( + final Uint8List bytes, [ + final int off = 0, + ]) { + final z = X448Field.create(); + _decode56(bytes, z, off); + _decode56(bytes, z, off + 7, 2); + _decode56(bytes, z, off + 14, 4); + _decode56(bytes, z, off + 21, 6); + _decode56(bytes, z, off + 28, 8); + _decode56(bytes, z, off + 35, 10); + _decode56(bytes, z, off + 42, 12); + _decode56(bytes, z, off + 49, 14); + return z; + } + + static Uint8List encode( + final Uint32List x, [ + final int off = 0, + ]) { + final z = Uint8List(payloadSize); + _encode56(x, z, off); + _encode56(x, z, off + 2, 7); + _encode56(x, z, off + 4, 14); + _encode56(x, z, off + 6, 21); + _encode56(x, z, off + 8, 28); + _encode56(x, z, off + 10, 35); + _encode56(x, z, off + 12, 42); + _encode56(x, z, off + 14, 49); + return z; + } + + static void _pointDouble( + final Uint32List x, + final Uint32List z, + ) { + final a = X448Field.create(); + final b = X448Field.create(); + + X448Field.add(x, z, a); + X448Field.sub(x, z, b); + X448Field.sqr(a, a); + X448Field.sqr(b, b); + X448Field.mul(a, b, x); + X448Field.sub(a, b, a); + X448Field.mulA24(a, a24, z); + X448Field.add(z, b, z); + X448Field.mul(z, a, z); + } + + static Uint32List _decodeScalar( + final Uint8List bytes, + final int off, + ) { + final n = Uint32List(14); + for (var i = 0; i < 14; ++i) { + n[i] = _decode32(bytes, off + i * 4); + } + + n[0] &= 0xfffffffc; + n[13] |= 0x80000000; + return n; + } + + static int _decode24( + final Uint8List bytes, [ + final int off = 0, + ]) { + var n = bytes[off]; + n |= bytes[off + 1] << 8; + n |= bytes[off + 2] << 16; + return n; + } + + static int _decode32( + final Uint8List bytes, [ + final int off = 0, + ]) { + var n = bytes[off]; + n |= bytes[off + 1] << 8; + n |= bytes[off + 2] << 16; + n |= bytes[off + 3] << 24; + return n; + } + + static void _decode56( + final Uint8List bytes, + final Uint32List z, [ + final int off = 0, + final int zOff = 0, + ]) { + final lo = _decode32(bytes, off); + final hi = _decode24(bytes, off + 4); + z[zOff] = lo & X448Field.m28; + z[zOff + 1] = (lo >>> 28) | (hi << 4); + } + + static void _encode24( + final int n, + final Uint8List bytes, [ + final int off = 0, + ]) { + bytes[off] = n & mask_8; + bytes[off + 1] = (n >>> 8) & mask_8; + bytes[off + 2] = (n >>> 16) & mask_8; + } + + static void _encode32( + final int n, + final Uint8List bytes, [ + final int off = 0, + ]) { + bytes[off] = n & mask_8; + bytes[off + 1] = (n >>> 8) & mask_8; + bytes[off + 2] = (n >>> 16) & mask_8; + bytes[off + 3] = (n >>> 24) & mask_8; + } + + static void _encode56( + final Uint32List x, + final Uint8List bytes, [ + final int xOff = 0, + final int off = 0, + ]) { + final lo = x[xOff], hi = x[xOff + 1]; + _encode32(lo | (hi << 28), bytes, off); + _encode24(hi >>> 4, bytes, off + 4); + } +} + +final class X448Field { + /// Field element size + static const size = 16; + + /// Mask 28 bits + static const m28 = 0x0fffffff; + + /// Calculate z = x+y mod p. + static void add( + final Uint32List x, + final Uint32List y, + final Uint32List z, + ) { + for (var i = 0; i < size; ++i) { + z[i] = x[i] + y[i]; + } + } + + /// Create field element + static Uint32List create() => Uint32List(size); + + /// Interchange a and b if swap is 1. + static void cswap( + final int swap, + final Uint32List a, + final Uint32List b, + ) { + assert(swap >>> 1 == 0); + final mask = 0 - swap; + for (var i = 0; i < size; ++i) { + final ai = a[i], bi = b[i]; + final dummy = mask & (ai ^ bi); + a[i] = ai ^ dummy; + b[i] = bi ^ dummy; + } + } + + /// Calculate z = 1/x mod p. + static void inv( + final Uint32List x, + final Uint32List z, + ) { + final t = create(); + _powPm3d4(x, t); + nSqr(t, 2, t); + mul(t, x, z); + } + + static void mulA24( + final Uint32List x, + final int y, + final Uint32List z, + ) { + final x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3]; + final x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7]; + final x8 = x[8], x9 = x[9], x10 = x[10], x11 = x[11]; + final x12 = x[12], x13 = x[13], x14 = x[14], x15 = x[15]; + + var c = x1 * y; + final z1 = c & m28; + c >>>= 28; + var d = x5 * y; + final z5 = d & m28; + d >>>= 28; + var e = x9 * y; + final z9 = e & m28; + e >>>= 28; + var f = x13 * y; + final z13 = f & m28; + f >>>= 28; + + c += x2 * y; + z[2] = c & m28; + c >>>= 28; + d += x6 * y; + z[6] = d & m28; + d >>>= 28; + e += x10 * y; + z[10] = e & m28; + e >>>= 28; + f += x14 * y; + z[14] = f & m28; + f >>>= 28; + + c += x3 * y; + z[3] = c & m28; + c >>>= 28; + d += x7 * y; + z[7] = d & m28; + d >>>= 28; + e += x11 * y; + z[11] = e & m28; + e >>>= 28; + f += x15 * y; + z[15] = f & m28; + f >>>= 28; + + d += f; + + c += x4 * y; + z[4] = c & m28; + c >>>= 28; + d += x8 * y; + z[8] = d & m28; + d >>>= 28; + e += x12 * y; + z[12] = e & m28; + e >>>= 28; + f += x0 * y; + z[0] = f & m28; + f >>>= 28; + + z[1] = z1 + f; + z[5] = z5 + c; + z[9] = z9 + d; + z[13] = z13 + e; + } + + /// Calculate z = x*y mod p. + static void mul( + final Uint32List x, + final Uint32List y, + final Uint32List z, + ) { + final x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3]; + final x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7]; + + final u0 = x[8], u1 = x[9], u2 = x[10], u3 = x[11]; + final u4 = x[12], u5 = x[13], u6 = x[14], u7 = x[15]; + + final y0 = y[0], y1 = y[1], y2 = y[2], y3 = y[3]; + final y4 = y[4], y5 = y[5], y6 = y[6], y7 = y[7]; + + final v0 = y[8], v1 = y[9], v2 = y[10], v3 = y[11]; + final v4 = y[12], v5 = y[13], v6 = y[14], v7 = y[15]; + + final s0 = x0 + u0; + final s1 = x1 + u1; + final s2 = x2 + u2; + final s3 = x3 + u3; + final s4 = x4 + u4; + final s5 = x5 + u5; + final s6 = x6 + u6; + final s7 = x7 + u7; + + final t0 = y0 + v0; + final t1 = y1 + v1; + final t2 = y2 + v2; + final t3 = y3 + v3; + final t4 = y4 + v4; + final t5 = y5 + v5; + final t6 = y6 + v6; + final t7 = y7 + v7; + + final f0 = x0 * y0; + final f8 = x7 * y1 + x6 * y2 + x5 * y3 + x4 * y4 + x3 * y5 + x2 * y6 + x1 * y7; + final g0 = u0 * v0; + final g8 = u7 * v1 + u6 * v2 + u5 * v3 + u4 * v4 + u3 * v5 + u2 * v6 + u1 * v7; + final h0 = s0 * t0; + final h8 = s7 * t1 + s6 * t2 + s5 * t3 + s4 * t4 + s3 * t5 + s2 * t6 + s1 * t7; + + var c = f0 + g0 + h8 - f8; + var z0 = c & m28; + c >>>= 28; + var d = g8 + h0 - f0 + h8; + var z8 = d & m28; + d >>>= 28; + + final f1 = x1 * y0 + x0 * y1; + final f9 = x7 * y2 + x6 * y3 + x5 * y4 + x4 * y5 + x3 * y6 + x2 * y7; + final g1 = u1 * v0 + u0 * v1; + final g9 = u7 * v2 + u6 * v3 + u5 * v4 + u4 * v5 + u3 * v6 + u2 * v7; + final h1 = s1 * t0 + s0 * t1; + final h9 = s7 * t2 + s6 * t3 + s5 * t4 + s4 * t5 + s3 * t6 + s2 * t7; + + c += f1 + g1 + h9 - f9; + var z1 = c & m28; + c >>>= 28; + d += g9 + h1 - f1 + h9; + var z9 = d & m28; + d >>>= 28; + + final f2 = x2 * y0 + x1 * y1 + x0 * y2; + final f10 = x7 * y3 + x6 * y4 + x5 * y5 + x4 * y6 + x3 * y7; + final g2 = u2 * v0 + u1 * v1 + u0 * v2; + final g10 = u7 * v3 + u6 * v4 + u5 * v5 + u4 * v6 + u3 * v7; + final h2 = s2 * t0 + s1 * t1 + s0 * t2; + final h10 = s7 * t3 + s6 * t4 + s5 * t5 + s4 * t6 + s3 * t7; + + c += f2 + g2 + h10 - f10; + var z2 = c & m28; + c >>>= 28; + d += g10 + h2 - f2 + h10; + var z10 = d & m28; + d >>>= 28; + + final f3 = x3 * y0 + x2 * y1 + x1 * y2 + x0 * y3; + final f11 = x7 * y4 + x6 * y5 + x5 * y6 + x4 * y7; + final g3 = u3 * v0 + u2 * v1 + u1 * v2 + u0 * v3; + final g11 = u7 * v4 + u6 * v5 + u5 * v6 + u4 * v7; + final h3 = s3 * t0 + s2 * t1 + s1 * t2 + s0 * t3; + final h11 = s7 * t4 + s6 * t5 + s5 * t6 + s4 * t7; + + c += f3 + g3 + h11 - f11; + var z3 = c & m28; + c >>>= 28; + d += g11 + h3 - f3 + h11; + var z11 = d & m28; + d >>>= 28; + + final f4 = x4 * y0 + x3 * y1 + x2 * y2 + x1 * y3 + x0 * y4; + final f12 = x7 * y5 + x6 * y6 + x5 * y7; + final g4 = u4 * v0 + u3 * v1 + u2 * v2 + u1 * v3 + u0 * v4; + final g12 = u7 * v5 + u6 * v6 + u5 * v7; + final h4 = s4 * t0 + s3 * t1 + s2 * t2 + s1 * t3 + s0 * t4; + final h12 = s7 * t5 + s6 * t6 + s5 * t7; + + c += f4 + g4 + h12 - f12; + var z4 = c & m28; + c >>>= 28; + d += g12 + h4 - f4 + h12; + var z12 = d & m28; + d >>>= 28; + + final f5 = x5 * y0 + x4 * y1 + x3 * y2 + x2 * y3 + x1 * y4 + x0 * y5; + final f13 = x7 * y6 + x6 * y7; + final g5 = u5 * v0 + u4 * v1 + u3 * v2 + u2 * v3 + u1 * v4 + u0 * v5; + final g13 = u7 * v6 + u6 * v7; + final h5 = s5 * t0 + s4 * t1 + s3 * t2 + s2 * t3 + s1 * t4 + s0 * t5; + final h13 = s7 * t6 + s6 * t7; + + c += f5 + g5 + h13 - f13; + var z5 = c & m28; + c >>>= 28; + d += g13 + h5 - f5 + h13; + var z13 = d & m28; + d >>>= 28; + + final f6 = x6 * y0 + x5 * y1 + x4 * y2 + x3 * y3 + x2 * y4 + x1 * y5 + x0 * y6; + final f14 = x7 * y7; + final g6 = u6 * v0 + u5 * v1 + u4 * v2 + u3 * v3 + u2 * v4 + u1 * v5 + u0 * v6; + final g14 = u7 * v7; + final h6 = s6 * t0 + s5 * t1 + s4 * t2 + s3 * t3 + s2 * t4 + s1 * t5 + s0 * t6; + final h14 = s7 * t7; + + c += f6 + g6 + h14 - f14; + var z6 = c & m28; + c >>>= 28; + d += g14 + h6 - f6 + h14; + var z14 = d & m28; + d >>>= 28; + + final f7 = x7 * y0 + x6 * y1 + x5 * y2 + x4 * y3 + x3 * y4 + x2 * y5 + x1 * y6 + x0 * y7; + final g7 = u7 * v0 + u6 * v1 + u5 * v2 + u4 * v3 + u3 * v4 + u2 * v5 + u1 * v6 + u0 * v7; + final h7 = s7 * t0 + s6 * t1 + s5 * t2 + s4 * t3 + s3 * t4 + s2 * t5 + s1 * t6 + s0 * t7; + + c += f7 + g7; + var z7 = c & m28; + c >>>= 28; + d += h7 - f7; + var z15 = d & m28; + d >>>= 28; + + c += d; + + c += z8; + z8 = c & m28; + c >>>= 28; + d += z0; + z0 = d & m28; + d >>>= 28; + z9 += c; + z1 += d; + + z.setAll(0, [ + z0, z1, z2, z3, z4, z5, z6, z7, // 0 -7 + z8, z9, z10, z11, z12, z13, z14, z15, + ]); + } + + static void normalize(final Uint32List z) { + _reduce(z, 1); + _reduce(z, -1); + } + + /// Calculate z = x^2 mod p. + static void sqr( + final Uint32List x, + final Uint32List z, + ) { + final x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3]; + final x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7]; + + final u0 = x[8], u1 = x[9], u2 = x[10], u3 = x[11]; + final u4 = x[12], u5 = x[13], u6 = x[14], u7 = x[15]; + + final x0_2 = x0 * 2; + final x1_2 = x1 * 2; + final x2_2 = x2 * 2; + final x3_2 = x3 * 2; + final x4_2 = x4 * 2; + final x5_2 = x5 * 2; + final x6_2 = x6 * 2; + + final u0_2 = u0 * 2; + final u1_2 = u1 * 2; + final u2_2 = u2 * 2; + final u3_2 = u3 * 2; + final u4_2 = u4 * 2; + final u5_2 = u5 * 2; + final u6_2 = u6 * 2; + + final s0 = x0 + u0; + final s1 = x1 + u1; + final s2 = x2 + u2; + final s3 = x3 + u3; + final s4 = x4 + u4; + final s5 = x5 + u5; + final s6 = x6 + u6; + final s7 = x7 + u7; + + final s0_2 = s0 * 2; + final s1_2 = s1 * 2; + final s2_2 = s2 * 2; + final s3_2 = s3 * 2; + final s4_2 = s4 * 2; + final s5_2 = s5 * 2; + final s6_2 = s6 * 2; + + final f0 = x0 * x0; + final f8 = x7 * x1_2 + x6 * x2_2 + x5 * x3_2 + x4 * x4; + final g0 = u0 * u0; + final g8 = u7 * u1_2 + u6 * u2_2 + u5 * u3_2 + u4 * u4; + final h0 = s0 * s0; + final h8 = s7 * s1_2 + s6 * s2_2 + s5 * s3_2 + s4 * s4; + + var c = f0 + g0 + h8 - f8; + var z0 = c & m28; + c >>>= 28; + var d = g8 + h0 - f0 + h8; + var z8 = d & m28; + d >>>= 28; + + final f1 = x1 * x0_2; + final f9 = x7 * x2_2 + x6 * x3_2 + x5 * x4_2; + final g1 = u1 * u0_2; + final g9 = u7 * u2_2 + u6 * u3_2 + u5 * u4_2; + final h1 = s1 * s0_2; + final h9 = s7 * s2_2 + s6 * s3_2 + s5 * s4_2; + + c += f1 + g1 + h9 - f9; + var z1 = c & m28; + c >>>= 28; + d += g9 + h1 - f1 + h9; + var z9 = d & m28; + d >>>= 28; + + final f2 = x2 * x0_2 + x1 * x1; + final f10 = x7 * x3_2 + x6 * x4_2 + x5 * x5; + final g2 = u2 * u0_2 + u1 * u1; + final g10 = u7 * u3_2 + u6 * u4_2 + u5 * u5; + final h2 = s2 * s0_2 + s1 * s1; + final h10 = s7 * s3_2 + s6 * s4_2 + s5 * s5; + + c += f2 + g2 + h10 - f10; + var z2 = c & m28; + c >>>= 28; + d += g10 + h2 - f2 + h10; + var z10 = d & m28; + d >>>= 28; + + final f3 = x3 * x0_2 + x2 * x1_2; + final f11 = x7 * x4_2 + x6 * x5_2; + final g3 = u3 * u0_2 + u2 * u1_2; + final g11 = u7 * u4_2 + u6 * u5_2; + final h3 = s3 * s0_2 + s2 * s1_2; + final h11 = s7 * s4_2 + s6 * s5_2; + + c += f3 + g3 + h11 - f11; + var z3 = c & m28; + c >>>= 28; + d += g11 + h3 - f3 + h11; + var z11 = d & m28; + d >>>= 28; + + final f4 = x4 * x0_2 + x3 * x1_2 + x2 * x2; + final f12 = x7 * x5_2 + x6 * x6; + final g4 = u4 * u0_2 + u3 * u1_2 + u2 * u2; + final g12 = u7 * u5_2 + u6 * u6; + final h4 = s4 * s0_2 + s3 * s1_2 + s2 * s2; + final h12 = s7 * s5_2 + s6 * s6; + + c += f4 + g4 + h12 - f12; + var z4 = c & m28; + c >>>= 28; + d += g12 + h4 - f4 + h12; + var z12 = d & m28; + d >>>= 28; + + final f5 = x5 * x0_2 + x4 * x1_2 + x3 * x2_2; + final f13 = x7 * x6_2; + final g5 = u5 * u0_2 + u4 * u1_2 + u3 * u2_2; + final g13 = u7 * u6_2; + final h5 = s5 * s0_2 + s4 * s1_2 + s3 * s2_2; + final h13 = s7 * s6_2; + + c += f5 + g5 + h13 - f13; + var z5 = c & m28; + c >>>= 28; + d += g13 + h5 - f5 + h13; + var z13 = d & m28; + d >>>= 28; + + final f6 = x6 * x0_2 + x5 * x1_2 + x4 * x2_2 + x3 * x3; + final f14 = x7 * x7; + final g6 = u6 * u0_2 + u5 * u1_2 + u4 * u2_2 + u3 * u3; + final g14 = u7 * u7; + final h6 = s6 * s0_2 + s5 * s1_2 + s4 * s2_2 + s3 * s3; + final h14 = s7 * s7; + + c += f6 + g6 + h14 - f14; + var z6 = c & m28; + c >>>= 28; + d += g14 + h6 - f6 + h14; + var z14 = d & m28; + d >>>= 28; + + final f7 = x7 * x0_2 + x6 * x1_2 + x5 * x2_2 + x4 * x3_2; + final g7 = u7 * u0_2 + u6 * u1_2 + u5 * u2_2 + u4 * u3_2; + final h7 = s7 * s0_2 + s6 * s1_2 + s5 * s2_2 + s4 * s3_2; + + c += f7 + g7; + var z7 = c & m28; + c >>>= 28; + d += h7 - f7; + var z15 = d & m28; + d >>>= 28; + + c += d; + + c += z8; + z8 = c & m28; + c >>>= 28; + d += z0; + z0 = d & m28; + d >>>= 28; + z9 += c; + z1 += d; + + z.setAll(0, [ + z0, z1, z2, z3, z4, z5, z6, z7, // 0 -7 + z8, z9, z10, z11, z12, z13, z14, z15, + ]); + } + + static void nSqr( + final Uint32List x, + final int n, + final Uint32List z, + ) { + sqr(x, z); + var m = n; + + while (--m > 0) { + sqr(z, z); + } + } + + /// Calculate z = x-y mod p. + static void sub( + final Uint32List x, + final Uint32List y, + final Uint32List z, + ) { + final x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3]; + final x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7]; + final x8 = x[8], x9 = x[9], x10 = x[10], x11 = x[11]; + final x12 = x[12], x13 = x[13], x14 = x[14], x15 = x[15]; + final y0 = y[0], y1 = y[1], y2 = y[2], y3 = y[3]; + final y4 = y[4], y5 = y[5], y6 = y[6], y7 = y[7]; + final y8 = y[8], y9 = y[9], y10 = y[10], y11 = y[11]; + final y12 = y[12], y13 = y[13], y14 = y[14], y15 = y[15]; + + var z0 = x0 + 0x1ffffffe - y0; + var z1 = x1 + 0x1ffffffe - y1; + var z2 = x2 + 0x1ffffffe - y2; + var z3 = x3 + 0x1ffffffe - y3; + var z4 = x4 + 0x1ffffffe - y4; + var z5 = x5 + 0x1ffffffe - y5; + var z6 = x6 + 0x1ffffffe - y6; + var z7 = x7 + 0x1ffffffe - y7; + var z8 = x8 + 0x1ffffffc - y8; + var z9 = x9 + 0x1ffffffe - y9; + var z10 = x10 + 0x1ffffffe - y10; + var z11 = x11 + 0x1ffffffe - y11; + var z12 = x12 + 0x1ffffffe - y12; + var z13 = x13 + 0x1ffffffe - y13; + var z14 = x14 + 0x1ffffffe - y14; + var z15 = x15 + 0x1ffffffe - y15; + + z2 += z1 >>> 28; + z1 &= m28; + z6 += z5 >>> 28; + z5 &= m28; + z10 += z9 >>> 28; + z9 &= m28; + z14 += z13 >>> 28; + z13 &= m28; + + z3 += z2 >>> 28; + z2 &= m28; + z7 += z6 >>> 28; + z6 &= m28; + z11 += z10 >>> 28; + z10 &= m28; + z15 += z14 >>> 28; + z14 &= m28; + + final t = z15 >>> 28; + z15 &= m28; + z0 += t; + z8 += t; + + z4 += z3 >>> 28; + z3 &= m28; + z8 += z7 >>> 28; + z7 &= m28; + z12 += z11 >>> 28; + z11 &= m28; + + z1 += z0 >>> 28; + z0 &= m28; + z5 += z4 >>> 28; + z4 &= m28; + z9 += z8 >>> 28; + z8 &= m28; + z13 += z12 >>> 28; + z12 &= m28; + + z.setAll(0, [ + z0, z1, z2, z3, z4, z5, z6, z7, // 0 -7 + z8, z9, z10, z11, z12, z13, z14, z15, + ]); + } + + /// Calculate z = x^k mod p, where k = (p-3)/4. + static void _powPm3d4( + final Uint32List x, + final Uint32List z, + ) { + final x2 = create(); + sqr(x, x2); + mul(x, x2, x2); + final x3 = create(); + sqr(x2, x3); + mul(x, x3, x3); + final x6 = create(); + nSqr(x3, 3, x6); + mul(x3, x6, x6); + final x9 = create(); + nSqr(x6, 3, x9); + mul(x3, x9, x9); + final x18 = create(); + nSqr(x9, 9, x18); + mul(x9, x18, x18); + final x19 = create(); + sqr(x18, x19); + mul(x, x19, x19); + final x37 = create(); + nSqr(x19, 18, x37); + mul(x18, x37, x37); + final x74 = create(); + nSqr(x37, 37, x74); + mul(x37, x74, x74); + final x111 = create(); + nSqr(x74, 37, x111); + mul(x37, x111, x111); + final x222 = create(); + nSqr(x111, 111, x222); + mul(x111, x222, x222); + final x223 = create(); + sqr(x222, x223); + mul(x, x223, x223); + + final t = create(); + nSqr(x223, 223, t); + mul(t, x222, z); + } + + static void _reduce( + final Uint32List z, + final int x, + ) { + final u = z[15], z15 = u & m28; + final t = (u >>> 28) + x; + + var cc = t; + for (var i = 0; i < 8; ++i) { + cc += z[i]; + z[i] = cc & m28; + cc >>>= 28; + } + cc += t; + for (var i = 8; i < 15; ++i) { + cc += z[i]; + z[i] = cc & m28; + cc >>>= 28; + } + z[15] = z15 + cc; + } +} diff --git a/lib/src/cryptor/symmetric/base_engine.dart b/lib/src/cryptor/symmetric/base_engine.dart new file mode 100644 index 00000000..4d893f93 --- /dev/null +++ b/lib/src/cryptor/symmetric/base_engine.dart @@ -0,0 +1,22 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; +import 'package:pointycastle/api.dart'; + +/// Base cipher engine class +/// Author Nguyen Van Nguyen +abstract class BaseEngine implements BlockCipher { + @override + Uint8List process(final Uint8List input) { + final output = Uint8List(blockSize); + final len = processBlock(input, 0, output, 0); + return output.sublist(0, len); + } + + @override + void reset() {} +} diff --git a/lib/src/crypto/symmetric/blowfish.dart b/lib/src/cryptor/symmetric/blowfish.dart similarity index 95% rename from lib/src/crypto/symmetric/blowfish.dart rename to lib/src/cryptor/symmetric/blowfish.dart index c82bd851..bceabe1e 100644 --- a/lib/src/crypto/symmetric/blowfish.dart +++ b/lib/src/cryptor/symmetric/blowfish.dart @@ -1,21 +1,21 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; -import 'package:pointycastle/api.dart'; import 'dart:typed_data'; +import 'package:pointycastle/api.dart'; -import '../math/byte_ext.dart'; -import '../math/int_ext.dart'; -import 'base_cipher.dart'; +import '../../common/extensions.dart'; +import 'base_engine.dart'; /// A class that provides Blowfish key encryption operations, /// such as encoding data and generating keys. /// All the algorithms herein are from Applied Cryptography /// and implement a simplified cryptography interface. -/// Ported from Bouncy Castle project /// Author Nguyen Van Nguyen -class BlowfishEngine extends BaseCipher { +class BlowfishEngine extends BaseEngine { static const _kp = [ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, // 0 - 3 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, @@ -293,7 +293,7 @@ class BlowfishEngine extends BaseCipher { ]; static const _rounds = 16; - static const _blockSize = 16; + static const _blockSize = 8; static const _sBoxSK = 256; static const _pSZ = _rounds + 2; @@ -316,9 +316,6 @@ class BlowfishEngine extends BaseCipher { @override int get blockSize => _blockSize; - @override - void reset() {} - @override void init(final bool forEncryption, final CipherParameters? params) { if (params is KeyParameter) { @@ -359,9 +356,7 @@ class BlowfishEngine extends BaseCipher { } int _f(final int x) { - return ((_s0[(x >> 24) & 0xff] + _s1[(x >> 16) & 0xff]) ^ - _s2[(x >> 8) & 0xff]) + - _s3[x & 0xff]; + return ((_s0[(x >> 24) & 0xff] + _s1[(x >> 16) & 0xff]) ^ _s2[(x >> 8) & 0xff]) + _s3[x & 0xff]; } /// apply the encryption cycle to each value pair in the table. @@ -421,8 +416,8 @@ class BlowfishEngine extends BaseCipher { final Uint8List dst, final int dstIndex, ) { - var xl = src.sublist(srcIndex).toInt32(); - var xr = src.sublist(srcIndex + 4).toInt32(); + var xl = src.sublist(srcIndex).unpack32(); + var xr = src.sublist(srcIndex + 4).unpack32(); xl ^= _p[0]; @@ -442,8 +437,8 @@ class BlowfishEngine extends BaseCipher { final Uint8List dst, final int dstIndex, ) { - var xl = src.sublist(srcIndex).toInt32(); - var xr = src.sublist(srcIndex + 4).toInt32(); + var xl = src.sublist(srcIndex).unpack32(); + var xr = src.sublist(srcIndex + 4).unpack32(); xl ^= _p[_rounds + 1]; diff --git a/lib/src/crypto/symmetric/buffered_cipher.dart b/lib/src/cryptor/symmetric/buffered_cipher.dart similarity index 93% rename from lib/src/crypto/symmetric/buffered_cipher.dart rename to lib/src/cryptor/symmetric/buffered_cipher.dart index 97036d26..2af7a09e 100644 --- a/lib/src/crypto/symmetric/buffered_cipher.dart +++ b/lib/src/cryptor/symmetric/buffered_cipher.dart @@ -1,12 +1,14 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; import 'package:pointycastle/api.dart'; -/// Ported from Bouncy Castle project +/// Buffered cipher. /// Author Nguyen Van Nguyen class BufferedCipher { final BlockCipher _underlyingCipher; @@ -153,4 +155,4 @@ class BufferedCipher { _bufOff = 0; _underlyingCipher.reset(); } -} +} \ No newline at end of file diff --git a/lib/src/crypto/symmetric/camellia.dart b/lib/src/cryptor/symmetric/camellia.dart similarity index 90% rename from lib/src/crypto/symmetric/camellia.dart rename to lib/src/cryptor/symmetric/camellia.dart index 67ebb1d3..4f0ba0ca 100644 --- a/lib/src/crypto/symmetric/camellia.dart +++ b/lib/src/cryptor/symmetric/camellia.dart @@ -1,19 +1,18 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. -import 'dart:typed_data'; +library; +import 'dart:typed_data'; import 'package:pointycastle/api.dart'; -import '../math/byte_ext.dart'; -import '../math/int_ext.dart'; -import 'base_cipher.dart'; +import 'base_engine.dart'; +import '../../common/extensions.dart'; /// Camellia - based on RFC 3713. -/// Ported from Bouncy Castle project /// Author Nguyen Van Nguyen -class CamelliaEngine extends BaseCipher { +class CamelliaEngine extends BaseEngine { static const _blockSize = 16; static const _mask8 = 0xff; @@ -238,7 +237,7 @@ class CamelliaEngine extends BaseCipher { int get blockSize => _blockSize; @override - void init(bool forEncryption, CipherParameters? params) { + void init(final bool forEncryption, final CipherParameters? params) { if (params is! KeyParameter) { throw Exception('only simple KeyParameter expected.'); } @@ -250,10 +249,10 @@ class CamelliaEngine extends BaseCipher { @override int processBlock( - Uint8List input, - int inOff, - Uint8List output, - int outOff, + final Uint8List input, + final int inOff, + final Uint8List output, + final int outOff, ) { if (!initialised) { throw Exception('Camellia engine not initialised'); @@ -272,9 +271,6 @@ class CamelliaEngine extends BaseCipher { } } - @override - void reset() {} - static void _roldq( int rot, List ki, @@ -282,14 +278,10 @@ class CamelliaEngine extends BaseCipher { List ko, int outOff, ) { - ko[outOff] = - ki[inOff].shiftLeft32(rot) | ki[1 + inOff].shiftRight32(32 - rot); - ko[1 + outOff] = - ki[1 + inOff].shiftLeft32(rot) | ki[2 + inOff].shiftRight32(32 - rot); - ko[2 + outOff] = - ki[2 + inOff].shiftLeft32(rot) | ki[3 + inOff].shiftRight32(32 - rot); - ko[3 + outOff] = - ki[3 + inOff].shiftLeft32(rot) | ki[inOff].shiftRight32(32 - rot); + ko[outOff] = _shiftLeft32(ki[inOff], rot) | _shiftRight32(ki[1 + inOff], 32 - rot); + ko[1 + outOff] = _shiftLeft32(ki[1 + inOff], rot) | _shiftRight32(ki[2 + inOff], 32 - rot); + ko[2 + outOff] = _shiftLeft32(ki[2 + inOff], rot) | _shiftRight32(ki[3 + inOff], 32 - rot); + ko[3 + outOff] = _shiftLeft32(ki[3 + inOff], rot) | _shiftRight32(ki[inOff], 32 - rot); ki[inOff] = ko[outOff]; ki[1 + inOff] = ko[1 + outOff]; ki[2 + inOff] = ko[2 + outOff]; @@ -303,14 +295,10 @@ class CamelliaEngine extends BaseCipher { List ko, int outOff, ) { - ko[2 + outOff] = - ki[inOff].shiftLeft32(rot) | ki[1 + inOff].shiftRight32(32 - rot); - ko[3 + outOff] = - ki[1 + inOff].shiftLeft32(rot) | ki[2 + inOff].shiftRight32(32 - rot); - ko[outOff] = - ki[2 + inOff].shiftLeft32(rot) | ki[3 + inOff].shiftRight32(32 - rot); - ko[1 + outOff] = - ki[3 + inOff].shiftLeft32(rot) | ki[inOff].shiftRight32(32 - rot); + ko[2 + outOff] = _shiftLeft32(ki[inOff], rot) | _shiftRight32(ki[1 + inOff], 32 - rot); + ko[3 + outOff] = _shiftLeft32(ki[1 + inOff], rot) | _shiftRight32(ki[2 + inOff], 32 - rot); + ko[outOff] = _shiftLeft32(ki[2 + inOff], rot) | _shiftRight32(ki[3 + inOff], 32 - rot); + ko[1 + outOff] = _shiftLeft32(ki[3 + inOff], rot) | _shiftRight32(ki[inOff], 32 - rot); ki[inOff] = ko[2 + outOff]; ki[1 + inOff] = ko[3 + outOff]; ki[2 + inOff] = ko[outOff]; @@ -324,14 +312,10 @@ class CamelliaEngine extends BaseCipher { List ko, int outOff, ) { - ko[outOff] = ki[1 + inOff].shiftLeft32(rot - 32) | - ki[2 + inOff].shiftRight32(64 - rot); - ko[1 + outOff] = ki[2 + inOff].shiftLeft32(rot - 32) | - ki[3 + inOff].shiftRight32(64 - rot); - ko[2 + outOff] = - ki[3 + inOff].shiftLeft32(rot - 32) | ki[inOff].shiftRight32(64 - rot); - ko[3 + outOff] = - ki[inOff].shiftLeft32(rot - 32) | ki[1 + inOff].shiftRight32(64 - rot); + ko[outOff] = _shiftLeft32(ki[1 + inOff], rot - 32) | _shiftRight32(ki[2 + inOff], 64 - rot); + ko[1 + outOff] = _shiftLeft32(ki[2 + inOff], rot - 32) | _shiftRight32(ki[3 + inOff], 64 - rot); + ko[2 + outOff] = _shiftLeft32(ki[3 + inOff], rot - 32) | _shiftRight32(ki[inOff], 64 - rot); + ko[3 + outOff] = _shiftLeft32(ki[inOff], rot - 32) | _shiftRight32(ki[1 + inOff], 64 - rot); ki[inOff] = ko[outOff]; ki[1 + inOff] = ko[1 + outOff]; ki[2 + inOff] = ko[2 + outOff]; @@ -345,14 +329,10 @@ class CamelliaEngine extends BaseCipher { List ko, int outOff, ) { - ko[2 + outOff] = ki[1 + inOff].shiftLeft32(rot - 32) | - ki[2 + inOff].shiftRight32(64 - rot); - ko[3 + outOff] = ki[2 + inOff].shiftLeft32(rot - 32) | - ki[3 + inOff].shiftRight32(64 - rot); - ko[outOff] = - ki[3 + inOff].shiftLeft32(rot - 32) | ki[inOff].shiftRight32(64 - rot); - ko[1 + outOff] = - ki[inOff].shiftLeft32(rot - 32) | ki[1 + inOff].shiftRight32(64 - rot); + ko[2 + outOff] = _shiftLeft32(ki[1 + inOff], rot - 32) | _shiftRight32(ki[2 + inOff], 64 - rot); + ko[3 + outOff] = _shiftLeft32(ki[2 + inOff], rot - 32) | _shiftRight32(ki[3 + inOff], 64 - rot); + ko[outOff] = _shiftLeft32(ki[3 + inOff], rot - 32) | _shiftRight32(ki[inOff], 64 - rot); + ko[1 + outOff] = _shiftLeft32(ki[inOff], rot - 32) | _shiftRight32(ki[1 + inOff], 64 - rot); ki[inOff] = ko[2 + outOff]; ki[1 + inOff] = ko[3 + outOff]; ki[2 + inOff] = ko[outOff]; @@ -360,7 +340,7 @@ class CamelliaEngine extends BaseCipher { } static int _bytes2uint(Uint8List src, int offset) { - return src.sublist(offset, offset + 4).toUint32(); + return src.sublist(offset, offset + 4).unpack32(); } static void _uint2bytes(int word, Uint8List dst, int offset) { @@ -381,7 +361,7 @@ class CamelliaEngine extends BaseCipher { v ^= _sbox2_0222[(t2 >> 24) & _mask8]; s[2] ^= u ^ v; - s[3] ^= u ^ v ^ u.rotateRight32(8); + s[3] ^= u ^ v ^ _rotateRight32(u, 8); t1 = s[2] ^ skey[2 + keyoff]; u = _sbox4_4404[t1 & _mask8]; @@ -395,15 +375,15 @@ class CamelliaEngine extends BaseCipher { v ^= _sbox2_0222[(t2 >> 24) & _mask8]; s[0] ^= u ^ v; - s[1] ^= u ^ v ^ u.rotateRight32(8); + s[1] ^= u ^ v ^ _rotateRight32(u, 8); } static void _camelliaFLs(List s, List fkey, int keyoff) { - s[1] ^= (s[0] & fkey[keyoff]).rotateLeft32(1); + s[1] ^= _rotateLeft32(s[0] & fkey[keyoff], 1); s[0] ^= fkey[1 + keyoff] | s[1]; s[2] ^= fkey[3 + keyoff] | s[3]; - s[3] ^= (fkey[2 + keyoff] & s[2]).rotateLeft32(1); + s[3] ^= _rotateLeft32(fkey[2 + keyoff] & s[2], 1); } void _setKey(bool forEncryption, Uint8List key) { @@ -666,4 +646,12 @@ class CamelliaEngine extends BaseCipher { _uint2bytes(_state[1], output, outOff + 12); return _blockSize; } + + static int _shiftLeft32(int x, int n) => x.shiftLeft32(n); + + static int _shiftRight32(int x, int n) => x.shiftRight32(n); + + static int _rotateLeft32(int x, int n) => x.rotateLeft32(n); + + static int _rotateRight32(int x, int n) => x.rotateRight32(n); } diff --git a/lib/src/crypto/symmetric/cast5.dart b/lib/src/cryptor/symmetric/cast5.dart similarity index 77% rename from lib/src/crypto/symmetric/cast5.dart rename to lib/src/cryptor/symmetric/cast5.dart index 5e99c031..a1a7d56d 100644 --- a/lib/src/crypto/symmetric/cast5.dart +++ b/lib/src/cryptor/symmetric/cast5.dart @@ -1,13 +1,14 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. -import 'package:pointycastle/api.dart'; +library; import 'dart:typed_data'; +import 'package:pointycastle/api.dart'; -import '../math/int_ext.dart'; -import 'base_cipher.dart'; +import 'base_engine.dart'; +import '../../common/extensions.dart'; /// A class that provides CAST key encryption operations, such as encoding data and generating keys. /// All the algorithms herein are from the Internet RFC's @@ -16,9 +17,8 @@ import 'base_cipher.dart'; /// RFC2612 - CAST6 (128bit block, 128-256bit key) /// /// and implement a simplified cryptography interface. -/// Ported from Bouncy Castle project /// Author Nguyen Van Nguyen -class CAST5Engine extends BaseCipher { +class CAST5Engine extends BaseEngine { static const _sBox1 = [ 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949, // 0 - 7 @@ -574,6 +574,7 @@ class CAST5Engine extends BaseCipher { @override void reset() { + super.reset(); _rotating.setAll(0, List.filled(17, 0)); _masking.setAll(0, List.filled(17, 0)); } @@ -630,441 +631,137 @@ class CAST5Engine extends BaseCipher { var x47 = _intsTo32bits(x, 0x4); var x8B = _intsTo32bits(x, 0x8); var xCF = _intsTo32bits(x, 0xc); - var z03 = x03 ^ - _sBox5[x[0xd]] ^ - _sBox6[x[0xf]] ^ - _sBox7[x[0xc]] ^ - _sBox8[x[0xe]] ^ - _sBox7[x[0x8]]; + var z03 = x03 ^ _sBox5[x[0xd]] ^ _sBox6[x[0xf]] ^ _sBox7[x[0xc]] ^ _sBox8[x[0xe]] ^ _sBox7[x[0x8]]; _bits32ToInts(z03, z, 0x0); - var z47 = x8B ^ - _sBox5[z[0x0]] ^ - _sBox6[z[0x2]] ^ - _sBox7[z[0x1]] ^ - _sBox8[z[0x3]] ^ - _sBox8[x[0xa]]; + var z47 = x8B ^ _sBox5[z[0x0]] ^ _sBox6[z[0x2]] ^ _sBox7[z[0x1]] ^ _sBox8[z[0x3]] ^ _sBox8[x[0xa]]; _bits32ToInts(z47, z, 0x4); - var z8B = xCF ^ - _sBox5[z[0x7]] ^ - _sBox6[z[0x6]] ^ - _sBox7[z[0x5]] ^ - _sBox8[z[0x4]] ^ - _sBox5[x[0x9]]; + var z8B = xCF ^ _sBox5[z[0x7]] ^ _sBox6[z[0x6]] ^ _sBox7[z[0x5]] ^ _sBox8[z[0x4]] ^ _sBox5[x[0x9]]; _bits32ToInts(z8B, z, 0x8); - var zCF = x47 ^ - _sBox5[z[0xa]] ^ - _sBox6[z[0x9]] ^ - _sBox7[z[0xb]] ^ - _sBox8[z[0x8]] ^ - _sBox6[x[0xb]]; + var zCF = x47 ^ _sBox5[z[0xa]] ^ _sBox6[z[0x9]] ^ _sBox7[z[0xb]] ^ _sBox8[z[0x8]] ^ _sBox6[x[0xb]]; _bits32ToInts(zCF, z, 0xc); - _masking[1] = _sBox5[z[0x8]] ^ - _sBox6[z[0x9]] ^ - _sBox7[z[0x7]] ^ - _sBox8[z[0x6]] ^ - _sBox5[z[0x2]]; - _masking[2] = _sBox5[z[0xa]] ^ - _sBox6[z[0xb]] ^ - _sBox7[z[0x5]] ^ - _sBox8[z[0x4]] ^ - _sBox6[z[0x6]]; - _masking[3] = _sBox5[z[0xc]] ^ - _sBox6[z[0xd]] ^ - _sBox7[z[0x3]] ^ - _sBox8[z[0x2]] ^ - _sBox7[z[0x9]]; - _masking[4] = _sBox5[z[0xe]] ^ - _sBox6[z[0xf]] ^ - _sBox7[z[0x1]] ^ - _sBox8[z[0x0]] ^ - _sBox8[z[0xc]]; + _masking[1] = _sBox5[z[0x8]] ^ _sBox6[z[0x9]] ^ _sBox7[z[0x7]] ^ _sBox8[z[0x6]] ^ _sBox5[z[0x2]]; + _masking[2] = _sBox5[z[0xa]] ^ _sBox6[z[0xb]] ^ _sBox7[z[0x5]] ^ _sBox8[z[0x4]] ^ _sBox6[z[0x6]]; + _masking[3] = _sBox5[z[0xc]] ^ _sBox6[z[0xd]] ^ _sBox7[z[0x3]] ^ _sBox8[z[0x2]] ^ _sBox7[z[0x9]]; + _masking[4] = _sBox5[z[0xe]] ^ _sBox6[z[0xf]] ^ _sBox7[z[0x1]] ^ _sBox8[z[0x0]] ^ _sBox8[z[0xc]]; z03 = _intsTo32bits(z, 0x0); z47 = _intsTo32bits(z, 0x4); z8B = _intsTo32bits(z, 0x8); zCF = _intsTo32bits(z, 0xc); - x03 = z8B ^ - _sBox5[z[0x5]] ^ - _sBox6[z[0x7]] ^ - _sBox7[z[0x4]] ^ - _sBox8[z[0x6]] ^ - _sBox7[z[0x0]]; + x03 = z8B ^ _sBox5[z[0x5]] ^ _sBox6[z[0x7]] ^ _sBox7[z[0x4]] ^ _sBox8[z[0x6]] ^ _sBox7[z[0x0]]; _bits32ToInts(x03, x, 0x0); - x47 = z03 ^ - _sBox5[x[0x0]] ^ - _sBox6[x[0x2]] ^ - _sBox7[x[0x1]] ^ - _sBox8[x[0x3]] ^ - _sBox8[z[0x2]]; + x47 = z03 ^ _sBox5[x[0x0]] ^ _sBox6[x[0x2]] ^ _sBox7[x[0x1]] ^ _sBox8[x[0x3]] ^ _sBox8[z[0x2]]; _bits32ToInts(x47, x, 0x4); - x8B = z47 ^ - _sBox5[x[0x7]] ^ - _sBox6[x[0x6]] ^ - _sBox7[x[0x5]] ^ - _sBox8[x[0x4]] ^ - _sBox5[z[0x1]]; + x8B = z47 ^ _sBox5[x[0x7]] ^ _sBox6[x[0x6]] ^ _sBox7[x[0x5]] ^ _sBox8[x[0x4]] ^ _sBox5[z[0x1]]; _bits32ToInts(x8B, x, 0x8); - xCF = zCF ^ - _sBox5[x[0xa]] ^ - _sBox6[x[0x9]] ^ - _sBox7[x[0xb]] ^ - _sBox8[x[0x8]] ^ - _sBox6[z[0x3]]; + xCF = zCF ^ _sBox5[x[0xa]] ^ _sBox6[x[0x9]] ^ _sBox7[x[0xb]] ^ _sBox8[x[0x8]] ^ _sBox6[z[0x3]]; _bits32ToInts(xCF, x, 0xc); - _masking[5] = _sBox5[x[0x3]] ^ - _sBox6[x[0x2]] ^ - _sBox7[x[0xc]] ^ - _sBox8[x[0xd]] ^ - _sBox5[x[0x8]]; - _masking[6] = _sBox5[x[0x1]] ^ - _sBox6[x[0x0]] ^ - _sBox7[x[0xe]] ^ - _sBox8[x[0xf]] ^ - _sBox6[x[0xd]]; - _masking[7] = _sBox5[x[0x7]] ^ - _sBox6[x[0x6]] ^ - _sBox7[x[0x8]] ^ - _sBox8[x[0x9]] ^ - _sBox7[x[0x3]]; - _masking[8] = _sBox5[x[0x5]] ^ - _sBox6[x[0x4]] ^ - _sBox7[x[0xa]] ^ - _sBox8[x[0xb]] ^ - _sBox8[x[0x7]]; + _masking[5] = _sBox5[x[0x3]] ^ _sBox6[x[0x2]] ^ _sBox7[x[0xc]] ^ _sBox8[x[0xd]] ^ _sBox5[x[0x8]]; + _masking[6] = _sBox5[x[0x1]] ^ _sBox6[x[0x0]] ^ _sBox7[x[0xe]] ^ _sBox8[x[0xf]] ^ _sBox6[x[0xd]]; + _masking[7] = _sBox5[x[0x7]] ^ _sBox6[x[0x6]] ^ _sBox7[x[0x8]] ^ _sBox8[x[0x9]] ^ _sBox7[x[0x3]]; + _masking[8] = _sBox5[x[0x5]] ^ _sBox6[x[0x4]] ^ _sBox7[x[0xa]] ^ _sBox8[x[0xb]] ^ _sBox8[x[0x7]]; x03 = _intsTo32bits(x, 0x0); x47 = _intsTo32bits(x, 0x4); x8B = _intsTo32bits(x, 0x8); xCF = _intsTo32bits(x, 0xc); - z03 = x03 ^ - _sBox5[x[0xd]] ^ - _sBox6[x[0xf]] ^ - _sBox7[x[0xc]] ^ - _sBox8[x[0xe]] ^ - _sBox7[x[0x8]]; + z03 = x03 ^ _sBox5[x[0xd]] ^ _sBox6[x[0xf]] ^ _sBox7[x[0xc]] ^ _sBox8[x[0xe]] ^ _sBox7[x[0x8]]; _bits32ToInts(z03, z, 0x0); - z47 = x8B ^ - _sBox5[z[0x0]] ^ - _sBox6[z[0x2]] ^ - _sBox7[z[0x1]] ^ - _sBox8[z[0x3]] ^ - _sBox8[x[0xa]]; + z47 = x8B ^ _sBox5[z[0x0]] ^ _sBox6[z[0x2]] ^ _sBox7[z[0x1]] ^ _sBox8[z[0x3]] ^ _sBox8[x[0xa]]; _bits32ToInts(z47, z, 0x4); - z8B = xCF ^ - _sBox5[z[0x7]] ^ - _sBox6[z[0x6]] ^ - _sBox7[z[0x5]] ^ - _sBox8[z[0x4]] ^ - _sBox5[x[0x9]]; + z8B = xCF ^ _sBox5[z[0x7]] ^ _sBox6[z[0x6]] ^ _sBox7[z[0x5]] ^ _sBox8[z[0x4]] ^ _sBox5[x[0x9]]; _bits32ToInts(z8B, z, 0x8); - zCF = x47 ^ - _sBox5[z[0xa]] ^ - _sBox6[z[0x9]] ^ - _sBox7[z[0xb]] ^ - _sBox8[z[0x8]] ^ - _sBox6[x[0xb]]; + zCF = x47 ^ _sBox5[z[0xa]] ^ _sBox6[z[0x9]] ^ _sBox7[z[0xb]] ^ _sBox8[z[0x8]] ^ _sBox6[x[0xb]]; _bits32ToInts(zCF, z, 0xc); - _masking[9] = _sBox5[z[0x3]] ^ - _sBox6[z[0x2]] ^ - _sBox7[z[0xc]] ^ - _sBox8[z[0xd]] ^ - _sBox5[z[0x9]]; - _masking[10] = _sBox5[z[0x1]] ^ - _sBox6[z[0x0]] ^ - _sBox7[z[0xe]] ^ - _sBox8[z[0xf]] ^ - _sBox6[z[0xc]]; - _masking[11] = _sBox5[z[0x7]] ^ - _sBox6[z[0x6]] ^ - _sBox7[z[0x8]] ^ - _sBox8[z[0x9]] ^ - _sBox7[z[0x2]]; - _masking[12] = _sBox5[z[0x5]] ^ - _sBox6[z[0x4]] ^ - _sBox7[z[0xa]] ^ - _sBox8[z[0xb]] ^ - _sBox8[z[0x6]]; + _masking[9] = _sBox5[z[0x3]] ^ _sBox6[z[0x2]] ^ _sBox7[z[0xc]] ^ _sBox8[z[0xd]] ^ _sBox5[z[0x9]]; + _masking[10] = _sBox5[z[0x1]] ^ _sBox6[z[0x0]] ^ _sBox7[z[0xe]] ^ _sBox8[z[0xf]] ^ _sBox6[z[0xc]]; + _masking[11] = _sBox5[z[0x7]] ^ _sBox6[z[0x6]] ^ _sBox7[z[0x8]] ^ _sBox8[z[0x9]] ^ _sBox7[z[0x2]]; + _masking[12] = _sBox5[z[0x5]] ^ _sBox6[z[0x4]] ^ _sBox7[z[0xa]] ^ _sBox8[z[0xb]] ^ _sBox8[z[0x6]]; z03 = _intsTo32bits(z, 0x0); z47 = _intsTo32bits(z, 0x4); z8B = _intsTo32bits(z, 0x8); zCF = _intsTo32bits(z, 0xc); - x03 = z8B ^ - _sBox5[z[0x5]] ^ - _sBox6[z[0x7]] ^ - _sBox7[z[0x4]] ^ - _sBox8[z[0x6]] ^ - _sBox7[z[0x0]]; + x03 = z8B ^ _sBox5[z[0x5]] ^ _sBox6[z[0x7]] ^ _sBox7[z[0x4]] ^ _sBox8[z[0x6]] ^ _sBox7[z[0x0]]; _bits32ToInts(x03, x, 0x0); - x47 = z03 ^ - _sBox5[x[0x0]] ^ - _sBox6[x[0x2]] ^ - _sBox7[x[0x1]] ^ - _sBox8[x[0x3]] ^ - _sBox8[z[0x2]]; + x47 = z03 ^ _sBox5[x[0x0]] ^ _sBox6[x[0x2]] ^ _sBox7[x[0x1]] ^ _sBox8[x[0x3]] ^ _sBox8[z[0x2]]; _bits32ToInts(x47, x, 0x4); - x8B = z47 ^ - _sBox5[x[0x7]] ^ - _sBox6[x[0x6]] ^ - _sBox7[x[0x5]] ^ - _sBox8[x[0x4]] ^ - _sBox5[z[0x1]]; + x8B = z47 ^ _sBox5[x[0x7]] ^ _sBox6[x[0x6]] ^ _sBox7[x[0x5]] ^ _sBox8[x[0x4]] ^ _sBox5[z[0x1]]; _bits32ToInts(x8B, x, 0x8); - xCF = zCF ^ - _sBox5[x[0xa]] ^ - _sBox6[x[0x9]] ^ - _sBox7[x[0xb]] ^ - _sBox8[x[0x8]] ^ - _sBox6[z[0x3]]; + xCF = zCF ^ _sBox5[x[0xa]] ^ _sBox6[x[0x9]] ^ _sBox7[x[0xb]] ^ _sBox8[x[0x8]] ^ _sBox6[z[0x3]]; _bits32ToInts(xCF, x, 0xc); - _masking[13] = _sBox5[x[0x8]] ^ - _sBox6[x[0x9]] ^ - _sBox7[x[0x7]] ^ - _sBox8[x[0x6]] ^ - _sBox5[x[0x3]]; - _masking[14] = _sBox5[x[0xa]] ^ - _sBox6[x[0xb]] ^ - _sBox7[x[0x5]] ^ - _sBox8[x[0x4]] ^ - _sBox6[x[0x7]]; - _masking[15] = _sBox5[x[0xc]] ^ - _sBox6[x[0xd]] ^ - _sBox7[x[0x3]] ^ - _sBox8[x[0x2]] ^ - _sBox7[x[0x8]]; - _masking[16] = _sBox5[x[0xe]] ^ - _sBox6[x[0xf]] ^ - _sBox7[x[0x1]] ^ - _sBox8[x[0x0]] ^ - _sBox8[x[0xd]]; + _masking[13] = _sBox5[x[0x8]] ^ _sBox6[x[0x9]] ^ _sBox7[x[0x7]] ^ _sBox8[x[0x6]] ^ _sBox5[x[0x3]]; + _masking[14] = _sBox5[x[0xa]] ^ _sBox6[x[0xb]] ^ _sBox7[x[0x5]] ^ _sBox8[x[0x4]] ^ _sBox6[x[0x7]]; + _masking[15] = _sBox5[x[0xc]] ^ _sBox6[x[0xd]] ^ _sBox7[x[0x3]] ^ _sBox8[x[0x2]] ^ _sBox7[x[0x8]]; + _masking[16] = _sBox5[x[0xe]] ^ _sBox6[x[0xf]] ^ _sBox7[x[0x1]] ^ _sBox8[x[0x0]] ^ _sBox8[x[0xd]]; x03 = _intsTo32bits(x, 0x0); x47 = _intsTo32bits(x, 0x4); x8B = _intsTo32bits(x, 0x8); xCF = _intsTo32bits(x, 0xc); - z03 = x03 ^ - _sBox5[x[0xd]] ^ - _sBox6[x[0xf]] ^ - _sBox7[x[0xc]] ^ - _sBox8[x[0xe]] ^ - _sBox7[x[0x8]]; + z03 = x03 ^ _sBox5[x[0xd]] ^ _sBox6[x[0xf]] ^ _sBox7[x[0xc]] ^ _sBox8[x[0xe]] ^ _sBox7[x[0x8]]; _bits32ToInts(z03, z, 0x0); - z47 = x8B ^ - _sBox5[z[0x0]] ^ - _sBox6[z[0x2]] ^ - _sBox7[z[0x1]] ^ - _sBox8[z[0x3]] ^ - _sBox8[x[0xa]]; + z47 = x8B ^ _sBox5[z[0x0]] ^ _sBox6[z[0x2]] ^ _sBox7[z[0x1]] ^ _sBox8[z[0x3]] ^ _sBox8[x[0xa]]; _bits32ToInts(z47, z, 0x4); - z8B = xCF ^ - _sBox5[z[0x7]] ^ - _sBox6[z[0x6]] ^ - _sBox7[z[0x5]] ^ - _sBox8[z[0x4]] ^ - _sBox5[x[0x9]]; + z8B = xCF ^ _sBox5[z[0x7]] ^ _sBox6[z[0x6]] ^ _sBox7[z[0x5]] ^ _sBox8[z[0x4]] ^ _sBox5[x[0x9]]; _bits32ToInts(z8B, z, 0x8); - zCF = x47 ^ - _sBox5[z[0xa]] ^ - _sBox6[z[0x9]] ^ - _sBox7[z[0xb]] ^ - _sBox8[z[0x8]] ^ - _sBox6[x[0xb]]; + zCF = x47 ^ _sBox5[z[0xa]] ^ _sBox6[z[0x9]] ^ _sBox7[z[0xb]] ^ _sBox8[z[0x8]] ^ _sBox6[x[0xb]]; _bits32ToInts(zCF, z, 0xc); - _rotating[1] = (_sBox5[z[0x8]] ^ - _sBox6[z[0x9]] ^ - _sBox7[z[0x7]] ^ - _sBox8[z[0x6]] ^ - _sBox5[z[0x2]]) & - 0x1f; - _rotating[2] = (_sBox5[z[0xa]] ^ - _sBox6[z[0xb]] ^ - _sBox7[z[0x5]] ^ - _sBox8[z[0x4]] ^ - _sBox6[z[0x6]]) & - 0x1f; - _rotating[3] = (_sBox5[z[0xc]] ^ - _sBox6[z[0xd]] ^ - _sBox7[z[0x3]] ^ - _sBox8[z[0x2]] ^ - _sBox7[z[0x9]]) & - 0x1f; - _rotating[4] = (_sBox5[z[0xe]] ^ - _sBox6[z[0xf]] ^ - _sBox7[z[0x1]] ^ - _sBox8[z[0x0]] ^ - _sBox8[z[0xc]]) & - 0x1f; + _rotating[1] = (_sBox5[z[0x8]] ^ _sBox6[z[0x9]] ^ _sBox7[z[0x7]] ^ _sBox8[z[0x6]] ^ _sBox5[z[0x2]]) & 0x1f; + _rotating[2] = (_sBox5[z[0xa]] ^ _sBox6[z[0xb]] ^ _sBox7[z[0x5]] ^ _sBox8[z[0x4]] ^ _sBox6[z[0x6]]) & 0x1f; + _rotating[3] = (_sBox5[z[0xc]] ^ _sBox6[z[0xd]] ^ _sBox7[z[0x3]] ^ _sBox8[z[0x2]] ^ _sBox7[z[0x9]]) & 0x1f; + _rotating[4] = (_sBox5[z[0xe]] ^ _sBox6[z[0xf]] ^ _sBox7[z[0x1]] ^ _sBox8[z[0x0]] ^ _sBox8[z[0xc]]) & 0x1f; z03 = _intsTo32bits(z, 0x0); z47 = _intsTo32bits(z, 0x4); z8B = _intsTo32bits(z, 0x8); zCF = _intsTo32bits(z, 0xc); - x03 = z8B ^ - _sBox5[z[0x5]] ^ - _sBox6[z[0x7]] ^ - _sBox7[z[0x4]] ^ - _sBox8[z[0x6]] ^ - _sBox7[z[0x0]]; + x03 = z8B ^ _sBox5[z[0x5]] ^ _sBox6[z[0x7]] ^ _sBox7[z[0x4]] ^ _sBox8[z[0x6]] ^ _sBox7[z[0x0]]; _bits32ToInts(x03, x, 0x0); - x47 = z03 ^ - _sBox5[x[0x0]] ^ - _sBox6[x[0x2]] ^ - _sBox7[x[0x1]] ^ - _sBox8[x[0x3]] ^ - _sBox8[z[0x2]]; + x47 = z03 ^ _sBox5[x[0x0]] ^ _sBox6[x[0x2]] ^ _sBox7[x[0x1]] ^ _sBox8[x[0x3]] ^ _sBox8[z[0x2]]; _bits32ToInts(x47, x, 0x4); - x8B = z47 ^ - _sBox5[x[0x7]] ^ - _sBox6[x[0x6]] ^ - _sBox7[x[0x5]] ^ - _sBox8[x[0x4]] ^ - _sBox5[z[0x1]]; + x8B = z47 ^ _sBox5[x[0x7]] ^ _sBox6[x[0x6]] ^ _sBox7[x[0x5]] ^ _sBox8[x[0x4]] ^ _sBox5[z[0x1]]; _bits32ToInts(x8B, x, 0x8); - xCF = zCF ^ - _sBox5[x[0xa]] ^ - _sBox6[x[0x9]] ^ - _sBox7[x[0xb]] ^ - _sBox8[x[0x8]] ^ - _sBox6[z[0x3]]; + xCF = zCF ^ _sBox5[x[0xa]] ^ _sBox6[x[0x9]] ^ _sBox7[x[0xb]] ^ _sBox8[x[0x8]] ^ _sBox6[z[0x3]]; _bits32ToInts(xCF, x, 0xc); - _rotating[5] = (_sBox5[x[0x3]] ^ - _sBox6[x[0x2]] ^ - _sBox7[x[0xc]] ^ - _sBox8[x[0xd]] ^ - _sBox5[x[0x8]]) & - 0x1f; - _rotating[6] = (_sBox5[x[0x1]] ^ - _sBox6[x[0x0]] ^ - _sBox7[x[0xe]] ^ - _sBox8[x[0xf]] ^ - _sBox6[x[0xd]]) & - 0x1f; - _rotating[7] = (_sBox5[x[0x7]] ^ - _sBox6[x[0x6]] ^ - _sBox7[x[0x8]] ^ - _sBox8[x[0x9]] ^ - _sBox7[x[0x3]]) & - 0x1f; - _rotating[8] = (_sBox5[x[0x5]] ^ - _sBox6[x[0x4]] ^ - _sBox7[x[0xa]] ^ - _sBox8[x[0xb]] ^ - _sBox8[x[0x7]]) & - 0x1f; + _rotating[5] = (_sBox5[x[0x3]] ^ _sBox6[x[0x2]] ^ _sBox7[x[0xc]] ^ _sBox8[x[0xd]] ^ _sBox5[x[0x8]]) & 0x1f; + _rotating[6] = (_sBox5[x[0x1]] ^ _sBox6[x[0x0]] ^ _sBox7[x[0xe]] ^ _sBox8[x[0xf]] ^ _sBox6[x[0xd]]) & 0x1f; + _rotating[7] = (_sBox5[x[0x7]] ^ _sBox6[x[0x6]] ^ _sBox7[x[0x8]] ^ _sBox8[x[0x9]] ^ _sBox7[x[0x3]]) & 0x1f; + _rotating[8] = (_sBox5[x[0x5]] ^ _sBox6[x[0x4]] ^ _sBox7[x[0xa]] ^ _sBox8[x[0xb]] ^ _sBox8[x[0x7]]) & 0x1f; x03 = _intsTo32bits(x, 0x0); x47 = _intsTo32bits(x, 0x4); x8B = _intsTo32bits(x, 0x8); xCF = _intsTo32bits(x, 0xc); - z03 = x03 ^ - _sBox5[x[0xd]] ^ - _sBox6[x[0xf]] ^ - _sBox7[x[0xc]] ^ - _sBox8[x[0xe]] ^ - _sBox7[x[0x8]]; + z03 = x03 ^ _sBox5[x[0xd]] ^ _sBox6[x[0xf]] ^ _sBox7[x[0xc]] ^ _sBox8[x[0xe]] ^ _sBox7[x[0x8]]; _bits32ToInts(z03, z, 0x0); - z47 = x8B ^ - _sBox5[z[0x0]] ^ - _sBox6[z[0x2]] ^ - _sBox7[z[0x1]] ^ - _sBox8[z[0x3]] ^ - _sBox8[x[0xa]]; + z47 = x8B ^ _sBox5[z[0x0]] ^ _sBox6[z[0x2]] ^ _sBox7[z[0x1]] ^ _sBox8[z[0x3]] ^ _sBox8[x[0xa]]; _bits32ToInts(z47, z, 0x4); - z8B = xCF ^ - _sBox5[z[0x7]] ^ - _sBox6[z[0x6]] ^ - _sBox7[z[0x5]] ^ - _sBox8[z[0x4]] ^ - _sBox5[x[0x9]]; + z8B = xCF ^ _sBox5[z[0x7]] ^ _sBox6[z[0x6]] ^ _sBox7[z[0x5]] ^ _sBox8[z[0x4]] ^ _sBox5[x[0x9]]; _bits32ToInts(z8B, z, 0x8); - zCF = x47 ^ - _sBox5[z[0xa]] ^ - _sBox6[z[0x9]] ^ - _sBox7[z[0xb]] ^ - _sBox8[z[0x8]] ^ - _sBox6[x[0xb]]; + zCF = x47 ^ _sBox5[z[0xa]] ^ _sBox6[z[0x9]] ^ _sBox7[z[0xb]] ^ _sBox8[z[0x8]] ^ _sBox6[x[0xb]]; _bits32ToInts(zCF, z, 0xc); - _rotating[9] = (_sBox5[z[0x3]] ^ - _sBox6[z[0x2]] ^ - _sBox7[z[0xc]] ^ - _sBox8[z[0xd]] ^ - _sBox5[z[0x9]]) & - 0x1f; - _rotating[10] = (_sBox5[z[0x1]] ^ - _sBox6[z[0x0]] ^ - _sBox7[z[0xe]] ^ - _sBox8[z[0xf]] ^ - _sBox6[z[0xc]]) & - 0x1f; - _rotating[11] = (_sBox5[z[0x7]] ^ - _sBox6[z[0x6]] ^ - _sBox7[z[0x8]] ^ - _sBox8[z[0x9]] ^ - _sBox7[z[0x2]]) & - 0x1f; - _rotating[12] = (_sBox5[z[0x5]] ^ - _sBox6[z[0x4]] ^ - _sBox7[z[0xa]] ^ - _sBox8[z[0xb]] ^ - _sBox8[z[0x6]]) & - 0x1f; + _rotating[9] = (_sBox5[z[0x3]] ^ _sBox6[z[0x2]] ^ _sBox7[z[0xc]] ^ _sBox8[z[0xd]] ^ _sBox5[z[0x9]]) & 0x1f; + _rotating[10] = (_sBox5[z[0x1]] ^ _sBox6[z[0x0]] ^ _sBox7[z[0xe]] ^ _sBox8[z[0xf]] ^ _sBox6[z[0xc]]) & 0x1f; + _rotating[11] = (_sBox5[z[0x7]] ^ _sBox6[z[0x6]] ^ _sBox7[z[0x8]] ^ _sBox8[z[0x9]] ^ _sBox7[z[0x2]]) & 0x1f; + _rotating[12] = (_sBox5[z[0x5]] ^ _sBox6[z[0x4]] ^ _sBox7[z[0xa]] ^ _sBox8[z[0xb]] ^ _sBox8[z[0x6]]) & 0x1f; z03 = _intsTo32bits(z, 0x0); z47 = _intsTo32bits(z, 0x4); z8B = _intsTo32bits(z, 0x8); zCF = _intsTo32bits(z, 0xc); - x03 = z8B ^ - _sBox5[z[0x5]] ^ - _sBox6[z[0x7]] ^ - _sBox7[z[0x4]] ^ - _sBox8[z[0x6]] ^ - _sBox7[z[0x0]]; + x03 = z8B ^ _sBox5[z[0x5]] ^ _sBox6[z[0x7]] ^ _sBox7[z[0x4]] ^ _sBox8[z[0x6]] ^ _sBox7[z[0x0]]; _bits32ToInts(x03, x, 0x0); - x47 = z03 ^ - _sBox5[x[0x0]] ^ - _sBox6[x[0x2]] ^ - _sBox7[x[0x1]] ^ - _sBox8[x[0x3]] ^ - _sBox8[z[0x2]]; + x47 = z03 ^ _sBox5[x[0x0]] ^ _sBox6[x[0x2]] ^ _sBox7[x[0x1]] ^ _sBox8[x[0x3]] ^ _sBox8[z[0x2]]; _bits32ToInts(x47, x, 0x4); - x8B = z47 ^ - _sBox5[x[0x7]] ^ - _sBox6[x[0x6]] ^ - _sBox7[x[0x5]] ^ - _sBox8[x[0x4]] ^ - _sBox5[z[0x1]]; + x8B = z47 ^ _sBox5[x[0x7]] ^ _sBox6[x[0x6]] ^ _sBox7[x[0x5]] ^ _sBox8[x[0x4]] ^ _sBox5[z[0x1]]; _bits32ToInts(x8B, x, 0x8); - xCF = zCF ^ - _sBox5[x[0xa]] ^ - _sBox6[x[0x9]] ^ - _sBox7[x[0xb]] ^ - _sBox8[x[0x8]] ^ - _sBox6[z[0x3]]; + xCF = zCF ^ _sBox5[x[0xa]] ^ _sBox6[x[0x9]] ^ _sBox7[x[0xb]] ^ _sBox8[x[0x8]] ^ _sBox6[z[0x3]]; _bits32ToInts(xCF, x, 0xc); - _rotating[13] = (_sBox5[x[0x8]] ^ - _sBox6[x[0x9]] ^ - _sBox7[x[0x7]] ^ - _sBox8[x[0x6]] ^ - _sBox5[x[0x3]]) & - 0x1f; - _rotating[14] = (_sBox5[x[0xa]] ^ - _sBox6[x[0xb]] ^ - _sBox7[x[0x5]] ^ - _sBox8[x[0x4]] ^ - _sBox6[x[0x7]]) & - 0x1f; - _rotating[15] = (_sBox5[x[0xc]] ^ - _sBox6[x[0xd]] ^ - _sBox7[x[0x3]] ^ - _sBox8[x[0x2]] ^ - _sBox7[x[0x8]]) & - 0x1f; - _rotating[16] = (_sBox5[x[0xe]] ^ - _sBox6[x[0xf]] ^ - _sBox7[x[0x1]] ^ - _sBox8[x[0x0]] ^ - _sBox8[x[0xd]]) & - 0x1f; + _rotating[13] = (_sBox5[x[0x8]] ^ _sBox6[x[0x9]] ^ _sBox7[x[0x7]] ^ _sBox8[x[0x6]] ^ _sBox5[x[0x3]]) & 0x1f; + _rotating[14] = (_sBox5[x[0xa]] ^ _sBox6[x[0xb]] ^ _sBox7[x[0x5]] ^ _sBox8[x[0x4]] ^ _sBox6[x[0x7]]) & 0x1f; + _rotating[15] = (_sBox5[x[0xc]] ^ _sBox6[x[0xd]] ^ _sBox7[x[0x3]] ^ _sBox8[x[0x2]] ^ _sBox7[x[0x8]]) & 0x1f; + _rotating[16] = (_sBox5[x[0xe]] ^ _sBox6[x[0xf]] ^ _sBox7[x[0x1]] ^ _sBox8[x[0x0]] ^ _sBox8[x[0xd]]) & 0x1f; } int _encryptBlock( @@ -1110,26 +807,20 @@ class CAST5Engine extends BaseCipher { int _f1(final int d, final int kmi, final int kri) { var i = kmi + d; - i = i.rotateLeft32(kri); - return ((_sBox1[(i >> 24) & 0xff] ^ _sBox2[(i >> 16) & 0xff]) - - _sBox3[(i >> 8) & 0xff]) + - _sBox4[i & 0xff]; + i = _rotateLeft32(i, kri); + return ((_sBox1[(i >> 24) & 0xff] ^ _sBox2[(i >> 16) & 0xff]) - _sBox3[(i >> 8) & 0xff]) + _sBox4[i & 0xff]; } int _f2(final int d, final int kmi, final int kri) { var i = kmi ^ d; - i = i.rotateLeft32(kri); - return ((_sBox1[(i >> 24) & 0xff] - _sBox2[(i >> 16) & 0xff]) + - _sBox3[(i >> 8) & 0xff]) ^ - _sBox4[i & 0xff]; + i = _rotateLeft32(i, kri); + return ((_sBox1[(i >> 24) & 0xff] - _sBox2[(i >> 16) & 0xff]) + _sBox3[(i >> 8) & 0xff]) ^ _sBox4[i & 0xff]; } int _f3(final int d, final int kmi, final int kri) { var i = kmi - d; - i = i.rotateLeft32(kri); - return ((_sBox1[(i >> 24) & 0xff] + _sBox2[(i >> 16) & 0xff]) ^ - _sBox3[(i >> 8) & 0xff]) - - _sBox4[i & 0xff]; + i = _rotateLeft32(i, kri); + return ((_sBox1[(i >> 24) & 0xff] + _sBox2[(i >> 16) & 0xff]) ^ _sBox3[(i >> 8) & 0xff]) - _sBox4[i & 0xff]; } void _encipher(final int l0, final int r0, final List result) { @@ -1247,4 +938,6 @@ class CAST5Engine extends BaseCipher { ((bytes[i + 3] & 0xff))) .toUnsigned(32); } + + static int _rotateLeft32(int x, int n) => x.rotateLeft32(n); } diff --git a/lib/src/crypto/symmetric/idea.dart b/lib/src/cryptor/symmetric/idea.dart similarity index 74% rename from lib/src/crypto/symmetric/idea.dart rename to lib/src/cryptor/symmetric/idea.dart index df99acbd..2b06ad7b 100644 --- a/lib/src/crypto/symmetric/idea.dart +++ b/lib/src/cryptor/symmetric/idea.dart @@ -1,19 +1,18 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. -import 'package:pointycastle/api.dart'; +library; import 'dart:typed_data'; +import 'package:pointycastle/api.dart'; -import '../math/byte_ext.dart'; -import '../math/int_ext.dart'; -import 'base_cipher.dart'; +import 'base_engine.dart'; +import '../../common/extensions.dart'; /// A class that provides a basic International Data Encryption Algorithm (IDEA) engine. -/// Ported from Bouncy Castle project /// Author Nguyen Van Nguyen -class IDEAEngine extends BaseCipher { +class IDEAEngine extends BaseEngine { static const _mask = 0xffff; static const _base = 0x10001; @@ -28,7 +27,7 @@ class IDEAEngine extends BaseCipher { int get blockSize => _blockSize; @override - void init(bool forEncryption, CipherParameters? params) { + void init(final bool forEncryption, final CipherParameters? params) { if (params is KeyParameter) { _workingKey = _generateWorkingKey(forEncryption, params.key); } else { @@ -40,10 +39,10 @@ class IDEAEngine extends BaseCipher { @override int processBlock( - Uint8List input, - int inOff, - Uint8List output, - int outOff, + final Uint8List input, + final int inOff, + final Uint8List output, + final int outOff, ) { if (_workingKey.isEmpty) { throw StateError('$algorithmName not initialised'); @@ -60,10 +59,7 @@ class IDEAEngine extends BaseCipher { return _blockSize; } - @override - void reset() {} - - List _generateWorkingKey(bool forEncryption, Uint8List key) { + List _generateWorkingKey(final bool forEncryption, final Uint8List key) { if (forEncryption) { return _expandKey(key); } else { @@ -71,7 +67,7 @@ class IDEAEngine extends BaseCipher { } } - List _expandKey(Uint8List uKey) { + List _expandKey(final Uint8List uKey) { final Uint8List tmpKey; if (uKey.length < 16) { tmpKey = Uint8List(16); @@ -82,7 +78,7 @@ class IDEAEngine extends BaseCipher { final key = List.filled(52, 0); for (var i = 0; i < 8; i++) { - key[i] = tmpKey.sublist(i * 2).toUint16(); + key[i] = _unpack16(tmpKey.sublist(i * 2)); } for (var i = 8; i < 52; i++) { if ((i & 7) < 6) { @@ -96,7 +92,7 @@ class IDEAEngine extends BaseCipher { return key; } - List _invertKey(List inKey) { + List _invertKey(final List inKey) { var p = 52; var inOff = 0; final key = List.filled(p, 0); @@ -144,16 +140,16 @@ class IDEAEngine extends BaseCipher { } void _ideaFunc( - List workingKey, - Uint8List input, - int inOff, - Uint8List output, - int outOff, + final List workingKey, + final Uint8List input, + final int inOff, + final Uint8List output, + final int outOff, ) { - var x0 = input.sublist(inOff).toUint16(); - var x1 = input.sublist(inOff + 2).toUint16(); - var x2 = input.sublist(inOff + 4).toUint16(); - var x3 = input.sublist(inOff + 6).toUint16(); + var x0 = _unpack16(input.sublist(inOff)); + var x1 = _unpack16(input.sublist(inOff + 2)); + var x2 = _unpack16(input.sublist(inOff + 4)); + var x3 = _unpack16(input.sublist(inOff + 6)); var keyOff = 0; for (var round = 0; round < 8; round++) { @@ -183,10 +179,10 @@ class IDEAEngine extends BaseCipher { x2 ^= t0; } - output.setAll(outOff, _mul(x0, workingKey[keyOff++]).pack16()); - output.setAll(outOff + 2, (x2 + workingKey[keyOff++]).pack16()); - output.setAll(outOff + 4, (x1 + workingKey[keyOff++]).pack16()); - output.setAll(outOff + 6, _mul(x3, workingKey[keyOff]).pack16()); + output.setAll(outOff, _pack16(_mul(x0, workingKey[keyOff++]))); + output.setAll(outOff + 2, _pack16(x2 + workingKey[keyOff++])); + output.setAll(outOff + 4, _pack16(x1 + workingKey[keyOff++])); + output.setAll(outOff + 6, _pack16(_mul(x3, workingKey[keyOff]))); } int _mulInv(int x) { @@ -233,4 +229,7 @@ class IDEAEngine extends BaseCipher { return x & _mask; } + + static int _unpack16(final Uint8List x) => x.unpack16(); + static Uint8List _pack16(final int x) => x.pack16(); } diff --git a/lib/src/crypto/symmetric/twofish.dart b/lib/src/cryptor/symmetric/twofish.dart similarity index 84% rename from lib/src/crypto/symmetric/twofish.dart rename to lib/src/cryptor/symmetric/twofish.dart index 10625708..da5abdc0 100644 --- a/lib/src/crypto/symmetric/twofish.dart +++ b/lib/src/cryptor/symmetric/twofish.dart @@ -1,19 +1,18 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. -import 'package:pointycastle/api.dart'; +library; import 'dart:typed_data'; +import 'package:pointycastle/api.dart'; -import '../math/byte_ext.dart'; -import '../math/int_ext.dart'; -import 'base_cipher.dart'; +import 'base_engine.dart'; +import '../../common/extensions.dart'; /// A class that provides Twofish encryption operations. -/// Ported from Bouncy Castle project /// Author Nguyen Van Nguyen -class TwofishEngine extends BaseCipher { +class TwofishEngine extends BaseEngine { /// Q-Table 0 static const _q0 = [ 0xa9, 0x67, 0xb3, 0xe8, 0x04, 0xfd, 0xa3, 0x76, // 0 - 7 @@ -386,7 +385,7 @@ class TwofishEngine extends BaseCipher { int get blockSize => _blockSize; @override - void init(bool forEncryption, CipherParameters? params) { + void init(final bool forEncryption, final CipherParameters? params) { if (params is KeyParameter) { _forEncryption = forEncryption; _workingKey = params.key; @@ -430,6 +429,7 @@ class TwofishEngine extends BaseCipher { @override void reset() { + super.reset(); if (_workingKey.isNotEmpty) { _setupKey(_workingKey); } @@ -438,10 +438,14 @@ class TwofishEngine extends BaseCipher { void _setupKey(final Uint8List key) { switch (key.length) { case 16: - final list1 = - _mdsrem(key.sublist(0).toLeUint32(), key.sublist(4).toLeUint32()); - final list2 = - _mdsrem(key.sublist(8).toLeUint32(), key.sublist(12).toLeUint32()); + final list1 = _mdsrem( + _unpack32Le(key.sublist(0)), + _unpack32Le(key.sublist(4)), + ); + final list2 = _mdsrem( + _unpack32Le(key.sublist(8)), + _unpack32Le(key.sublist(12)), + ); for (var i = 0, j = 1; i < 40; i += 2, j += 2) { var a = _m0[_q0[_q0[i] ^ key[8]] ^ key[0]] ^ @@ -467,12 +471,18 @@ class TwofishEngine extends BaseCipher { } break; case 24: - final list1 = - _mdsrem(key.sublist(0).toLeUint32(), key.sublist(4).toLeUint32()); - final list2 = - _mdsrem(key.sublist(8).toLeUint32(), key.sublist(12).toLeUint32()); - final list3 = - _mdsrem(key.sublist(16).toLeUint32(), key.sublist(20).toLeUint32()); + final list1 = _mdsrem( + _unpack32Le(key.sublist(0)), + _unpack32Le(key.sublist(4)), + ); + final list2 = _mdsrem( + _unpack32Le(key.sublist(8)), + _unpack32Le(key.sublist(12)), + ); + final list3 = _mdsrem( + _unpack32Le(key.sublist(16)), + _unpack32Le(key.sublist(20)), + ); for (var i = 0, j = 1; i < 40; i += 2, j += 2) { var a = _m0[_q0[_q0[_q1[i] ^ key[16]] ^ key[8]] ^ key[0]] ^ @@ -499,28 +509,31 @@ class TwofishEngine extends BaseCipher { } break; case 32: - final list1 = - _mdsrem(key.sublist(0).toLeUint32(), key.sublist(4).toLeUint32()); - final list2 = - _mdsrem(key.sublist(8).toLeUint32(), key.sublist(12).toLeUint32()); - final list3 = - _mdsrem(key.sublist(16).toLeUint32(), key.sublist(20).toLeUint32()); - final list4 = - _mdsrem(key.sublist(24).toLeUint32(), key.sublist(28).toLeUint32()); + final list1 = _mdsrem( + _unpack32Le(key.sublist(0)), + _unpack32Le(key.sublist(4)), + ); + final list2 = _mdsrem( + _unpack32Le(key.sublist(8)), + _unpack32Le(key.sublist(12)), + ); + final list3 = _mdsrem( + _unpack32Le(key.sublist(16)), + _unpack32Le(key.sublist(20)), + ); + final list4 = _mdsrem( + _unpack32Le(key.sublist(24)), + _unpack32Le(key.sublist(28)), + ); for (var i = 0, j = 1; i < 40; i += 2, j += 2) { - var a = _m0[ - _q0[_q0[_q1[_q1[i] ^ key[24]] ^ key[16]] ^ key[8]] ^ key[0]] ^ + var a = _m0[_q0[_q0[_q1[_q1[i] ^ key[24]] ^ key[16]] ^ key[8]] ^ key[0]] ^ _m1[_q0[_q1[_q1[_q0[i] ^ key[25]] ^ key[17]] ^ key[9]] ^ key[1]] ^ - _m2[_q1[_q0[_q0[_q0[i] ^ key[26]] ^ key[18]] ^ key[10]] ^ - key[2]] ^ + _m2[_q1[_q0[_q0[_q0[i] ^ key[26]] ^ key[18]] ^ key[10]] ^ key[2]] ^ _m3[_q1[_q1[_q0[_q1[i] ^ key[27]] ^ key[19]] ^ key[11]] ^ key[3]]; - var b = _m0[_q0[_q0[_q1[_q1[j] ^ key[28]] ^ key[20]] ^ key[12]] ^ - key[4]] ^ - _m1[_q0[_q1[_q1[_q0[j] ^ key[29]] ^ key[21]] ^ key[13]] ^ - key[5]] ^ - _m2[_q1[_q0[_q0[_q0[j] ^ key[30]] ^ key[22]] ^ key[14]] ^ - key[6]] ^ + var b = _m0[_q0[_q0[_q1[_q1[j] ^ key[28]] ^ key[20]] ^ key[12]] ^ key[4]] ^ + _m1[_q0[_q1[_q1[_q0[j] ^ key[29]] ^ key[21]] ^ key[13]] ^ key[5]] ^ + _m2[_q1[_q0[_q0[_q0[j] ^ key[30]] ^ key[22]] ^ key[14]] ^ key[6]] ^ _m3[_q1[_q1[_q0[_q1[j] ^ key[31]] ^ key[23]] ^ key[15]] ^ key[7]]; b = (b << 8) | (b >> 24 & 0xff); a = a + b; @@ -530,18 +543,10 @@ class TwofishEngine extends BaseCipher { } for (var i = 0; i < 256; ++i) { - _sTable0[i] = _m0[ - _q0[_q0[_q1[_q1[i] ^ list1[3]] ^ list2[3]] ^ list3[3]] ^ - list4[3]]; - _sTable1[i] = _m1[ - _q0[_q1[_q1[_q0[i] ^ list1[2]] ^ list2[2]] ^ list3[2]] ^ - list4[2]]; - _sTable2[i] = _m2[ - _q1[_q0[_q0[_q0[i] ^ list1[1]] ^ list2[1]] ^ list3[1]] ^ - list4[1]]; - _sTable3[i] = _m3[ - _q1[_q1[_q0[_q1[i] ^ list1[0]] ^ list2[0]] ^ list3[0]] ^ - list4[0]]; + _sTable0[i] = _m0[_q0[_q0[_q1[_q1[i] ^ list1[3]] ^ list2[3]] ^ list3[3]] ^ list4[3]]; + _sTable1[i] = _m1[_q0[_q1[_q1[_q0[i] ^ list1[2]] ^ list2[2]] ^ list3[2]] ^ list4[2]]; + _sTable2[i] = _m2[_q1[_q0[_q0[_q0[i] ^ list1[1]] ^ list2[1]] ^ list3[1]] ^ list4[1]]; + _sTable3[i] = _m3[_q1[_q1[_q0[_q1[i] ^ list1[0]] ^ list2[0]] ^ list3[0]] ^ list4[0]]; } break; default: @@ -557,44 +562,34 @@ class TwofishEngine extends BaseCipher { final Uint8List output, final int outOff, ) { - var r0 = input.sublist(inOff).toLeInt32() ^ _subKey[0]; - var r1 = input.sublist(inOff + 4).toLeInt32() ^ _subKey[1]; - var r2 = input.sublist(inOff + 8).toLeInt32() ^ _subKey[2]; - var r3 = input.sublist(inOff + 12).toLeInt32() ^ _subKey[3]; + var r0 = _unpack32Le(input.sublist(inOff)) ^ _subKey[0]; + var r1 = _unpack32Le(input.sublist(inOff + 4)) ^ _subKey[1]; + var r2 = _unpack32Le(input.sublist(inOff + 8)) ^ _subKey[2]; + var r3 = _unpack32Le(input.sublist(inOff + 12)) ^ _subKey[3]; var ki = 7; while (ki < 39) { - var t0 = _sTable0[r0 & 0xff] ^ - _sTable1[(r0 >> 8) & 0xff] ^ - _sTable2[(r0 >> 16) & 0xff] ^ - _sTable3[(r0 >> 24) & 0xff]; - var t1 = _sTable0[(r1 >> 24) & 0xff] ^ - _sTable1[r1 & 0xff] ^ - _sTable2[(r1 >> 8) & 0xff] ^ - _sTable3[(r1 >> 16) & 0xff]; + var t0 = + _sTable0[r0 & 0xff] ^ _sTable1[(r0 >> 8) & 0xff] ^ _sTable2[(r0 >> 16) & 0xff] ^ _sTable3[(r0 >> 24) & 0xff]; + var t1 = + _sTable0[(r1 >> 24) & 0xff] ^ _sTable1[r1 & 0xff] ^ _sTable2[(r1 >> 8) & 0xff] ^ _sTable3[(r1 >> 16) & 0xff]; r2 ^= t0 + t1 + _subKey[++ki]; r2 = (r2 >> 1 & 0x7fffffff) | (r2 << 31); r3 = (((r3 >> 31) & 1) | (r3 << 1)) ^ (t0 + (t1 << 1) + _subKey[++ki]); - t0 = _sTable0[r2 & 0xff] ^ - _sTable1[(r2 >> 8) & 0xff] ^ - _sTable2[(r2 >> 16) & 0xff] ^ - _sTable3[(r2 >> 24) & 0xff]; - t1 = _sTable0[(r3 >> 24) & 0xff] ^ - _sTable1[r3 & 0xff] ^ - _sTable2[(r3 >> 8) & 0xff] ^ - _sTable3[(r3 >> 16) & 0xff]; + t0 = _sTable0[r2 & 0xff] ^ _sTable1[(r2 >> 8) & 0xff] ^ _sTable2[(r2 >> 16) & 0xff] ^ _sTable3[(r2 >> 24) & 0xff]; + t1 = _sTable0[(r3 >> 24) & 0xff] ^ _sTable1[r3 & 0xff] ^ _sTable2[(r3 >> 8) & 0xff] ^ _sTable3[(r3 >> 16) & 0xff]; r0 ^= t0 + t1 + _subKey[++ki]; r0 = (r0 >> 1 & 0x7fffffff) | (r0 << 31); r1 = (((r1 >> 31) & 1) | (r1 << 1)) ^ (t0 + (t1 << 1) + _subKey[++ki]); } - output.setAll(outOff, (r2 ^ _subKey[4]).pack32Le()); - output.setAll(outOff + 4, (r3 ^ _subKey[5]).pack32Le()); - output.setAll(outOff + 8, (r0 ^ _subKey[6]).pack32Le()); - output.setAll(outOff + 12, (r1 ^ _subKey[7]).pack32Le()); + output.setAll(outOff, _pack32Le(r2 ^ _subKey[4])); + output.setAll(outOff + 4, _pack32Le(r3 ^ _subKey[5])); + output.setAll(outOff + 8, _pack32Le(r0 ^ _subKey[6])); + output.setAll(outOff + 12, _pack32Le(r1 ^ _subKey[7])); } /// Decrypt the given input starting at the given offset and place @@ -606,44 +601,32 @@ class TwofishEngine extends BaseCipher { final Uint8List output, final int outOff, ) { - var r0 = _subKey[4] ^ input.sublist(inOff).toLeInt32(); - var r1 = _subKey[5] ^ input.sublist(inOff + 4).toLeInt32(); - var r2 = _subKey[6] ^ input.sublist(inOff + 8).toLeInt32(); - var r3 = _subKey[7] ^ input.sublist(inOff + 12).toLeInt32(); + var r0 = _subKey[4] ^ _unpack32Le(input.sublist(inOff)); + var r1 = _subKey[5] ^ _unpack32Le(input.sublist(inOff + 4)); + var r2 = _subKey[6] ^ _unpack32Le(input.sublist(inOff + 8)); + var r3 = _subKey[7] ^ _unpack32Le(input.sublist(inOff + 12)); var ki = 40; while (ki > 8) { - var t0 = _sTable0[r0 & 0xff] ^ - _sTable1[r0 >> 8 & 0xff] ^ - _sTable2[r0 >> 16 & 0xff] ^ - _sTable3[r0 >> 24 & 0xff]; - var t1 = _sTable0[r1 >> 24 & 0xff] ^ - _sTable1[r1 & 0xff] ^ - _sTable2[r1 >> 8 & 0xff] ^ - _sTable3[r1 >> 16 & 0xff]; + var t0 = _sTable0[r0 & 0xff] ^ _sTable1[r0 >> 8 & 0xff] ^ _sTable2[r0 >> 16 & 0xff] ^ _sTable3[r0 >> 24 & 0xff]; + var t1 = _sTable0[r1 >> 24 & 0xff] ^ _sTable1[r1 & 0xff] ^ _sTable2[r1 >> 8 & 0xff] ^ _sTable3[r1 >> 16 & 0xff]; r3 ^= t0 + (t1 << 1) + _subKey[--ki]; r3 = r3 >> 1 & 0x7fffffff | r3 << 31; r2 = (r2 >> 31 & 0x1 | r2 << 1) ^ (t0 + t1 + _subKey[--ki]); - t0 = _sTable0[r2 & 0xff] ^ - _sTable1[r2 >> 8 & 0xff] ^ - _sTable2[r2 >> 16 & 0xff] ^ - _sTable3[r2 >> 24 & 0xff]; - t1 = _sTable0[r3 >> 24 & 0xff] ^ - _sTable1[r3 & 0xff] ^ - _sTable2[r3 >> 8 & 0xff] ^ - _sTable3[r3 >> 16 & 0xff]; + t0 = _sTable0[r2 & 0xff] ^ _sTable1[r2 >> 8 & 0xff] ^ _sTable2[r2 >> 16 & 0xff] ^ _sTable3[r2 >> 24 & 0xff]; + t1 = _sTable0[r3 >> 24 & 0xff] ^ _sTable1[r3 & 0xff] ^ _sTable2[r3 >> 8 & 0xff] ^ _sTable3[r3 >> 16 & 0xff]; r1 ^= t0 + (t1 << 1) + _subKey[--ki]; r1 = r1 >> 1 & 0x7fffffff | r1 << 31; r0 = (r0 >> 31 & 0x1 | r0 << 1) ^ (t0 + t1 + _subKey[--ki]); } - output.setAll(outOff, (r2 ^ _subKey[0]).pack32Le()); - output.setAll(outOff + 4, (r3 ^ _subKey[1]).pack32Le()); - output.setAll(outOff + 8, (r0 ^ _subKey[2]).pack32Le()); - output.setAll(outOff + 12, (r1 ^ _subKey[3]).pack32Le()); + output.setAll(outOff, _pack32Le(r2 ^ _subKey[0])); + output.setAll(outOff + 4, _pack32Le(r3 ^ _subKey[1])); + output.setAll(outOff + 8, _pack32Le(r0 ^ _subKey[2])); + output.setAll(outOff + 12, _pack32Le(r1 ^ _subKey[3])); } List _mdsrem(final int x, final int y) { @@ -682,4 +665,7 @@ class TwofishEngine extends BaseCipher { } return [0xff & b >> 24, 0xff & b >> 16, 0xff & b >> 8, 0xff & b]; } + + static int _unpack32Le(final Uint8List x) => x.unpack32Le(); + static Uint8List _pack32Le(final int x) => x.pack32Le(); } diff --git a/lib/src/enum/aead_algorithm.dart b/lib/src/enum/aead_algorithm.dart index f710c306..3e88d148 100644 --- a/lib/src/enum/aead_algorithm.dart +++ b/lib/src/enum/aead_algorithm.dart @@ -1,61 +1,46 @@ -// Copyright 2023-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; -import '../crypto/aead/base.dart'; +import '../cryptor/aead/eax.dart'; +import '../cryptor/aead/gcm.dart'; +import '../cryptor/aead/ocb.dart'; +import '../type/aead.dart'; import 'symmetric_algorithm.dart'; /// Aead Algorithms -/// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-rfc4880bis#name-aead-encrypted-data-packet- +/// See https://www.rfc-editor.org/rfc/rfc9580#section-9.6 /// Author Nguyen Van Nguyen enum AeadAlgorithm { eax(1), ocb(2), - gcm(100); + gcm(3); final int value; const AeadAlgorithm(this.value); - int get blockLength { - switch (this) { - case eax: - case ocb: - case gcm: - return 16; - } - } - - int get ivLength { - switch (this) { - case eax: - return 16; - case ocb: - return 15; - case gcm: - return 12; - } - } - - int get tagLength { - switch (this) { - case eax: - case ocb: - case gcm: - return 16; - } - } - - Base cipherEngine(Uint8List key, SymmetricAlgorithm symmetric) { - switch (this) { - case eax: - return Eax(key, symmetric); - case ocb: - return Ocb(key, symmetric); - case gcm: - return Gcm(key, symmetric); - } - } + int get blockLength => 16; + + int get ivLength => switch (this) { + eax => 16, + ocb => 15, + gcm => 12, + }; + + int get tagLength => 16; + + AeadInterface cipherEngine( + Uint8List key, [ + SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + ]) => + switch (this) { + eax => Eax(key, symmetric), + ocb => Ocb(key, symmetric), + gcm => Gcm(key, symmetric), + }; } diff --git a/lib/src/enum/armor_type.dart b/lib/src/enum/armor_type.dart index 29e12a07..6ac87cb1 100644 --- a/lib/src/enum/armor_type.dart +++ b/lib/src/enum/armor_type.dart @@ -1,6 +1,8 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; /// Armor type /// Author Nguyen Van Nguyen diff --git a/lib/src/enum/compression_algorithm.dart b/lib/src/enum/compression_algorithm.dart index 84b2baf0..abeb66dc 100644 --- a/lib/src/enum/compression_algorithm.dart +++ b/lib/src/enum/compression_algorithm.dart @@ -1,9 +1,11 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. -/// Compression algorithm enum -/// See https://www.rfc-editor.org/rfc/rfc4880#section-9.3 +library; + +/// Compression algorithms enum +/// See https://www.rfc-editor.org/rfc/rfc9580#section-9.4 /// Author Nguyen Van Nguyen enum CompressionAlgorithm { uncompressed(0), diff --git a/lib/src/enum/curve_info.dart b/lib/src/enum/curve_info.dart deleted file mode 100644 index de8f520f..00000000 --- a/lib/src/enum/curve_info.dart +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'package:pointycastle/asn1.dart'; - -import 'hash_algorithm.dart'; -import 'symmetric_algorithm.dart'; - -/// Curve information enum -/// Author Nguyen Van Nguyen -enum CurveInfo { - prime256v1([1, 2, 840, 10045, 3, 1, 7], '1.2.840.10045.3.1.7'), - secp256k1([1, 3, 132, 0, 10], '1.3.132.0.10'), - secp384r1([1, 3, 132, 0, 34], '1.3.132.0.34'), - secp521r1([1, 3, 132, 0, 35], '1.3.132.0.35'), - brainpoolP256r1([1, 3, 36, 3, 3, 2, 8, 1, 1, 7], '1.3.36.3.3.2.8.1.1.7'), - brainpoolP384r1([1, 3, 36, 3, 3, 2, 8, 1, 1, 11], '1.3.36.3.3.2.8.1.1.11'), - brainpoolP512r1([1, 3, 36, 3, 3, 2, 8, 1, 1, 13], '1.3.36.3.3.2.8.1.1.13'), - ed25519([1, 3, 6, 1, 4, 1, 11591, 15, 1], '1.3.6.1.4.1.11591.15.1'), - curve25519([1, 3, 6, 1, 4, 1, 3029, 1, 5, 1], '1.3.6.1.4.1.3029.1.5.1'); - - final List identifier; - - final String identifierString; - - const CurveInfo(this.identifier, this.identifierString); - - ASN1ObjectIdentifier get asn1Oid => ASN1ObjectIdentifier(identifier); - - String get curveName { - switch (this) { - case prime256v1: - return 'Prime 256 v1'; - case secp256k1: - return 'Sec P256 k1'; - case secp384r1: - return 'Sec P384 r1'; - case secp521r1: - return 'Sec P521 r1'; - case brainpoolP256r1: - return 'Brainpool P256 r1'; - case brainpoolP384r1: - return 'Brainpool P384 r1'; - case brainpoolP512r1: - return 'Brainpool P512 r1'; - case ed25519: - return 'Ed 25519'; - case curve25519: - return 'Curve 25519'; - } - } - - int get fieldSize { - switch (this) { - case prime256v1: - case secp256k1: - case brainpoolP256r1: - return 256; - case secp384r1: - case brainpoolP384r1: - return 384; - case secp521r1: - return 521; - case brainpoolP512r1: - return 512; - case ed25519: - case curve25519: - return 255; - } - } - - HashAlgorithm get hashAlgorithm { - switch (this) { - case brainpoolP256r1: - case curve25519: - case prime256v1: - case secp256k1: - return HashAlgorithm.sha256; - case brainpoolP384r1: - case secp384r1: - return HashAlgorithm.sha384; - case brainpoolP512r1: - case ed25519: - case secp521r1: - return HashAlgorithm.sha512; - } - } - - SymmetricAlgorithm get symmetricAlgorithm { - switch (this) { - case brainpoolP256r1: - case curve25519: - case ed25519: - case prime256v1: - case secp256k1: - return SymmetricAlgorithm.aes128; - case brainpoolP384r1: - case secp384r1: - return SymmetricAlgorithm.aes192; - case brainpoolP512r1: - case secp521r1: - return SymmetricAlgorithm.aes256; - } - } -} diff --git a/lib/src/enum/dh_key_size.dart b/lib/src/enum/dh_key_size.dart deleted file mode 100644 index 4093e98b..00000000 --- a/lib/src/enum/dh_key_size.dart +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -/// Author Nguyen Van Nguyen -enum DHKeySize { - l1024n160(1024, 160), - l2048n224(2048, 224), - l2048n256(2048, 256), - l3072n256(3072, 256); - - final int lSize; - - final int nSize; - - const DHKeySize(this.lSize, this.nSize); -} diff --git a/lib/src/enum/ecc.dart b/lib/src/enum/ecc.dart new file mode 100644 index 00000000..2cb2d41c --- /dev/null +++ b/lib/src/enum/ecc.dart @@ -0,0 +1,45 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'package:pointycastle/asn1.dart'; + +import 'hash_algorithm.dart'; +import 'symmetric_algorithm.dart'; + +/// Elliptic curve cryptography enum +/// See https://www.rfc-editor.org/rfc/rfc9580#section-9.2 +/// Author Nguyen Van Nguyen +enum Ecc { + prime256v1([1, 2, 840, 10045, 3, 1, 7], '1.2.840.10045.3.1.7'), + secp256k1([1, 3, 132, 0, 10], '1.3.132.0.10'), + secp384r1([1, 3, 132, 0, 34], '1.3.132.0.34'), + secp521r1([1, 3, 132, 0, 35], '1.3.132.0.35'), + brainpoolP256r1([1, 3, 36, 3, 3, 2, 8, 1, 1, 7], '1.3.36.3.3.2.8.1.1.7'), + brainpoolP384r1([1, 3, 36, 3, 3, 2, 8, 1, 1, 11], '1.3.36.3.3.2.8.1.1.11'), + brainpoolP512r1([1, 3, 36, 3, 3, 2, 8, 1, 1, 13], '1.3.36.3.3.2.8.1.1.13'), + ed25519([1, 3, 6, 1, 4, 1, 11591, 15, 1], '11.3.6.1.4.1.3029.1.5.1'), + curve25519([1, 3, 6, 1, 4, 1, 3029, 1, 5, 1], '1.3.6.1.4.1.3029.1.5.1'); + + final List identifier; + + final String identifierString; + + const Ecc(this.identifier, this.identifierString); + + ASN1ObjectIdentifier get asn1Oid => ASN1ObjectIdentifier(identifier); + + HashAlgorithm get hashAlgorithm => switch (this) { + brainpoolP256r1 || curve25519 || prime256v1 || secp256k1 => HashAlgorithm.sha256, + brainpoolP384r1 || secp384r1 => HashAlgorithm.sha384, + brainpoolP512r1 || ed25519 || secp521r1 => HashAlgorithm.sha512, + }; + + SymmetricAlgorithm get symmetricAlgorithm => switch (this) { + brainpoolP256r1 || curve25519 || ed25519 || prime256v1 || secp256k1 => SymmetricAlgorithm.aes128, + brainpoolP384r1 || secp384r1 => SymmetricAlgorithm.aes192, + brainpoolP512r1 || secp521r1 => SymmetricAlgorithm.aes256, + }; +} diff --git a/lib/src/enum/eddsa_curve.dart b/lib/src/enum/eddsa_curve.dart new file mode 100644 index 00000000..08fed881 --- /dev/null +++ b/lib/src/enum/eddsa_curve.dart @@ -0,0 +1,24 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'hash_algorithm.dart'; + +/// EdDSA curves enum +/// Author Nguyen Van Nguyen +enum EdDSACurve { + ed25519, + ed448; + + int get payloadSize => switch (this) { + ed25519 => 32, + ed448 => 57, + }; + + HashAlgorithm get hashAlgorithm => switch (this) { + ed25519 => HashAlgorithm.sha256, + ed448 => HashAlgorithm.sha512, + }; +} diff --git a/lib/src/enum/hash_algorithm.dart b/lib/src/enum/hash_algorithm.dart index 915b5ad6..76657afc 100644 --- a/lib/src/enum/hash_algorithm.dart +++ b/lib/src/enum/hash_algorithm.dart @@ -1,9 +1,11 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. -/// Hash Algorithms -/// See https://www.rfc-editor.org/rfc/rfc4880#section-9.4 +library; + +/// Hash Algorithms enum +/// See https://www.rfc-editor.org/rfc/rfc9580#section-9.5 /// Author Nguyen Van Nguyen enum HashAlgorithm { md5(1), @@ -12,64 +14,41 @@ enum HashAlgorithm { sha256(8), sha384(9), sha512(10), - sha224(11); + sha224(11), + sha3_256(12), + sha3_512(14); final int value; const HashAlgorithm(this.value); /// pointy castle digest name - String get digestName { - switch (this) { - case md5: - return 'MD5'; - case sha1: - return 'SHA-1'; - case ripemd160: - return 'RIPEMD-160'; - case sha256: - return 'SHA-256'; - case sha384: - return 'SHA-384'; - case sha512: - return 'SHA-512'; - case sha224: - return 'SHA-224'; - } - } + String get digestName => switch (this) { + md5 => 'MD5', + sha1 => 'SHA-1', + ripemd160 => 'RIPEMD-160', + sha256 => 'SHA-256', + sha384 => 'SHA-384', + sha512 => 'SHA-512', + sha224 => 'SHA-224', + sha3_256 => 'SHA3-256', + sha3_512 => 'SHA3-512', + }; - int get digestSize { - switch (this) { - case md5: - return 16; - case sha1: - return 20; - case ripemd160: - return 20; - case sha256: - return 32; - case sha384: - return 48; - case sha512: - return 64; - case sha224: - return 28; - } - } + int get digestSize => switch (this) { + md5 => 16, + sha1 => 20, + ripemd160 => 20, + sha256 || sha3_256 => 32, + sha384 => 48, + sha512 || sha3_512 => 64, + sha224 => 28, + }; - int get saltSize { - switch (this) { - case md5: - case sha1: - case ripemd160: - return 8; - case sha224: - case sha256: - return 16; - case sha384: - return 24; - case sha512: - return 32; - } - } + int get saltSize => switch (this) { + md5 || sha1 || ripemd160 => 0, + sha224 || sha256 || sha3_256 => 16, + sha384 => 24, + sha512 || sha3_512 => 32, + }; } diff --git a/lib/src/enum/kek_size.dart b/lib/src/enum/kek_size.dart new file mode 100644 index 00000000..81369d79 --- /dev/null +++ b/lib/src/enum/kek_size.dart @@ -0,0 +1,17 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +/// Key encryption key size enum +/// Author Nguyen Van Nguyen +enum KekSize { + normal(16), + medium(24), + high(32); + + final int size; + + const KekSize(this.size); +} diff --git a/lib/src/enum/key_algorithm.dart b/lib/src/enum/key_algorithm.dart index 4d3a29c7..415608c5 100644 --- a/lib/src/enum/key_algorithm.dart +++ b/lib/src/enum/key_algorithm.dart @@ -1,9 +1,14 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import '../common/config.dart'; +import 'key_version.dart'; /// Public-Key Algorithms -/// See https://www.rfc-editor.org/rfc/rfc4880#section-9.1 +/// See https://www.rfc-editor.org/rfc/rfc9580#section-9.1 /// Author Nguyen Van Nguyen enum KeyAlgorithm { /// RSA (Encrypt or Sign) [HAC] @@ -29,16 +34,43 @@ enum KeyAlgorithm { elgamalEncryptSign(20), diffieHellman(21), - /// EdDSA (Sign only) - eddsa(22), + /// EdDSA Legacy (Sign only) + eddsaLegacy(22), /// Reserved for AEDH aedh(23), /// Reserved for AEDSA - aedsa(24); + aedsa(24), + + /// X25519 (Encrypt only) + x25519(25), + + /// X448 (Encrypt only) + x448(26), + + /// Ed25519 (Sign only) + ed25519(27), + + /// Ed448 (Sign only) + ed448(28); final int value; const KeyAlgorithm(this.value); + + bool get forSigning => switch (this) { + rsaEncrypt || elgamal || ecdh || aedh || x25519 || x448 => false, + _ => true, + }; + + bool get forEncryption => switch (this) { + rsaSign || dsa || eddsaLegacy || aedsa || ed25519 || ed448 => false, + _ => true, + }; + + int get keyVersion => switch (this) { + x25519 || x448 || ed25519 || ed448 => KeyVersion.v6.value, + _ => Config.useV6Key ? KeyVersion.v6.value : KeyVersion.v4.value, + }; } diff --git a/lib/src/enum/key_flag.dart b/lib/src/enum/key_flag.dart deleted file mode 100644 index 00201767..00000000 --- a/lib/src/enum/key_flag.dart +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -/// Key flag -/// Author Nguyen Van Nguyen -enum KeyFlag { - /// 0x01 - This key may be used to certify other keys. - certifyKeys(1), - - /// 0x02 - This key may be used to sign data. - signData(2), - - /// 0x04 - This key may be used to encrypt communications. - encryptCommunication(4), - - /// 0x08 - This key may be used to encrypt storage. - encryptStorage(8), - - /// 0x10 - The private component of this key may have been split by a secret-sharing mechanism. - splitPrivateKey(16), - - /// 0x20 - This key may be used for authentication. - authentication(32), - - /// 0x80 - The private component of this key may be in the possession of more than one person. - sharedPrivateKey(128); - - final int value; - - const KeyFlag(this.value); -} diff --git a/lib/src/enum/key_generation_type.dart b/lib/src/enum/key_generation_type.dart deleted file mode 100644 index bf057c58..00000000 --- a/lib/src/enum/key_generation_type.dart +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -/// Author Nguyen Van Nguyen -enum KeyGenerationType { rsa, dsa, ecdsa, eddsa } diff --git a/lib/src/enum/key_version.dart b/lib/src/enum/key_version.dart new file mode 100644 index 00000000..661b54aa --- /dev/null +++ b/lib/src/enum/key_version.dart @@ -0,0 +1,16 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +/// Key versions enum +/// Author Nguyen Van Nguyen +enum KeyVersion { + v4(4), + v6(6); + + final int value; + + const KeyVersion(this.value); +} diff --git a/lib/src/enum/literal_format.dart b/lib/src/enum/literal_format.dart index 34efb2bf..bcbfb1d4 100644 --- a/lib/src/enum/literal_format.dart +++ b/lib/src/enum/literal_format.dart @@ -1,7 +1,10 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. +library; + +/// Literal formats enum /// Author Nguyen Van Nguyen enum LiteralFormat { /// Binary data 'b' diff --git a/lib/src/enum/montgomery_curve.dart b/lib/src/enum/montgomery_curve.dart new file mode 100644 index 00000000..28833c2b --- /dev/null +++ b/lib/src/enum/montgomery_curve.dart @@ -0,0 +1,44 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../common/helpers.dart'; +import 'hash_algorithm.dart'; +import 'kek_size.dart'; +import 'symmetric_algorithm.dart'; + +/// Montgomery curves enum +/// Author Nguyen Van Nguyen +enum MontgomeryCurve { + x25519, + x448; + + int get payloadSize => switch (this) { + x25519 => 32, + x448 => 56, + }; + + HashAlgorithm get hkdfHash => switch (this) { + x25519 => HashAlgorithm.sha256, + x448 => HashAlgorithm.sha512, + }; + + Uint8List get hkdfInfo => switch (this) { + x25519 => 'OpenPGP X25519'.toBytes(), + x448 => 'OpenPGP X448'.toBytes(), + }; + + int get kekSize => switch (this) { + x25519 => KekSize.normal.size, + x448 => KekSize.high.size, + }; + + SymmetricAlgorithm get symmetric => switch (this) { + x25519 => SymmetricAlgorithm.aes128, + x448 => SymmetricAlgorithm.aes256, + }; +} diff --git a/lib/src/enum/packet_tag.dart b/lib/src/enum/packet_type.dart similarity index 56% rename from lib/src/enum/packet_tag.dart rename to lib/src/enum/packet_type.dart index 9c74ded7..486bc988 100644 --- a/lib/src/enum/packet_tag.dart +++ b/lib/src/enum/packet_type.dart @@ -1,10 +1,13 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; /// A list of packet types and numeric tags associated with them. +/// See https://www.rfc-editor.org/rfc/rfc9580#section-5 /// Author Nguyen Van Nguyen -enum PacketTag { +enum PacketType { publicKeyEncryptedSessionKey(1), signature(2), symEncryptedSessionKey(3), @@ -21,10 +24,10 @@ enum PacketTag { publicSubkey(14), userAttribute(17), symEncryptedIntegrityProtectedData(18), - modificationDetectionCode(19), - aeadEncryptedData(20); + aeadEncryptedData(20), + padding(21); final int value; - const PacketTag(this.value); -} + const PacketType(this.value); +} \ No newline at end of file diff --git a/lib/src/enum/revocation_key_tag.dart b/lib/src/enum/revocation_key_tag.dart deleted file mode 100644 index f309b184..00000000 --- a/lib/src/enum/revocation_key_tag.dart +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -/// Author Nguyen Van Nguyen -enum RevocationKeyTag { - classDefault(128), - classSensitive(64); - - final int value; - - const RevocationKeyTag(this.value); -} diff --git a/lib/src/enum/revocation_reason_tag.dart b/lib/src/enum/revocation_reason_tag.dart deleted file mode 100644 index 606308ea..00000000 --- a/lib/src/enum/revocation_reason_tag.dart +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -/// Reason for Revocation -/// See https://tools.ietf.org/html/rfc4880#section-5.2.3.23 -/// Author Nguyen Van Nguyen -enum RevocationReasonTag { - /// No reason specified (key revocations or cert revocations) - noReason(0), - - /// Key is superseded (key revocations) - keySuperseded(1), - - /// Key material has been compromised (key revocations) - keyCompromised(2), - - /// Key is retired and no longer used (key revocations) - keyRetired(3), - - /// User ID information is no longer valid (cert revocations) - userIDInvalid(32); - - final int value; - - const RevocationReasonTag(this.value); -} diff --git a/lib/src/enum/rsa_key_size.dart b/lib/src/enum/rsa_key_size.dart index ce2073c0..2867296b 100644 --- a/lib/src/enum/rsa_key_size.dart +++ b/lib/src/enum/rsa_key_size.dart @@ -1,14 +1,17 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. +library; + +/// RSA key size /// Author Nguyen Van Nguyen enum RSAKeySize { - s2048(2048), - s2560(2560), - s3072(3072), - s3584(3584), - s4096(4096); + normal(2048), + medium(2560), + high(3072), + veryHigh(3584), + ultraHigh(4096); final int bits; diff --git a/lib/src/enum/s2k_type.dart b/lib/src/enum/s2k_type.dart index ed3a1455..3b4576b7 100644 --- a/lib/src/enum/s2k_type.dart +++ b/lib/src/enum/s2k_type.dart @@ -1,29 +1,28 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. -/// A string to key specifier type +library; + +/// String to key specifier type +/// See https://www.rfc-editor.org/rfc/rfc9580#section-3.7.1 /// Author Nguyen Van Nguyen enum S2kType { simple(0), salted(1), iterated(3), + argon2(4), gnu(101); final int value; const S2kType(this.value); - int get length { - switch (this) { - case simple: - return 2; - case salted: - return 10; - case iterated: - return 11; - case gnu: - return 6; - } - } + int get length => switch (this) { + simple => 2, + salted => 10, + iterated => 11, + argon2 => 20, + gnu => 6, + }; } diff --git a/lib/src/enum/s2k_usage.dart b/lib/src/enum/s2k_usage.dart index 4a4a4e1a..d99347d1 100644 --- a/lib/src/enum/s2k_usage.dart +++ b/lib/src/enum/s2k_usage.dart @@ -1,13 +1,18 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. +library; + +/// S2k usage +/// Indicating whether and how the secret key material is protected by a passphrase +/// See https://www.rfc-editor.org/rfc/rfc9580#section-3.7.2 /// Author Nguyen Van Nguyen enum S2kUsage { none(0), - checksum(255), - sha1(254), - aeadProtect(253); + aeadProtect(253), + cfb(254), + malleableCfb(255); final int value; diff --git a/lib/src/enum/signature_subpacket_type.dart b/lib/src/enum/signature_subpacket_type.dart deleted file mode 100644 index 3337a985..00000000 --- a/lib/src/enum/signature_subpacket_type.dart +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -/// Signature subpacket type -/// Author Nguyen Van Nguyen -enum SignatureSubpacketType { - signatureCreationTime(2), - signatureExpirationTime(3), - exportableCertification(4), - trustSignature(5), - regularExpression(6), - revocable(7), - keyExpirationTime(9), - placeholderBackwardCompatibility(10), - preferredSymmetricAlgorithms(11), - revocationKey(12), - issuerKeyID(16), - notationData(20), - preferredHashAlgorithms(21), - preferredCompressionAlgorithms(22), - keyServerPreferences(23), - preferredKeyServer(24), - primaryUserID(25), - policyURI(26), - keyFlags(27), - signerUserID(28), - revocationReason(29), - features(30), - signatureTarget(31), - embeddedSignature(32), - issuerFingerprint(33), - preferredAeadAlgorithms(34), - intendedRecipientFingerprint(35), - attestedCertifications(37); - - final int value; - - const SignatureSubpacketType(this.value); -} diff --git a/lib/src/enum/signature_type.dart b/lib/src/enum/signature_type.dart deleted file mode 100644 index a937b501..00000000 --- a/lib/src/enum/signature_type.dart +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -/// Author Nguyen Van Nguyen -enum SignatureType { - /// Signature of a binary document. - binary(0), - - /// Signature of a canonical text document. - /// - /// Canonicalyzing the document by converting line endings. - text(1), - - /// Standalone signature. - /// - /// This signature is a signature of only its own subpacket contents. - /// It is calculated identically to a signature over a zero-lengh binary document. - /// Note that it doesn't make sense to have a V3 standalone signature. - standalone(2), - - /// Generic certification of a User ID and Public-Key packet. - /// - /// The issuer of this certification does not make any particular - /// assertion as to how well the certifier has checked that the owner - /// of the key is in fact the person described by the User ID. - certGeneric(16), - - /// Persona certification of a User ID and Public-Key packet. - /// - /// The issuer of this certification has not done any verification of - /// the claim that the owner of this key is the User ID specified. - certPersona(17), - - /// Casual certification of a User ID and Public-Key packet. - /// - /// The issuer of this certification has done some casual verification of the claim of identity. - certCasual(18), - - /// Positive certification of a User ID and Public-Key packet. - /// - /// The issuer of this certification has done substantial verification of the claim of identity. - /// Most OpenPGP implementations make their "key signatures" as 0x10 certifications. - /// Some implementations can issue 0x11-0x13 certifications, but few differentiate between the types. - certPositive(19), - - /// Certification revocation signature. - /// - /// This signature revokes an earlier User ID certification signature - /// (signature class 0x10 through 0x13) or direct-key signature (0x1F). - /// It should be issued by the same key that issued the revoked signature or an authorized revocation key. - /// The signature is computed over the same data as the certificate that it - /// revokes, and should have a later creation date than that certificate. - certRevocation(48), - - /// Subkey binding signature. - /// - /// This signature is a statement by the top-level signing key that indicates that it owns the subkey. - /// This signature is calculated directly on the primary key and subkey, - /// and not on any User ID or other packets. - /// A signature that binds a signing subkey MUST have an Embedded Signature subpacket in this binding signature - /// that contains a 0x19 signature made by the signing subkey on the primary key and subkey. - subkeyBinding(24), - - /// Primary Key Binding Signature - /// - /// This signature is a statement by a signing subkey, indicating - /// that it is owned by the primary key and subkey. - /// This signature is calculated the same way as a 0x18 signature: directly on the - /// primary key and subkey, and not on any User ID or other packets. - /// - /// When a signature is made over a key, the hash data starts with the octet 0x99, - /// followed by a two-octet length of the key, and then body of the key packet. - /// (Note that this is an old-style packet header for a key packet with two-octet length.) - /// A subkey binding signature (type 0x18) or primary key binding signature (type 0x19) then hashes - /// the subkey using the same format as the main key (also using 0x99 as the first octet). - keyBinding(25), - - /// Signature directly on a key - /// - /// This signature is calculated directly on a key. - /// It binds the information in the Signature subpackets to the key, - /// and is appropriate to be used for subpackets that provide information - /// about the key, such as the Revocation Key subpacket. - /// It is also appropriate for statements that non-self certifiers want to make - /// about the key itself, rather than the binding between a key and a name. - key(31), - - /// Key revocation signature - /// - /// The signature is calculated directly on the key being revoked. - /// A revoked key is not to be used. - /// Only revocation signatures by the key being revoked, or by an authorized revocation key, - /// should be considered valid revocation signatures. - keyRevocation(32), - - /// Subkey revocation signature - /// - /// The signature is calculated directly on the subkey being revoked. - /// A revoked subkey is not to be used. Only revocation signatures - /// by the top-level signature key that is bound to this subkey, or - /// by an authorized revocation key, should be considered valid revocation signatures. - /// Key revocation signatures (types 0x20 and 0x28) hash only the key being revoked. - subkeyRevocation(40), - - /// Timestamp signature. - /// - /// This signature is only meaningful for the timestamp contained in it. - timestamp(64), - - /// Third-Party Confirmation signature. - /// - /// This signature is a signature over some other OpenPGP Signature packet(s). - /// It is analogous to a notary seal on the signed data. - /// A third-party signature SHOULD include Signature Target - /// subpacket(s) to give easy identification. Note that we really do - /// mean SHOULD. There are plausible uses for this (such as a blind - /// party that only sees the signature, not the key or source - /// document) that cannot include a target subpacket. - thirdParty(80); - - final int value; - - const SignatureType(this.value); -} diff --git a/lib/src/enum/support_feature.dart b/lib/src/enum/support_feature.dart deleted file mode 100644 index 6490acdf..00000000 --- a/lib/src/enum/support_feature.dart +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -/// Support Feature -/// Author Nguyen Van Nguyen -enum SupportFeature { - /// 0x01 - Modification Detection (packets 18 and 19) - modificationDetection(1), - - /// 0x02 - AEAD Encrypted Data Packet (packet 20) and version 5 Symmetric-Key Encrypted Session Key Packets (packet 3) - aeadEncryptedData(2), - - /// 0x04 - Version 5 Public-Key Packet format and corresponding new fingerprint format - version5PublicKey(4); - - final int value; - - const SupportFeature(this.value); -} diff --git a/lib/src/enum/symmetric_algorithm.dart b/lib/src/enum/symmetric_algorithm.dart index efc84ba9..1941865a 100644 --- a/lib/src/enum/symmetric_algorithm.dart +++ b/lib/src/enum/symmetric_algorithm.dart @@ -4,10 +4,14 @@ import 'package:pointycastle/export.dart'; -import '../crypto/symmetric/base_cipher.dart'; +import '../cryptor/symmetric/blowfish.dart'; +import '../cryptor/symmetric/camellia.dart'; +import '../cryptor/symmetric/cast5.dart'; +import '../cryptor/symmetric/idea.dart'; +import '../cryptor/symmetric/twofish.dart'; -/// Symmetric-Key Algorithms -/// See https://tools.ietf.org/html/rfc4880#section-9.2 +/// Symmetric Key Algorithms enum +/// See https://www.rfc-editor.org/rfc/rfc9580#section-9.3 /// Author Nguyen Van Nguyen enum SymmetricAlgorithm { plaintext(0), @@ -27,98 +31,40 @@ enum SymmetricAlgorithm { const SymmetricAlgorithm(this.value); - int get keySize { - switch (this) { - case plaintext: - return 0; - case idea: - case cast5: - case blowfish: - case aes128: - case camellia128: - return 128; - case tripledes: - case aes192: - case camellia192: - return 192; - case aes256: - case twofish: - case camellia256: - return 256; - } - } + int get keySize => switch (this) { + plaintext => 0, + idea || cast5 || blowfish || aes128 || camellia128 => 128, + tripledes || aes192 || camellia192 => 192, + aes256 || twofish || camellia256 => 256, + }; - int get keySizeInByte { - return (keySize + 7) >> 3; - } + int get keySizeInByte => (keySize + 7) >> 3; - int get blockSize { - switch (this) { - case plaintext: - return 0; - case blowfish: - case cast5: - case idea: - case tripledes: - return 8; - case aes128: - case aes192: - case aes256: - case camellia128: - case camellia192: - case camellia256: - case twofish: - return 16; - } - } + int get blockSize => switch (this) { + plaintext => 0, + blowfish || cast5 || idea || tripledes => 8, + aes128 || aes192 || aes256 || camellia128 || camellia192 || camellia256 || twofish => 16, + }; - BlockCipher get cfbCipherEngine { - switch (this) { - case aes128: - case aes192: - case aes256: - return BlockCipher('AES/CFB-${blockSize * 8}'); - case blowfish: - return CFBBlockCipher(BlowfishEngine(), blockSize); - case camellia128: - case camellia192: - case camellia256: - return CFBBlockCipher(CamelliaEngine(), blockSize); - case cast5: - return CFBBlockCipher(CAST5Engine(), blockSize); - case idea: - return CFBBlockCipher(IDEAEngine(), blockSize); - case tripledes: - return BlockCipher('DESede/CFB-${blockSize * 8}'); - case twofish: - return CFBBlockCipher(TwofishEngine(), blockSize); - default: - throw UnsupportedError('Unsupported symmetric algorithm encountered'); - } - } + BlockCipher get cfbCipherEngine => switch (this) { + aes128 || aes192 || aes256 => BlockCipher('AES/CFB-${blockSize * 8}'), + blowfish => CFBBlockCipher(BlowfishEngine(), blockSize), + camellia128 || camellia192 || camellia256 => CFBBlockCipher(CamelliaEngine(), blockSize), + cast5 => CFBBlockCipher(CAST5Engine(), blockSize), + idea => CFBBlockCipher(IDEAEngine(), blockSize), + tripledes => BlockCipher('DESede/CFB-${blockSize * 8}'), + twofish => CFBBlockCipher(TwofishEngine(), blockSize), + _ => throw UnsupportedError('Unsupported symmetric algorithm encountered'), + }; - BlockCipher get cipherEngine { - switch (this) { - case aes128: - case aes192: - case aes256: - return AESEngine(); - case blowfish: - return BlowfishEngine(); - case camellia128: - case camellia192: - case camellia256: - return CamelliaEngine(); - case cast5: - return CAST5Engine(); - case idea: - return IDEAEngine(); - case tripledes: - return DESedeEngine(); - case twofish: - return TwofishEngine(); - default: - throw UnsupportedError('Unsupported symmetric algorithm encountered'); - } - } + BlockCipher get cipherEngine => switch (this) { + aes128 || aes192 || aes256 => AESEngine(), + blowfish => BlowfishEngine(), + camellia128 || camellia192 || camellia256 => CamelliaEngine(), + cast5 => CAST5Engine(), + idea => IDEAEngine(), + tripledes => DESedeEngine(), + twofish => TwofishEngine(), + _ => throw UnsupportedError('Unsupported symmetric algorithm encountered'), + }; } diff --git a/lib/src/helpers.dart b/lib/src/helpers.dart deleted file mode 100644 index e720cbe6..00000000 --- a/lib/src/helpers.dart +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:convert'; -import 'dart:math'; -import 'dart:typed_data'; - -import 'package:pointycastle/api.dart' as pc; - -import 'crypto/math/byte_ext.dart'; -import 'crypto/math/int_ext.dart'; -import 'enum/hash_algorithm.dart'; -import 'enum/symmetric_algorithm.dart'; - -/// Author Nguyen Van Nguyen -extension StringHelper on String { - List chunk(final int chunkSize) { - assert(chunkSize > 0); - final chunkCount = (length / chunkSize).ceil(); - return List.generate(chunkCount, (index) { - final sliceStart = index * chunkSize; - final sliceEnd = sliceStart + chunkSize; - return substring( - sliceStart, - (sliceEnd < length) ? sliceEnd : length, - ); - }); - } - - Uint8List hexToBytes() { - final hex = replaceAll(RegExp(r'\s'), ''); - final result = Uint8List(hex.length ~/ 2); - - for (var i = 0; i < hex.length; i += 2) { - final num = hex.substring(i, i + 2); - final byte = int.parse(num, radix: 16); - result[i ~/ 2] = byte; - } - - return result; - } - - Uint8List stringToBytes() => utf8.encoder.convert(this); - - bool hasMatch(final String text) => RegExp(this).hasMatch(text); -} - -extension DateTimeHelper on DateTime { - Uint8List toBytes() => (millisecondsSinceEpoch ~/ 1000).pack32(); -} - -class Helper { - static final _random = Random.secure(); - - static final _secureRandom = pc.SecureRandom('Fortuna') - ..seed( - pc.KeyParameter( - Uint8List.fromList( - List.generate( - 32, - ((_) => _random.nextInt(0xffffffff)), - ), - ), - ), - ); - - static BigInt readMPI(Uint8List bytes) { - final bitLength = bytes.sublist(0, 2).toUint16(); - return bytes.sublist(2, ((bitLength + 7) >> 3) + 2).toBigIntWithSign(1); - } - - static pc.SecureRandom secureRandom() => _secureRandom; - - static Uint8List generatePrefix([ - final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - ]) { - final prefix = _secureRandom.nextBytes(symmetric.blockSize); - final repeat = [prefix[prefix.length - 2], prefix[prefix.length - 1]]; - return Uint8List.fromList([...prefix, ...repeat]); - } - - static Uint8List generateEncryptionKey([ - final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - ]) => - _secureRandom.nextBytes((symmetric.keySize + 7) >> 3); - - static Uint8List hashDigest( - final Uint8List input, [ - HashAlgorithm hash = HashAlgorithm.sha256, - ]) { - return pc.Digest(hash.digestName).process(input); - } - - static BigInt randomBigInt( - final BigInt min, - final BigInt max, { - pc.SecureRandom? random, - }) { - random = random ?? secureRandom(); - BigInt k; - do { - k = random.nextBigInteger(max.bitLength); - } while (k.compareTo(min) <= 0 || k.compareTo(max) >= 0); - return k; - } -} diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index cacdcf03..b544d630 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -1,233 +1,4 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import 'enum/compression_algorithm.dart'; -import 'enum/curve_info.dart'; -import 'enum/dh_key_size.dart'; -import 'enum/key_generation_type.dart'; -import 'enum/rsa_key_size.dart'; -import 'enum/symmetric_algorithm.dart'; - -import 'type/cleartext_message.dart'; -import 'type/message.dart'; -import 'type/private_key.dart'; -import 'type/public_key.dart'; -import 'type/signature.dart'; -import 'type/signed_message.dart'; - -export 'enum/compression_algorithm.dart'; -export 'enum/curve_info.dart'; -export 'enum/dh_key_size.dart'; -export 'enum/key_generation_type.dart'; -export 'enum/rsa_key_size.dart'; -export 'enum/symmetric_algorithm.dart'; - -export 'type/cleartext_message.dart'; -export 'type/message.dart'; -export 'type/private_key.dart'; -export 'type/public_key.dart'; -export 'type/signature.dart'; -export 'type/signed_message.dart'; - -/// Export high level API for Dart developers. -/// Author Nguyen Van Nguyen +/// Checks if you are awesome. Spoiler: you are. class OpenPGP { - /// Generate a new OpenPGP key pair. Supports RSA and ECC keys. - /// By default, primary and subkeys will be of same type. - /// The generated primary key will have signing capabilities. - /// By default, one subkey with encryption capabilities is also generated. - static Future generateKey( - final Iterable userIDs, - final String passphrase, { - final KeyGenerationType type = KeyGenerationType.rsa, - final RSAKeySize rsaKeySize = RSAKeySize.s4096, - final DHKeySize dhKeySize = DHKeySize.l2048n224, - final CurveInfo curve = CurveInfo.secp521r1, - final int keyExpirationTime = 0, - final String? subkeyPassphrase, - final DateTime? date, - }) async => - PrivateKey.generate( - userIDs, - passphrase, - type: type, - rsaKeySize: rsaKeySize, - dhKeySize: dhKeySize, - curve: curve, - keyExpirationTime: keyExpirationTime, - subkeyPassphrase: subkeyPassphrase, - date: date, - ); - - /// Read an armored & unlock OpenPGP private key with the given passphrase. - static Future decryptPrivateKey( - final String armoredPrivateKey, - final String passphrase, [ - final Iterable subkeyPassphrases = const [], - ]) async => - PrivateKey.fromArmored(armoredPrivateKey).decrypt( - passphrase, - subkeyPassphrases, - ); - - /// Read an armored OpenPGP private key and returns a PrivateKey object - static Future readPrivateKey( - final String armoredPrivateKey, - ) async => - PrivateKey.fromArmored(armoredPrivateKey); - - /// Read an armored OpenPGP public key and returns a PublicKey object - static Future readPublicKey( - final String armoredPublicKey, - ) async => - PublicKey.fromArmored(armoredPublicKey); - - /// Read an armored OpenPGP public key list. - static List readPublicKeys(final String armoredPublicKeys) { - return PublicKey.readPublicKeys(armoredPublicKeys); - } - - /// Sign a cleartext message. - static Future sign( - final String cleartext, - final Iterable signingKeys, { - final DateTime? date, - }) async => - SignedMessage.signCleartext(cleartext, signingKeys, date: date); - - /// Sign a cleartext message & return detached signature - static Future signDetached( - final String cleartext, - final Iterable signingKeys, { - final DateTime? date, - }) async => - SignedMessage.signCleartext( - cleartext, - signingKeys, - date: date, - ).then((signedMessage) => signedMessage.signature); - - /// Verify signatures of cleartext signed message - /// Return signed message with verifications - static Future verify( - final String armoredSignedMessage, - final Iterable verificationKeys, { - final DateTime? date, - }) async => - SignedMessage.fromArmored(armoredSignedMessage).verify( - verificationKeys, - date: date, - ); - - /// Verify detached signatures of cleartext message - /// Returns cleartext message with verifications - static Future verifyDetached( - final String cleartext, - final String armoredSignature, - final Iterable verificationKeys, { - final DateTime? date, - }) async => - CleartextMessage(cleartext).verifySignature( - Signature.fromArmored(armoredSignature), - verificationKeys, - date: date, - ); - - /// Read an armored OpenPGP signature and returns a Signature object - static Future readSignature( - final String armoredSignature, - ) async => - Signature.fromArmored(armoredSignature); - - /// Read an armored OpenPGP signed message and returns a SignedMessage object - static Future readSignedMessage( - final String armoredSignedMessage, - ) async => - SignedMessage.fromArmored(armoredSignedMessage); - - /// Read an armored OpenPGP message and returns a Message object - static Future readMessage( - final String armoredMessage, - ) async => - Message.fromArmored(armoredMessage); - - /// Create new message object from cleartext - static Future createTextMessage( - final String cleartext, { - final DateTime? time, - }) async => - Message.createTextMessage(cleartext, time: time); - - /// Create new message object from binary data. - static Future createBinaryMessage( - final Uint8List data, { - final String filename = '', - final DateTime? time, - }) async => - Message.createBinaryMessage(data, filename: filename, time: time); - - /// Encrypt a message using public keys, passwords or both at once. - /// At least one of `encryptionKeys`, `passwords`must be specified. - /// If signing keys are specified, those will be used to sign the message. - static Future encrypt( - final Message message, { - final Iterable encryptionKeys = const [], - final Iterable signingKeys = const [], - final Iterable passwords = const [], - final SymmetricAlgorithm sessionKeySymmetric = SymmetricAlgorithm.aes128, - final SymmetricAlgorithm encryptionKeySymmetric = SymmetricAlgorithm.aes128, - final CompressionAlgorithm compression = CompressionAlgorithm.uncompressed, - final bool aeadProtect = false, - final DateTime? date, - }) async => - (signingKeys.isNotEmpty) - ? message - .sign(signingKeys, date: date) - .then( - (message) => message.compress(compression), - ) - .then( - (message) => message.encrypt( - encryptionKeys: encryptionKeys, - passwords: passwords, - sessionKeySymmetric: sessionKeySymmetric, - encryptionKeySymmetric: encryptionKeySymmetric, - aeadProtect: aeadProtect, - ), - ) - : message.compress(compression).then((message) => message.encrypt( - encryptionKeys: encryptionKeys, - passwords: passwords, - sessionKeySymmetric: sessionKeySymmetric, - encryptionKeySymmetric: encryptionKeySymmetric, - aeadProtect: aeadProtect, - )); - - /// Decrypt a message with the user's private key, or a password. - /// One of `decryptionKeys` or `passwords` must be specified - /// return object containing decrypted message with verifications - static Future decrypt( - final Message message, { - final Iterable decryptionKeys = const [], - final Iterable verificationKeys = const [], - final Iterable passwords = const [], - final bool allowUnauthenticatedMessages = false, - final DateTime? date, - }) async => - (verificationKeys.isNotEmpty) - ? message - .decrypt( - decryptionKeys: decryptionKeys, - passwords: passwords, - allowUnauthenticatedMessages: allowUnauthenticatedMessages, - ) - .then((message) => message.verify(verificationKeys, date: date)) - : message.decrypt( - decryptionKeys: decryptionKeys, - passwords: passwords, - allowUnauthenticatedMessages: allowUnauthenticatedMessages, - ); + bool get isAwesome => true; } diff --git a/lib/src/packet/aead_encrypted_data.dart b/lib/src/packet/aead_encrypted_data.dart index 3ae70285..57725de5 100644 --- a/lib/src/packet/aead_encrypted_data.dart +++ b/lib/src/packet/aead_encrypted_data.dart @@ -1,22 +1,23 @@ -// Copyright 2022-present by Nguyen Van Nguyen . All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; -import '../crypto/math/int_ext.dart'; +import '../common/helpers.dart'; import '../enum/aead_algorithm.dart'; -import '../enum/packet_tag.dart'; +import '../enum/packet_type.dart'; import '../enum/symmetric_algorithm.dart'; -import '../helpers.dart'; -import 'contained_packet.dart'; +import '../type/packet_list.dart'; +import 'base.dart'; import 'packet_list.dart'; /// Implementation of the Symmetrically Encrypted Authenticated Encryption with /// Additional Data (AEAD) Protected Data Packet(Tag 20) -/// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-rfc4880bis#name-aead-encrypted-data-packet- /// Author Nguyen Van Nguyen -class AeadEncryptedData extends ContainedPacket { +class AeadEncryptedDataPacket extends BasePacket { static const version = 1; final SymmetricAlgorithm symmetric; @@ -28,18 +29,18 @@ class AeadEncryptedData extends ContainedPacket { final Uint8List encrypted; /// Decrypted packets contained within. - final PacketList? packets; + final PacketListInterface? packets; - AeadEncryptedData( + AeadEncryptedDataPacket( this.symmetric, this.aead, this.chunkSize, this.iv, this.encrypted, { this.packets, - }) : super(PacketTag.aeadEncryptedData); + }) : super(PacketType.aeadEncryptedData); - factory AeadEncryptedData.fromByteData( + factory AeadEncryptedDataPacket.fromBytes( final Uint8List bytes, ) { var pos = 0; @@ -68,7 +69,7 @@ class AeadEncryptedData extends ContainedPacket { final iv = bytes.sublist(pos, pos + aead.ivLength); pos += aead.ivLength; - return AeadEncryptedData( + return AeadEncryptedDataPacket( symmetric, aead, chunkSize, @@ -77,15 +78,15 @@ class AeadEncryptedData extends ContainedPacket { ); } - static Future encryptPackets( + factory AeadEncryptedDataPacket.encryptPackets( final Uint8List key, - final PacketList packets, { + final PacketListInterface packets, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, final AeadAlgorithm aead = AeadAlgorithm.ocb, final int chunkSize = 12, - }) async { + }) { final iv = Helper.secureRandom().nextBytes(aead.ivLength); - return AeadEncryptedData( + return AeadEncryptedDataPacket( symmetric, aead, chunkSize, @@ -104,26 +105,24 @@ class AeadEncryptedData extends ContainedPacket { } @override - Uint8List toByteData() { - return Uint8List.fromList([ - version, - symmetric.value, - aead.value, - chunkSize, - ...iv, - ...encrypted, - ]); - } + Uint8List get data => Uint8List.fromList([ + version, + symmetric.value, + aead.value, + chunkSize, + ...iv, + ...encrypted, + ]); /// Encrypt the payload in the packet. - Future encrypt( + AeadEncryptedDataPacket encrypt( final Uint8List key, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, final AeadAlgorithm aead = AeadAlgorithm.ocb, final int chunkSize = 12, - }) async { + }) { if (packets != null && packets!.isNotEmpty) { - return AeadEncryptedData.encryptPackets( + return AeadEncryptedDataPacket.encryptPackets( key, packets!, symmetric: symmetric, @@ -133,20 +132,20 @@ class AeadEncryptedData extends ContainedPacket { } /// Decrypts the encrypted data contained in the packet. - Future decrypt( + AeadEncryptedDataPacket decrypt( final Uint8List key, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - }) async { + }) { final length = encrypted.length; final data = encrypted.sublist(0, length - aead.tagLength); final authTag = encrypted.sublist(length - aead.tagLength); - return AeadEncryptedData( + return AeadEncryptedDataPacket( symmetric, aead, chunkSize, iv, encrypted, - packets: PacketList.packetDecode(_crypt( + packets: PacketList.decode(_crypt( false, key, data, @@ -164,11 +163,11 @@ class AeadEncryptedData extends ContainedPacket { bool forEncryption, Uint8List key, Uint8List data, { + Uint8List? finalChunk, final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, AeadAlgorithm aead = AeadAlgorithm.gcm, final chunkSizeByte = 0, final Uint8List? iv, - Uint8List? finalChunk, }) { final cipher = aead.cipherEngine(key, symmetric); final dataLength = data.length; @@ -178,18 +177,23 @@ class AeadEncryptedData extends ContainedPacket { final adataBuffer = Uint8List(13); adataBuffer.setAll( - 0, - Uint8List.fromList([ - 0xc0 | PacketTag.aeadEncryptedData.value, - version, - symmetric.value, - aead.value, - chunkSize, - ])); + 0, + Uint8List.fromList([ + 0xc0 | PacketType.aeadEncryptedData.value, + version, + symmetric.value, + aead.value, + chunkSizeByte, + ]), + ); final processed = dataLength - tagLength * (dataLength / chunkSize).ceil(); - final crypted = Uint8List(processed); + final crypted = Uint8List( + processed + (forEncryption ? aead.tagLength : 0), + ); for (var chunkIndex = 0; chunkIndex == 0 || data.isNotEmpty;) { + /// We take a chunk of data, en/decrypt it, + /// and shift `data` to the next chunk. final chunkIndexData = adataBuffer.sublist(5, 13); final size = chunkSize < data.length ? chunkSize : data.length; crypted.setAll( @@ -197,23 +201,16 @@ class AeadEncryptedData extends ContainedPacket { forEncryption ? cipher.encrypt( data.sublist(0, size), - cipher.getNonce( - iv ?? Uint8List(aead.ivLength), - chunkIndexData, - ), + cipher.getNonce(iv ?? Uint8List(aead.ivLength), chunkIndexData), adataBuffer, ) : cipher.decrypt( data.sublist(0, size), - cipher.getNonce( - iv ?? Uint8List(aead.ivLength), - chunkIndexData, - ), + cipher.getNonce(iv ?? Uint8List(aead.ivLength), chunkIndexData), adataBuffer, ), ); - /// We take a chunk of data, en/decrypt it, and shift `data` to the next chunk. data = data.sublist(size); adataBuffer.setAll(9, (++chunkIndex).pack32()); } @@ -224,7 +221,10 @@ class AeadEncryptedData extends ContainedPacket { final chunkIndexData = adataBuffer.sublist(5, 13); final adataTagBuffer = Uint8List(21); adataTagBuffer.setAll(0, adataBuffer); - adataTagBuffer.setAll(17, processed.pack32()); + adataTagBuffer.setAll( + 17, + processed.pack32(), + ); final finalCrypted = forEncryption ? cipher.encrypt( finalChunk ?? Uint8List(0), @@ -243,9 +243,6 @@ class AeadEncryptedData extends ContainedPacket { adataTagBuffer, ); - return Uint8List.fromList([ - ...crypted, - ...finalCrypted, - ]); + return Uint8List.fromList([...crypted, ...finalCrypted]); } } diff --git a/lib/src/packet/contained_packet.dart b/lib/src/packet/base.dart similarity index 57% rename from lib/src/packet/contained_packet.dart rename to lib/src/packet/base.dart index 9c1503c3..9bb06e36 100644 --- a/lib/src/packet/contained_packet.dart +++ b/lib/src/packet/base.dart @@ -1,40 +1,41 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:math'; import 'dart:typed_data'; -import '../enum/packet_tag.dart'; -import '../crypto/math/int_ext.dart'; +import '../common/extensions.dart'; +import '../enum/packet_type.dart'; +import '../type/packet.dart'; +/// Base packet abstract class /// Author Nguyen Van Nguyen -abstract class ContainedPacket { +abstract class BasePacket implements PacketInterface { static const partialMinSize = 512; static const partialMaxSize = 1024; - final PacketTag tag; - - ContainedPacket(this.tag); + @override + final PacketType type; - /// Serializes packet data to bytes - Uint8List toByteData(); + BasePacket(this.type); - /// Serializes packet to bytes + @override Uint8List encode() { - switch (tag) { - case PacketTag.aeadEncryptedData: - case PacketTag.compressedData: - case PacketTag.literalData: - case PacketTag.symEncryptedData: - case PacketTag.symEncryptedIntegrityProtectedData: + switch (type) { + case PacketType.aeadEncryptedData: + case PacketType.compressedData: + case PacketType.literalData: + case PacketType.symEncryptedData: + case PacketType.symEncryptedIntegrityProtectedData: return _partialEncode(); default: - final bodyData = toByteData(); return Uint8List.fromList([ - 0xc0 | tag.value, - ..._simpleLength(bodyData.length), - ...bodyData, + type.value | 0xc0, + ..._simpleLength(data.length), + ...data, ]); } } @@ -42,7 +43,7 @@ abstract class ContainedPacket { /// Encode package to the openpgp partial body specifier Uint8List _partialEncode() { final List partialData = []; - var bodyData = toByteData(); + var bodyData = data; var dataLengh = bodyData.length; while (dataLengh >= partialMinSize) { final maxSize = min(partialMaxSize, dataLengh); @@ -61,7 +62,7 @@ abstract class ContainedPacket { ..._simpleLength(dataLengh), ...bodyData, ]); - return Uint8List.fromList([0xc0 | tag.value, ...partialData]); + return Uint8List.fromList([0xc0 | type.value, ...partialData]); } List _simpleLength(int length) { diff --git a/lib/src/packet/compressed_data.dart b/lib/src/packet/compressed_data.dart index 58d10334..d8f48bee 100644 --- a/lib/src/packet/compressed_data.dart +++ b/lib/src/packet/compressed_data.dart @@ -1,22 +1,20 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:io'; import 'dart:typed_data'; import '../enum/compression_algorithm.dart'; -import '../enum/packet_tag.dart'; -import 'contained_packet.dart'; +import '../enum/packet_type.dart'; +import 'base.dart'; import 'packet_list.dart'; /// Implementation of the Compressed Data Packet (Tag 8) -/// -/// The Compressed Data packet contains compressed data. -/// Typically, this packet is found as the contents of an encrypted packet, -/// or following a Signature or One-Pass Signature packet, and contains a literal data packet. /// Author Nguyen Van Nguyen -class CompressedDataPacket extends ContainedPacket { +class CompressedDataPacket extends BasePacket { /// Default zip/zlib compression level, between 1 and 9 static const deflateLevel = 6; @@ -32,9 +30,9 @@ class CompressedDataPacket extends ContainedPacket { this.compressed, this.packets, { this.algorithm = CompressionAlgorithm.uncompressed, - }) : super(PacketTag.compressedData); + }) : super(PacketType.compressedData); - factory CompressedDataPacket.fromByteData(final Uint8List bytes) { + factory CompressedDataPacket.fromBytes(final Uint8List bytes) { final algorithm = CompressionAlgorithm.values.firstWhere( (algo) => algo.value == bytes[0], orElse: () => CompressionAlgorithm.uncompressed, @@ -50,56 +48,50 @@ class CompressedDataPacket extends ContainedPacket { factory CompressedDataPacket.fromPacketList( final PacketList packets, { final CompressionAlgorithm algorithm = CompressionAlgorithm.uncompressed, - }) { - return CompressedDataPacket( - _compress(packets, algorithm), - packets, - algorithm: algorithm, - ); - } + }) => + CompressedDataPacket( + _compress(packets, algorithm), + packets, + algorithm: algorithm, + ); @override - Uint8List toByteData() { - return Uint8List.fromList([algorithm.value, ...compressed]); - } + Uint8List get data => Uint8List.fromList([ + algorithm.value, + ...compressed, + ]); static Uint8List _compress( final PacketList packets, final CompressionAlgorithm algorithm, - ) { - switch (algorithm) { - case CompressionAlgorithm.zip: - case CompressionAlgorithm.zlib: - return Uint8List.fromList(ZLibCodec( - level: deflateLevel, - raw: algorithm == CompressionAlgorithm.zip, - ).encode(packets.encode())); - case CompressionAlgorithm.bzip2: - throw UnsupportedError( - 'Compression algorithm ${algorithm.name} is unsupported.', - ); - default: - return packets.encode(); - } - } + ) => + switch (algorithm) { + CompressionAlgorithm.zip || CompressionAlgorithm.zlib => Uint8List.fromList( + ZLibCodec( + level: deflateLevel, + raw: algorithm == CompressionAlgorithm.zip, + ).encode(packets.encode()), + ), + CompressionAlgorithm.bzip2 => throw UnsupportedError( + 'Compression algorithm ${algorithm.name} is unsupported.', + ), + _ => packets.encode(), + }; static PacketList _decompress( final Uint8List compressed, final CompressionAlgorithm algorithm, - ) { - switch (algorithm) { - case CompressionAlgorithm.zip: - case CompressionAlgorithm.zlib: - return PacketList.packetDecode(Uint8List.fromList(ZLibCodec( - level: deflateLevel, - raw: algorithm == CompressionAlgorithm.zip, - ).decode(compressed))); - case CompressionAlgorithm.bzip2: - throw UnsupportedError( - 'Compression algorithm ${algorithm.name} is unsupported.', - ); - default: - return PacketList.packetDecode(compressed); - } - } + ) => + switch (algorithm) { + CompressionAlgorithm.zip || CompressionAlgorithm.zlib => PacketList.decode( + Uint8List.fromList(ZLibCodec( + level: deflateLevel, + raw: algorithm == CompressionAlgorithm.zip, + ).decode(compressed)), + ), + CompressionAlgorithm.bzip2 => throw UnsupportedError( + 'Compression algorithm ${algorithm.name} is unsupported.', + ), + _ => PacketList.decode(compressed), + }; } diff --git a/lib/src/packet/image_attribute.dart b/lib/src/packet/image_attribute.dart deleted file mode 100644 index 80a985af..00000000 --- a/lib/src/packet/image_attribute.dart +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import 'user_attribute_subpacket.dart'; - -/// Author Nguyen Van Nguyen -class ImageAttributeSubpacket extends UserAttributeSubpacket { - static const jpeg = 1; - - ImageAttributeSubpacket(final Uint8List data, {super.isLong}) - : super(jpeg, data); - - factory ImageAttributeSubpacket.fromImageData( - final Uint8List imageData, { - final int imageType = jpeg, - }) { - return ImageAttributeSubpacket(Uint8List.fromList([ - 0x10, - 0x00, - 0x01, - imageType & 0xff, - ...Uint8List(12), - ...imageData, - ])); - } - - int get hdrLength => (data[1] << 8) | data[0]; - - int get version => data[2]; - - int get encoding => data[3]; - - Uint8List get imageData => data.sublist(hdrLength); -} diff --git a/lib/src/packet/image_user_attribute.dart b/lib/src/packet/image_user_attribute.dart new file mode 100644 index 00000000..cbbb3046 --- /dev/null +++ b/lib/src/packet/image_user_attribute.dart @@ -0,0 +1,42 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'user_attribute_subpacket.dart'; + +/// Implementation of the Image User Attribute Subpacket +/// Author Nguyen Van Nguyen +class ImageUserAttribute extends UserAttributeSubpacket { + static const jpeg = 1; + + ImageUserAttribute( + final Uint8List data, { + super.isLong, + }) : super(jpeg, data); + + factory ImageUserAttribute.fromBytes( + final Uint8List imageData, { + final int imageType = jpeg, + }) { + return ImageUserAttribute(Uint8List.fromList([ + 0x10, + 0x00, + 0x01, + imageType & 0xff, + ...Uint8List(12), + ...imageData, + ])); + } + + int get hdrLength => (data[1] << 8) | data[0]; + + int get version => data[2]; + + int get encoding => data[3]; + + Uint8List get imageData => data.sublist(hdrLength); +} diff --git a/lib/src/packet/key/aes_key_wrap.dart b/lib/src/packet/key/aes_key_wrap.dart deleted file mode 100644 index b2d14bdb..00000000 --- a/lib/src/packet/key/aes_key_wrap.dart +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'package:pointycastle/api.dart'; - -import 'key_wrap.dart'; - -/// An implementation of the AES Key Wrapper from the NIST Key Wrap Specification. -/// Author Nguyen Van Nguyen -class AesKeyWrap extends KeyWrap { - AesKeyWrap(final int keySize) : super(BlockCipher('AES/ECB'), keySize); -} diff --git a/lib/src/packet/key/aes_key_wrapper.dart b/lib/src/packet/key/aes_key_wrapper.dart new file mode 100644 index 00000000..a61d5475 --- /dev/null +++ b/lib/src/packet/key/aes_key_wrapper.dart @@ -0,0 +1,15 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'package:pointycastle/api.dart'; + +import 'key_wrapper.dart'; + +/// An implementation of the AES Key Wrapper from the NIST Key Wrap Specification. +/// Author Nguyen Van Nguyen +class AesKeyWrapper extends KeyWrapper { + AesKeyWrapper(final int keySize) : super(BlockCipher('AES/ECB'), keySize); +} diff --git a/lib/src/packet/key/camellia_key_wrap.dart b/lib/src/packet/key/camellia_key_wrap.dart deleted file mode 100644 index dbc60156..00000000 --- a/lib/src/packet/key/camellia_key_wrap.dart +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'package:pointycastle/export.dart'; - -import '../../crypto/symmetric/camellia.dart'; -import 'key_wrap.dart'; - -/// An implementation of the Camellia key wrapper based on RFC 3657/RFC 3394. -/// Author Nguyen Van Nguyen -class CamelliaKeyWrap extends KeyWrap { - CamelliaKeyWrap(final int keySize) - : super(ECBBlockCipher(CamelliaEngine()), keySize); -} diff --git a/lib/src/packet/key/camellia_key_wrapper.dart b/lib/src/packet/key/camellia_key_wrapper.dart new file mode 100644 index 00000000..5a7eb7fb --- /dev/null +++ b/lib/src/packet/key/camellia_key_wrapper.dart @@ -0,0 +1,20 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'package:pointycastle/block/modes/ecb.dart'; + +import '../../cryptor/symmetric/camellia.dart'; +import 'key_wrapper.dart'; + +/// An implementation of the Camellia key wrapper based on RFC 3657/RFC 3394. +/// Author Nguyen Van Nguyen +class CamelliaKeyWrapper extends KeyWrapper { + CamelliaKeyWrapper(final int keySize) + : super( + ECBBlockCipher(CamelliaEngine()), + keySize, + ); +} diff --git a/lib/src/packet/key/dsa_public_params.dart b/lib/src/packet/key/dsa_public_material.dart similarity index 71% rename from lib/src/packet/key/dsa_public_params.dart rename to lib/src/packet/key/dsa_public_material.dart index 66fddbf7..82b51d9a 100644 --- a/lib/src/packet/key/dsa_public_params.dart +++ b/lib/src/packet/key/dsa_public_material.dart @@ -1,20 +1,21 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; -import 'package:pointycastle/api.dart'; +import 'package:pointycastle/export.dart'; -import '../../crypto/math/big_int.dart'; -import '../../crypto/math/int_ext.dart'; -import '../../crypto/signer/dsa.dart'; +import '../../common/helpers.dart'; +import '../../cryptor/asymmetric/dsa.dart'; import '../../enum/hash_algorithm.dart'; -import '../../helpers.dart'; -import 'key_params.dart'; +import '../../type/public_key_material.dart'; +/// DSA public key material /// Author Nguyen Van Nguyen -class DSAPublicParams implements VerificationParams { +class DSAPublicMaterial implements PublicKeyMaterialInterface { /// DSA prime p final BigInt prime; @@ -29,7 +30,7 @@ class DSAPublicParams implements VerificationParams { final DSAPublicKey publicKey; - DSAPublicParams( + DSAPublicMaterial( this.prime, this.order, this.generator, @@ -41,7 +42,7 @@ class DSAPublicParams implements VerificationParams { generator, ); - factory DSAPublicParams.fromByteData(final Uint8List bytes) { + factory DSAPublicMaterial.fromBytes(final Uint8List bytes) { final prime = Helper.readMPI(bytes); var pos = prime.byteLength + 2; @@ -53,7 +54,7 @@ class DSAPublicParams implements VerificationParams { pos += generator.byteLength + 2; final exponent = Helper.readMPI(bytes.sublist(pos)); - return DSAPublicParams( + return DSAPublicMaterial( prime, order, generator, @@ -62,7 +63,10 @@ class DSAPublicParams implements VerificationParams { } @override - Uint8List encode() => Uint8List.fromList([ + int get keyLength => prime.bitLength; + + @override + Uint8List get toBytes => Uint8List.fromList([ ...prime.bitLength.pack16(), ...prime.toUnsignedBytes(), ...order.bitLength.pack16(), @@ -74,20 +78,19 @@ class DSAPublicParams implements VerificationParams { ]); @override - Future verify( + bool verify( final Uint8List message, final HashAlgorithm hash, final Uint8List signature, - ) async { + ) { + final r = Helper.readMPI(signature); + final s = Helper.readMPI(signature.sublist(r.byteLength + 2)); + final signer = DSASigner(Digest(hash.digestName)) ..init( false, PublicKeyParameter(publicKey), ); - - final r = Helper.readMPI(signature); - final s = Helper.readMPI(signature.sublist(r.byteLength + 2)); - return signer.verifySignature(message, DSASignature(r, s)); } } diff --git a/lib/src/packet/key/dsa_secret_material.dart b/lib/src/packet/key/dsa_secret_material.dart new file mode 100644 index 00000000..16239697 --- /dev/null +++ b/lib/src/packet/key/dsa_secret_material.dart @@ -0,0 +1,105 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; +import 'package:pointycastle/export.dart'; + +import '../../common/helpers.dart'; +import '../../cryptor/asymmetric/dsa.dart'; +import '../../enum/hash_algorithm.dart'; +import '../../type/signing_key_material.dart'; +import 'dsa_public_material.dart'; + +/// DSA secret key material +/// Author Nguyen Van Nguyen +class DSASecretMaterial implements SigningKeyMaterialInterface { + /// DSA secret exponent x + final BigInt exponent; + + /// DSA private key + final DSAPrivateKey privateKey; + + @override + final DSAPublicMaterial publicMaterial; + + DSASecretMaterial(this.exponent, this.publicMaterial) + : privateKey = DSAPrivateKey( + exponent, + publicMaterial.prime, + publicMaterial.order, + publicMaterial.generator, + ); + + factory DSASecretMaterial.fromBytes( + final Uint8List bytes, + final DSAPublicMaterial publicMaterial, + ) => + DSASecretMaterial(Helper.readMPI(bytes), publicMaterial); + + @override + int get keyLength => publicMaterial.keyLength; + + @override + Uint8List sign(Uint8List message, HashAlgorithm hash) { + final signer = DSASigner(Digest(hash.digestName)) + ..init( + true, + PrivateKeyParameter(privateKey), + ); + return signer.generateSignature(message).encode(); + } + + @override + Uint8List get toBytes => Uint8List.fromList([ + ...exponent.bitLength.pack16(), + ...exponent.toUnsignedBytes(), + ]); + + @override + bool get isValid { + // Check that 1 < g < p + if (publicMaterial.generator.compareTo(BigInt.one) <= 0 || + publicMaterial.generator.compareTo(publicMaterial.prime) >= 0) { + return false; + } + + // Check that subgroup order q divides p-1 + if (((publicMaterial.prime - BigInt.one) % publicMaterial.order).sign != 0) { + return false; + } + + // g has order q + // Check that g ** q = 1 mod p + if (publicMaterial.generator + .modPow( + publicMaterial.order, + publicMaterial.prime, + ) + .compareTo(BigInt.one) != + 0) { + return false; + } + + /// Check q is large + final qSize = publicMaterial.order.bitLength; + if (qSize < 150) { + return false; + } + + // Re-derive public key y' = g ** x mod p + // Expect y == y' + // Blinded exponentiation computes g**{rq + x} to compare to y + final r = Helper.randomBigInt( + BigInt.two << (qSize - 1), + BigInt.two << qSize, + ); + final rqx = (publicMaterial.order * r) + exponent; + return publicMaterial.exponent.compareTo( + publicMaterial.generator.modPow(rqx, publicMaterial.prime), + ) == + 0; + } +} diff --git a/lib/src/packet/key/dsa_secret_params.dart b/lib/src/packet/key/dsa_secret_params.dart deleted file mode 100644 index 76f6e247..00000000 --- a/lib/src/packet/key/dsa_secret_params.dart +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import 'package:pointycastle/api.dart'; - -import '../../crypto/math/big_int.dart'; -import '../../crypto/math/int_ext.dart'; -import '../../crypto/signer/dsa.dart'; -import '../../enum/hash_algorithm.dart'; -import '../../helpers.dart'; -import 'key_params.dart'; - -/// Author Nguyen Van Nguyen -class DSASecretParams extends KeyParams { - /// DSA secret exponent x - final BigInt exponent; - - DSASecretParams(this.exponent); - - factory DSASecretParams.fromByteData(final Uint8List bytes) => - DSASecretParams( - Helper.readMPI(bytes), - ); - - @override - Uint8List encode() => Uint8List.fromList([ - ...exponent.bitLength.pack16(), - ...exponent.toUnsignedBytes(), - ]); - - Future sign( - final DSAPublicParams publicParams, - final Uint8List message, - final HashAlgorithm hash, - ) async { - final signer = DSASigner(Digest(hash.digestName)) - ..init( - true, - PrivateKeyParameter(DSAPrivateKey( - exponent, - publicParams.prime, - publicParams.order, - publicParams.generator, - )), - ); - return signer.generateSignature(message).encode(); - } - - /// Validate DSA parameters - bool validatePublicParams(final DSAPublicParams publicParams) { - // Check that 1 < g < p - if (publicParams.generator.compareTo(BigInt.one) <= 0 || - publicParams.generator.compareTo(publicParams.prime) >= 0) { - return false; - } - - // Check that subgroup order q divides p-1 - if (((publicParams.prime - BigInt.one) % publicParams.order).sign != 0) { - return false; - } - - // g has order q - // Check that g ** q = 1 mod p - if (publicParams.generator - .modPow(publicParams.order, publicParams.prime) - .compareTo(BigInt.one) != - 0) { - return false; - } - - // Check q is large and probably prime (we mainly want to avoid small factors) - final qSize = publicParams.order.bitLength; - if (qSize < 150 || !(publicParams.order.isProbablePrime(32))) { - return false; - } - - // Re-derive public key y' = g ** x mod p - // Expect y == y' - // Blinded exponentiation computes g**{rq + x} to compare to y - final r = Helper.randomBigInt( - BigInt.two << (qSize - 1), - BigInt.two << qSize, - ); - final rqx = (publicParams.order * r) + exponent; - return publicParams.exponent.compareTo( - publicParams.generator.modPow(rqx, publicParams.prime), - ) == - 0; - } -} diff --git a/lib/src/packet/key/ec_public_material.dart b/lib/src/packet/key/ec_public_material.dart new file mode 100644 index 00000000..91941ecd --- /dev/null +++ b/lib/src/packet/key/ec_public_material.dart @@ -0,0 +1,42 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; +import 'package:pointycastle/pointycastle.dart'; + +import '../../common/extensions.dart'; +import '../../enum/ecc.dart'; +import '../../type/key_material.dart'; + +/// Abstract ec public key material +/// Author Nguyen Van Nguyen +abstract class ECPublicMaterial implements KeyMaterialInterface { + /// The curve OID + final ASN1ObjectIdentifier oid; + + /// Ecc point public key + final BigInt q; + + /// Ecc curve + final Ecc curve; + + ECPublicMaterial(this.oid, this.q) + : curve = Ecc.values.firstWhere( + (ecc) => ecc.identifierString == oid.objectIdentifierAsString, + ); + + @override + int get keyLength => q.bitLength; + + @override + Uint8List get toBytes { + return Uint8List.fromList([ + ...oid.encode().sublist(1), + ...q.bitLength.pack16(), + ...q.toUnsignedBytes(), + ]); + } +} diff --git a/lib/src/packet/key/ec_public_params.dart b/lib/src/packet/key/ec_public_params.dart deleted file mode 100644 index ea70b318..00000000 --- a/lib/src/packet/key/ec_public_params.dart +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import 'package:pointycastle/pointycastle.dart'; - -import '../../crypto/math/big_int.dart'; -import '../../crypto/math/int_ext.dart'; -import '../../enum/curve_info.dart'; -import 'key_params.dart'; - -/// Author Nguyen Van Nguyen -abstract class ECPublicParams extends KeyParams { - final ASN1ObjectIdentifier oid; - - final BigInt q; - - final CurveInfo curve; - - ECPublicParams(this.oid, this.q) - : curve = CurveInfo.values.firstWhere( - (info) => info.identifierString == oid.objectIdentifierAsString, - ); - - @override - Uint8List encode() { - return Uint8List.fromList([ - ...oid.encode().sublist(1), - ...q.bitLength.pack16(), - ...q.toUnsignedBytes(), - ]); - } - - static validateOidLength(final int length) { - if (length == 0 || length == 0xff) { - throw Exception('Future extensions not yet implemented'); - } - if (length > 127) { - throw UnsupportedError('Unsupported OID'); - } - } -} diff --git a/lib/src/packet/key/ec_secret_material.dart b/lib/src/packet/key/ec_secret_material.dart new file mode 100644 index 00000000..172c4a0c --- /dev/null +++ b/lib/src/packet/key/ec_secret_material.dart @@ -0,0 +1,39 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; +import 'package:pointycastle/pointycastle.dart'; + +import '../../common/helpers.dart'; +import '../../type/key_material.dart'; + +/// Abstract ec secret key material +/// Author Nguyen Van Nguyen +abstract class ECSecretMaterial implements KeyMaterialInterface { + /// ECC's d private parameter + final BigInt d; + + ECSecretMaterial(this.d); + + @override + Uint8List get toBytes => Uint8List.fromList([ + ...d.bitLength.pack16(), + ...d.toUnsignedBytes(), + ]); + + static AsymmetricKeyPair generateKeyPair(final String curve) { + final keyGen = KeyGenerator('EC') + ..init( + ParametersWithRandom( + ECKeyGeneratorParameters( + ECDomainParameters(curve.toLowerCase()), + ), + Helper.secureRandom(), + ), + ); + return keyGen.generateKeyPair(); + } +} diff --git a/lib/src/packet/key/ec_secret_params.dart b/lib/src/packet/key/ec_secret_params.dart deleted file mode 100644 index f123b962..00000000 --- a/lib/src/packet/key/ec_secret_params.dart +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import 'package:pinenacl/api.dart' as nacl; -import 'package:pointycastle/export.dart'; - -import '../../crypto/math/big_int.dart'; -import '../../crypto/math/byte_ext.dart'; -import '../../crypto/math/int_ext.dart'; -import '../../enum/curve_info.dart'; -import '../../enum/hash_algorithm.dart'; -import '../../helpers.dart'; -import 'key_params.dart'; - -/// Author Nguyen Van Nguyen -class ECSecretParams extends KeyParams { - /// ECC's d private parameter - final BigInt d; - - ECSecretParams(this.d); - - factory ECSecretParams.fromByteData(final Uint8List bytes) => ECSecretParams( - Helper.readMPI(bytes), - ); - - @override - Uint8List encode() => Uint8List.fromList([ - ...d.bitLength.pack16(), - ...d.toUnsignedBytes(), - ]); - - Future sign( - final ECPublicParams publicParams, - final Uint8List message, - final HashAlgorithm hash, - ) async { - final signer = Signer('${hash.digestName}/DET-ECDSA') - ..init( - true, - PrivateKeyParameter( - ECPrivateKey( - d, - ECDomainParameters( - publicParams.curve.name.toLowerCase(), - ), - ), - ), - ); - final signature = signer.generateSignature(message) as ECSignature; - return Uint8List.fromList([ - ...signature.r.bitLength.pack16(), - ...signature.r.toUnsignedBytes(), - ...signature.s.bitLength.pack16(), - ...signature.s.toUnsignedBytes(), - ]); - } - - /// Validate EC parameters - bool validatePublicParams(final ECPublicParams publicParams) { - switch (publicParams.curve) { - case CurveInfo.curve25519: - final privateKey = nacl.PrivateKey( - Uint8List.fromList(d.toUnsignedBytes().reversed.toList()), - ); - final dG = Uint8List.fromList([ - 0x40, - ...privateKey.publicKey.asTypedList, - ]); - return publicParams.q.compareTo(dG.toBigIntWithSign(1)) == 0; - case CurveInfo.ed25519: - return false; - default: - final parameters = - ECDomainParameters(publicParams.curve.name.toLowerCase()); - final q = - parameters.curve.decodePoint(publicParams.q.toUnsignedBytes()); - return q != null && !q.isInfinity && (parameters.G * d) == q; - } - } -} diff --git a/lib/src/packet/key/ecdh_public_params.dart b/lib/src/packet/key/ecdh_public_material.dart similarity index 63% rename from lib/src/packet/key/ecdh_public_params.dart rename to lib/src/packet/key/ecdh_public_material.dart index 6de13e7b..057481fc 100644 --- a/lib/src/packet/key/ecdh_public_params.dart +++ b/lib/src/packet/key/ecdh_public_material.dart @@ -1,37 +1,32 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. -import 'dart:typed_data'; +library; +import 'dart:typed_data'; import 'package:pointycastle/pointycastle.dart'; -import '../../crypto/math/big_int.dart'; +import 'ec_public_material.dart'; +import '../../common/helpers.dart'; import '../../enum/hash_algorithm.dart'; import '../../enum/symmetric_algorithm.dart'; -import '../../helpers.dart'; -import 'ec_public_params.dart'; +/// ECDH public key material /// Author Nguyen Van Nguyen -class ECDHPublicParams extends ECPublicParams { +class ECDHPublicMaterial extends ECPublicMaterial { final int reserved; + /// Hash algorithm used with the KDF final HashAlgorithm kdfHash; + /// symmetric algorithm used to + /// wrap the symmetric key for message encryption final SymmetricAlgorithm kdfSymmetric; - ECDHPublicParams( - super.oid, - super.q, - this.kdfHash, - this.kdfSymmetric, [ - this.reserved = 0x1, - ]); - - factory ECDHPublicParams.fromByteData(final Uint8List bytes) { + factory ECDHPublicMaterial.fromBytes(final Uint8List bytes) { var pos = 0; final length = bytes[pos++]; - ECPublicParams.validateOidLength(length); final oid = ASN1ObjectIdentifier.fromBytes(Uint8List.fromList([ 0x06, @@ -51,7 +46,7 @@ class ECDHPublicParams extends ECPublicParams { final kdfSymmetric = SymmetricAlgorithm.values.firstWhere( (sym) => sym.value == kdfBytes[3], ); - return ECDHPublicParams( + return ECDHPublicMaterial( oid, q, kdfHash, @@ -60,9 +55,17 @@ class ECDHPublicParams extends ECPublicParams { ); } + ECDHPublicMaterial( + super.oid, + super.q, + this.kdfHash, + this.kdfSymmetric, [ + this.reserved = 0x1, + ]); + @override - Uint8List encode() => Uint8List.fromList([ - ...super.encode(), + Uint8List get toBytes => Uint8List.fromList([ + ...super.toBytes, 0x3, reserved, kdfHash.value, diff --git a/lib/src/packet/key/ecdh_secret_material.dart b/lib/src/packet/key/ecdh_secret_material.dart new file mode 100644 index 00000000..b175d29b --- /dev/null +++ b/lib/src/packet/key/ecdh_secret_material.dart @@ -0,0 +1,111 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; +import 'package:pinenacl/ed25519.dart' as nacl; +import 'package:pinenacl/tweetnacl.dart'; +import 'package:pointycastle/pointycastle.dart'; + +import '../../common/helpers.dart'; +import '../../enum/ecc.dart'; +import '../../type/secret_key_material.dart'; +import 'ec_secret_material.dart'; +import 'ecdh_public_material.dart'; + +/// ECDH secret key material +/// Author Nguyen Van Nguyen +class ECDHSecretMaterial extends ECSecretMaterial implements SecretKeyMaterialInterface { + @override + final ECDHPublicMaterial publicMaterial; + + ECDHSecretMaterial(super.d, this.publicMaterial); + + factory ECDHSecretMaterial.fromBytes( + final Uint8List bytes, + final ECDHPublicMaterial publicMaterial, + ) => + ECDHSecretMaterial( + Helper.readMPI(bytes), + publicMaterial, + ); + + factory ECDHSecretMaterial.generate(Ecc curve) { + switch (curve) { + case Ecc.curve25519: + final secret = Helper.secureRandom().nextBytes( + TweetNaCl.secretKeyLength, + ); + + /// The highest bit must be 0 & the second highest bit must be 1 + secret[0] = (secret[0] & 0x7F) | 0x40; + + /// The lowest three bits must be 0 + secret[TweetNaCl.secretKeyLength - 1] &= 0xF8; + final privateKey = nacl.PrivateKey( + Uint8List.fromList(secret.reversed.toList()), + ); + return ECDHSecretMaterial( + Uint8List.fromList( + privateKey.asTypedList.reversed.toList(), + ).toBigIntWithSign(1), + ECDHPublicMaterial( + curve.asn1Oid, + Uint8List.fromList([ + 0x40, + ...privateKey.publicKey.asTypedList, + ]).toBigIntWithSign(1), + curve.hashAlgorithm, + curve.symmetricAlgorithm, + ), + ); + case Ecc.ed25519: + throw UnsupportedError( + 'Curve ${curve.name} is unsupported for ECDH key generation.', + ); + default: + final keyPair = ECSecretMaterial.generateKeyPair(curve.name); + final privateKey = keyPair.privateKey as ECPrivateKey; + final q = (keyPair.publicKey as ECPublicKey).Q!; + return ECDHSecretMaterial( + privateKey.d!, + ECDHPublicMaterial( + curve.asn1Oid, + q + .getEncoded( + q.isCompressed, + ) + .toBigIntWithSign(1), + curve.hashAlgorithm, + curve.symmetricAlgorithm, + ), + ); + } + } + + @override + int get keyLength => publicMaterial.keyLength; + + @override + bool get isValid { + switch (publicMaterial.curve) { + case Ecc.curve25519: + final privateKey = nacl.PrivateKey( + Uint8List.fromList(d.toUnsignedBytes().reversed.toList()), + ); + final dG = Uint8List.fromList([ + 0x40, + ...privateKey.publicKey.asTypedList, + ]); + return publicMaterial.q.compareTo(dG.toBigIntWithSign(1)) == 0; + case Ecc.ed25519: + return false; + default: + final parameters = ECDomainParameters(publicMaterial.curve.name.toLowerCase()); + final q = parameters.curve.decodePoint(publicMaterial.q.toUnsignedBytes()); + return q != null && !q.isInfinity && (parameters.G * d) == q; + } + } +} diff --git a/lib/src/packet/key/ecdh_session_key_params.dart b/lib/src/packet/key/ecdh_session_key_cryptor.dart similarity index 51% rename from lib/src/packet/key/ecdh_session_key_params.dart rename to lib/src/packet/key/ecdh_session_key_cryptor.dart index ff27e5c6..78798f7e 100644 --- a/lib/src/packet/key/ecdh_session_key_params.dart +++ b/lib/src/packet/key/ecdh_session_key_cryptor.dart @@ -1,6 +1,8 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; @@ -8,23 +10,20 @@ import 'package:pinenacl/api.dart' as nacl; import 'package:pinenacl/tweetnacl.dart'; import 'package:pointycastle/export.dart'; -import '../../crypto/math/big_int.dart'; -import '../../crypto/math/byte_ext.dart'; -import '../../crypto/math/int_ext.dart'; -import '../../enum/curve_info.dart'; +import '../../common/helpers.dart'; +import '../../enum/ecc.dart'; import '../../enum/hash_algorithm.dart'; import '../../enum/key_algorithm.dart'; import '../../enum/symmetric_algorithm.dart'; -import '../../helpers.dart'; -import 'ec_secret_params.dart'; -import 'ecdh_public_params.dart'; -import 'key_wrap.dart'; -import 'session_key.dart'; -import 'session_key_params.dart'; - -/// Algorithm Specific Params for ECDH encryption +import '../../type/secret_key_material.dart'; +import 'ecdh_public_material.dart'; +import 'ecdh_secret_material.dart'; +import 'key_wrapper.dart'; +import 'session_key_cryptor.dart'; + +/// ECDH session key cryptor class /// Author Nguyen Van Nguyen -class ECDHSessionKeyParams extends SessionKeyParams { +class ECDHSessionKeyCryptor extends SessionKeyCryptor { /// 20 octets representing the UTF-8 encoding of the string 'Anonymous Sender ' static const _anonymousSender = [ 0x41, 0x6e, 0x6f, 0x6e, // 0 - 3 @@ -37,34 +36,39 @@ class ECDHSessionKeyParams extends SessionKeyParams { /// MPI containing the ephemeral key used to establish the shared secret final BigInt ephemeralKey; - /// ECDH symmetric key + /// ECDH wrapped key final Uint8List wrappedKey; - ECDHSessionKeyParams(this.ephemeralKey, this.wrappedKey); + /// OpenPGP public key fingerprint + Uint8List fingerprint = Uint8List(0); + + ECDHSessionKeyCryptor(this.ephemeralKey, this.wrappedKey); - factory ECDHSessionKeyParams.fromByteData(Uint8List bytes) { + factory ECDHSessionKeyCryptor.fromBytes( + final Uint8List bytes, + ) { final ephemeralKey = Helper.readMPI(bytes); final pos = ephemeralKey.byteLength + 2; final length = bytes[pos]; - return ECDHSessionKeyParams( + return ECDHSessionKeyCryptor( ephemeralKey, bytes.sublist(pos + 1, pos + length + 1), ); } - static Future encryptSessionKey( - final ECDHPublicParams publicParams, - final SessionKey sessionKey, + factory ECDHSessionKeyCryptor.encryptSessionKey( + final Uint8List sessionKey, + final ECDHPublicMaterial key, final Uint8List fingerprint, - ) async { + ) { final BigInt ephemeralKey; final Uint8List sharedKey; /// Generate the ephemeral key - switch (publicParams.curve) { - case CurveInfo.curve25519: + switch (key.curve) { + case Ecc.curve25519: final privateKey = nacl.PrivateKey.fromSeed( Helper.secureRandom().nextBytes(TweetNaCl.seedSize), ); @@ -72,16 +76,16 @@ class ECDHSessionKeyParams extends SessionKeyParams { sharedKey = TweetNaCl.crypto_scalarmult( Uint8List(TweetNaCl.sharedKeyLength), privateKey.asTypedList, - publicParams.q.toUnsignedBytes().sublist(1), + key.q.toUnsignedBytes().sublist(1), ); break; - case CurveInfo.ed25519: + case Ecc.ed25519: throw UnsupportedError( - 'Curve ${publicParams.curve.name} is unsupported for ephemeral key generation.', + 'Curve ${key.curve.name} is unsupported for ephemeral key generation.', ); default: final parameters = ECDomainParameters( - publicParams.curve.name.toLowerCase(), + key.curve.name.toLowerCase(), ); final keyGen = KeyGenerator('EC') ..init( @@ -97,92 +101,86 @@ class ECDHSessionKeyParams extends SessionKeyParams { ); sharedKey = agreement .calculateAgreement(ECPublicKey( - parameters.curve.decodePoint(publicParams.q.toUnsignedBytes()), + parameters.curve.decodePoint(key.q.toUnsignedBytes()), parameters, )) .toUnsignedBytes(); final publicKey = keyPair.publicKey as ECPublicKey; ephemeralKey = publicKey.Q!.getEncoded(false).toBigIntWithSign(1); } - - final keyWrapper = _selectKeyWrapper(publicParams.kdfSymmetric); - final wrappedKey = await keyWrapper.wrap( - _kdf( - publicParams.kdfHash, - sharedKey, - _ecdhParam(publicParams, fingerprint), - (publicParams.kdfSymmetric.keySize + 7) >> 3, + final keyWrapper = _selectKeyWrapper(key.kdfSymmetric); + return ECDHSessionKeyCryptor( + ephemeralKey, + keyWrapper.wrap( + _kdf( + key.kdfHash, + sharedKey, + _ecdhParam(key, fingerprint), + (key.kdfSymmetric.keySize + 7) >> 3, + ), + _pkcs5Encode(sessionKey), ), - _pkcs5Encode(Uint8List.fromList([ - ...sessionKey.encode(), - ...sessionKey.computeChecksum(), - ])), ); + } - return ECDHSessionKeyParams( - ephemeralKey, - wrappedKey, - ); + @override + Uint8List decrypt(SecretKeyMaterialInterface key) { + if (key is ECDHSecretMaterial) { + final Uint8List sharedKey; + switch (key.publicMaterial.curve) { + case Ecc.curve25519: + sharedKey = TweetNaCl.crypto_scalarmult( + Uint8List(TweetNaCl.sharedKeyLength), + Uint8List.fromList( + key.d.toUnsignedBytes().reversed.toList(), + ), + ephemeralKey.toUnsignedBytes(), + ); + break; + case Ecc.ed25519: + throw UnsupportedError( + 'Curve ${key.publicMaterial.curve.name} is unsupported for key agreement calculation.', + ); + default: + final parameters = ECDomainParameters( + key.publicMaterial.curve.name.toLowerCase(), + ); + final privateKey = ECPrivateKey(key.d, parameters); + final agreement = ECDHBasicAgreement()..init(privateKey); + sharedKey = agreement + .calculateAgreement( + ECPublicKey( + parameters.curve.decodePoint(ephemeralKey.toUnsignedBytes()), + parameters, + ), + ) + .toUnsignedBytes(); + } + final keyWrapper = _selectKeyWrapper(key.publicMaterial.kdfSymmetric); + return _pkcs5Decode( + keyWrapper.unwrap( + _kdf( + key.publicMaterial.kdfHash, + sharedKey, + _ecdhParam(key.publicMaterial, fingerprint), + (key.publicMaterial.kdfSymmetric.keySize + 7) >> 3, + ), + wrappedKey, + ), + ); + } else { + throw ArgumentError('Secret key material is not ECDH key.'); + } } @override Uint8List encode() => Uint8List.fromList([ ...ephemeralKey.bitLength.pack16(), ...ephemeralKey.toUnsignedBytes(), - wrappedKey.lengthInBytes, + wrappedKey.length, ...wrappedKey, ]); - Future decrypt( - final ECSecretParams secretParams, - final ECDHPublicParams publicParams, - final Uint8List fingerprint, - ) async { - final Uint8List sharedKey; - switch (publicParams.curve) { - case CurveInfo.curve25519: - sharedKey = TweetNaCl.crypto_scalarmult( - Uint8List(TweetNaCl.sharedKeyLength), - Uint8List.fromList( - secretParams.d.toUnsignedBytes().reversed.toList(), - ), - ephemeralKey.toUnsignedBytes(), - ); - break; - case CurveInfo.ed25519: - throw UnsupportedError( - 'Curve ${publicParams.curve.name} is unsupported for key agreement calculation.', - ); - default: - final parameters = ECDomainParameters( - publicParams.curve.name.toLowerCase(), - ); - final privateKey = ECPrivateKey(secretParams.d, parameters); - final agreement = ECDHBasicAgreement()..init(privateKey); - sharedKey = agreement - .calculateAgreement( - ECPublicKey( - parameters.curve.decodePoint(ephemeralKey.toUnsignedBytes()), - parameters, - ), - ) - .toUnsignedBytes(); - } - - final keyWrapper = _selectKeyWrapper(publicParams.kdfSymmetric); - return decodeSessionKey(_pkcs5Decode( - await keyWrapper.unwrap( - _kdf( - publicParams.kdfHash, - sharedKey, - _ecdhParam(publicParams, fingerprint), - (publicParams.kdfSymmetric.keySize + 7) >> 3, - ), - wrappedKey, - ), - )); - } - /// Key Derivation Function (RFC 6637) static Uint8List _kdf( final HashAlgorithm hash, @@ -204,18 +202,18 @@ class ECDHSessionKeyParams extends SessionKeyParams { /// Build Param for ECDH algorithm (RFC 6637) static Uint8List _ecdhParam( - final ECDHPublicParams publicParams, + final ECDHPublicMaterial publicMaterial, final Uint8List fingerprint, ) => Uint8List.fromList([ - ...publicParams.oid.encode().sublist(1), + ...publicMaterial.oid.encode().sublist(1), KeyAlgorithm.ecdh.value, 0x3, - publicParams.reserved, - publicParams.kdfHash.value, - publicParams.kdfSymmetric.value, + publicMaterial.reserved, + publicMaterial.kdfHash.value, + publicMaterial.kdfSymmetric.value, ..._anonymousSender, - ...fingerprint.sublist(0, 20), + ...fingerprint, ]); /// Add pkcs5 padding to a message @@ -242,14 +240,14 @@ class ECDHSessionKeyParams extends SessionKeyParams { return Uint8List(0); } - static KeyWrap _selectKeyWrapper(final SymmetricAlgorithm symmetric) { + static KeyWrapper _selectKeyWrapper(final SymmetricAlgorithm symmetric) { switch (symmetric) { case SymmetricAlgorithm.camellia128: case SymmetricAlgorithm.camellia192: case SymmetricAlgorithm.camellia256: - return CamelliaKeyWrap((symmetric.keySize + 7) >> 3); + return CamelliaKeyWrapper((symmetric.keySize + 7) >> 3); default: - return AesKeyWrap((symmetric.keySize + 7) >> 3); + return AesKeyWrapper((symmetric.keySize + 7) >> 3); } } } diff --git a/lib/src/packet/key/ecdsa_public_params.dart b/lib/src/packet/key/ecdsa_public_material.dart similarity index 61% rename from lib/src/packet/key/ecdsa_public_params.dart rename to lib/src/packet/key/ecdsa_public_material.dart index 252fbe24..8811701d 100644 --- a/lib/src/packet/key/ecdsa_public_params.dart +++ b/lib/src/packet/key/ecdsa_public_material.dart @@ -1,25 +1,25 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. -import 'dart:typed_data'; +library; +import 'dart:typed_data'; import 'package:pointycastle/pointycastle.dart'; -import '../../crypto/math/big_int.dart'; +import '../../common/helpers.dart'; import '../../enum/hash_algorithm.dart'; -import '../../helpers.dart'; -import 'ec_public_params.dart'; -import 'verification_params.dart'; +import '../../packet/key/ec_public_material.dart'; +import '../../type/public_key_material.dart'; +/// ECDSA public key material /// Author Nguyen Van Nguyen -class ECDSAPublicParams extends ECPublicParams implements VerificationParams { - ECDSAPublicParams(super.oid, super.q); +class ECDSAPublicMaterial extends ECPublicMaterial implements PublicKeyMaterialInterface { + ECDSAPublicMaterial(super.oid, super.q); - factory ECDSAPublicParams.fromByteData(final Uint8List bytes) { + factory ECDSAPublicMaterial.fromBytes(final Uint8List bytes) { final length = bytes[0]; - ECPublicParams.validateOidLength(length); - return ECDSAPublicParams( + return ECDSAPublicMaterial( ASN1ObjectIdentifier.fromBytes(Uint8List.fromList([ 0x06, length, @@ -30,11 +30,11 @@ class ECDSAPublicParams extends ECPublicParams implements VerificationParams { } @override - Future verify( + bool verify( final Uint8List message, final HashAlgorithm hash, final Uint8List signature, - ) async { + ) { final parameters = ECDomainParameters(curve.name.toLowerCase()); final signer = Signer('${hash.digestName}/DET-ECDSA') ..init( diff --git a/lib/src/packet/key/ecdsa_secret_material.dart b/lib/src/packet/key/ecdsa_secret_material.dart new file mode 100644 index 00000000..35d973fe --- /dev/null +++ b/lib/src/packet/key/ecdsa_secret_material.dart @@ -0,0 +1,91 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; +import 'package:pointycastle/pointycastle.dart'; + +import '../../common/helpers.dart'; +import '../../enum/ecc.dart'; +import '../../enum/hash_algorithm.dart'; +import '../../type/signing_key_material.dart'; +import 'ec_secret_material.dart'; +import 'ecdsa_public_material.dart'; + +/// ECDSA secret key material +/// Author Nguyen Van Nguyen +class ECDSASecretMaterial extends ECSecretMaterial implements SigningKeyMaterialInterface { + @override + final ECDSAPublicMaterial publicMaterial; + + ECDSASecretMaterial(super.d, this.publicMaterial); + + factory ECDSASecretMaterial.fromBytes( + final Uint8List bytes, + final ECDSAPublicMaterial publicMaterial, + ) => + ECDSASecretMaterial( + Helper.readMPI(bytes), + publicMaterial, + ); + + factory ECDSASecretMaterial.generate(Ecc curve) { + switch (curve) { + case Ecc.curve25519: + case Ecc.ed25519: + throw UnsupportedError( + 'Curve ${curve.name} is unsupported for ECDSA key generation.', + ); + default: + final keyPair = ECSecretMaterial.generateKeyPair(curve.name); + final privateKey = keyPair.privateKey as ECPrivateKey; + final q = (keyPair.publicKey as ECPublicKey).Q!; + return ECDSASecretMaterial( + privateKey.d!, + ECDSAPublicMaterial( + curve.asn1Oid, + q + .getEncoded( + q.isCompressed, + ) + .toBigIntWithSign(1), + ), + ); + } + } + + @override + bool get isValid { + final parameters = ECDomainParameters(publicMaterial.curve.name.toLowerCase()); + final q = parameters.curve.decodePoint(publicMaterial.q.toUnsignedBytes()); + return q != null && !q.isInfinity && (parameters.G * d) == q; + } + + @override + int get keyLength => publicMaterial.keyLength; + + @override + Uint8List sign(final Uint8List message, final HashAlgorithm hash) { + final signer = Signer('${hash.digestName}/DET-ECDSA') + ..init( + true, + PrivateKeyParameter( + ECPrivateKey( + d, + ECDomainParameters( + publicMaterial.curve.name.toLowerCase(), + ), + ), + ), + ); + final signature = signer.generateSignature(message) as ECSignature; + return Uint8List.fromList([ + ...signature.r.bitLength.pack16(), + ...signature.r.toUnsignedBytes(), + ...signature.s.bitLength.pack16(), + ...signature.s.toUnsignedBytes(), + ]); + } +} diff --git a/lib/src/packet/key/ed_secret_params.dart b/lib/src/packet/key/ed_secret_params.dart deleted file mode 100644 index ee20ba4b..00000000 --- a/lib/src/packet/key/ed_secret_params.dart +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'package:pinenacl/ed25519.dart'; - -import '../../crypto/math/big_int.dart'; -import '../../crypto/math/byte_ext.dart'; -import '../../crypto/math/int_ext.dart'; -import '../../enum/hash_algorithm.dart'; -import '../../helpers.dart'; -import 'key_params.dart'; - -/// Author Nguyen Van Nguyen -class EdSecretParams extends KeyParams { - /// Ed's seed parameter - final BigInt seed; - - EdSecretParams(this.seed); - - factory EdSecretParams.fromByteData(final Uint8List bytes) => EdSecretParams( - Helper.readMPI(bytes), - ); - - @override - Uint8List encode() => Uint8List.fromList([ - ...seed.bitLength.pack16(), - ...seed.toUnsignedBytes(), - ]); - - Future sign( - final Uint8List message, - final HashAlgorithm hash, - ) async { - final signed = SigningKey.fromSeed(seed.toUnsignedBytes()).sign( - Helper.hashDigest(message, hash), - ); - final bitLength = (SignedMessage.signatureLength * 4).pack16(); - return Uint8List.fromList([ - ...bitLength, // r bit length - ...signed.signature.sublist(0, SignedMessage.signatureLength ~/ 2), // r - ...bitLength, // s bit length - ...signed.signature.sublist(SignedMessage.signatureLength ~/ 2), // s - ]); - } - - /// Validate EdDSA parameters - bool validatePublicParams(final EdDSAPublicParams publicParams) { - final signingKey = SigningKey.fromSeed(seed.toUnsignedBytes()); - final dG = Uint8List.fromList([ - 0x40, - ...signingKey.verifyKey.asTypedList, - ]); - return publicParams.q.compareTo(dG.toBigIntWithSign(1)) == 0; - } -} diff --git a/lib/src/packet/key/eddsa_public_params.dart b/lib/src/packet/key/eddsa_legacy_public_material.dart similarity index 57% rename from lib/src/packet/key/eddsa_public_params.dart rename to lib/src/packet/key/eddsa_legacy_public_material.dart index 93d89207..fe918a9a 100644 --- a/lib/src/packet/key/eddsa_public_params.dart +++ b/lib/src/packet/key/eddsa_legacy_public_material.dart @@ -1,23 +1,25 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'package:pinenacl/ed25519.dart'; import 'package:pointycastle/asn1.dart'; -import '../../crypto/math/big_int.dart'; +import '../../common/helpers.dart'; import '../../enum/hash_algorithm.dart'; -import '../../helpers.dart'; -import 'key_params.dart'; +import '../../type/public_key_material.dart'; +import 'ec_public_material.dart'; +/// EdDSA legacy public key material /// Author Nguyen Van Nguyen -class EdDSAPublicParams extends ECPublicParams implements VerificationParams { - EdDSAPublicParams(super.oid, super.q); +class EdDSALegacyPublicMaterial extends ECPublicMaterial implements PublicKeyMaterialInterface { + EdDSALegacyPublicMaterial(super.oid, super.q); - factory EdDSAPublicParams.fromByteData(final Uint8List bytes) { + factory EdDSALegacyPublicMaterial.fromBytes(final Uint8List bytes) { final length = bytes[0]; - ECPublicParams.validateOidLength(length); - return EdDSAPublicParams( + return EdDSALegacyPublicMaterial( ASN1ObjectIdentifier.fromBytes(Uint8List.fromList([ 0x06, length, @@ -28,11 +30,11 @@ class EdDSAPublicParams extends ECPublicParams implements VerificationParams { } @override - Future verify( + bool verify( final Uint8List message, final HashAlgorithm hash, final Uint8List signature, - ) async { + ) { final r = Helper.readMPI(signature); final s = Helper.readMPI(signature.sublist(r.byteLength + 2)); diff --git a/lib/src/packet/key/eddsa_legacy_secret_material.dart b/lib/src/packet/key/eddsa_legacy_secret_material.dart new file mode 100644 index 00000000..ccfd0a04 --- /dev/null +++ b/lib/src/packet/key/eddsa_legacy_secret_material.dart @@ -0,0 +1,83 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'package:pinenacl/ed25519.dart' as nacl; +import 'package:pinenacl/tweetnacl.dart'; + +import '../../common/helpers.dart'; +import '../../enum/ecc.dart'; +import '../../enum/hash_algorithm.dart'; +import '../../type/signing_key_material.dart'; +import 'eddsa_legacy_public_material.dart'; + +/// EdDSA legacy secret key material +/// Author Nguyen Van Nguyen +class EdDSALegacySecretMaterial implements SigningKeyMaterialInterface { + /// Ed's seed parameter + final BigInt seed; + + @override + final EdDSALegacyPublicMaterial publicMaterial; + + EdDSALegacySecretMaterial(this.seed, this.publicMaterial); + + factory EdDSALegacySecretMaterial.fromBytes( + final Uint8List bytes, + final EdDSALegacyPublicMaterial publicMaterial, + ) => + EdDSALegacySecretMaterial( + Helper.readMPI(bytes), + publicMaterial, + ); + + factory EdDSALegacySecretMaterial.generate() { + final seed = Helper.secureRandom().nextBytes(TweetNaCl.seedSize); + return EdDSALegacySecretMaterial( + seed.toBigIntWithSign(1), + EdDSALegacyPublicMaterial( + Ecc.ed25519.asn1Oid, + Uint8List.fromList([ + 0x40, + ...nacl.SigningKey.fromSeed(seed).verifyKey.asTypedList, + ]).toBigIntWithSign(1)), + ); + } + + @override + bool get isValid { + final signingKey = nacl.SigningKey.fromSeed(seed.toUnsignedBytes()); + final dG = Uint8List.fromList([ + 0x40, + ...signingKey.verifyKey.asTypedList, + ]); + return publicMaterial.q.compareTo(dG.toBigIntWithSign(1)) == 0; + } + + @override + int get keyLength => publicMaterial.keyLength; + + @override + Uint8List sign(Uint8List message, HashAlgorithm hash) { + final signed = nacl.SigningKey.fromSeed(seed.toUnsignedBytes()).sign( + Helper.hashDigest(message, hash), + ); + final bitLength = (nacl.SignedMessage.signatureLength * 4).pack16(); + return Uint8List.fromList([ + ...bitLength, // r bit length + ...signed.signature.sublist(0, nacl.SignedMessage.signatureLength ~/ 2), // r + ...bitLength, // s bit length + ...signed.signature.sublist(nacl.SignedMessage.signatureLength ~/ 2), // s + ]); + } + + @override + Uint8List get toBytes => Uint8List.fromList([ + ...seed.bitLength.pack16(), + ...seed.toUnsignedBytes(), + ]); +} diff --git a/lib/src/packet/key/eddsa_public_material.dart b/lib/src/packet/key/eddsa_public_material.dart new file mode 100644 index 00000000..5dbba7f4 --- /dev/null +++ b/lib/src/packet/key/eddsa_public_material.dart @@ -0,0 +1,63 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'package:pinenacl/ed25519.dart' as nacl; +import 'package:sign_dart/sign_dart.dart'; + +import '../../common/helpers.dart'; +import '../../enum/eddsa_curve.dart'; +import '../../enum/hash_algorithm.dart'; +import '../../type/public_key_material.dart'; + +/// EdDSA public key material +/// Author Nguyen Van Nguyen +class EdDSAPublicMaterial implements PublicKeyMaterialInterface { + final Uint8List publicKey; + + final EdDSACurve curve; + + EdDSAPublicMaterial(this.publicKey, this.curve); + + factory EdDSAPublicMaterial.fromBytes( + final Uint8List bytes, + final EdDSACurve curve, + ) => + EdDSAPublicMaterial( + bytes.sublist( + 0, + curve.payloadSize, + ), + curve, + ); + + @override + int get keyLength => publicKey.toBigInt().bitLength; + + @override + Uint8List get toBytes => publicKey; + + @override + bool verify( + final Uint8List message, + final HashAlgorithm hash, + final Uint8List signature, + ) => + switch (curve) { + EdDSACurve.ed25519 => nacl.VerifyKey(publicKey).verify( + signature: nacl.Signature(signature), + message: Helper.hashDigest(message, hash), + ), + EdDSACurve.ed448 => EdPublicKey.fromBytes( + publicKey, + TwistedEdwardCurve.ed448(), + ).verify( + Helper.hashDigest(message, hash), + signature, + ), + }; +} diff --git a/lib/src/packet/key/eddsa_secret_material.dart b/lib/src/packet/key/eddsa_secret_material.dart new file mode 100644 index 00000000..43232892 --- /dev/null +++ b/lib/src/packet/key/eddsa_secret_material.dart @@ -0,0 +1,97 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; +import 'package:pinenacl/ed25519.dart' as nacl; +import 'package:sign_dart/sign_dart.dart'; + +import '../../common/helpers.dart'; +import '../../enum/eddsa_curve.dart'; +import '../../enum/hash_algorithm.dart'; +import '../../type/signing_key_material.dart'; +import 'eddsa_public_material.dart'; + +/// EdDSA secret key material +/// Author Nguyen Van Nguyen +class EdDSASecretMaterial implements SigningKeyMaterialInterface { + final Uint8List secretKey; + + @override + final EdDSAPublicMaterial publicMaterial; + + EdDSASecretMaterial(this.secretKey, this.publicMaterial); + + factory EdDSASecretMaterial.fromBytes( + final Uint8List bytes, + final EdDSAPublicMaterial publicMaterial, + ) => + EdDSASecretMaterial( + bytes.sublist( + 0, + publicMaterial.curve.payloadSize, + ), + publicMaterial, + ); + + factory EdDSASecretMaterial.generate(final EdDSACurve curve) { + final secretKey = Helper.secureRandom().nextBytes(curve.payloadSize); + final publicKey = switch (curve) { + EdDSACurve.ed25519 => nacl.SigningKey.fromValidBytes( + secretKey, + ).verifyKey.asTypedList, + EdDSACurve.ed448 => EdPrivateKey.fromBytes( + secretKey, + TwistedEdwardCurve.ed448(), + ).getPublicKey().publicKey, + }; + return EdDSASecretMaterial( + secretKey, + EdDSAPublicMaterial( + publicKey, + curve, + ), + ); + } + + @override + bool get isValid { + final publicKey = switch (publicMaterial.curve) { + EdDSACurve.ed25519 => nacl.SigningKey.fromValidBytes( + secretKey, + ).verifyKey.asTypedList, + EdDSACurve.ed448 => EdPrivateKey.fromBytes( + secretKey, + TwistedEdwardCurve.ed448(), + ).getPublicKey().publicKey, + }; + return publicMaterial.publicKey.equals(publicKey); + } + + @override + int get keyLength => publicMaterial.keyLength; + + @override + Uint8List sign( + final Uint8List message, + final HashAlgorithm hash, + ) => + switch (publicMaterial.curve) { + EdDSACurve.ed25519 => nacl.SigningKey.fromValidBytes(secretKey) + .sign( + Helper.hashDigest(message, hash), + ) + .asTypedList, + EdDSACurve.ed448 => EdPrivateKey.fromBytes( + secretKey, + TwistedEdwardCurve.ed448(), + ).sign( + Helper.hashDigest(message, hash), + ), + }; + + @override + Uint8List get toBytes => secretKey; +} diff --git a/lib/src/packet/key/elgamal_public_params.dart b/lib/src/packet/key/elgamal_public_material.dart similarity index 54% rename from lib/src/packet/key/elgamal_public_params.dart rename to lib/src/packet/key/elgamal_public_material.dart index 052838d0..2f1f3a64 100644 --- a/lib/src/packet/key/elgamal_public_params.dart +++ b/lib/src/packet/key/elgamal_public_material.dart @@ -1,17 +1,18 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; -import '../../crypto/math/big_int.dart'; -import '../../crypto/math/int_ext.dart'; -import '../../crypto/asymmetric/elgamal.dart'; -import '../../helpers.dart'; -import 'key_params.dart'; +import '../../common/helpers.dart'; +import '../../cryptor/asymmetric/elgamal.dart'; +import '../../type/key_material.dart'; +/// ElGamal public key material /// Author Nguyen Van Nguyen -class ElGamalPublicParams implements KeyParams { +class ElGamalPublicMaterial implements KeyMaterialInterface { /// Elgamal prime p final BigInt prime; @@ -23,10 +24,10 @@ class ElGamalPublicParams implements KeyParams { final ElGamalPublicKey publicKey; - ElGamalPublicParams(this.prime, this.generator, this.exponent) + ElGamalPublicMaterial(this.prime, this.generator, this.exponent) : publicKey = ElGamalPublicKey(exponent, prime, generator); - factory ElGamalPublicParams.fromByteData(final Uint8List bytes) { + factory ElGamalPublicMaterial.fromBytes(final Uint8List bytes) { final prime = Helper.readMPI(bytes); var pos = prime.byteLength + 2; @@ -35,11 +36,14 @@ class ElGamalPublicParams implements KeyParams { pos += generator.byteLength + 2; final exponent = Helper.readMPI(bytes.sublist(pos)); - return ElGamalPublicParams(prime, generator, exponent); + return ElGamalPublicMaterial(prime, generator, exponent); } @override - Uint8List encode() => Uint8List.fromList([ + int get keyLength => prime.bitLength; + + @override + Uint8List get toBytes => Uint8List.fromList([ ...prime.bitLength.pack16(), ...prime.toUnsignedBytes(), ...generator.bitLength.pack16(), diff --git a/lib/src/packet/key/elgamal_secret_material.dart b/lib/src/packet/key/elgamal_secret_material.dart new file mode 100644 index 00000000..772413d8 --- /dev/null +++ b/lib/src/packet/key/elgamal_secret_material.dart @@ -0,0 +1,85 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../common/helpers.dart'; +import '../../cryptor/asymmetric/elgamal.dart'; +import '../../type/secret_key_material.dart'; +import 'elgamal_public_material.dart'; + +class ElGamalSecretMaterial implements SecretKeyMaterialInterface { + /// Elgamal secret exponent x. + final BigInt exponent; + + /// DSA private key + final ElGamalPrivateKey privateKey; + + @override + final ElGamalPublicMaterial publicMaterial; + + ElGamalSecretMaterial(this.exponent, this.publicMaterial) + : privateKey = ElGamalPrivateKey( + exponent, + publicMaterial.prime, + publicMaterial.generator, + ); + + factory ElGamalSecretMaterial.fromBytes( + final Uint8List bytes, + ElGamalPublicMaterial publicMaterial, + ) => + ElGamalSecretMaterial( + Helper.readMPI(bytes), + publicMaterial, + ); + + @override + int get keyLength => publicMaterial.keyLength; + + @override + bool get isValid { + // Check that 1 < g < p + if (publicMaterial.generator.compareTo(BigInt.one) <= 0 || + publicMaterial.generator.compareTo(publicMaterial.prime) >= 0) { + return false; + } + + // Expect p-1 to be large + final pSize = publicMaterial.prime.bitLength; + if (pSize < 1023) { + return false; + } + + // g should have order p-1 + // Check that g ** (p-1) = 1 mod p + if (publicMaterial.generator + .modPow( + publicMaterial.prime - BigInt.one, + publicMaterial.prime, + ) + .compareTo(BigInt.one) != + 0) { + return false; + } + + // Re-derive public key y' = g ** x mod p + // Expect y == y' + // Blinded exponentiation computes g**{r(p-1) + x} to compare to y + final r = Helper.randomBigInt(BigInt.two << (pSize - 1), BigInt.two << pSize); + final rqx = ((publicMaterial.prime - BigInt.one) * r) + exponent; + return publicMaterial.exponent.compareTo( + publicMaterial.generator.modPow(rqx, publicMaterial.prime), + ) == + 0; + } + + @override + Uint8List get toBytes => Uint8List.fromList([ + ...exponent.bitLength.pack16(), + ...exponent.toUnsignedBytes(), + ]); +} diff --git a/lib/src/packet/key/elgamal_secret_params.dart b/lib/src/packet/key/elgamal_secret_params.dart deleted file mode 100644 index 268d4c82..00000000 --- a/lib/src/packet/key/elgamal_secret_params.dart +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../crypto/math/big_int.dart'; -import '../../crypto/math/int_ext.dart'; -import '../../helpers.dart'; -import 'key_params.dart'; - -/// Author Nguyen Van Nguyen -class ElGamalSecretParams implements KeyParams { - /// Elgamal secret exponent x. - final BigInt exponent; - - ElGamalSecretParams(this.exponent); - - factory ElGamalSecretParams.fromByteData(final Uint8List bytes) => - ElGamalSecretParams( - Helper.readMPI(bytes), - ); - - @override - Uint8List encode() => Uint8List.fromList([ - ...exponent.bitLength.pack16(), - ...exponent.toUnsignedBytes(), - ]); - - /// Validate ElGamal parameters - validatePublicParams(final ElGamalPublicParams publicParams) { - // Check that 1 < g < p - if (publicParams.generator.compareTo(BigInt.one) <= 0 || - publicParams.generator.compareTo(publicParams.prime) >= 0) { - return false; - } - - // Expect p-1 to be large - final pSize = publicParams.prime.bitLength; - if (pSize < 1023) { - return false; - } - - // g should have order p-1 - // Check that g ** (p-1) = 1 mod p - if (publicParams.generator - .modPow(publicParams.prime - BigInt.one, publicParams.prime) - .compareTo(BigInt.one) != - 0) { - return false; - } - - // Since p-1 is not prime, g might have a smaller order that divides p-1 - // We want to make sure that the order is large enough to hinder a small subgroup attack - // We just check g**i != 1 for all i up to a threshold - // var res = publicParams.generator; - // var i = BigInt.one; - // final threshold = BigInt.two << 17; - // while (i.compareTo(threshold) < 0) { - // res = (res * publicParams.generator).modInverse(publicParams.prime); - // if (res.compareTo(BigInt.one) == 0) { - // return false; - // } - // i = i + BigInt.one; - // } - - // Re-derive public key y' = g ** x mod p - // Expect y == y' - // Blinded exponentiation computes g**{r(p-1) + x} to compare to y - final r = - Helper.randomBigInt(BigInt.two << (pSize - 1), BigInt.two << pSize); - final rqx = ((publicParams.prime - BigInt.one) * r) + exponent; - return publicParams.exponent.compareTo( - publicParams.generator.modPow(rqx, publicParams.prime)) == - 0; - } -} diff --git a/lib/src/packet/key/elgamal_session_key_cryptor.dart b/lib/src/packet/key/elgamal_session_key_cryptor.dart new file mode 100644 index 00000000..1205fa1c --- /dev/null +++ b/lib/src/packet/key/elgamal_session_key_cryptor.dart @@ -0,0 +1,70 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; +import 'package:pointycastle/api.dart'; + +import '../../common/helpers.dart'; +import '../../cryptor/asymmetric/elgamal.dart'; +import '../../type/secret_key_material.dart'; +import 'elgamal_secret_material.dart'; +import 'session_key_cryptor.dart'; + +/// ElGamal session key cryptor class +/// Author Nguyen Van Nguyen +class ElGamalSessionKeyCryptor extends SessionKeyCryptor { + /// MPI of ElGamal (Diffie-Hellman) value g**k mod p. + final BigInt gamma; + + /// MPI of ElGamal (Diffie-Hellman) value m * y**k mod p. + final BigInt phi; + + ElGamalSessionKeyCryptor(this.gamma, this.phi); + + factory ElGamalSessionKeyCryptor.fromBytes(final Uint8List bytes) { + final gamma = Helper.readMPI(bytes); + final phi = Helper.readMPI(bytes.sublist(gamma.byteLength + 2)); + + return ElGamalSessionKeyCryptor(gamma, phi); + } + + @override + Uint8List decrypt(final SecretKeyMaterialInterface key) { + if (key is ElGamalSecretMaterial) { + return _pkcs1Decode(SessionKeyCryptor.processInBlocks( + ElGamalEngine() + ..init( + false, + PrivateKeyParameter(key.privateKey), + ), + Uint8List.fromList([ + ...gamma.toUnsignedBytes(), + ...phi.toUnsignedBytes(), + ]), + )); + } else { + throw ArgumentError('Secret key material is not ElGamal key.'); + } + } + + @override + Uint8List encode() => Uint8List.fromList([ + ...gamma.bitLength.pack16(), + ...gamma.toUnsignedBytes(), + ...phi.bitLength.pack16(), + ...phi.toUnsignedBytes(), + ]); + + static Uint8List _pkcs1Decode(final Uint8List encoded) { + var offset = 2; + var separatorNotFound = 1; + for (var j = offset; j < encoded.length; j++) { + separatorNotFound &= (encoded[j] != 0) ? 1 : 0; + offset += separatorNotFound; + } + return encoded.sublist(offset + 1); + } +} diff --git a/lib/src/packet/key/elgamal_session_key_params.dart b/lib/src/packet/key/elgamal_session_key_params.dart deleted file mode 100644 index 6430fbc5..00000000 --- a/lib/src/packet/key/elgamal_session_key_params.dart +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import 'package:pointycastle/export.dart'; - -import '../../crypto/math/big_int.dart'; -import '../../crypto/math/byte_ext.dart'; -import '../../crypto/math/int_ext.dart'; -import '../../crypto/asymmetric/elgamal.dart'; -import '../../helpers.dart'; -import 'session_key.dart'; -import 'session_key_params.dart'; - -/// Algorithm Specific Params for Elgamal encryption -/// Author Nguyen Van Nguyen -class ElGamalSessionKeyParams extends SessionKeyParams { - /// MPI of Elgamal (Diffie-Hellman) value g**k mod p. - final BigInt gamma; - - /// MPI of Elgamal (Diffie-Hellman) value m * y**k mod p. - final BigInt phi; - - ElGamalSessionKeyParams(this.gamma, this.phi); - - factory ElGamalSessionKeyParams.fromByteData(final Uint8List bytes) { - final gamma = Helper.readMPI(bytes); - final phi = Helper.readMPI(bytes.sublist(gamma.byteLength + 2)); - - return ElGamalSessionKeyParams(gamma, phi); - } - - static Future encryptSessionKey( - final ElGamalPublicKey key, - final SessionKey sessionKey, - ) async { - final engine = PKCS1Encoding(ElGamalEngine()) - ..init( - true, - PublicKeyParameter(key), - ); - final cipherData = SessionKeyParams.processInBlocks( - engine, - Uint8List.fromList([ - ...sessionKey.encode(), - ...sessionKey.computeChecksum(), - ]), - ); - return ElGamalSessionKeyParams( - cipherData.sublist(0, engine.outputBlockSize ~/ 2).toBigIntWithSign(1), - cipherData.sublist(engine.outputBlockSize ~/ 2).toBigIntWithSign(1), - ); - } - - @override - Uint8List encode() => Uint8List.fromList([ - ...gamma.bitLength.pack16(), - ...gamma.toUnsignedBytes(), - ...phi.bitLength.pack16(), - ...phi.toUnsignedBytes(), - ]); - - Future decrypt(final ElGamalPrivateKey key) async { - final plainData = SessionKeyParams.processInBlocks( - ElGamalEngine() - ..init( - false, - PrivateKeyParameter(key), - ), - Uint8List.fromList([ - ...gamma.toUnsignedBytes(), - ...phi.toUnsignedBytes(), - ]), - ); - return decodeSessionKey(_pkcs1Decode(plainData)); - } - - static _pkcs1Decode(final Uint8List encoded) { - var offset = 2; - var separatorNotFound = 1; - for (var j = offset; j < encoded.length; j++) { - separatorNotFound &= (encoded[j] != 0) ? 1 : 0; - offset += separatorNotFound; - } - return encoded.sublist(offset + 1); - } -} diff --git a/lib/src/packet/key/key_id.dart b/lib/src/packet/key/key_id.dart deleted file mode 100644 index 1e46d48e..00000000 --- a/lib/src/packet/key/key_id.dart +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../crypto/math/byte_ext.dart'; -import '../../helpers.dart'; - -/// Author Nguyen Van Nguyen -class KeyID { - final Uint8List bytes; - - KeyID(this.bytes); - - factory KeyID.fromString(final String id) => KeyID(id.hexToBytes()); - - factory KeyID.wildcard() => KeyID( - Uint8List.fromList(List.filled(8, 0, growable: false)), - ); - - String get id => bytes.toHexadecimal(); - - @override - String toString() => id; - - @override - bool operator ==(other) { - if (other is! KeyID) return false; - return (other.bytes.equals(bytes)); - } - - @override - int get hashCode { - return bytes.hashCode; - } -} diff --git a/lib/src/packet/key/key_pair_params.dart b/lib/src/packet/key/key_pair_params.dart deleted file mode 100644 index 718808f0..00000000 --- a/lib/src/packet/key/key_pair_params.dart +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import 'package:pinenacl/ed25519.dart' as nacl; -import 'package:pinenacl/tweetnacl.dart'; -import 'package:pointycastle/pointycastle.dart'; - -import '../../crypto/asymmetric/elgamal.dart'; -import '../../crypto/math/byte_ext.dart'; -import '../../crypto/signer/dsa.dart'; -import '../../enum/curve_info.dart'; -import '../../enum/dh_key_size.dart'; -import '../../enum/key_algorithm.dart'; -import '../../enum/rsa_key_size.dart'; -import '../../helpers.dart'; -import 'key_params.dart'; - -/// Author Nguyen Van Nguyen -class KeyPairParams { - /// The number of Miller-Rabin primality tests - static const mrTests = 64; - - /// RSA public exponent - static const rsaPublicExponent = 65537; - - final KeyParams publicParams; - - final KeyParams secretParams; - - KeyPairParams(this.publicParams, this.secretParams); - - static Future generate( - final KeyAlgorithm algorithm, { - final RSAKeySize rsaKeySize = RSAKeySize.s4096, - final DHKeySize dhKeySize = DHKeySize.l2048n224, - final CurveInfo curve = CurveInfo.secp521r1, - }) async { - switch (algorithm) { - case KeyAlgorithm.rsaEncryptSign: - case KeyAlgorithm.rsaEncrypt: - case KeyAlgorithm.rsaSign: - return _generateRSAKeyPair(rsaKeySize); - case KeyAlgorithm.ecdsa: - final keyPair = _generateECKeyPair(curve); - final q = keyPair.publicKey.Q!; - return KeyPairParams( - ECDSAPublicParams( - curve.asn1Oid, - q.getEncoded(q.isCompressed).toBigIntWithSign(1), - ), - ECSecretParams(keyPair.privateKey.d!), - ); - case KeyAlgorithm.ecdh: - if (curve == CurveInfo.curve25519) { - return _generateCurve25519KeyPair(); - } else { - final keyPair = _generateECKeyPair(curve); - final q = keyPair.publicKey.Q!; - return KeyPairParams( - ECDHPublicParams( - curve.asn1Oid, - q.getEncoded(q.isCompressed).toBigIntWithSign(1), - curve.hashAlgorithm, - curve.symmetricAlgorithm, - ), - ECSecretParams(keyPair.privateKey.d!), - ); - } - case KeyAlgorithm.eddsa: - return _generateEd25519KeyPair(); - case KeyAlgorithm.dsa: - return _generateDSAKeyPair(dhKeySize); - case KeyAlgorithm.elgamal: - return _generateElGamalKeyPair(dhKeySize); - default: - throw UnimplementedError( - 'Unknown public key algorithm for key generation.', - ); - } - } - - @override - bool operator ==(Object other) => - identical(this, other) || - other is KeyPairParams && - runtimeType == other.runtimeType && - publicParams == other.publicParams && - secretParams == other.secretParams; - - @override - int get hashCode => publicParams.hashCode ^ secretParams.hashCode; - - static KeyPairParams _generateRSAKeyPair([ - final RSAKeySize rsaKeySize = RSAKeySize.s4096, - ]) { - final keyGen = KeyGenerator('RSA') - ..init( - ParametersWithRandom( - RSAKeyGeneratorParameters( - BigInt.from(rsaPublicExponent), - rsaKeySize.bits, - mrTests, - ), - Helper.secureRandom(), - ), - ); - final keyPair = keyGen.generateKeyPair(); - final publicKey = keyPair.publicKey as RSAPublicKey; - final privateKey = keyPair.privateKey as RSAPrivateKey; - - return KeyPairParams( - RSAPublicParams( - publicKey.modulus!, - publicKey.publicExponent!, - ), - RSASecretParams( - privateKey.privateExponent!, - privateKey.p!, - privateKey.q!, - ), - ); - } - - static KeyPairParams _generateDSAKeyPair([ - final DHKeySize keySize = DHKeySize.l2048n224, - ]) { - final keyGen = DSAKeyGenerator() - ..init( - ParametersWithRandom( - DSAKeyGeneratorParameters( - keySize.lSize, - keySize.nSize, - mrTests, - ), - Helper.secureRandom(), - ), - ); - final keyPair = keyGen.generateKeyPair(); - final publicKey = keyPair.publicKey as DSAPublicKey; - final privateKey = keyPair.privateKey as DSAPrivateKey; - return KeyPairParams( - DSAPublicParams( - publicKey.prime, - publicKey.order, - publicKey.generator, - publicKey.y, - ), - DSASecretParams( - privateKey.x, - ), - ); - } - - static KeyPairParams _generateElGamalKeyPair([ - final DHKeySize keySize = DHKeySize.l2048n224, - ]) { - final keyGen = ElGamalKeyGenerator() - ..init( - ParametersWithRandom( - ElGamalKeyGeneratorParameters( - keySize.lSize, - keySize.nSize, - mrTests, - ), - Helper.secureRandom(), - ), - ); - final keyPair = keyGen.generateKeyPair(); - final publicKey = keyPair.publicKey as ElGamalPublicKey; - final privateKey = keyPair.privateKey as ElGamalPrivateKey; - - return KeyPairParams( - ElGamalPublicParams( - publicKey.prime, - publicKey.generator, - publicKey.y, - ), - ElGamalSecretParams( - privateKey.x, - ), - ); - } - - static AsymmetricKeyPair _generateECKeyPair([ - final CurveInfo curve = CurveInfo.secp521r1, - ]) { - switch (curve) { - case CurveInfo.curve25519: - case CurveInfo.ed25519: - throw UnsupportedError( - 'Curve ${curve.name} is unsupported for key generation.', - ); - default: - final keyGen = KeyGenerator('EC') - ..init( - ParametersWithRandom( - ECKeyGeneratorParameters( - ECDomainParameters(curve.name.toLowerCase()), - ), - Helper.secureRandom(), - ), - ); - final keyPair = keyGen.generateKeyPair(); - return AsymmetricKeyPair( - keyPair.publicKey as ECPublicKey, - keyPair.privateKey as ECPrivateKey, - ); - } - } - - static KeyPairParams _generateEd25519KeyPair() { - final seed = Helper.secureRandom().nextBytes(TweetNaCl.seedSize); - return KeyPairParams( - EdDSAPublicParams( - CurveInfo.ed25519.asn1Oid, - Uint8List.fromList([ - 0x40, - ...nacl.SigningKey.fromSeed(seed).verifyKey.asTypedList, - ]).toBigIntWithSign(1), - ), - EdSecretParams(seed.toBigIntWithSign(1)), - ); - } - - static KeyPairParams _generateCurve25519KeyPair() { - final secretKey = - Helper.secureRandom().nextBytes(TweetNaCl.secretKeyLength); - secretKey[0] = (secretKey[0] & 127) | 64; - secretKey[TweetNaCl.secretKeyLength - 1] &= 248; - final privateKey = nacl.PrivateKey( - Uint8List.fromList(secretKey.reversed.toList()), - ); - - return KeyPairParams( - ECDHPublicParams( - CurveInfo.curve25519.asn1Oid, - Uint8List.fromList([ - 0x40, - ...privateKey.publicKey.asTypedList, - ]).toBigIntWithSign(1), - CurveInfo.curve25519.hashAlgorithm, - CurveInfo.curve25519.symmetricAlgorithm, - ), - ECSecretParams( - Uint8List.fromList( - privateKey.asTypedList.reversed.toList(), - ).toBigIntWithSign(1), - ), - ); - } -} diff --git a/lib/src/packet/key/key_params.dart b/lib/src/packet/key/key_params.dart deleted file mode 100644 index b3f25c3e..00000000 --- a/lib/src/packet/key/key_params.dart +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -export 'dsa_public_params.dart'; -export 'dsa_secret_params.dart'; -export 'ec_public_params.dart'; -export 'ec_secret_params.dart'; -export 'ecdh_public_params.dart'; -export 'ecdsa_public_params.dart'; -export 'ed_secret_params.dart'; -export 'eddsa_public_params.dart'; -export 'elgamal_public_params.dart'; -export 'elgamal_secret_params.dart'; -export 'rsa_public_params.dart'; -export 'rsa_secret_params.dart'; -export 'verification_params.dart'; - -/// Author Nguyen Van Nguyen -abstract class KeyParams { - Uint8List encode(); -} diff --git a/lib/src/packet/key/key_wrap.dart b/lib/src/packet/key/key_wrapper.dart similarity index 84% rename from lib/src/packet/key/key_wrap.dart rename to lib/src/packet/key/key_wrapper.dart index 0282dd0a..f232992d 100644 --- a/lib/src/packet/key/key_wrap.dart +++ b/lib/src/packet/key/key_wrapper.dart @@ -1,19 +1,20 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. -import 'dart:typed_data'; +library; +import 'dart:typed_data'; import 'package:pointycastle/api.dart'; -import '../../crypto/math/byte_ext.dart'; +import '../../common/extensions.dart'; -export 'aes_key_wrap.dart'; -export 'camellia_key_wrap.dart'; +export 'aes_key_wrapper.dart'; +export 'camellia_key_wrapper.dart'; /// An implementation of the key wrapper based on RFC 3394. /// Author Nguyen Van Nguyen -abstract class KeyWrap { +abstract class KeyWrapper { static final _iv = Uint8List.fromList([ 0xa6, 0xa6, 0xa6, 0xa6, // 0 - 3 0xa6, 0xa6, 0xa6, 0xa6 @@ -23,12 +24,12 @@ abstract class KeyWrap { final int keySize; - KeyWrap(this.cipher, this.keySize); + KeyWrapper(this.cipher, this.keySize); - Future wrap( + Uint8List wrap( final Uint8List kek, final Uint8List key, - ) async { + ) { if (kek.lengthInBytes != keySize) { throw ArgumentError('Key encryption key size must be $keySize bytes.'); } @@ -59,10 +60,10 @@ abstract class KeyWrap { return Uint8List.fromList([...a, ...r]); } - Future unwrap( + Uint8List unwrap( final Uint8List kek, final Uint8List wrappedKey, - ) async { + ) { if (kek.lengthInBytes != keySize) { throw ArgumentError('Key encryption key size must be $keySize bytes.'); } diff --git a/lib/src/packet/key/montgomery_public_material.dart b/lib/src/packet/key/montgomery_public_material.dart new file mode 100644 index 00000000..97a84ab5 --- /dev/null +++ b/lib/src/packet/key/montgomery_public_material.dart @@ -0,0 +1,36 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../type/key_material.dart'; +import '../../common/extensions.dart'; +import '../../enum/montgomery_curve.dart'; + +/// Montgomery public key material +/// Author Nguyen Van Nguyen +class MontgomeryPublicMaterial implements KeyMaterialInterface { + final Uint8List publicKey; + + final MontgomeryCurve curve; + + MontgomeryPublicMaterial(this.publicKey, this.curve); + + factory MontgomeryPublicMaterial.fromBytes( + Uint8List bytes, + MontgomeryCurve curve, + ) => + MontgomeryPublicMaterial( + bytes.sublist(0, curve.payloadSize), + curve, + ); + + @override + int get keyLength => publicKey.toBigInt().bitLength; + + @override + Uint8List get toBytes => publicKey; +} diff --git a/lib/src/packet/key/montgomery_secret_material.dart b/lib/src/packet/key/montgomery_secret_material.dart new file mode 100644 index 00000000..8f1e3786 --- /dev/null +++ b/lib/src/packet/key/montgomery_secret_material.dart @@ -0,0 +1,101 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; +import 'package:pinenacl/ed25519.dart' as nacl; +import 'package:pinenacl/tweetnacl.dart'; + +import '../../common/helpers.dart'; +import '../../type/secret_key_material.dart'; +import '../../cryptor/ecc/x448.dart'; +import '../../enum/montgomery_curve.dart'; +import 'montgomery_public_material.dart'; + +/// Montgomery secret key material +/// Author Nguyen Van Nguyen +class MontgomerySecretMaterial implements SecretKeyMaterialInterface { + final Uint8List secretKey; + + @override + final MontgomeryPublicMaterial publicMaterial; + + MontgomerySecretMaterial(this.secretKey, this.publicMaterial); + + factory MontgomerySecretMaterial.fromBytes( + final Uint8List bytes, + final MontgomeryPublicMaterial publicMaterial, + ) => + MontgomerySecretMaterial( + bytes.sublist(0, publicMaterial.curve.payloadSize), + publicMaterial, + ); + + factory MontgomerySecretMaterial.generate(final MontgomeryCurve curve) { + final secretKey = _generateSecretKey(curve); + final publicKey = switch (curve) { + MontgomeryCurve.x25519 => nacl.PrivateKey( + secretKey, + ).publicKey.asTypedList, + MontgomeryCurve.x448 => X448.scalarMultBase(secretKey), + }; + return MontgomerySecretMaterial( + secretKey, + MontgomeryPublicMaterial( + publicKey, + curve, + ), + ); + } + + @override + int get keyLength => publicMaterial.keyLength; + + @override + Uint8List get toBytes => secretKey; + + @override + bool get isValid { + final publicKey = switch (publicMaterial.curve) { + MontgomeryCurve.x25519 => nacl.PrivateKey( + secretKey, + ).publicKey.asTypedList, + MontgomeryCurve.x448 => X448.scalarMultBase(secretKey), + }; + return publicMaterial.publicKey.equals(publicKey); + } + + /// Compute shared secret + Uint8List computeSecret(final Uint8List publicKey) { + assert(publicKey.length == publicMaterial.curve.payloadSize); + return switch (publicMaterial.curve) { + MontgomeryCurve.x25519 => TweetNaCl.crypto_scalarmult( + Uint8List(publicMaterial.curve.payloadSize), + secretKey, + publicKey, + ), + MontgomeryCurve.x448 => X448.scalarMult(secretKey, publicKey), + }; + } + + static Uint8List _generateSecretKey(final MontgomeryCurve curve) { + final payloadSize = curve.payloadSize; + final random = Helper.secureRandom(); + final key = random.nextBytes(payloadSize); + switch (curve) { + case MontgomeryCurve.x25519: + // The lowest three bits must be 0 + key[0] &= 0xf8; + // The highest bit must be 0 & the second highest bit must be 1 + key[payloadSize - 1] = (key[payloadSize - 1] & 0x7f) | 0x40; + case MontgomeryCurve.x448: + // The two least significant bits of the first byte to 0 + key[0] &= 0xfc; + // The most significant bit of the last byte to 1 + key[payloadSize - 1] |= 0x80; + } + return key; + } +} diff --git a/lib/src/packet/key/montgomery_session_key_cryptor.dart b/lib/src/packet/key/montgomery_session_key_cryptor.dart new file mode 100644 index 00000000..77aa1a71 --- /dev/null +++ b/lib/src/packet/key/montgomery_session_key_cryptor.dart @@ -0,0 +1,94 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../common/helpers.dart'; +import '../../enum/montgomery_curve.dart'; +import '../../type/secret_key_material.dart'; +import 'key_wrapper.dart'; +import 'montgomery_public_material.dart'; +import 'montgomery_secret_material.dart'; +import 'session_key_cryptor.dart'; + +class MontgomerySessionKeyCryptor extends SessionKeyCryptor { + /// The ephemeral key used to establish the shared secret + final Uint8List ephemeralKey; + + /// ECDH wrapped key + final Uint8List wrappedKey; + + MontgomerySessionKeyCryptor(this.ephemeralKey, this.wrappedKey); + + factory MontgomerySessionKeyCryptor.fromBytes( + final Uint8List bytes, + final MontgomeryCurve curve, + ) { + return MontgomerySessionKeyCryptor( + bytes.sublist(0, curve.payloadSize), + bytes.sublist( + curve.payloadSize + 1, + curve.payloadSize + 1 + bytes[curve.payloadSize], + ), + ); + } + + factory MontgomerySessionKeyCryptor.encryptSessionKey( + final Uint8List sessionKey, + final MontgomeryPublicMaterial key, + ) { + final secretKey = MontgomerySecretMaterial.generate(key.curve); + final ephemeralKey = secretKey.publicMaterial.publicKey; + final keyWrapper = AesKeyWrapper(key.curve.kekSize); + return MontgomerySessionKeyCryptor( + ephemeralKey, + keyWrapper.wrap( + Helper.hkdf( + Uint8List.fromList([ + ...ephemeralKey, + ...key.publicKey, + ...secretKey.computeSecret(key.publicKey), + ]), + key.curve.kekSize, + info: key.curve.hkdfInfo, + hash: key.curve.hkdfHash, + ), + sessionKey, + ), + ); + } + + @override + Uint8List decrypt(final SecretKeyMaterialInterface key) { + if (key is MontgomerySecretMaterial) { + final keyWrapper = AesKeyWrapper( + key.publicMaterial.curve.kekSize, + ); + return keyWrapper.unwrap( + Helper.hkdf( + Uint8List.fromList([ + ...ephemeralKey, + ...key.publicMaterial.publicKey, + ...key.computeSecret(ephemeralKey), + ]), + key.publicMaterial.curve.kekSize, + info: key.publicMaterial.curve.hkdfInfo, + hash: key.publicMaterial.curve.hkdfHash, + ), + wrappedKey, + ); + } else { + throw ArgumentError('Secret key material is not Montgomery key.'); + } + } + + @override + Uint8List encode() => Uint8List.fromList([ + ...ephemeralKey, + wrappedKey.length, + ...wrappedKey, + ]); +} diff --git a/lib/src/packet/key/public_material.dart b/lib/src/packet/key/public_material.dart new file mode 100644 index 00000000..b5eba66e --- /dev/null +++ b/lib/src/packet/key/public_material.dart @@ -0,0 +1,14 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +export 'dsa_public_material.dart'; +export 'ecdh_public_material.dart'; +export 'ecdsa_public_material.dart'; +export 'eddsa_legacy_public_material.dart'; +export 'eddsa_public_material.dart'; +export 'elgamal_public_material.dart'; +export 'montgomery_public_material.dart'; +export 'rsa_public_material.dart'; diff --git a/lib/src/packet/key/rsa_public_params.dart b/lib/src/packet/key/rsa_public_material.dart similarity index 61% rename from lib/src/packet/key/rsa_public_params.dart rename to lib/src/packet/key/rsa_public_material.dart index b294306d..ab807bb0 100644 --- a/lib/src/packet/key/rsa_public_params.dart +++ b/lib/src/packet/key/rsa_public_material.dart @@ -1,42 +1,47 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; import 'package:pointycastle/pointycastle.dart'; -import '../../crypto/math/big_int.dart'; -import '../../crypto/math/int_ext.dart'; +import '../../common/helpers.dart'; import '../../enum/hash_algorithm.dart'; -import '../../helpers.dart'; -import 'key_params.dart'; +import '../../type/public_key_material.dart'; +/// RSA public key material /// Author Nguyen Van Nguyen -class RSAPublicParams implements VerificationParams { +class RSAPublicMaterial implements PublicKeyMaterialInterface { /// RSA modulus n final BigInt modulus; /// RSA public encryption exponent e final BigInt exponent; + /// RSA public key final RSAPublicKey publicKey; - RSAPublicParams(this.modulus, this.exponent) + RSAPublicMaterial(this.modulus, this.exponent) : publicKey = RSAPublicKey( modulus, exponent, ); - factory RSAPublicParams.fromByteData(final Uint8List bytes) { + factory RSAPublicMaterial.fromBytes(final Uint8List bytes) { final modulus = Helper.readMPI(bytes); - return RSAPublicParams( + return RSAPublicMaterial( modulus, Helper.readMPI(bytes.sublist(modulus.byteLength + 2)), ); } @override - Uint8List encode() => Uint8List.fromList([ + int get keyLength => modulus.bitLength; + + @override + Uint8List get toBytes => Uint8List.fromList([ ...modulus.bitLength.pack16(), ...modulus.toUnsignedBytes(), ...exponent.bitLength.pack16(), @@ -44,11 +49,11 @@ class RSAPublicParams implements VerificationParams { ]); @override - Future verify( + bool verify( final Uint8List message, final HashAlgorithm hash, final Uint8List signature, - ) async { + ) { final signer = Signer('${hash.digestName}/RSA') ..init( false, diff --git a/lib/src/packet/key/rsa_secret_material.dart b/lib/src/packet/key/rsa_secret_material.dart new file mode 100644 index 00000000..ba4bb8d7 --- /dev/null +++ b/lib/src/packet/key/rsa_secret_material.dart @@ -0,0 +1,154 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'package:pointycastle/pointycastle.dart'; + +import '../../common/helpers.dart'; +import '../../enum/hash_algorithm.dart'; +import '../../enum/rsa_key_size.dart'; +import '../../type/signing_key_material.dart'; +import 'rsa_public_material.dart'; + +/// RSA secret key material +/// Author Nguyen Van Nguyen +class RSASecretMaterial implements SigningKeyMaterialInterface { + static const publicExponent = 65537; + static const mrTests = 64; + + /// RSA secret exponent d + final BigInt exponent; + + /// RSA secret prime value p + final BigInt primeP; + + /// RSA secret prime value q (p < q) + final BigInt primeQ; + + /// The multiplicative inverse of p, mod q + final BigInt coefficient; + + /// RSA private key + final RSAPrivateKey privateKey; + + @override + final RSAPublicMaterial publicMaterial; + + RSASecretMaterial( + this.exponent, + this.primeP, + this.primeQ, + this.publicMaterial, { + BigInt? coefficient, + }) : coefficient = coefficient ?? primeP.modInverse(primeQ), + privateKey = RSAPrivateKey( + primeP * primeQ, + exponent, + primeP, + primeQ, + ); + + /// Read key material from bytes + factory RSASecretMaterial.fromBytes( + final Uint8List bytes, + final RSAPublicMaterial publicMaterial, + ) { + final exponent = Helper.readMPI(bytes); + + var pos = exponent.byteLength + 2; + final primeP = Helper.readMPI(bytes.sublist(pos)); + + pos += primeP.byteLength + 2; + final primeQ = Helper.readMPI(bytes.sublist(pos)); + + pos += primeQ.byteLength + 2; + final coefficient = Helper.readMPI(bytes.sublist(pos)); + + return RSASecretMaterial( + exponent, + primeP, + primeQ, + publicMaterial, + coefficient: coefficient, + ); + } + + factory RSASecretMaterial.generate([ + final RSAKeySize keySize = RSAKeySize.normal, + ]) { + final keyGen = KeyGenerator('RSA') + ..init( + ParametersWithRandom( + RSAKeyGeneratorParameters( + BigInt.from(publicExponent), + keySize.bits, + mrTests, + ), + Helper.secureRandom(), + ), + ); + final keyPair = keyGen.generateKeyPair(); + final publicKey = keyPair.publicKey as RSAPublicKey; + final privateKey = keyPair.privateKey as RSAPrivateKey; + + return RSASecretMaterial( + privateKey.privateExponent!, + privateKey.p!, + privateKey.q!, + RSAPublicMaterial( + publicKey.modulus!, + publicKey.publicExponent!, + ), + ); + } + + @override + bool get isValid { + // expect pq = n + if ((primeP * primeQ).compareTo(publicMaterial.exponent) != 0) { + return false; + } + // expect p*u = 1 mod q + if (((primeP * coefficient) % primeQ).compareTo(BigInt.one) != 0) { + return false; + } + + final sizeOver3 = (privateKey.modulus!.bitLength / 3).floor(); + final r = Helper.randomBigInt(BigInt.two, BigInt.two << sizeOver3); + final rde = r * exponent * publicMaterial.exponent; + return (rde % (primeP - BigInt.one)).compareTo(r) == 0 && (rde % (primeQ - BigInt.one)).compareTo(r) == 0; + } + + @override + int get keyLength => publicMaterial.keyLength; + + @override + Uint8List sign(final Uint8List message, final HashAlgorithm hash) { + final signer = Signer('${hash.digestName}/RSA') + ..init( + true, + PrivateKeyParameter(privateKey), + ); + final signature = signer.generateSignature(message) as RSASignature; + return Uint8List.fromList([ + ...(signature.bytes.lengthInBytes * 8).pack16(), + ...signature.bytes, + ]); + } + + @override + Uint8List get toBytes => Uint8List.fromList([ + ...exponent.bitLength.pack16(), + ...exponent.toUnsignedBytes(), + ...primeP.bitLength.pack16(), + ...primeP.toUnsignedBytes(), + ...primeQ.bitLength.pack16(), + ...primeQ.toUnsignedBytes(), + ...coefficient.bitLength.pack16(), + ...coefficient.toUnsignedBytes(), + ]); +} diff --git a/lib/src/packet/key/rsa_secret_params.dart b/lib/src/packet/key/rsa_secret_params.dart deleted file mode 100644 index d36cdf4c..00000000 --- a/lib/src/packet/key/rsa_secret_params.dart +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; -import 'package:pointycastle/pointycastle.dart'; - -import '../../crypto/math/big_int.dart'; -import '../../crypto/math/int_ext.dart'; -import '../../enum/hash_algorithm.dart'; -import '../../helpers.dart'; -import 'key_params.dart'; - -/// Author Nguyen Van Nguyen -class RSASecretParams extends KeyParams { - /// RSA secret exponent d - final BigInt exponent; - - /// RSA secret prime value p - final BigInt primeP; - - /// RSA secret prime value q (p < q) - final BigInt primeQ; - - /// The multiplicative inverse of p, mod q - final BigInt coefficient; - - final RSAPrivateKey privateKey; - - RSASecretParams( - this.exponent, - this.primeP, - this.primeQ, { - BigInt? coefficient, - }) : coefficient = coefficient ?? primeP.modInverse(primeQ), - privateKey = RSAPrivateKey( - primeP * primeQ, - exponent, - primeP, - primeQ, - ); - - /// RSA modulus n - BigInt get modulus => privateKey.modulus!; - - /// RSA public encryption exponent e - BigInt get publicExponent => privateKey.publicExponent!; - - factory RSASecretParams.fromByteData(final Uint8List bytes) { - final privateExponent = Helper.readMPI(bytes); - - var pos = privateExponent.byteLength + 2; - final primeP = Helper.readMPI(bytes.sublist(pos)); - - pos += primeP.byteLength + 2; - final primeQ = Helper.readMPI(bytes.sublist(pos)); - - pos += primeQ.byteLength + 2; - final coefficient = Helper.readMPI(bytes.sublist(pos)); - - return RSASecretParams( - privateExponent, - primeP, - primeQ, - coefficient: coefficient, - ); - } - - @override - Uint8List encode() => Uint8List.fromList([ - ...exponent.bitLength.pack16(), - ...exponent.toUnsignedBytes(), - ...primeP.bitLength.pack16(), - ...primeP.toUnsignedBytes(), - ...primeQ.bitLength.pack16(), - ...primeQ.toUnsignedBytes(), - ...coefficient.bitLength.pack16(), - ...coefficient.toUnsignedBytes(), - ]); - - Future sign( - final Uint8List message, - final HashAlgorithm hash, - ) async { - final signer = Signer('${hash.digestName}/RSA') - ..init( - true, - PrivateKeyParameter(privateKey), - ); - final signature = signer.generateSignature(message) as RSASignature; - return Uint8List.fromList([ - ...(signature.bytes.lengthInBytes * 8).pack16(), - ...signature.bytes, - ]); - } - - /// Validate RSA parameters - bool validatePublicParams(final RSAPublicParams publicParams) { - // expect pq = n - if ((primeP * primeQ).compareTo(publicParams.modulus) != 0) { - return false; - } - // expect p*u = 1 mod q - if (((primeP * coefficient) % primeQ).compareTo(BigInt.one) != 0) { - return false; - } - - final nSizeOver3 = (publicParams.modulus.bitLength / 3).floor(); - final r = Helper.randomBigInt(BigInt.two, BigInt.two << nSizeOver3); - final rde = r * exponent * publicParams.exponent; - return (rde % (primeP - BigInt.one)).compareTo(r) == 0 && - (rde % (primeQ - BigInt.one)).compareTo(r) == 0; - } -} diff --git a/lib/src/packet/key/rsa_session_key_cryptor.dart b/lib/src/packet/key/rsa_session_key_cryptor.dart new file mode 100644 index 00000000..6d973419 --- /dev/null +++ b/lib/src/packet/key/rsa_session_key_cryptor.dart @@ -0,0 +1,66 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; +import 'package:pointycastle/export.dart'; + +import '../../common/helpers.dart'; +import '../../type/secret_key_material.dart'; +import 'rsa_public_material.dart'; +import 'rsa_secret_material.dart'; +import 'session_key_cryptor.dart'; + +/// RSA session key cryptor class +/// Author Nguyen Van Nguyen +class RSASessionKeyCryptor extends SessionKeyCryptor { + /// multiprecision integer (MPI) of RSA encrypted value m**e mod n. + final BigInt encrypted; + + RSASessionKeyCryptor(this.encrypted); + + factory RSASessionKeyCryptor.fromBytes( + final Uint8List bytes, + ) => + RSASessionKeyCryptor(Helper.readMPI(bytes)); + + factory RSASessionKeyCryptor.encryptSessionKey( + final Uint8List sessionKey, + final RSAPublicMaterial key, + ) { + return RSASessionKeyCryptor( + SessionKeyCryptor.processInBlocks( + AsymmetricBlockCipher('RSA/PKCS1') + ..init( + true, + PublicKeyParameter(key.publicKey), + ), + sessionKey, + ).toBigIntWithSign(1), + ); + } + + @override + Uint8List decrypt(final SecretKeyMaterialInterface key) { + if (key is RSASecretMaterial) { + return SessionKeyCryptor.processInBlocks( + AsymmetricBlockCipher('RSA/PKCS1') + ..init( + false, + PrivateKeyParameter(key.privateKey), + ), + encrypted.toUnsignedBytes(), + ); + } else { + throw ArgumentError('Secret key material is not RSA key.'); + } + } + + @override + Uint8List encode() => Uint8List.fromList([ + ...encrypted.bitLength.pack16(), + ...encrypted.toUnsignedBytes(), + ]); +} diff --git a/lib/src/packet/key/rsa_session_key_params.dart b/lib/src/packet/key/rsa_session_key_params.dart deleted file mode 100644 index 437a0ef9..00000000 --- a/lib/src/packet/key/rsa_session_key_params.dart +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import 'package:pointycastle/export.dart'; - -import '../../crypto/math/big_int.dart'; -import '../../crypto/math/byte_ext.dart'; -import '../../crypto/math/int_ext.dart'; -import '../../helpers.dart'; -import 'session_key.dart'; -import 'session_key_params.dart'; - -/// Algorithm Specific Params for RSA encryption -/// Author Nguyen Van Nguyen -class RSASessionKeyParams extends SessionKeyParams { - /// multiprecision integer (MPI) of RSA encrypted value m**e mod n. - final BigInt encrypted; - - RSASessionKeyParams(this.encrypted); - - factory RSASessionKeyParams.fromByteData( - final Uint8List bytes, - ) => - RSASessionKeyParams(Helper.readMPI(bytes)); - - static Future encryptSessionKey( - final RSAPublicKey key, - final SessionKey sessionKey, - ) async { - return RSASessionKeyParams( - SessionKeyParams.processInBlocks( - AsymmetricBlockCipher('RSA/PKCS1') - ..init( - true, - PublicKeyParameter(key), - ), - Uint8List.fromList([ - ...sessionKey.encode(), - ...sessionKey.computeChecksum(), - ]), - ).toBigIntWithSign(1), - ); - } - - @override - Uint8List encode() => Uint8List.fromList([ - ...encrypted.bitLength.pack16(), - ...encrypted.toUnsignedBytes(), - ]); - - Future decrypt(final RSAPrivateKey key) async { - return decodeSessionKey( - SessionKeyParams.processInBlocks( - AsymmetricBlockCipher('RSA/PKCS1') - ..init( - false, - PrivateKeyParameter(key), - ), - encrypted.toUnsignedBytes(), - ), - ); - } -} diff --git a/lib/src/packet/key/secret_material.dart b/lib/src/packet/key/secret_material.dart new file mode 100644 index 00000000..6b71c117 --- /dev/null +++ b/lib/src/packet/key/secret_material.dart @@ -0,0 +1,14 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +export 'dsa_secret_material.dart'; +export 'ecdh_secret_material.dart'; +export 'ecdsa_secret_material.dart'; +export 'eddsa_legacy_secret_material.dart'; +export 'eddsa_secret_material.dart'; +export 'elgamal_secret_material.dart'; +export 'montgomery_secret_material.dart'; +export 'rsa_secret_material.dart'; diff --git a/lib/src/packet/key/session_key.dart b/lib/src/packet/key/session_key.dart index 601c0b6e..2c7a1344 100644 --- a/lib/src/packet/key/session_key.dart +++ b/lib/src/packet/key/session_key.dart @@ -1,51 +1,65 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. -import 'dart:typed_data'; +library; -import 'package:dart_pg/src/crypto/math/int_ext.dart'; +import 'dart:typed_data'; -import '../../crypto/math/byte_ext.dart'; +import '../../type/session_key.dart'; +import '../../common/helpers.dart'; import '../../enum/symmetric_algorithm.dart'; -import '../../helpers.dart'; +/// Session key class /// Author Nguyen Van Nguyen -class SessionKey { - /// Algorithm to encrypt the message with +class SessionKey implements SessionKeyInterface { + @override final SymmetricAlgorithm symmetric; - /// Encryption key - final Uint8List key; + @override + final Uint8List encryptionKey; - SessionKey(this.key, [this.symmetric = SymmetricAlgorithm.aes128]); + SessionKey(this.encryptionKey, [this.symmetric = SymmetricAlgorithm.aes128]); factory SessionKey.produceKey([ - SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - ]) { - return SessionKey(Helper.generateEncryptionKey(symmetric), symmetric); + final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + ]) => + SessionKey( + Helper.generateEncryptionKey(symmetric), + symmetric, + ); + + factory SessionKey.decode(final Uint8List data) { + final sessionKeySymmetric = SymmetricAlgorithm.values.firstWhere( + (algo) => algo.value == data[0], + ); + final sessionKey = SessionKey( + data.sublist(1, data.length - 2), + sessionKeySymmetric, + ); + sessionKey.checksum(data.sublist(data.length - 2)); + return sessionKey; } - /// Serializes session key to bytes - Uint8List encode() => Uint8List.fromList([symmetric.value, ...key]); + @override + Uint8List encode() => Uint8List.fromList( + [symmetric.value, ...encryptionKey], + ); - /// Compute checksum + @override Uint8List computeChecksum() { var sum = 0; - for (var i = 0; i < key.lengthInBytes; i++) { - sum = (sum + key[i]) & 0xffff; + for (var i = 0; i < encryptionKey.length; i++) { + sum = (sum + encryptionKey[i]) & 0xffff; } return sum.pack16(); } @override - bool operator ==(other) { - if (other is! SessionKey) return false; - return (other.symmetric == symmetric) && (other.key.equals(key)); - } - - @override - int get hashCode { - return symmetric.hashCode + key.hashCode; + void checksum(Uint8List checksum) { + final computedChecksum = computeChecksum(); + if (!((computedChecksum[0] == checksum[0]) && (computedChecksum[1] == checksum[1]))) { + throw StateError('Session key checksum mismatch!'); + } } } diff --git a/lib/src/packet/key/session_key_cryptor.dart b/lib/src/packet/key/session_key_cryptor.dart new file mode 100644 index 00000000..7e6c3049 --- /dev/null +++ b/lib/src/packet/key/session_key_cryptor.dart @@ -0,0 +1,56 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; +import 'package:pointycastle/api.dart'; + +import '../../type/session_key.dart'; +import '../../type/session_key_cryptor.dart'; +import 'session_key.dart'; + +export 'session_key.dart'; +export 'ecdh_session_key_cryptor.dart'; +export 'elgamal_session_key_cryptor.dart'; +export 'montgomery_session_key_cryptor.dart'; +export 'rsa_session_key_cryptor.dart'; + +/// Abstract session key cryptor class +/// Author Nguyen Van Nguyen +abstract class SessionKeyCryptor implements SessionKeyCryptorInterface { + static SessionKeyInterface decodeSessionKey(final Uint8List data) { + return SessionKey.decode(data); + } + + static Uint8List processInBlocks( + final AsymmetricBlockCipher engine, + final Uint8List input, + ) { + final numBlocks = + input.length ~/ engine.inputBlockSize + ((input.lengthInBytes % engine.inputBlockSize != 0) ? 1 : 0); + + final output = Uint8List(numBlocks * engine.outputBlockSize); + + var inpOff = 0; + var outOff = 0; + while (inpOff < input.length) { + final chunkSize = (inpOff + engine.inputBlockSize <= input.lengthInBytes) + ? engine.inputBlockSize + : input.lengthInBytes - inpOff; + + outOff += engine.processBlock( + input, + inpOff, + chunkSize, + output, + outOff, + ); + + inpOff += chunkSize; + } + + return (output.length == outOff) ? output : output.sublist(0, outOff); + } +} diff --git a/lib/src/packet/key/session_key_params.dart b/lib/src/packet/key/session_key_params.dart deleted file mode 100644 index 253f43e0..00000000 --- a/lib/src/packet/key/session_key_params.dart +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import 'package:pointycastle/api.dart'; - -import '../../enum/symmetric_algorithm.dart'; -import 'session_key.dart'; - -export 'ecdh_session_key_params.dart'; -export 'elgamal_session_key_params.dart'; -export 'rsa_session_key_params.dart'; - -/// Session key params -/// Author Nguyen Van Nguyen -abstract class SessionKeyParams { - Uint8List encode(); - - SessionKey decodeSessionKey(final Uint8List data) { - final sessionKeySymmetric = SymmetricAlgorithm.values.firstWhere( - (algo) => algo.value == data[0], - ); - final sessionKey = SessionKey( - data.sublist(1, data.length - 2), - sessionKeySymmetric, - ); - final checksum = data.sublist(data.length - 2); - final computedChecksum = sessionKey.computeChecksum(); - final isValidChecksum = (computedChecksum[0] == checksum[0]) && - (computedChecksum[1] == checksum[1]); - if (!isValidChecksum) { - throw StateError('Session key decryption error'); - } - return sessionKey; - } - - static Uint8List processInBlocks( - final AsymmetricBlockCipher engine, - final Uint8List input, - ) { - final numBlocks = input.length ~/ engine.inputBlockSize + - ((input.lengthInBytes % engine.inputBlockSize != 0) ? 1 : 0); - - final output = Uint8List(numBlocks * engine.outputBlockSize); - - var inpOff = 0; - var outOff = 0; - while (inpOff < input.length) { - final chunkSize = (inpOff + engine.inputBlockSize <= input.lengthInBytes) - ? engine.inputBlockSize - : input.lengthInBytes - inpOff; - - outOff += engine.processBlock( - input, - inpOff, - chunkSize, - output, - outOff, - ); - - inpOff += chunkSize; - } - - return (output.length == outOff) ? output : output.sublist(0, outOff); - } -} diff --git a/lib/src/packet/key/verification_params.dart b/lib/src/packet/key/verification_params.dart deleted file mode 100644 index afc4436a..00000000 --- a/lib/src/packet/key/verification_params.dart +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../enum/hash_algorithm.dart'; -import 'key_params.dart'; - -/// Author Nguyen Van Nguyen -abstract class VerificationParams extends KeyParams { - Future verify( - final Uint8List message, - final HashAlgorithm hash, - final Uint8List signature, - ); -} diff --git a/lib/src/packet/key_packet.dart b/lib/src/packet/key_packet.dart deleted file mode 100644 index 317f4229..00000000 --- a/lib/src/packet/key_packet.dart +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../enum/key_algorithm.dart'; -import 'key/key_id.dart'; -import 'contained_packet.dart'; -import 'key/key_params.dart'; -import 'public_key.dart'; - -export 'public_key.dart'; -export 'public_subkey.dart'; -export 'secret_key.dart'; -export 'secret_subkey.dart'; -export 'subkey_packet.dart'; - -/// Author Nguyen Van Nguyen -abstract class KeyPacket implements ContainedPacket { - int get version; - - DateTime get creationTime; - - KeyAlgorithm get algorithm; - - KeyParams get publicParams; - - String get fingerprint; - - KeyID get keyID; - - int get keyStrength; - - bool get isEncrypted; - - bool get isDecrypted; - - bool get isSigningKey; - - bool get isEncryptionKey; - - PublicKeyPacket get publicKey; - - Uint8List writeForSign(); - - static isSigningAlgorithm(final KeyAlgorithm algorithm) { - switch (algorithm) { - case KeyAlgorithm.rsaEncrypt: - case KeyAlgorithm.elgamal: - case KeyAlgorithm.ecdh: - case KeyAlgorithm.diffieHellman: - case KeyAlgorithm.aedh: - return false; - default: - return true; - } - } - - static isEncryptionAlgorithm(final KeyAlgorithm algorithm) { - switch (algorithm) { - case KeyAlgorithm.rsaSign: - case KeyAlgorithm.dsa: - case KeyAlgorithm.ecdsa: - case KeyAlgorithm.eddsa: - case KeyAlgorithm.aedsa: - return false; - default: - return true; - } - } -} diff --git a/lib/src/packet/literal_data.dart b/lib/src/packet/literal_data.dart index f0f1214a..b464a4ab 100644 --- a/lib/src/packet/literal_data.dart +++ b/lib/src/packet/literal_data.dart @@ -1,45 +1,50 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:convert'; import 'dart:typed_data'; -import '../crypto/math/byte_ext.dart'; +import '../common/helpers.dart'; +import '../enum/packet_type.dart'; import '../enum/literal_format.dart'; -import '../enum/packet_tag.dart'; -import '../helpers.dart'; -import 'contained_packet.dart'; +import '../type/literal_data.dart'; +import 'base.dart'; /// Implementation of the Literal Data Packet (Tag 11) -/// -/// See RFC 4880, section 5.9. -/// A Literal Data packet contains the body of a message; data that is not to be further interpreted. /// Author Nguyen Van Nguyen -class LiteralDataPacket extends ContainedPacket { +class LiteralDataPacket extends BasePacket implements LiteralDataInterface { + @override final LiteralFormat format; + @override final DateTime time; - final Uint8List data; + @override + final Uint8List binary; + @override final String text; + @override final String filename; LiteralDataPacket( - this.data, { - this.format = LiteralFormat.utf8, + this.binary, { + this.format = LiteralFormat.binary, final DateTime? time, this.text = '', this.filename = '', }) : time = time ?? DateTime.now(), - super(PacketTag.literalData); + super(PacketType.literalData); - factory LiteralDataPacket.fromByteData(final Uint8List bytes) { + factory LiteralDataPacket.fromBytes(final Uint8List bytes) { var pos = 0; - final format = - LiteralFormat.values.firstWhere((format) => format.value == bytes[pos]); + final format = LiteralFormat.values.firstWhere( + (format) => format.value == bytes[pos], + ); pos++; final length = bytes[pos++]; final filename = utf8.decode(bytes.sublist(pos, pos + length)); @@ -49,9 +54,10 @@ class LiteralDataPacket extends ContainedPacket { pos += 4; final data = bytes.sublist(pos); - final text = (format == LiteralFormat.text || format == LiteralFormat.utf8) - ? utf8.decode(data) - : ''; + final text = switch (format) { + LiteralFormat.text || LiteralFormat.utf8 => utf8.decode(data), + _ => '', + }; return LiteralDataPacket( data, @@ -74,21 +80,26 @@ class LiteralDataPacket extends ContainedPacket { ); @override - Uint8List toByteData() { - return Uint8List.fromList([ - format.value, - filename.length, - ...filename.stringToBytes(), - ...time.toBytes(), - ...writeForSign(), - ]); - } + Uint8List get data => Uint8List.fromList([ + ...header, + ...signBytes, + ]); - Uint8List writeForSign() { - return data.isNotEmpty - ? data - : text - .replaceAll(RegExp(r'\r?\n', multiLine: true), '\r\n') - .stringToBytes(); - } + @override + Uint8List get signBytes => binary.isNotEmpty + ? binary + : text + .replaceAll( + RegExp(r'\r?\n', multiLine: true), + '\r\n', + ) + .toBytes(); + + @override + Uint8List get header => Uint8List.fromList([ + format.value, + filename.length, + ...filename.toBytes(), + ...time.toBytes(), + ]); } diff --git a/lib/src/packet/marker.dart b/lib/src/packet/marker.dart new file mode 100644 index 00000000..56d55c89 --- /dev/null +++ b/lib/src/packet/marker.dart @@ -0,0 +1,22 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:convert'; +import 'dart:typed_data'; + +import '../enum/packet_type.dart'; +import 'base.dart'; + +/// Implementation of the strange "Marker packet" (Tag 10) +/// Author Nguyen Van Nguyen +class MarkerPacket extends BasePacket { + static const marker = 'PGP'; + + MarkerPacket() : super(PacketType.marker); + + @override + Uint8List get data => utf8.encoder.convert(marker); +} diff --git a/lib/src/packet/marker_packet.dart b/lib/src/packet/marker_packet.dart deleted file mode 100644 index 14b9c2ed..00000000 --- a/lib/src/packet/marker_packet.dart +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:convert'; -import 'dart:typed_data'; - -import '../enum/packet_tag.dart'; -import 'contained_packet.dart'; - -/// Implementation of the strange "Marker packet" (Tag 10) -/// Author Nguyen Van Nguyen -class MarkerPacket extends ContainedPacket { - static const marker = 'PGP'; - - MarkerPacket() : super(PacketTag.marker); - - @override - Uint8List toByteData() { - return utf8.encoder.convert(marker); - } -} diff --git a/lib/src/packet/modification_detection_code.dart b/lib/src/packet/modification_detection_code.dart deleted file mode 100644 index eda08fa0..00000000 --- a/lib/src/packet/modification_detection_code.dart +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../enum/packet_tag.dart'; -import 'contained_packet.dart'; - -/// Implementation of the Modification Detection Code Packet (Tag 19) -/// See RFC 4880, section 5.14 -/// . -/// The Modification Detection Code packet contains a SHA-1 hash of plaintext data, which is used to detect message modification. -/// It is only used with a Symmetrically Encrypted Integrity Protected Data packet. -/// The Modification Detection Code packet MUST be the last packet in the plaintext data that is encrypted -/// in the Symmetrically Encrypted Integrity Protected Data packet, and MUST appear in no other place. -/// Author Nguyen Van Nguyen -class ModificationDetectionCodePacket extends ContainedPacket { - final Uint8List digest; - - ModificationDetectionCodePacket(this.digest) - : super(PacketTag.modificationDetectionCode); - - factory ModificationDetectionCodePacket.fromByteData(final Uint8List bytes) => - ModificationDetectionCodePacket(bytes); - - @override - Uint8List toByteData() { - return digest; - } -} diff --git a/lib/src/packet/one_pass_signature.dart b/lib/src/packet/one_pass_signature.dart deleted file mode 100644 index 37867170..00000000 --- a/lib/src/packet/one_pass_signature.dart +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../enum/hash_algorithm.dart'; -import '../enum/key_algorithm.dart'; -import '../enum/packet_tag.dart'; -import '../enum/signature_type.dart'; -import 'contained_packet.dart'; - -/// OnePassSignature represents a One-Pass Signature packet. -/// See RFC 4880, section 5.4. -/// -/// The One-Pass Signature packet precedes the signed data and contains enough information -/// to allow the receiver to begin calculating any hashes needed to verify the signature. -/// It allows the Signature packet to be placed at the end of the message, -/// so that the signer can compute the entire signed message in one pass. -/// Author Nguyen Van Nguyen -class OnePassSignaturePacket extends ContainedPacket { - static const version = 3; - - final SignatureType signatureType; - - final HashAlgorithm hashAlgorithm; - - final KeyAlgorithm keyAlgorithm; - - final Uint8List issuerKeyID; - - final int nested; - - OnePassSignaturePacket( - this.signatureType, - this.hashAlgorithm, - this.keyAlgorithm, - this.issuerKeyID, - this.nested, - ) : super(PacketTag.onePassSignature); - - factory OnePassSignaturePacket.fromByteData(final Uint8List bytes) { - var pos = 0; - final version = bytes[pos++]; - if (version != 3) { - throw UnsupportedError( - 'Version $version of the one-pass signature packet is unsupported.', - ); - } - - final signatureType = SignatureType.values.firstWhere( - (type) => type.value == bytes[pos], - ); - pos++; - final hashAlgorithm = HashAlgorithm.values.firstWhere( - (algo) => algo.value == bytes[pos], - ); - pos++; - final keyAlgorithm = KeyAlgorithm.values.firstWhere( - (algo) => algo.value == bytes[pos], - ); - pos++; - final issuerKeyID = bytes.sublist(pos, pos + 8); - return OnePassSignaturePacket( - signatureType, - hashAlgorithm, - keyAlgorithm, - issuerKeyID, - bytes[pos + 8], - ); - } - - @override - Uint8List toByteData() { - return Uint8List.fromList([ - version, - signatureType.value, - hashAlgorithm.value, - keyAlgorithm.value, - ...issuerKeyID, - nested, - ]); - } -} diff --git a/lib/src/packet/packet_list.dart b/lib/src/packet/packet_list.dart index 112f39df..b7290c6b 100644 --- a/lib/src/packet/packet_list.dart +++ b/lib/src/packet/packet_list.dart @@ -1,133 +1,51 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:collection'; import 'dart:typed_data'; -import '../enum/packet_tag.dart'; -import 'aead_encrypted_data.dart'; -import 'compressed_data.dart'; -import 'contained_packet.dart'; -import 'literal_data.dart'; -import 'marker_packet.dart'; -import 'modification_detection_code.dart'; -import 'one_pass_signature.dart'; -import 'packet_reader.dart'; -import 'public_key.dart'; -import 'public_key_encrypted_session_key.dart'; -import 'public_subkey.dart'; -import 'secret_key.dart'; -import 'secret_subkey.dart'; -import 'signature_packet.dart'; -import 'sym_encrypted_integrity_protected_data.dart'; -import 'sym_encrypted_session_key.dart'; -import 'sym_encrypted_data.dart'; -import 'trust_packet.dart'; -import 'user_attribute.dart'; -import 'user_id.dart'; +import 'package:dart_pg/src/enum/packet_type.dart'; + +import '../type/packet.dart'; +import '../type/packet_list.dart'; -/// This class represents a list of openpgp packets. +/// This class represents a list of OpenPGP packets. /// Author Nguyen Van Nguyen -class PacketList extends ListBase { - final List packets; +class PacketList extends ListBase implements PacketListInterface { + @override + final List packets; - PacketList(final Iterable packets) + PacketList(final Iterable packets) : packets = packets.toList( growable: false, ); - factory PacketList.packetDecode(final Uint8List bytes) { - final packets = []; - var offset = 0; - while (offset < bytes.length) { - final reader = PacketReader.read(bytes, offset); - offset = reader.offset; - - switch (reader.tag) { - case PacketTag.publicKeyEncryptedSessionKey: - packets.add( - PublicKeyEncryptedSessionKeyPacket.fromByteData(reader.data), - ); - break; - case PacketTag.signature: - packets.add(SignaturePacket.fromByteData(reader.data)); - break; - case PacketTag.symEncryptedSessionKey: - packets.add(SymEncryptedSessionKeyPacket.fromByteData(reader.data)); - break; - case PacketTag.onePassSignature: - packets.add(OnePassSignaturePacket.fromByteData(reader.data)); - break; - case PacketTag.secretKey: - packets.add(SecretKeyPacket.fromByteData(reader.data)); - break; - case PacketTag.publicKey: - packets.add(PublicKeyPacket.fromByteData(reader.data)); - break; - case PacketTag.secretSubkey: - packets.add(SecretSubkeyPacket.fromByteData(reader.data)); - break; - case PacketTag.compressedData: - packets.add(CompressedDataPacket.fromByteData(reader.data)); - break; - case PacketTag.symEncryptedData: - packets.add(SymEncryptedDataPacket.fromByteData(reader.data)); - break; - case PacketTag.marker: - packets.add(MarkerPacket()); - break; - case PacketTag.literalData: - packets.add(LiteralDataPacket.fromByteData(reader.data)); - break; - case PacketTag.trust: - packets.add(TrustPacket.fromByteData(reader.data)); - break; - case PacketTag.userID: - packets.add(UserIDPacket.fromByteData(reader.data)); - break; - case PacketTag.publicSubkey: - packets.add(PublicSubkeyPacket.fromByteData(reader.data)); - break; - case PacketTag.userAttribute: - packets.add(UserAttributePacket.fromByteData(reader.data)); - break; - case PacketTag.symEncryptedIntegrityProtectedData: - packets.add( - SymEncryptedIntegrityProtectedDataPacket.fromByteData(reader.data), - ); - break; - case PacketTag.modificationDetectionCode: - packets.add( - ModificationDetectionCodePacket.fromByteData(reader.data), - ); - break; - case PacketTag.aeadEncryptedData: - packets.add( - AeadEncryptedData.fromByteData(reader.data), - ); - break; - } - } + /// Decode packets from bytes + factory PacketList.decode(Uint8List bytes) { + final packets = []; return PacketList(packets); } + @override Uint8List encode() => Uint8List.fromList( packets.map((packet) => packet.encode()).expand((byte) => byte).toList(growable: false), ); - PacketList filterByTags([final List tags = const []]) { + PacketList filterByTypes([final List tags = const []]) { if (tags.isNotEmpty) { - return PacketList(packets.where((packet) => tags.contains(packet.tag))); + return PacketList(packets.where((packet) => tags.contains(packet.type))); } return this; } - List indexOfTags([final List tags = const []]) { + List indexOfTypes([final List tags = const []]) { final indexes = []; for (var i = 0; i < packets.length; i++) { final packet = packets[i]; - if (tags.contains(packet.tag)) { + if (tags.contains(packet.type)) { indexes.add(i); } } @@ -139,10 +57,10 @@ class PacketList extends ListBase { int get length => packets.length; @override - ContainedPacket operator [](int index) => packets[index]; + PacketInterface operator [](int index) => packets[index]; @override - void operator []=(int index, ContainedPacket packet) { + void operator []=(int index, PacketInterface packet) { packets[index] = packet; } diff --git a/lib/src/packet/packet_reader.dart b/lib/src/packet/packet_reader.dart index e4712243..4fb9c2f5 100644 --- a/lib/src/packet/packet_reader.dart +++ b/lib/src/packet/packet_reader.dart @@ -1,27 +1,27 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. -import 'package:pinenacl/api.dart'; +library; -import '../crypto/math/byte_ext.dart'; -import '../enum/packet_tag.dart'; +import 'dart:typed_data'; -/// Generic Packet Data Reader function +import '../common/extensions.dart'; +import '../enum/packet_type.dart'; + +/// Packet Data Reader /// Author Nguyen Van Nguyen -class PacketReader { - final PacketTag tag; +final class PacketReader { + final PacketType type; final Uint8List data; final int offset; - PacketReader(this.tag, this.data, this.offset); + PacketReader(this.type, this.data, this.offset); factory PacketReader.read(final Uint8List bytes, [final int offset = 0]) { - if (bytes.length <= offset || - bytes.sublist(offset).length < 2 || - (bytes[offset] & 0x80) == 0) { + if (bytes.length <= offset || bytes.sublist(offset).length < 2 || (bytes[offset] & 0x80) == 0) { throw StateError( 'Error during parsing. This data probably does not conform to a valid OpenPGP format.', ); @@ -32,87 +32,84 @@ class PacketReader { final headerByte = bytes[pos++]; final oldFormat = ((headerByte & 0x40) != 0) ? false : true; final tagByte = oldFormat ? (headerByte & 0x3f) >> 2 : headerByte & 0x3f; - final tag = PacketTag.values.firstWhere((tag) => tag.value == tagByte); + final type = PacketType.values.firstWhere((tag) => tag.value == tagByte); final Uint8List packetData; - var packetLength = bytes.length - pos; + var packetLen = bytes.length - pos; if (oldFormat) { final lengthType = headerByte & 0x03; switch (lengthType) { case 0: - packetLength = bytes[pos++]; + packetLen = bytes[pos++]; break; case 1: - packetLength = bytes.sublist(pos, pos + 2).toIn16(); + packetLen = bytes.sublist(pos, pos + 2).unpack16(); pos += 2; break; case 2: - packetLength = bytes.sublist(pos, pos + 4).toInt32(); + packetLen = bytes.sublist(pos, pos + 4).unpack32(); pos += 4; break; } - packetData = bytes.sublist(pos, pos + packetLength); + packetData = bytes.sublist(pos, pos + packetLen); } else { final length = bytes[pos++]; if (length < 192) { - packetLength = length; - packetData = bytes.sublist(pos, pos + packetLength); + packetLen = length; + packetData = bytes.sublist(pos, pos + packetLen); } else if (length < 224) { - packetLength = ((length - 192) << 8) + (bytes[pos++]) + 192; - packetData = bytes.sublist(pos, pos + packetLength); + packetLen = ((length - 192) << 8) + (bytes[pos++]) + 192; + packetData = bytes.sublist(pos, pos + packetLen); } else if (length < 255) { - var partialLength = 1 << (length & 0x1f); + var partialLen = 1 << (length & 0x1f); final List partialData = List.empty(growable: true); - partialData.add(bytes.sublist(pos, pos + partialLength)); - var partialPos = pos + partialLength; + partialData.add(bytes.sublist(pos, pos + partialLen)); + var partialPos = pos + partialLen; while (true) { - partialLength = bytes[partialPos++]; - if (partialLength < 192) { - partialData - .add(bytes.sublist(partialPos, partialPos + partialLength)); - partialPos += partialLength; + partialLen = bytes[partialPos++]; + if (partialLen < 192) { + partialData.add( + bytes.sublist(partialPos, partialPos + partialLen), + ); + partialPos += partialLen; break; - } else if (partialLength < 224) { - partialLength = - ((partialLength - 192) << 8) + (bytes[partialPos++]) + 192; - partialData - .add(bytes.sublist(partialPos, partialPos + partialLength)); - partialPos += partialLength; + } else if (partialLen < 224) { + partialLen = ((partialLen - 192) << 8) + (bytes[partialPos++]) + 192; + partialData.add(bytes.sublist(partialPos, partialPos + partialLen)); + partialPos += partialLen; break; - } else if (partialLength < 255) { - partialLength = 1 << (partialLength & 0x1f); - partialData - .add(bytes.sublist(partialPos, partialPos + partialLength)); - partialPos += partialLength; + } else if (partialLen < 255) { + partialLen = 1 << (partialLen & 0x1f); + partialData.add(bytes.sublist(partialPos, partialPos + partialLen)); + partialPos += partialLen; } else { - partialLength = bytes + partialLen = bytes .sublist( partialPos, partialPos + 4, ) - .toInt32(); + .unpack32(); partialPos += 4; - partialData - .add(bytes.sublist(partialPos, partialPos + partialLength)); - partialPos += partialLength; + partialData.add(bytes.sublist(partialPos, partialPos + partialLen)); + partialPos += partialLen; break; } } packetData = Uint8List.fromList([ ...partialData.expand((element) => element), ]); - packetLength = partialPos - pos; + packetLen = partialPos - pos; } else { - packetLength = bytes.sublist(pos, pos + 4).toInt32(); + packetLen = bytes.sublist(pos, pos + 4).unpack32(); pos += 4; - packetData = bytes.sublist(pos, pos + packetLength); + packetData = bytes.sublist(pos, pos + packetLen); } } return PacketReader( - tag, + type, packetData, - pos + packetLength, + pos + packetLen, ); } } diff --git a/lib/src/packet/padding.dart b/lib/src/packet/padding.dart new file mode 100644 index 00000000..703ab5b7 --- /dev/null +++ b/lib/src/packet/padding.dart @@ -0,0 +1,30 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../common/helpers.dart'; +import '../enum/packet_type.dart'; +import 'base.dart'; + +/// Implementation of the Padding Packet (Type 21) +/// Author Nguyen Van Nguyen +class PaddingPacket extends BasePacket { + static const paddingMin = 16; + static const paddingMax = 32; + + final Uint8List padding; + + PaddingPacket(this.padding) : super(PacketType.marker); + + factory PaddingPacket.createPadding(int lengh) { + assert(paddingMin <= lengh && lengh <= paddingMax); + return PaddingPacket(Helper.secureRandom().nextBytes(lengh)); + } + + @override + Uint8List get data => padding; +} diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index 77262b87..e4d741a1 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -1,63 +1,65 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; -import '../crypto/math/byte_ext.dart'; -import '../crypto/math/int_ext.dart'; -import '../enum/key_algorithm.dart'; +import '../common/helpers.dart'; +import '../enum/eddsa_curve.dart'; import '../enum/hash_algorithm.dart'; -import '../enum/packet_tag.dart'; -import '../helpers.dart'; -import 'key/key_id.dart'; -import 'key/key_params.dart'; -import 'contained_packet.dart'; -import 'key_packet.dart'; - -/// PublicKey represents an OpenPGP public key. -/// See RFC 4880, section 5.5.2. +import '../enum/key_version.dart'; +import '../enum/montgomery_curve.dart'; +import '../enum/key_algorithm.dart'; +import '../enum/packet_type.dart'; +import '../type/key_material.dart'; +import '../type/key_packet.dart'; +import '../type/subkey_packet.dart'; +import 'base.dart'; +import 'key/public_material.dart'; + +/// Implementation of the Public Key Packet (Type 6) /// Author Nguyen Van Nguyen -class PublicKeyPacket extends ContainedPacket implements KeyPacket { - static const keyVersion = 4; - +class PublicKeyPacket extends BasePacket implements KeyPacketInterface { @override - final int version = keyVersion; + final int keyVersion; @override final DateTime creationTime; @override - final KeyAlgorithm algorithm; + final KeyAlgorithm keyAlgorithm; @override - final KeyParams publicParams; + final KeyMaterialInterface keyMaterial; late final Uint8List _fingerprint; - late final KeyID _keyID; + late final Uint8List _keyID; PublicKeyPacket( + this.keyVersion, this.creationTime, - this.publicParams, { - this.algorithm = KeyAlgorithm.rsaEncryptSign, - }) : super(PacketTag.publicKey) { + this.keyMaterial, { + this.keyAlgorithm = KeyAlgorithm.rsaEncryptSign, + }) : super(PacketType.publicKey) { + if (keyVersion != KeyVersion.v4.value || keyVersion != KeyVersion.v6.value) { + throw UnsupportedError( + 'Version $keyVersion of the key packet is unsupported.', + ); + } _calculateFingerprintAndKeyID(); } - factory PublicKeyPacket.fromByteData(final Uint8List bytes) { + factory PublicKeyPacket.fromBytes(final Uint8List bytes) { var pos = 0; - /// A one-octet version number (3 or 4 or 5). + /// /// A one-octet version number (4 or 6). final version = bytes[pos++]; - if (version != keyVersion) { - throw UnsupportedError( - 'Version $version of the key packet is unsupported.', - ); - } /// A four-octet number denoting the time that the key was created. - final creationTime = bytes.sublist(pos, pos + 4).toDateTime(); + final creation = bytes.sublist(pos, pos + 4).toDateTime(); pos += 4; /// A one-octet number denoting the public-key algorithm of this key. @@ -66,109 +68,107 @@ class PublicKeyPacket extends ContainedPacket implements KeyPacket { ); pos++; - /// A series of values comprising the key material. - /// This is algorithm-specific and described in section XXXX. - final KeyParams publicParams; - switch (algorithm) { - case KeyAlgorithm.rsaEncryptSign: - case KeyAlgorithm.rsaEncrypt: - case KeyAlgorithm.rsaSign: - publicParams = RSAPublicParams.fromByteData(bytes.sublist(pos)); - break; - case KeyAlgorithm.dsa: - publicParams = DSAPublicParams.fromByteData(bytes.sublist(pos)); - break; - case KeyAlgorithm.elgamal: - publicParams = ElGamalPublicParams.fromByteData(bytes.sublist(pos)); - break; - case KeyAlgorithm.ecdsa: - publicParams = ECDSAPublicParams.fromByteData(bytes.sublist(pos)); - break; - case KeyAlgorithm.ecdh: - publicParams = ECDHPublicParams.fromByteData(bytes.sublist(pos)); - break; - case KeyAlgorithm.eddsa: - publicParams = EdDSAPublicParams.fromByteData(bytes.sublist(pos)); - break; - default: - throw UnsupportedError( - 'Unsupported PGP public key algorithm encountered', - ); - } return PublicKeyPacket( - creationTime, - publicParams, - algorithm: algorithm, + version, + creation, + _readKeyMaterial( + bytes.sublist(pos), + algorithm, + ), + keyAlgorithm: algorithm, ); } - /// Computes and set the fingerprint of the key - void _calculateFingerprintAndKeyID() { - _fingerprint = Uint8List.fromList( - Helper.hashDigest(writeForSign(), HashAlgorithm.sha1)); - _keyID = KeyID(_fingerprint.sublist(12, 20)); - } - @override - String get fingerprint => _fingerprint.toHexadecimal(); + Uint8List get data => Uint8List.fromList([ + keyVersion, + ...creationTime.toBytes(), + keyAlgorithm.value, + ...keyMaterial.toBytes, + ]); @override - KeyID get keyID => _keyID; + Uint8List get fingerprint => _fingerprint; @override - bool get isEncrypted => false; + bool get isEncryptionKey => keyAlgorithm.forEncryption; @override - bool get isDecrypted => true; + bool get isSigningKey => keyAlgorithm.forSigning; @override - bool get isSigningKey { - return KeyPacket.isSigningAlgorithm(algorithm); - } + bool get isSubkey => this is SubkeyPacketInterface; @override - bool get isEncryptionKey { - return KeyPacket.isEncryptionAlgorithm(algorithm); - } + Uint8List get keyID => _keyID; @override - PublicKeyPacket get publicKey => this; + int get keyStrength => keyMaterial.keyLength; @override - int get keyStrength { - final keyParams = publicParams; - if (keyParams is RSAPublicParams) { - return keyParams.modulus.bitLength; - } - if (keyParams is DSAPublicParams) { - return keyParams.prime.bitLength; - } - if (keyParams is ElGamalPublicParams) { - return keyParams.prime.bitLength; - } - if (keyParams is ECPublicParams) { - return keyParams.curve.fieldSize; + Uint8List get signBytes => Uint8List.fromList([ + 0x99, + ...data.length.pack16(), + ...data, + ]); + + _calculateFingerprintAndKeyID() { + if (keyVersion == KeyVersion.v6.value) { + _fingerprint = Uint8List.fromList( + Helper.hashDigest(signBytes, HashAlgorithm.sha256), + ); + _keyID = _fingerprint.sublist(0, 12); + } else { + _fingerprint = Uint8List.fromList( + Helper.hashDigest(signBytes, HashAlgorithm.sha1), + ); + _keyID = _fingerprint.sublist(12, 20); } - return -1; - } - - @override - Uint8List writeForSign() { - final bytes = toByteData(); - return Uint8List.fromList([ - 0x99, - ...bytes.lengthInBytes.pack16(), - ...bytes, - ]); } - @override - Uint8List toByteData() { - return Uint8List.fromList([ - version, - ...creationTime.toBytes(), - algorithm.value, - ...publicParams.encode(), - ]); + static KeyMaterialInterface _readKeyMaterial( + final Uint8List keyData, + final KeyAlgorithm algorithm, + ) { + return switch (algorithm) { + KeyAlgorithm.rsaEncryptSign || + KeyAlgorithm.rsaSign || + KeyAlgorithm.rsaEncrypt => + RSAPublicMaterial.fromBytes(keyData), + KeyAlgorithm.dsa => DSAPublicMaterial.fromBytes( + keyData, + ), + KeyAlgorithm.elgamal => ElGamalPublicMaterial.fromBytes( + keyData, + ), + KeyAlgorithm.ecdsa => ECDSAPublicMaterial.fromBytes( + keyData, + ), + KeyAlgorithm.ecdh => ECDHPublicMaterial.fromBytes( + keyData, + ), + KeyAlgorithm.eddsaLegacy => EdDSALegacyPublicMaterial.fromBytes( + keyData, + ), + KeyAlgorithm.x25519 => MontgomeryPublicMaterial.fromBytes( + keyData, + MontgomeryCurve.x25519, + ), + KeyAlgorithm.x448 => MontgomeryPublicMaterial.fromBytes( + keyData, + MontgomeryCurve.x448, + ), + KeyAlgorithm.ed25519 => EdDSAPublicMaterial.fromBytes( + keyData, + EdDSACurve.ed25519, + ), + KeyAlgorithm.ed448 => EdDSAPublicMaterial.fromBytes( + keyData, + EdDSACurve.ed448, + ), + _ => throw UnsupportedError( + 'Unsupported public key algorithm encountered', + ), + }; } } diff --git a/lib/src/packet/public_key_encrypted_session_key.dart b/lib/src/packet/public_key_encrypted_session_key.dart index da6c8d70..6c656354 100644 --- a/lib/src/packet/public_key_encrypted_session_key.dart +++ b/lib/src/packet/public_key_encrypted_session_key.dart @@ -1,184 +1,220 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; -import '../crypto/asymmetric/elgamal.dart'; +import '../type/key_packet.dart'; import '../enum/key_algorithm.dart'; -import '../enum/packet_tag.dart'; -import '../helpers.dart'; -import 'contained_packet.dart'; -import 'key/key_id.dart'; -import 'key/key_params.dart'; -import 'key/session_key.dart'; -import 'key/session_key_params.dart'; -import 'key_packet.dart'; - -/// PublicKeyEncryptedSessionKey represents a Public-Key Encrypted Session Key packet. -/// -/// See RFC 4880, section 5.1. -/// A Public-Key Encrypted Session Key packet holds the session key used to encrypt a message. -/// Zero or more Public-Key Encrypted Session Key packets and/or Symmetric-Key Encrypted Session Key -/// packets may precede a Symmetrically Encrypted Data Packet, which holds an encrypted message. -/// The message is encrypted with the session key, and the session key is itself -/// encrypted and stored in the Encrypted Session Key packet(s). -/// The Symmetrically Encrypted Data Packet is preceded by one Public-Key Encrypted -/// Session Key packet for each OpenPGP key to which the message is encrypted. -/// The recipient of the message finds a session key that is encrypted to their public key, -/// decrypts the session key, and then uses the session key to decrypt the message. +import '../enum/montgomery_curve.dart'; +import '../enum/packet_type.dart'; +import '../type/secret_key_packet.dart'; +import '../type/session_key.dart'; +import '../type/session_key_cryptor.dart'; +import 'base.dart'; +import 'key/public_material.dart'; +import 'key/session_key_cryptor.dart'; + +/// Implementation of the Public-Key Encrypted Session Key (PKESK) Packet (Type 1) /// Author Nguyen Van Nguyen -class PublicKeyEncryptedSessionKeyPacket extends ContainedPacket { - static const version = 3; +class PublicKeyEncryptedSessionKeyPacket extends BasePacket { + final int version; + + final int keyVersion; - final KeyID publicKeyID; + final Uint8List keyFingerprint; - final KeyAlgorithm publicKeyAlgorithm; + final Uint8List keyID; - /// Encrypted session key params - final SessionKeyParams sessionKeyParams; + final KeyAlgorithm keyAlgorithm; - /// Session key - final SessionKey? sessionKey; + final SessionKeyCryptorInterface cryptor; - bool get isDecrypted => sessionKey != null; + final SessionKeyInterface? sessionKey; PublicKeyEncryptedSessionKeyPacket( - this.publicKeyID, - this.publicKeyAlgorithm, - this.sessionKeyParams, { + this.version, + this.keyVersion, + this.keyFingerprint, + this.keyID, + this.keyAlgorithm, + this.cryptor, { this.sessionKey, - }) : super(PacketTag.publicKeyEncryptedSessionKey); - - factory PublicKeyEncryptedSessionKeyPacket.fromByteData( - final Uint8List bytes) { - var pos = 0; - final pkeskVersion = bytes[pos++]; - if (pkeskVersion != version) { + }) : super(PacketType.publicKeyEncryptedSessionKey) { + if (version != 3 && version != 6) { throw UnsupportedError( - 'Version $pkeskVersion of the PKESK packet is unsupported.', + 'Version $version of the PKESK packet is unsupported.', ); } + if (version == 6 && keyAlgorithm == KeyAlgorithm.elgamal) { + throw UnsupportedError( + 'Key algorithm ${keyAlgorithm.name} cannot be used with version {$version} PKESK packet.', + ); + } + } - final keyID = bytes.sublist(pos, pos + 8); - pos += 8; - - final keyAlgorithm = - KeyAlgorithm.values.firstWhere((algo) => algo.value == bytes[pos]); + factory PublicKeyEncryptedSessionKeyPacket.fromBytes(final Uint8List bytes) { + var pos = 0; + final version = bytes[pos++]; + + final int keyVersion; + final Uint8List keyFingerprint; + final Uint8List keyID; + if (version == 6) { + final length = bytes[pos++]; + keyVersion = bytes[pos++]; + keyFingerprint = bytes.sublist(pos, pos + length - 1); + pos += length - 1; + keyID = keyVersion == 6 ? keyFingerprint.sublist(0, 8) : keyFingerprint.sublist(12, 20); + } else { + keyVersion = 0; + keyFingerprint = Uint8List(0); + keyID = bytes.sublist(pos, pos + 8); + pos += 8; + } + final keyAlgorithm = KeyAlgorithm.values.firstWhere( + (algo) => algo.value == bytes[pos], + ); pos++; - final SessionKeyParams params; - switch (keyAlgorithm) { - case KeyAlgorithm.rsaEncryptSign: - case KeyAlgorithm.rsaEncrypt: - params = RSASessionKeyParams.fromByteData(bytes.sublist(pos)); - break; - case KeyAlgorithm.elgamal: - params = ElGamalSessionKeyParams.fromByteData(bytes.sublist(pos)); - break; - case KeyAlgorithm.ecdh: - params = ECDHSessionKeyParams.fromByteData(bytes.sublist(pos)); - break; - default: - throw UnsupportedError( + final sessionKeyCryptor = switch (keyAlgorithm) { + KeyAlgorithm.rsaEncryptSign || KeyAlgorithm.rsaEncrypt => RSASessionKeyCryptor.fromBytes( + bytes.sublist(pos), + ), + KeyAlgorithm.elgamal => ElGamalSessionKeyCryptor.fromBytes( + bytes.sublist(pos), + ), + KeyAlgorithm.ecdh => ECDHSessionKeyCryptor.fromBytes( + bytes.sublist(pos), + ), + KeyAlgorithm.x25519 => MontgomerySessionKeyCryptor.fromBytes( + bytes, + MontgomeryCurve.x25519, + ), + KeyAlgorithm.x448 => MontgomerySessionKeyCryptor.fromBytes( + bytes, + MontgomeryCurve.x448, + ), + _ => throw UnsupportedError( 'Public key algorithm ${keyAlgorithm.name} of the PKESK packet is unsupported.', - ); - } + ) + }; return PublicKeyEncryptedSessionKeyPacket( - KeyID(keyID), + version, + keyVersion, + keyFingerprint, + keyID, keyAlgorithm, - params, + sessionKeyCryptor, ); } - static Future encryptSessionKey( - final PublicKeyPacket publicKey, - final SessionKey sessionKey, - ) async { - final SessionKeyParams params; - final keyParams = publicKey.publicParams; - if (keyParams is RSAPublicParams) { - params = await RSASessionKeyParams.encryptSessionKey( - keyParams.publicKey, - sessionKey, - ); - } else if (keyParams is ElGamalPublicParams) { - params = await ElGamalSessionKeyParams.encryptSessionKey( - keyParams.publicKey, - sessionKey, - ); - } else if (keyParams is ECDHPublicParams) { - params = await ECDHSessionKeyParams.encryptSessionKey( - keyParams, - sessionKey, - publicKey.fingerprint.hexToBytes(), - ); - } else { - throw UnsupportedError( - 'Public key algorithm ${publicKey.algorithm.name} is unsupported for session key encryption.', - ); - } + factory PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( + KeyPacketInterface keyPacket, + SessionKeyInterface sessionKey, + ) { + final version = keyPacket.keyVersion == 6 ? 6 : 3; + final keyData = switch (keyPacket.keyAlgorithm) { + KeyAlgorithm.x25519 || KeyAlgorithm.x448 => sessionKey.encryptionKey, + _ => version == 3 + ? Uint8List.fromList([ + ...sessionKey.encode(), + ...sessionKey.computeChecksum(), + ]) + : Uint8List.fromList([ + ...sessionKey.encryptionKey, + ...sessionKey.computeChecksum(), + ]), + }; + + final cryptor = switch (keyPacket.keyAlgorithm) { + KeyAlgorithm.rsaEncryptSign || KeyAlgorithm.rsaEncrypt => RSASessionKeyCryptor.encryptSessionKey( + keyData, + keyPacket.keyMaterial as RSAPublicMaterial, + ), + KeyAlgorithm.ecdh => ECDHSessionKeyCryptor.encryptSessionKey( + keyData, + keyPacket.keyMaterial as ECDHPublicMaterial, + keyPacket.fingerprint, + ), + KeyAlgorithm.x25519 || KeyAlgorithm.x448 => MontgomerySessionKeyCryptor.encryptSessionKey( + keyData, + keyPacket.keyMaterial as MontgomeryPublicMaterial, + ), + _ => throw UnsupportedError( + 'Public key algorithm ${keyPacket.keyAlgorithm.name} is unsupported for session key encryption.', + ), + }; return PublicKeyEncryptedSessionKeyPacket( - publicKey.keyID, - publicKey.algorithm, - params, - sessionKey: sessionKey, + version, + keyPacket.keyVersion, + keyPacket.fingerprint, + keyPacket.keyID, + keyPacket.keyAlgorithm, + cryptor, ); } @override - Uint8List toByteData() { - return Uint8List.fromList([ - version, - ...publicKeyID.bytes, - publicKeyAlgorithm.value, - ...sessionKeyParams.encode(), - ]); - } - - Future decrypt( - final SecretKeyPacket key) async { - if (isDecrypted) { + Uint8List get data => Uint8List.fromList([ + version, + ...version == 6 ? [keyFingerprint.length + 1] : [], + ...version == 6 ? [keyVersion] : [], + ...version == 6 ? keyFingerprint : [], + ...version == 3 ? keyID : [], + keyAlgorithm.value, + ...cryptor.encode(), + ]); + + PublicKeyEncryptedSessionKeyPacket decrypt( + final SecretKeyPacketInterface key, + ) { + if (sessionKey != null) { return this; } else { - // check that session key algo matches the secret key algo and secret key is decrypted - if (publicKeyAlgorithm != key.algorithm || !key.isDecrypted) { + if (keyAlgorithm != key.keyAlgorithm || !key.isDecrypted) { throw ArgumentError( 'Secret key packet is invalid for session key decryption', ); } - - final SessionKey? sessionKey; - final keyParams = sessionKeyParams; - if (keyParams is RSASessionKeyParams) { - final privateKey = (key.secretParams as RSASecretParams).privateKey; - sessionKey = await keyParams.decrypt(privateKey); - } else if (keyParams is ElGamalSessionKeyParams) { - final publicKey = (key.publicParams as ElGamalPublicParams).publicKey; - sessionKey = await keyParams.decrypt( - ElGamalPrivateKey( - (key.secretParams as ElGamalSecretParams).exponent, - publicKey.prime, - publicKey.generator, - ), - ); - } else if (keyParams is ECDHSessionKeyParams) { - sessionKey = await keyParams.decrypt( - key.secretParams as ECSecretParams, - key.publicParams as ECDHPublicParams, - key.fingerprint.hexToBytes(), - ); + if (cryptor is ECDHSessionKeyCryptor) { + (cryptor as ECDHSessionKeyCryptor).fingerprint = key.fingerprint; + } + final keyData = cryptor.decrypt(key.secretKeyMaterial!); + final SessionKeyInterface sessionKey; + if (version == 3) { + sessionKey = SessionKey.decode(keyData); } else { - throw UnsupportedError( - 'Public key algorithm ${key.algorithm.name} is unsupported for session key decryption.', - ); + switch (keyAlgorithm) { + case KeyAlgorithm.x25519: + sessionKey = SessionKey( + keyData, + MontgomeryCurve.x25519.symmetric, + ); + break; + case KeyAlgorithm.x448: + sessionKey = SessionKey( + keyData, + MontgomeryCurve.x448.symmetric, + ); + break; + default: + final keyLength = keyData.length - 2; + sessionKey = SessionKey(keyData.sublist(0, keyLength)); + sessionKey.checksum(keyData.sublist(keyLength)); + } } return PublicKeyEncryptedSessionKeyPacket( - publicKeyID, - publicKeyAlgorithm, - sessionKeyParams, + version, + keyVersion, + keyFingerprint, + keyID, + keyAlgorithm, + cryptor, sessionKey: sessionKey, ); } diff --git a/lib/src/packet/public_subkey.dart b/lib/src/packet/public_subkey.dart index 4212571b..0772f023 100644 --- a/lib/src/packet/public_subkey.dart +++ b/lib/src/packet/public_subkey.dart @@ -1,30 +1,35 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; -import '../enum/packet_tag.dart'; +import '../enum/packet_type.dart'; +import '../type/subkey_packet.dart'; import 'public_key.dart'; -import 'subkey_packet.dart'; +/// Implementation of the Public Subkey Packet (Type 14) /// Author Nguyen Van Nguyen -class PublicSubkeyPacket extends PublicKeyPacket implements SubkeyPacket { +class PublicSubkeyPacket extends PublicKeyPacket implements SubkeyPacketInterface { @override - PacketTag get tag => PacketTag.publicSubkey; + PacketType get type => PacketType.publicSubkey; PublicSubkeyPacket( - super.createdTime, - super.publicParams, { - super.algorithm, + super.keyVersion, + super.creationTime, + super.keyMaterial, { + super.keyAlgorithm, }); - factory PublicSubkeyPacket.fromByteData(final Uint8List bytes) { - final publicKey = PublicKeyPacket.fromByteData(bytes); + factory PublicSubkeyPacket.fromBytes(final Uint8List bytes) { + final publicKey = PublicKeyPacket.fromBytes(bytes); return PublicSubkeyPacket( + publicKey.keyVersion, publicKey.creationTime, - publicKey.publicParams, - algorithm: publicKey.algorithm, + publicKey.keyMaterial, + keyAlgorithm: publicKey.keyAlgorithm, ); } } diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 7ff375db..89a200b6 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -1,36 +1,45 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; -import 'package:pointycastle/export.dart'; +import 'package:pointycastle/api.dart'; + +import 'key/public_material.dart'; +import 'key/secret_material.dart'; -import '../crypto/math/byte_ext.dart'; -import '../crypto/math/int_ext.dart'; -import '../crypto/symmetric/base_cipher.dart'; -import '../enum/curve_info.dart'; -import '../enum/dh_key_size.dart'; +import '../common/argon2_s2k.dart'; +import '../common/generic_s2k.dart'; +import '../common/helpers.dart'; +import '../cryptor/symmetric/buffered_cipher.dart'; +import '../enum/rsa_key_size.dart'; +import '../enum/aead_algorithm.dart'; +import '../enum/ecc.dart'; +import '../enum/eddsa_curve.dart'; +import '../enum/montgomery_curve.dart'; import '../enum/hash_algorithm.dart'; +import '../enum/key_version.dart'; import '../enum/key_algorithm.dart'; -import '../enum/packet_tag.dart'; -import '../enum/rsa_key_size.dart'; +import '../enum/packet_type.dart'; import '../enum/s2k_type.dart'; import '../enum/s2k_usage.dart'; import '../enum/symmetric_algorithm.dart'; -import '../helpers.dart'; -import 'key/key_id.dart'; -import 'key/key_pair_params.dart'; -import 'key/key_params.dart'; -import 'key/s2k.dart'; -import 'contained_packet.dart'; -import 'key_packet.dart'; - -/// SecretKey represents a possibly encrypted private key. -/// See RFC 4880, section 5.5.3. +import '../type/key_material.dart'; +import '../type/s2k.dart'; +import '../type/secret_key_material.dart'; +import '../type/secret_key_packet.dart'; +import '../type/subkey_packet.dart'; +import 'base.dart'; +import 'public_key.dart'; + +/// Implementation of the Secret Key Packet (Type 5) /// Author Nguyen Van Nguyen -class SecretKeyPacket extends ContainedPacket implements KeyPacket { - final PublicKeyPacket _publicKey; +class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { + @override + final PublicKeyPacket publicKey; final Uint8List keyData; @@ -38,56 +47,96 @@ class SecretKeyPacket extends ContainedPacket implements KeyPacket { final SymmetricAlgorithm symmetric; - final S2K? s2k; + @override + final AeadAlgorithm? aead; + + final S2kInterface? s2k; final Uint8List? iv; - final KeyParams? secretParams; + @override + final SecretKeyMaterialInterface? secretKeyMaterial; SecretKeyPacket( - this._publicKey, + this.publicKey, this.keyData, { - this.s2kUsage = S2kUsage.sha1, + this.s2kUsage = S2kUsage.cfb, this.symmetric = SymmetricAlgorithm.aes128, + this.aead, this.s2k, this.iv, - this.secretParams, - }) : super(PacketTag.secretKey); + this.secretKeyMaterial, + }) : super(PacketType.secretKey); - factory SecretKeyPacket.fromByteData(final Uint8List bytes) { - final publicKey = PublicKeyPacket.fromByteData(bytes); + factory SecretKeyPacket.fromBytes(final Uint8List bytes) { + final publicKey = PublicKeyPacket.fromBytes(bytes); + final isV6 = publicKey.keyVersion == KeyVersion.v6.value; - var pos = publicKey.toByteData().length; + var pos = publicKey.data.length; final s2kUsage = S2kUsage.values.firstWhere( (usage) => usage.value == bytes[pos], ); - pos++; - final S2K? s2k; + // Only for a version 6 packet where the secret key material encrypted + if (isV6 && s2kUsage != S2kUsage.none) { + pos++; + } + + final S2kInterface? s2k; + final AeadAlgorithm? aead; final SymmetricAlgorithm symmetric; switch (s2kUsage) { - case S2kUsage.checksum: - case S2kUsage.sha1: + case S2kUsage.malleableCfb: + case S2kUsage.cfb: + case S2kUsage.aeadProtect: symmetric = SymmetricAlgorithm.values.firstWhere( (usage) => usage.value == bytes[pos], ); pos++; - s2k = S2K.fromByteData(bytes.sublist(pos)); + + // If s2k usage octet was 253, a one-octet AEAD algorithm. + if (s2kUsage == S2kUsage.aeadProtect) { + aead = AeadAlgorithm.values.firstWhere( + (usage) => usage.value == bytes[pos], + ); + pos++; + } else { + aead = null; + } + + // Only for a version 6 packet, and if string-to-key usage + // octet was 253 or 254, an one-octet count of the following field. + if (isV6 && (s2kUsage == S2kUsage.aeadProtect || s2kUsage == S2kUsage.cfb)) { + pos++; + } + + final s2kType = S2kType.values.firstWhere( + (usage) => usage.value == bytes[pos], + ); + + if (s2kType == S2kType.argon2) { + s2k = Argon2S2k.fromBytes(bytes.sublist(pos)); + } else { + s2k = GenericS2k.fromBytes(bytes.sublist(pos)); + } pos += s2k.length; break; default: symmetric = SymmetricAlgorithm.plaintext; s2k = null; + aead = null; } Uint8List? iv; - if (!(s2k != null && s2k.type == S2kType.gnu) && s2kUsage != S2kUsage.none) { - final blockSize = symmetric.blockSize; - iv = bytes.sublist(pos, pos + blockSize); - pos += blockSize; + if (aead != null) { + iv = bytes.sublist(pos, pos + aead.blockLength); + pos += aead.blockLength; + } else if (!(s2k != null && s2k.type == S2kType.gnu) && s2kUsage != S2kUsage.none) { + iv = bytes.sublist(pos, pos + symmetric.blockSize); + pos += symmetric.blockSize; } - KeyParams? secretParams; + SecretKeyMaterialInterface? secretKeyMaterial; var keyData = bytes.sublist(pos); if (s2kUsage == S2kUsage.none) { final checksum = keyData.sublist(keyData.length - 2); @@ -95,270 +144,325 @@ class SecretKeyPacket extends ContainedPacket implements KeyPacket { if (!checksum.equals(_computeChecksum(keyData))) { throw StateError('Key checksum mismatch!'); } - secretParams = _parseSecretParams( + secretKeyMaterial = _readKeyMaterial( keyData, - publicKey.algorithm, + publicKey, ); } - return SecretKeyPacket( publicKey, keyData, s2kUsage: s2kUsage, symmetric: symmetric, + aead: aead, s2k: s2k, iv: iv, - secretParams: secretParams, + secretKeyMaterial: secretKeyMaterial, ); } - static Future generate( + /// Generate secret key packet + factory SecretKeyPacket.generate( final KeyAlgorithm algorithm, { - final RSAKeySize rsaKeySize = RSAKeySize.s4096, - final DHKeySize dhKeySize = DHKeySize.l2048n224, - final CurveInfo curve = CurveInfo.secp521r1, + final RSAKeySize rsaKeySize = RSAKeySize.normal, + final Ecc curve = Ecc.secp521r1, final DateTime? date, - }) async { - final keyPair = await KeyPairParams.generate( - algorithm, - rsaKeySize: rsaKeySize, - dhKeySize: dhKeySize, - curve: curve, - ); + }) { + final keyMaterial = switch (algorithm) { + KeyAlgorithm.rsaEncryptSign || + KeyAlgorithm.rsaSign || + KeyAlgorithm.rsaEncrypt => + RSASecretMaterial.generate(rsaKeySize), + KeyAlgorithm.ecdsa => ECDSASecretMaterial.generate(curve), + KeyAlgorithm.ecdh => ECDHSecretMaterial.generate(curve), + KeyAlgorithm.eddsaLegacy => EdDSALegacySecretMaterial.generate(), + KeyAlgorithm.x25519 => MontgomerySecretMaterial.generate(MontgomeryCurve.x25519), + KeyAlgorithm.x448 => MontgomerySecretMaterial.generate(MontgomeryCurve.x448), + KeyAlgorithm.ed25519 => EdDSASecretMaterial.generate(EdDSACurve.ed25519), + KeyAlgorithm.ed448 => EdDSASecretMaterial.generate(EdDSACurve.ed448), + _ => throw UnsupportedError("Key algorithm ${algorithm.name} is unsupported."), + }; return SecretKeyPacket( PublicKeyPacket( + algorithm.keyVersion, date ?? DateTime.now(), - keyPair.publicParams, - algorithm: algorithm, + keyMaterial.publicMaterial, + keyAlgorithm: algorithm, ), - keyPair.secretParams.encode(), - secretParams: keyPair.secretParams, + keyMaterial.toBytes, + secretKeyMaterial: keyMaterial, ); } @override - PublicKeyPacket get publicKey => _publicKey; - - @override - bool get isEncrypted => s2kUsage != S2kUsage.none; - - @override - bool get isDecrypted => secretParams != null; - - @override - bool get isSigningKey { - return KeyPacket.isSigningAlgorithm(algorithm); - } - - @override - bool get isEncryptionKey { - return KeyPacket.isEncryptionAlgorithm(algorithm); - } - - bool get isDummy => s2k != null && s2k!.type == S2kType.gnu; - - @override - KeyAlgorithm get algorithm => _publicKey.algorithm; - - @override - DateTime get creationTime => _publicKey.creationTime; - - @override - KeyParams get publicParams => _publicKey.publicParams; - - @override - String get fingerprint => _publicKey.fingerprint; - - @override - KeyID get keyID => _publicKey.keyID; - - @override - int get version => _publicKey.version; - - @override - int get keyStrength => _publicKey.keyStrength; - - HashAlgorithm get preferredHash { - final keyParams = publicParams; - if ((keyParams is ECPublicParams)) { - final curve = CurveInfo.values.firstWhere( - (info) => info.asn1Oid == keyParams.oid, - orElse: () => CurveInfo.secp521r1, - ); - return curve.hashAlgorithm; - } else { - return HashAlgorithm.sha256; - } - } - - Future encrypt( - final String passphrase, { - final S2kUsage s2kUsage = S2kUsage.sha1, - final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - final HashAlgorithm hash = HashAlgorithm.sha1, - final S2kType type = S2kType.iterated, - }) async { - if (secretParams != null) { + encrypt( + final String passphrase, + final SymmetricAlgorithm symmetric, + final AeadAlgorithm? aead, + ) { + if (secretKeyMaterial != null) { if (passphrase.isEmpty) { throw ArgumentError('passphrase are required for key encryption'); } assert(s2kUsage != S2kUsage.none); - assert(symmetric != SymmetricAlgorithm.plaintext); - + Helper.assertSymmetric(symmetric); + final aeadProtect = aead != null; + if (aeadProtect && keyVersion != KeyVersion.v6.value) { + throw ArgumentError('Using AEAD with version $keyVersion of the key packet is not allowed.'); + } + final s2k = aeadProtect ? Helper.stringToKey(S2kType.argon2) : Helper.stringToKey(S2kType.iterated); final random = Helper.secureRandom(); - final s2k = S2K( - random.nextBytes(S2K.saltLength), - hash: hash, - type: type, - ); final iv = random.nextBytes(symmetric.blockSize); + final kek = _produceEncryptionKey( + passphrase, + symmetric, + type, + s2k: s2k, + aead: aead, + ); + final clearText = secretKeyMaterial!.toBytes; + final Uint8List cipherText; + if (aeadProtect) { + final cipher = aead.cipherEngine(kek, symmetric); + cipherText = cipher.encrypt( + clearText, + iv, + Uint8List.fromList([ + type.value | 0xc0, + ...publicKey.data, + ])); + } else { + final cipher = BufferedCipher(symmetric.cfbCipherEngine) + ..init( + true, + ParametersWithIV(KeyParameter(kek), iv), + ); - final key = await s2k.produceKey(passphrase, symmetric.keySizeInByte); - final cipher = BufferedCipher(symmetric.cfbCipherEngine) - ..init( - true, - ParametersWithIV(KeyParameter(key), iv), - ); - - final clearText = secretParams!.encode(); - final cipherText = cipher.process(Uint8List.fromList([ - ...clearText, - ...Helper.hashDigest(clearText, HashAlgorithm.sha1), - ])); - + cipherText = cipher.process(Uint8List.fromList([ + ...clearText, + ...Helper.hashDigest(clearText, HashAlgorithm.sha1), + ])); + } return SecretKeyPacket( publicKey, cipherText, s2kUsage: s2kUsage, symmetric: symmetric, + aead: aead, s2k: s2k, iv: iv, - secretParams: secretParams, + secretKeyMaterial: secretKeyMaterial, ); } else { return this; } } - Future decrypt(final String passphrase) async { - if (secretParams == null) { + @override + decrypt(final String passphrase) { + if (secretKeyMaterial == null) { final Uint8List clearText; if (isEncrypted) { - final key = await s2k?.produceKey(passphrase, symmetric.keySizeInByte) ?? Uint8List(symmetric.keySizeInByte); - final cipher = BufferedCipher(symmetric.cfbCipherEngine) - ..init( - false, - ParametersWithIV( - KeyParameter(key), - iv ?? Uint8List(symmetric.blockSize), - ), - ); - - final clearTextWithHash = cipher.process(keyData); - clearText = clearTextWithHash.sublist( - 0, - clearTextWithHash.length - HashAlgorithm.sha1.digestSize, + final kek = _produceEncryptionKey( + passphrase, + symmetric, + type, + s2k: s2k, + aead: aead, ); - final hashText = clearTextWithHash.sublist( - clearTextWithHash.length - HashAlgorithm.sha1.digestSize, - ); - final hashed = Helper.hashDigest(clearText, HashAlgorithm.sha1); - if (!hashed.equals(hashText)) { - throw ArgumentError('Incorrect key passphrase'); + if (aead != null) { + final cipher = aead!.cipherEngine(kek, symmetric); + clearText = cipher.decrypt( + keyData, + iv!, + Uint8List.fromList([ + type.value | 0xc0, + ...publicKey.data, + ])); + } else { + final cipher = BufferedCipher(symmetric.cfbCipherEngine) + ..init( + false, + ParametersWithIV( + KeyParameter(kek), + iv ?? Uint8List(symmetric.blockSize), + ), + ); + + final clearTextWithHash = cipher.process(keyData); + clearText = clearTextWithHash.sublist( + 0, + clearTextWithHash.length - HashAlgorithm.sha1.digestSize, + ); + final hashText = clearTextWithHash.sublist( + clearTextWithHash.length - HashAlgorithm.sha1.digestSize, + ); + final hashed = Helper.hashDigest(clearText, HashAlgorithm.sha1); + if (!hashed.equals(hashText)) { + throw ArgumentError('Incorrect key passphrase'); + } } } else { clearText = keyData; } - return SecretKeyPacket( publicKey, keyData, s2kUsage: s2kUsage, symmetric: symmetric, + aead: aead, s2k: s2k, iv: iv, - secretParams: _parseSecretParams(clearText, publicKey.algorithm), + secretKeyMaterial: _readKeyMaterial(clearText, publicKey), ); } else { return this; } } - /// Check whether the private and public primary key parameters correspond - /// Together with verification of binding signatures, this guarantees key integrity - bool validate() { - if (secretParams == null) { - return false; - } - final keyParams = secretParams; - if (keyParams is RSASecretParams) { - return keyParams.validatePublicParams(publicParams as RSAPublicParams); - } - if (keyParams is DSASecretParams) { - return keyParams.validatePublicParams(publicParams as DSAPublicParams); - } - if (keyParams is ElGamalSecretParams) { - return keyParams.validatePublicParams(publicParams as ElGamalPublicParams); - } - if (keyParams is ECSecretParams) { - return keyParams.validatePublicParams(publicParams as ECPublicParams); - } - if (keyParams is EdSecretParams) { - return keyParams.validatePublicParams(publicParams as EdDSAPublicParams); - } - return false; - } - - @override - Uint8List writeForSign() { - return publicKey.writeForSign(); - } - @override - Uint8List toByteData() { - if (s2kUsage != S2kUsage.none && s2k != null) { - return Uint8List.fromList([ - ...publicKey.toByteData(), - s2kUsage.value, + Uint8List get data { + final isV6 = publicKey.keyVersion == KeyVersion.v6.value; + if (isEncrypted) { + final optBytes = Uint8List.fromList([ symmetric.value, - ...s2k!.encode(), + ...aead != null ? [aead!.value] : [], + ...isV6 ? [s2k!.length] : [], + ...s2k!.toBytes, ...iv ?? [], + ]); + return Uint8List.fromList([ + ...publicKey.data, + s2kUsage.value, + ...isV6 ? [optBytes.length] : [], + ...optBytes, ...keyData, ]); } else { return Uint8List.fromList([ - ...publicKey.toByteData(), + ...publicKey.data, S2kUsage.none.value, ...keyData, - ..._computeChecksum(keyData), + ...isV6 ? [] : _computeChecksum(keyData), ]); } } - static KeyParams _parseSecretParams( - final Uint8List packetData, - final KeyAlgorithm algorithm, + @override + Uint8List get signBytes => publicKey.signBytes; + + @override + DateTime get creationTime => publicKey.creationTime; + + @override + Uint8List get fingerprint => publicKey.fingerprint; + + @override + bool get isDecrypted => secretKeyMaterial != null; + + @override + bool get isEncrypted => s2kUsage != S2kUsage.none; + + @override + bool get isEncryptionKey => publicKey.isEncryptionKey; + + @override + bool get isSigningKey => publicKey.isSigningKey; + + @override + bool get isSubkey => this is SubkeyPacketInterface; + + @override + KeyAlgorithm get keyAlgorithm => publicKey.keyAlgorithm; + + @override + Uint8List get keyID => publicKey.keyID; + + @override + KeyMaterialInterface get keyMaterial => publicKey.keyMaterial; + + @override + int get keyStrength => publicKey.keyStrength; + + @override + int get keyVersion => publicKey.keyVersion; + + static Uint8List _produceEncryptionKey( + final String passphrase, + final SymmetricAlgorithm symmetric, + final PacketType type, { + final S2kInterface? s2k, + final AeadAlgorithm? aead, + }) { + final derivedKey = s2k != null + ? s2k.produceKey( + passphrase, + symmetric.keySizeInByte, + ) + : Uint8List(symmetric.keySizeInByte); + if (aead != null) { + return Helper.hkdf( + derivedKey, + symmetric.keySizeInByte, + info: Uint8List.fromList([ + type.value | 0xc0, + KeyVersion.v6.value, + symmetric.value, + aead.value, + ]), + ); + } + return derivedKey; + } + + static SecretKeyMaterialInterface _readKeyMaterial( + final Uint8List keyData, + final PublicKeyPacket publicKey, ) { - switch (algorithm) { - case KeyAlgorithm.rsaEncryptSign: - case KeyAlgorithm.rsaEncrypt: - case KeyAlgorithm.rsaSign: - return RSASecretParams.fromByteData(packetData); - case KeyAlgorithm.dsa: - return DSASecretParams.fromByteData(packetData); - case KeyAlgorithm.elgamal: - return ElGamalSecretParams.fromByteData(packetData); - case KeyAlgorithm.ecdsa: - case KeyAlgorithm.ecdh: - return ECSecretParams.fromByteData(packetData); - case KeyAlgorithm.eddsa: - return EdSecretParams.fromByteData(packetData); - default: - throw UnsupportedError( - 'Public key algorithm ${algorithm.name} is unsupported.', - ); + final keyMaterial = switch (publicKey.keyAlgorithm) { + KeyAlgorithm.rsaEncryptSign || KeyAlgorithm.rsaSign || KeyAlgorithm.rsaEncrypt => RSASecretMaterial.fromBytes( + keyData, + publicKey.keyMaterial as RSAPublicMaterial, + ), + KeyAlgorithm.dsa => DSASecretMaterial.fromBytes( + keyData, + publicKey.keyMaterial as DSAPublicMaterial, + ), + KeyAlgorithm.elgamal => ElGamalSecretMaterial.fromBytes( + keyData, + publicKey.keyMaterial as ElGamalPublicMaterial, + ), + KeyAlgorithm.ecdsa => ECDSASecretMaterial.fromBytes( + keyData, + publicKey.keyMaterial as ECDSAPublicMaterial, + ), + KeyAlgorithm.ecdh => ECDHSecretMaterial.fromBytes( + keyData, + publicKey.keyMaterial as ECDHPublicMaterial, + ), + KeyAlgorithm.eddsaLegacy => EdDSALegacySecretMaterial.fromBytes( + keyData, + publicKey.keyMaterial as EdDSALegacyPublicMaterial, + ), + KeyAlgorithm.x25519 || KeyAlgorithm.x448 => MontgomerySecretMaterial.fromBytes( + keyData, + publicKey.keyMaterial as MontgomeryPublicMaterial, + ), + KeyAlgorithm.ed25519 || KeyAlgorithm.ed448 => EdDSASecretMaterial.fromBytes( + keyData, + publicKey.keyMaterial as EdDSAPublicMaterial, + ), + _ => throw UnsupportedError( + 'Public key algorithm ${publicKey.keyAlgorithm.name} is unsupported.', + ), + }; + + if (!keyMaterial.isValid) { + throw StateError('Key material is not consistent.'); } + + return keyMaterial; } static Uint8List _computeChecksum(Uint8List keyData) { diff --git a/lib/src/packet/secret_subkey.dart b/lib/src/packet/secret_subkey.dart index 86107c56..3a9a1f01 100644 --- a/lib/src/packet/secret_subkey.dart +++ b/lib/src/packet/secret_subkey.dart @@ -1,115 +1,104 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; -import '../enum/curve_info.dart'; -import '../enum/dh_key_size.dart'; -import '../enum/hash_algorithm.dart'; +import '../enum/aead_algorithm.dart'; +import '../enum/ecc.dart'; import '../enum/key_algorithm.dart'; -import '../enum/packet_tag.dart'; +import '../enum/packet_type.dart'; import '../enum/rsa_key_size.dart'; -import '../enum/s2k_type.dart'; -import '../enum/s2k_usage.dart'; import '../enum/symmetric_algorithm.dart'; -import 'key/key_pair_params.dart'; +import '../type/subkey_packet.dart'; import 'public_subkey.dart'; import 'secret_key.dart'; -import 'subkey_packet.dart'; +/// Implementation of the Secret Subkey Packet (Type 7) /// Author Nguyen Van Nguyen -class SecretSubkeyPacket extends SecretKeyPacket implements SubkeyPacket { +class SecretSubkeyPacket extends SecretKeyPacket implements SubkeyPacketInterface { @override - PacketTag get tag => PacketTag.secretSubkey; + PacketType get type => PacketType.secretSubkey; SecretSubkeyPacket( - final PublicSubkeyPacket publicKey, - final Uint8List keyData, { + PublicSubkeyPacket super.publicKey, + super.keyData, { super.s2kUsage, super.symmetric, + super.aead, super.s2k, super.iv, - super.secretParams, - }) : super(publicKey, keyData); + super.secretKeyMaterial, + }); - factory SecretSubkeyPacket.fromByteData(final Uint8List bytes) { - final secretKey = SecretKeyPacket.fromByteData(bytes); - return _fromSecretKey(secretKey); - } + factory SecretSubkeyPacket.fromBytes( + final Uint8List bytes, + ) => + _fromSecretKey( + SecretKeyPacket.fromBytes(bytes), + ); - static Future generate( + /// Generate secret subkey packet + factory SecretSubkeyPacket.generate( final KeyAlgorithm algorithm, { - final RSAKeySize rsaKeySize = RSAKeySize.s4096, - final DHKeySize dhKeySize = DHKeySize.l2048n224, - final CurveInfo curve = CurveInfo.secp521r1, + final RSAKeySize rsaKeySize = RSAKeySize.normal, + final Ecc curve = Ecc.secp521r1, final DateTime? date, - }) async { - final keyPair = await KeyPairParams.generate( - algorithm, - rsaKeySize: rsaKeySize, - dhKeySize: dhKeySize, - curve: curve, - ); - - return SecretSubkeyPacket( - PublicSubkeyPacket( - date ?? DateTime.now(), - keyPair.publicParams, - algorithm: algorithm, - ), - keyPair.secretParams.encode(), - secretParams: keyPair.secretParams, - ); - } - - @override - PublicSubkeyPacket get publicKey => super.publicKey as PublicSubkeyPacket; + }) => + _fromSecretKey( + SecretKeyPacket.generate( + algorithm, + rsaKeySize: rsaKeySize, + curve: curve, + date: date, + ), + ); @override - Future encrypt( - final String passphrase, { - final S2kUsage s2kUsage = S2kUsage.sha1, - final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - final HashAlgorithm hash = HashAlgorithm.sha1, - final S2kType type = S2kType.iterated, - }) async { - if (secretParams != null) { - return _fromSecretKey(await super.encrypt( + encrypt( + final String passphrase, + final SymmetricAlgorithm symmetric, + final AeadAlgorithm? aead, + ) { + if (secretKeyMaterial != null) { + return _fromSecretKey(super.encrypt( passphrase, - s2kUsage: s2kUsage, - symmetric: symmetric, - hash: hash, - type: type, - )); + symmetric, + aead, + ) as SecretKeyPacket); } else { return this; } } @override - Future decrypt(final String passphrase) async { - if (secretParams == null) { - return _fromSecretKey(await super.decrypt(passphrase)); + decrypt(final String passphrase) { + if (secretKeyMaterial == null) { + return _fromSecretKey(super.decrypt(passphrase) as SecretKeyPacket); } else { return this; } } - static SecretSubkeyPacket _fromSecretKey(final SecretKeyPacket secretKey) { + static SecretSubkeyPacket _fromSecretKey( + final SecretKeyPacket secretKey, + ) { final publicKey = secretKey.publicKey; return SecretSubkeyPacket( PublicSubkeyPacket( + publicKey.keyVersion, publicKey.creationTime, - publicKey.publicParams, - algorithm: publicKey.algorithm, + publicKey.keyMaterial, + keyAlgorithm: publicKey.keyAlgorithm, ), secretKey.keyData, s2kUsage: secretKey.s2kUsage, symmetric: secretKey.symmetric, s2k: secretKey.s2k, iv: secretKey.iv, - secretParams: secretKey.secretParams, + secretKeyMaterial: secretKey.secretKeyMaterial, ); } } diff --git a/lib/src/packet/signature/embedded_signature.dart b/lib/src/packet/signature/embedded_signature.dart deleted file mode 100644 index e84638c0..00000000 --- a/lib/src/packet/signature/embedded_signature.dart +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../enum/signature_subpacket_type.dart'; -import '../signature_packet.dart'; -import '../signature_subpacket.dart'; - -/// This subpacket contains a complete Signature packet body specified in Section 5.2 -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.26 -/// Author Nguyen Van Nguyen -class EmbeddedSignature extends SignatureSubpacket { - EmbeddedSignature(final Uint8List data, {super.critical, super.isLong}) - : super(SignatureSubpacketType.embeddedSignature, data); - - factory EmbeddedSignature.fromSignature(final SignaturePacket signature) => - EmbeddedSignature(signature.toByteData()); -} diff --git a/lib/src/packet/signature/exportable_certification.dart b/lib/src/packet/signature/exportable_certification.dart deleted file mode 100644 index da041a53..00000000 --- a/lib/src/packet/signature/exportable_certification.dart +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../enum/signature_subpacket_type.dart'; -import '../signature_subpacket.dart'; - -/// This subpacket denotes whether a certification signature is -/// "exportable", to be used by other users than the signature's issuer. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.11 -/// Author Nguyen Van Nguyen -class ExportableCertification extends SignatureSubpacket { - ExportableCertification( - final Uint8List data, { - super.critical, - super.isLong, - }) : super(SignatureSubpacketType.exportableCertification, data); - - factory ExportableCertification.fromExportable( - final bool exportable, { - final bool critical = false, - }) => - ExportableCertification( - Uint8List.fromList([exportable ? 1 : 0]), - critical: critical, - ); - - bool get isExportable => data[0] != 0; -} diff --git a/lib/src/packet/signature/features.dart b/lib/src/packet/signature/features.dart deleted file mode 100644 index 5570deb5..00000000 --- a/lib/src/packet/signature/features.dart +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../enum/signature_subpacket_type.dart'; -import '../../enum/support_feature.dart'; -import '../signature_subpacket.dart'; - -/// The Features subpacket denotes which advanced OpenPGP features a -/// user's implementation supports. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.24 -/// Author Nguyen Van Nguyen -class Features extends SignatureSubpacket { - Features( - final Uint8List data, { - super.critical, - super.isLong, - }) : super(SignatureSubpacketType.features, data); - - factory Features.fromFeatures( - final int features, { - final bool critical = false, - }) => - Features(Uint8List.fromList([features]), critical: critical); - - bool get supprtModificationDetection => - (data[0] & SupportFeature.modificationDetection.value) == - SupportFeature.modificationDetection.value; - - bool get supportAeadEncryptedData => - (data[0] & SupportFeature.aeadEncryptedData.value) == - SupportFeature.aeadEncryptedData.value; - - bool get supportVersion5PublicKey => - (data[0] & SupportFeature.version5PublicKey.value) == - SupportFeature.version5PublicKey.value; -} diff --git a/lib/src/packet/signature/issuer_fingerprint.dart b/lib/src/packet/signature/issuer_fingerprint.dart deleted file mode 100644 index ba3f4eec..00000000 --- a/lib/src/packet/signature/issuer_fingerprint.dart +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import 'package:dart_pg/src/helpers.dart'; - -import '../../crypto/math/byte_ext.dart'; -import '../../enum/signature_subpacket_type.dart'; -import '../key_packet.dart'; -import '../signature_subpacket.dart'; - -/// The OpenPGP Key fingerprint of the key issuing the signature. -/// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-rfc4880bis#section-5.2.3.28 -/// Author Nguyen Van Nguyen -class IssuerFingerprint extends SignatureSubpacket { - IssuerFingerprint(final Uint8List data, {super.critical, super.isLong}) - : super(SignatureSubpacketType.issuerFingerprint, data); - - factory IssuerFingerprint.fromKey(final KeyPacket key) => IssuerFingerprint( - Uint8List.fromList([key.version, ...key.fingerprint.hexToBytes()]), - ); - - int get keyVersion => data[0]; - - String get fingerprint => data.sublist(1).toHexadecimal(); -} diff --git a/lib/src/packet/signature/issuer_key_id.dart b/lib/src/packet/signature/issuer_key_id.dart deleted file mode 100644 index 7007e605..00000000 --- a/lib/src/packet/signature/issuer_key_id.dart +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../crypto/math/byte_ext.dart'; -import '../../enum/signature_subpacket_type.dart'; -import '../../helpers.dart'; -import '../signature_subpacket.dart'; - -/// The OpenPGP Key ID of the key issuing the signature. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.5 -/// Author Nguyen Van Nguyen -class IssuerKeyID extends SignatureSubpacket { - IssuerKeyID(final Uint8List data, {super.critical, super.isLong}) - : super(SignatureSubpacketType.issuerKeyID, data); - - factory IssuerKeyID.fromString( - final String id, { - final bool critical = false, - }) => - IssuerKeyID(id.hexToBytes(), critical: critical); - - factory IssuerKeyID.wildcard() => IssuerKeyID( - Uint8List.fromList(List.filled(8, 0, growable: false)), - ); - - String get id => data.toHexadecimal(); -} diff --git a/lib/src/packet/signature/key_expiration_time.dart b/lib/src/packet/signature/key_expiration_time.dart deleted file mode 100644 index 742c695b..00000000 --- a/lib/src/packet/signature/key_expiration_time.dart +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../crypto/math/byte_ext.dart'; -import '../../crypto/math/int_ext.dart'; -import '../../enum/signature_subpacket_type.dart'; -import '../signature_subpacket.dart'; - -/// The validity period of the key. This is the number of seconds after -/// the key creation time that the key expires. If this is not present -/// or has a value of zero, the key never expires. This is found only on -/// a self-signature. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.6 -/// Author Nguyen Van Nguyen -class KeyExpirationTime extends SignatureSubpacket { - KeyExpirationTime( - final Uint8List data, { - super.critical, - super.isLong, - }) : super(SignatureSubpacketType.keyExpirationTime, data); - - factory KeyExpirationTime.fromTime( - final int seconds, { - final bool critical = false, - }) => - KeyExpirationTime(seconds.pack32(), critical: critical); - - /// Return the number of seconds after creation time a key is valid for. - int get time => data.toUint32(); -} diff --git a/lib/src/packet/signature/key_flags.dart b/lib/src/packet/signature/key_flags.dart deleted file mode 100644 index 1f58b116..00000000 --- a/lib/src/packet/signature/key_flags.dart +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../enum/key_flag.dart'; -import '../../enum/signature_subpacket_type.dart'; -import '../signature_subpacket.dart'; - -/// This subpacket contains a list of binary flags that hold information about a key. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.21 -/// Author Nguyen Van Nguyen -class KeyFlags extends SignatureSubpacket { - KeyFlags( - final Uint8List data, { - super.critical, - super.isLong, - }) : super(SignatureSubpacketType.keyFlags, data); - - factory KeyFlags.fromFlags( - final int flags, { - final bool critical = false, - }) => - KeyFlags(_flagsToBytes(flags), critical: critical); - - /// Return the flag values contained in the first 4 octets - /// (note: at the moment the standard only uses the first one). - int get flags { - var value = 0; - for (var i = 0; i != data.length; i++) { - value |= data[i] << (i * 8); - } - return value; - } - - bool get isCertifyKeys => - flags & KeyFlag.certifyKeys.value == KeyFlag.certifyKeys.value; - - bool get isSignData => - flags & KeyFlag.signData.value == KeyFlag.signData.value; - - bool get isEncryptCommunication => - flags & KeyFlag.encryptCommunication.value == - KeyFlag.encryptCommunication.value; - - bool get isEncryptStorage => - flags & KeyFlag.encryptStorage.value == KeyFlag.encryptStorage.value; - - static Uint8List _flagsToBytes(final int flags) { - final bytes = Uint8List(4); - var size = 0; - for (int i = 0; i != 4; i++) { - bytes[i] = (flags >> (i * 8)) & 0xff; - if (bytes[i] != 0) { - size = i; - } - } - return bytes.sublist(0, size + 1); - } -} diff --git a/lib/src/packet/signature/key_server_preferences.dart b/lib/src/packet/signature/key_server_preferences.dart deleted file mode 100644 index 6c269496..00000000 --- a/lib/src/packet/signature/key_server_preferences.dart +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:convert'; -import 'dart:typed_data'; - -import '../../enum/signature_subpacket_type.dart'; -import '../../helpers.dart'; -import '../signature_subpacket.dart'; - -/// This is a list of one-bit flags that indicate preferences that the -/// key holder has about how the key is handled on a key server. -/// All undefined flags MUST be zero. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.17 -/// Author Nguyen Van Nguyen -class KeyServerPreferences extends SignatureSubpacket { - KeyServerPreferences( - final Uint8List data, { - super.critical, - super.isLong, - }) : super(SignatureSubpacketType.keyServerPreferences, data); - - factory KeyServerPreferences.fromServerPreferences( - final String serverPreferences, { - final bool critical = false, - }) => - KeyServerPreferences( - serverPreferences.stringToBytes(), - critical: critical, - ); - - String get serverPreferences => utf8.decode(data); -} diff --git a/lib/src/packet/signature/notation_data.dart b/lib/src/packet/signature/notation_data.dart deleted file mode 100644 index 771d2b96..00000000 --- a/lib/src/packet/signature/notation_data.dart +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:convert'; -import 'dart:math'; -import 'dart:typed_data'; - -import '../../enum/signature_subpacket_type.dart'; -import '../../helpers.dart'; -import '../signature_subpacket.dart'; - -/// This subpacket describes a "notation" on the signature that the issuer wishes to make. -/// The notation has a name and a value, each of which are strings of octets. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.16 -/// Author Nguyen Van Nguyen -class NotationData extends SignatureSubpacket { - static const saltName = "salt@notations.dart-pg.org"; - - static const headerFlagLength = 4; - static const headerNameLength = 2; - static const headerValueLength = 2; - - NotationData(final Uint8List data, {super.critical, super.isLong}) - : super(SignatureSubpacketType.notationData, data); - - factory NotationData.fromNotation( - final bool humanReadable, - final String notationName, - final String notationValue, { - final bool critical = false, - }) => - NotationData( - _notationToBytes(humanReadable, notationName, notationValue), - critical: critical, - ); - - factory NotationData.saltNotation( - int saltSize, { - final bool critical = false, - }) { - final salt = Helper.secureRandom().nextBytes(saltSize); - final nameData = utf8.encode(saltName); - final nameLength = min(nameData.length, 0xffff); - return NotationData( - Uint8List.fromList([ - ...[0, 0, 0, 0], - (nameLength >> 8) & 0xff, - (nameLength >> 0) & 0xff, - (salt.length >> 8) & 0xff, - (salt.length >> 0) & 0xff, - ...nameData, - ...salt, - ]), - critical: critical, - ); - } - - bool get isHumanReadable => data[0] == 0x80; - - String get notationName { - final nameLength = (((data[headerFlagLength] & 0xff) << 8) + - (data[headerFlagLength + 1] & 0xff)); - final nameOffset = headerFlagLength + headerNameLength + headerValueLength; - return utf8.decode(data.sublist(nameOffset, nameOffset + nameLength)); - } - - String get notationValue { - final nameLength = (((data[headerFlagLength] & 0xff) << 8) + - (data[headerFlagLength + 1] & 0xff)); - final valueLength = - (((data[headerFlagLength + headerNameLength] & 0xff) << 8) + - (data[headerFlagLength + headerNameLength + 1] & 0xff)); - final valueOffset = - headerFlagLength + headerNameLength + headerValueLength + nameLength; - return utf8.decode(data.sublist(valueOffset, valueOffset + valueLength)); - } - - static Uint8List _notationToBytes( - final bool humanReadable, - final String notationName, - final String notationValue, - ) { - final nameData = utf8.encode(notationName); - final nameLength = min(nameData.length, 0xffff); - if (nameLength != nameData.length) { - throw ArgumentError('notationName exceeds maximum length.'); - } - - final valueData = utf8.encode(notationValue); - final valueLength = min(valueData.length, 0xffff); - if (valueLength != valueData.length) { - throw ArgumentError('notationValue exceeds maximum length.'); - } - - return Uint8List.fromList([ - ...[humanReadable ? 0x80 : 0, 0, 0, 0], - (nameLength >> 8) & 0xff, - (nameLength >> 0) & 0xff, - (valueLength >> 8) & 0xff, - (valueLength >> 0) & 0xff, - ...nameData.sublist(0, nameLength), - ...valueData.sublist(0, valueLength), - ]); - } -} diff --git a/lib/src/packet/signature/policy_uri.dart b/lib/src/packet/signature/policy_uri.dart deleted file mode 100644 index 00b5c48f..00000000 --- a/lib/src/packet/signature/policy_uri.dart +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:convert'; -import 'dart:typed_data'; - -import '../../enum/signature_subpacket_type.dart'; -import '../../helpers.dart'; -import '../signature_subpacket.dart'; - -/// This subpacket contains a URI of a document that describes the policy -/// under which the signature was issued. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.20 -/// Author Nguyen Van Nguyen -class PolicyURI extends SignatureSubpacket { - PolicyURI( - final Uint8List data, { - super.critical, - super.isLong, - }) : super(SignatureSubpacketType.policyURI, data); - - factory PolicyURI.fromURI( - final String uri, { - final bool critical = false, - }) => - PolicyURI(uri.stringToBytes(), critical: critical); - - String get uri => utf8.decode(data); -} diff --git a/lib/src/packet/signature/preferred_aead_algorithms.dart b/lib/src/packet/signature/preferred_aead_algorithms.dart deleted file mode 100644 index 8da4125f..00000000 --- a/lib/src/packet/signature/preferred_aead_algorithms.dart +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2022-present by Nguyen Van Nguyen . All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../enum/aead_algorithm.dart'; -import '../../enum/signature_subpacket_type.dart'; -import '../signature_subpacket.dart'; - -class PreferredAeadAlgorithms extends SignatureSubpacket { - PreferredAeadAlgorithms( - final Uint8List data, { - super.critical, - super.isLong, - }) : super( - SignatureSubpacketType.preferredAeadAlgorithms, - data, - ); - - List get preferences => data - .map( - (pref) => AeadAlgorithm.values.firstWhere((alg) => alg.value == pref)) - .toList(); -} diff --git a/lib/src/packet/signature/preferred_compression_algorithms.dart b/lib/src/packet/signature/preferred_compression_algorithms.dart deleted file mode 100644 index 777e7e6b..00000000 --- a/lib/src/packet/signature/preferred_compression_algorithms.dart +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../enum/compression_algorithm.dart'; -import '../../enum/signature_subpacket_type.dart'; -import '../signature_subpacket.dart'; - -/// Compression algorithm numbers that indicate which algorithms the key -/// holder prefers to use. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.9 -/// Author Nguyen Van Nguyen -class PreferredCompressionAlgorithms extends SignatureSubpacket { - PreferredCompressionAlgorithms( - final Uint8List data, { - super.critical, - super.isLong, - }) : super( - SignatureSubpacketType.preferredCompressionAlgorithms, - data, - ); - - List get preferences => data - .map((pref) => - CompressionAlgorithm.values.firstWhere((alg) => alg.value == pref)) - .toList(); -} diff --git a/lib/src/packet/signature/preferred_hash_algorithms.dart b/lib/src/packet/signature/preferred_hash_algorithms.dart deleted file mode 100644 index 28a391ba..00000000 --- a/lib/src/packet/signature/preferred_hash_algorithms.dart +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../enum/hash_algorithm.dart'; -import '../../enum/signature_subpacket_type.dart'; -import '../signature_subpacket.dart'; - -/// Message digest algorithm numbers that indicate which algorithms the -/// key holder prefers to receive. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.8 -/// Author Nguyen Van Nguyen -class PreferredHashAlgorithms extends SignatureSubpacket { - PreferredHashAlgorithms( - final Uint8List data, { - super.critical, - super.isLong, - }) : super(SignatureSubpacketType.preferredHashAlgorithms, data); - - List get preferences => data - .map( - (pref) => HashAlgorithm.values.firstWhere((alg) => alg.value == pref)) - .toList(); -} diff --git a/lib/src/packet/signature/preferred_key_server.dart b/lib/src/packet/signature/preferred_key_server.dart deleted file mode 100644 index 2f97ed31..00000000 --- a/lib/src/packet/signature/preferred_key_server.dart +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:convert'; -import 'dart:typed_data'; - -import '../../enum/signature_subpacket_type.dart'; -import '../../helpers.dart'; -import '../signature_subpacket.dart'; - -/// This is a URI of a key server that the key holder prefers be used for updates. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.18 -/// Author Nguyen Van Nguyen -class PreferredKeyServer extends SignatureSubpacket { - PreferredKeyServer( - final Uint8List data, { - super.critical, - super.isLong, - }) : super(SignatureSubpacketType.preferredKeyServer, data); - - factory PreferredKeyServer.fromKeyServer( - final String keyServer, { - final bool critical = false, - }) => - PreferredKeyServer(keyServer.stringToBytes(), critical: critical); - - String get keyServer => utf8.decode(data); -} diff --git a/lib/src/packet/signature/preferred_symmetric_algorithms.dart b/lib/src/packet/signature/preferred_symmetric_algorithms.dart deleted file mode 100644 index c84dcc87..00000000 --- a/lib/src/packet/signature/preferred_symmetric_algorithms.dart +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../enum/signature_subpacket_type.dart'; -import '../../enum/symmetric_algorithm.dart'; -import '../signature_subpacket.dart'; - -/// Symmetric algorithm numbers that indicate which algorithms the key -/// holder prefers to use. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.7 -/// Author Nguyen Van Nguyen -class PreferredSymmetricAlgorithms extends SignatureSubpacket { - PreferredSymmetricAlgorithms( - final Uint8List data, { - super.critical, - super.isLong, - }) : super(SignatureSubpacketType.preferredSymmetricAlgorithms, data); - - List get preferences => data - .map((pref) => - SymmetricAlgorithm.values.firstWhere((alg) => alg.value == pref)) - .toList(growable: false); -} diff --git a/lib/src/packet/signature/primary_user_id.dart b/lib/src/packet/signature/primary_user_id.dart deleted file mode 100644 index 5f338933..00000000 --- a/lib/src/packet/signature/primary_user_id.dart +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../enum/signature_subpacket_type.dart'; -import '../signature_subpacket.dart'; - -/// This is a flag in a User ID's self-signature that states whether this -/// User ID is the main User ID for this key. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.19 -/// Author Nguyen Van Nguyen -class PrimaryUserID extends SignatureSubpacket { - PrimaryUserID(final Uint8List data, {super.critical, super.isLong}) - : super(SignatureSubpacketType.primaryUserID, data); - - factory PrimaryUserID.fromIsPrimary( - final bool isPrimary, { - final bool critical = false, - }) => - PrimaryUserID(Uint8List.fromList([isPrimary ? 1 : 0]), - critical: critical); - - bool get isPrimary => data[0] != 0; -} diff --git a/lib/src/packet/signature/regular_expression.dart b/lib/src/packet/signature/regular_expression.dart deleted file mode 100644 index 4eb640c3..00000000 --- a/lib/src/packet/signature/regular_expression.dart +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:convert'; -import 'dart:typed_data'; - -import '../../enum/signature_subpacket_type.dart'; -import '../../helpers.dart'; -import '../signature_subpacket.dart'; - -/// Used in conjunction with trust Signature packets (of level > 0) to -/// limit the scope of trust that is extended. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.14 -/// Author Nguyen Van Nguyen -class RegularExpression extends SignatureSubpacket { - RegularExpression(final Uint8List data, {super.critical, super.isLong}) - : super(SignatureSubpacketType.revocable, data); - - factory RegularExpression.fromExpression( - final String expression, { - final bool critical = false, - }) => - RegularExpression(expression.stringToBytes(), critical: critical); - - String get expression => utf8.decode(data); -} diff --git a/lib/src/packet/signature/revocable.dart b/lib/src/packet/signature/revocable.dart deleted file mode 100644 index 8b0f9bf6..00000000 --- a/lib/src/packet/signature/revocable.dart +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../enum/signature_subpacket_type.dart'; -import '../signature_subpacket.dart'; - -/// Signature's revocability status. The packet body contains a -/// Boolean flag indicating whether the signature is revocable. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.12 -/// Author Nguyen Van Nguyen -class Revocable extends SignatureSubpacket { - Revocable( - final Uint8List data, { - super.critical, - super.isLong, - }) : super(SignatureSubpacketType.revocable, data); - - factory Revocable.fromRevocable( - final bool isRevocable, { - final bool critical = false, - }) => - Revocable( - Uint8List.fromList([isRevocable ? 1 : 0]), - critical: critical, - ); - - bool get isRevocable => data[0] != 0; -} diff --git a/lib/src/packet/signature/revocation_key.dart b/lib/src/packet/signature/revocation_key.dart deleted file mode 100644 index c1d836b8..00000000 --- a/lib/src/packet/signature/revocation_key.dart +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../crypto/math/byte_ext.dart'; -import '../../enum/key_algorithm.dart'; -import '../../enum/revocation_key_tag.dart'; -import '../../enum/signature_subpacket_type.dart'; -import '../signature_subpacket.dart'; - -/// Authorizes the specified key to issue revocation signatures for this key. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.15 -/// Author Nguyen Van Nguyen -class RevocationKey extends SignatureSubpacket { - RevocationKey(final Uint8List data, {super.critical, super.isLong}) - : super(SignatureSubpacketType.revocationKey, data); - - factory RevocationKey.fromRevocation( - final RevocationKeyTag signatureClass, - final KeyAlgorithm keyAlgorithm, - final Uint8List fingerprint, { - final bool critical = false, - }) => - RevocationKey( - _revocationToBytes(signatureClass, keyAlgorithm, fingerprint), - critical: critical, - ); - - RevocationKeyTag get signatureClass => RevocationKeyTag.values.firstWhere( - (tag) => tag.value == data[0], - ); - - KeyAlgorithm get keyAlgorithm => KeyAlgorithm.values.firstWhere( - (alg) => alg.value == data[1], - ); - - String get fingerprint => data.sublist(2).toHexadecimal(); - - static Uint8List _revocationToBytes( - final RevocationKeyTag signatureClass, - final KeyAlgorithm keyAlgorithm, - final Uint8List fingerprint, - ) => - Uint8List.fromList([ - signatureClass.value, - keyAlgorithm.value, - ...fingerprint, - ]); -} diff --git a/lib/src/packet/signature/revocation_reason.dart b/lib/src/packet/signature/revocation_reason.dart deleted file mode 100644 index b6134564..00000000 --- a/lib/src/packet/signature/revocation_reason.dart +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:convert'; -import 'dart:typed_data'; - -import '../../enum/revocation_reason_tag.dart'; -import '../../enum/signature_subpacket_type.dart'; -import '../signature_subpacket.dart'; - -/// This subpacket is used only in key revocation and certification -/// revocation signatures. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.23 -/// Author Nguyen Van Nguyen -class RevocationReason extends SignatureSubpacket { - RevocationReason(final Uint8List data, {super.critical, super.isLong}) - : super(SignatureSubpacketType.revocationReason, data); - - factory RevocationReason.fromRevocation( - final RevocationReasonTag reason, - final String description, { - final bool critical = false, - }) => - RevocationReason( - _revocationToBytes(reason, description), - critical: critical, - ); - - RevocationReasonTag get reason => RevocationReasonTag.values.firstWhere( - (reason) => reason.value == data[0], - ); - - String get description => utf8.decode(data.sublist(1)); - - static Uint8List _revocationToBytes( - final RevocationReasonTag reason, - final String description, - ) => - Uint8List.fromList([ - reason.value, - ...utf8.encode(description), - ]); -} diff --git a/lib/src/packet/signature/signature_creation_time.dart b/lib/src/packet/signature/signature_creation_time.dart deleted file mode 100644 index f8224a55..00000000 --- a/lib/src/packet/signature/signature_creation_time.dart +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../crypto/math/byte_ext.dart'; -import '../../enum/signature_subpacket_type.dart'; -import '../../helpers.dart'; -import '../signature_subpacket.dart'; - -/// The time the signature was made. -/// MUST be present in the hashed area. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.4 -/// Author Nguyen Van Nguyen -class SignatureCreationTime extends SignatureSubpacket { - SignatureCreationTime( - final Uint8List data, { - super.critical, - super.isLong, - }) : super(SignatureSubpacketType.signatureCreationTime, data); - - factory SignatureCreationTime.fromTime( - final DateTime time, { - final bool critical = false, - }) => - SignatureCreationTime(time.toBytes(), critical: critical); - - DateTime get creationTime => data.toDateTime(); -} diff --git a/lib/src/packet/signature/signature_expiration_time.dart b/lib/src/packet/signature/signature_expiration_time.dart deleted file mode 100644 index 063b97b3..00000000 --- a/lib/src/packet/signature/signature_expiration_time.dart +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../crypto/math/byte_ext.dart'; -import '../../enum/signature_subpacket_type.dart'; -import '../../helpers.dart'; -import '../signature_subpacket.dart'; - -/// The validity period of the signature. This is the number of seconds -/// after the signature creation time that the signature expires. -/// If this is not present or has a value of zero, it never expires. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.10 -/// Author Nguyen Van Nguyen -class SignatureExpirationTime extends SignatureSubpacket { - SignatureExpirationTime( - final Uint8List data, { - super.critical, - super.isLong, - }) : super(SignatureSubpacketType.signatureExpirationTime, data); - - factory SignatureExpirationTime.fromExpirationTime( - final DateTime time, { - final bool critical = false, - }) => - SignatureExpirationTime(time.toBytes(), critical: critical); - - DateTime get expirationTime => data.toDateTime(); -} diff --git a/lib/src/packet/signature/signature_target.dart b/lib/src/packet/signature/signature_target.dart deleted file mode 100644 index f7b0b7a4..00000000 --- a/lib/src/packet/signature/signature_target.dart +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../enum/hash_algorithm.dart'; -import '../../enum/key_algorithm.dart'; -import '../../enum/signature_subpacket_type.dart'; -import '../signature_subpacket.dart'; - -/// This subpacket identifies a specific target signature to which a signature refers. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.25 -/// Author Nguyen Van Nguyen -class SignatureTarget extends SignatureSubpacket { - SignatureTarget(final Uint8List data, {super.critical, super.isLong}) - : super(SignatureSubpacketType.signatureTarget, data); - - factory SignatureTarget.fromHashData( - final KeyAlgorithm keyAlgorithm, - final HashAlgorithm hashAlgorithm, - final Uint8List hashData, { - final bool critical = false, - }) => - SignatureTarget( - _hashDataBytes(keyAlgorithm, hashAlgorithm, hashData), - critical: critical, - ); - - KeyAlgorithm get keyAlgorithm => KeyAlgorithm.values.firstWhere( - (alg) => alg.value == data[0], - ); - - HashAlgorithm get hashAlgorithm => HashAlgorithm.values.firstWhere( - (alg) => alg.value == data[1], - ); - - Uint8List get hashData => data.sublist(2); - - static Uint8List _hashDataBytes( - final KeyAlgorithm keyAlgorithm, - final HashAlgorithm hashAlgorithm, - final Uint8List hashData, - ) => - Uint8List.fromList([ - keyAlgorithm.value, - hashAlgorithm.value, - ...hashData, - ]); -} diff --git a/lib/src/packet/signature/signer_user_id.dart b/lib/src/packet/signature/signer_user_id.dart deleted file mode 100644 index 5b01b579..00000000 --- a/lib/src/packet/signature/signer_user_id.dart +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:convert'; -import 'dart:typed_data'; - -import '../../enum/signature_subpacket_type.dart'; -import '../signature_subpacket.dart'; - -/// This subpacket allows a keyholder to state which User ID is -/// responsible for the signing. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.22 -/// Author Nguyen Van Nguyen -class SignerUserID extends SignatureSubpacket { - SignerUserID(final Uint8List data, {super.critical, super.isLong}) - : super(SignatureSubpacketType.signerUserID, data); - - factory SignerUserID.fromUserID( - final String userID, { - final bool critical = false, - }) => - SignerUserID(utf8.encoder.convert(userID), critical: critical); - - String get userID => utf8.decode(data); -} diff --git a/lib/src/packet/signature/trust_signature.dart b/lib/src/packet/signature/trust_signature.dart deleted file mode 100644 index 7250a8d0..00000000 --- a/lib/src/packet/signature/trust_signature.dart +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../../enum/signature_subpacket_type.dart'; -import '../signature_subpacket.dart'; - -/// Signer asserts that the key is not only valid but also trustworthy at -/// the specified level. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.13 -/// Author Nguyen Van Nguyen -class TrustSignature extends SignatureSubpacket { - TrustSignature(final Uint8List data, {super.critical, super.isLong}) - : super(SignatureSubpacketType.trustSignature, data); - - factory TrustSignature.fromTrust( - final int trustLevel, - final int trustAmount, { - final bool critical = false, - }) => - TrustSignature( - Uint8List.fromList([trustLevel, trustAmount]), - critical: critical, - ); - - int get trustLevel => data[0]; - - int get trustAmount => data[1]; -} diff --git a/lib/src/packet/signature_packet.dart b/lib/src/packet/signature_packet.dart deleted file mode 100644 index bb9e4fb8..00000000 --- a/lib/src/packet/signature_packet.dart +++ /dev/null @@ -1,814 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../crypto/math/byte_ext.dart'; -import '../crypto/math/int_ext.dart'; -import '../enum/compression_algorithm.dart'; -import '../enum/hash_algorithm.dart'; -import '../enum/key_algorithm.dart'; -import '../enum/key_flag.dart'; -import '../enum/literal_format.dart'; -import '../enum/packet_tag.dart'; -import '../enum/revocation_reason_tag.dart'; -import '../enum/signature_subpacket_type.dart'; -import '../enum/signature_type.dart'; -import '../enum/support_feature.dart'; -import '../enum/symmetric_algorithm.dart'; -import '../helpers.dart'; -import 'contained_packet.dart'; -import 'key/key_params.dart'; -import 'key_packet.dart'; -import 'literal_data.dart'; -import 'signature_subpacket.dart'; -import 'subpacket_reader.dart'; -import 'user_attribute.dart'; -import 'user_id.dart'; - -/// Signature represents a signature. -/// See RFC 4880, section 5.2. -/// Author Nguyen Van Nguyen -class SignaturePacket extends ContainedPacket { - final int version; - - final SignatureType signatureType; - - final KeyAlgorithm keyAlgorithm; - - final HashAlgorithm hashAlgorithm; - - final Uint8List signatureData; - - final Uint8List signedHashValue; - - final Uint8List signature; - - final List hashedSubpackets; - - final List unhashedSubpackets; - - SignaturePacket( - this.version, - this.signatureType, - this.keyAlgorithm, - this.hashAlgorithm, - this.signedHashValue, - this.signature, { - this.hashedSubpackets = const [], - this.unhashedSubpackets = const [], - }) : signatureData = Uint8List.fromList([ - version, - signatureType.value, - keyAlgorithm.value, - hashAlgorithm.value, - ..._writeSubpackets(hashedSubpackets), - ]), - super(PacketTag.signature); - - SignatureCreationTime get creationTime => - _getSubpacket(hashedSubpackets) ?? - SignatureCreationTime.fromTime(DateTime.now()); - - IssuerKeyID get issuerKeyID { - final issuerKeyID = _getSubpacket(hashedSubpackets) ?? - _getSubpacket(unhashedSubpackets); - if (issuerKeyID != null) { - return issuerKeyID; - } else if (issuerFingerprint != null) { - final fingerprint = issuerFingerprint!.data.sublist(1); - return IssuerKeyID(fingerprint.sublist(12, 20)); - } else { - return IssuerKeyID.wildcard(); - } - } - - SignatureExpirationTime? get signatureExpirationTime => - _getSubpacket(hashedSubpackets); - - ExportableCertification? get exportable => - _getSubpacket(hashedSubpackets); - - TrustSignature? get trustSignature => - _getSubpacket(hashedSubpackets); - - RegularExpression? get regularExpression => - _getSubpacket(hashedSubpackets); - - Revocable? get revocable => _getSubpacket(hashedSubpackets); - - KeyExpirationTime? get keyExpirationTime => - _getSubpacket(hashedSubpackets); - - PreferredSymmetricAlgorithms? get preferredSymmetricAlgorithms => - _getSubpacket(hashedSubpackets); - - RevocationKey? get revocationKey => - _getSubpacket(hashedSubpackets); - - NotationData? get notationData => - _getSubpacket(hashedSubpackets); - - PreferredHashAlgorithms? get preferredHashAlgorithms => - _getSubpacket(hashedSubpackets); - - PreferredCompressionAlgorithms? get preferredCompressionAlgorithms => - _getSubpacket(hashedSubpackets); - - KeyServerPreferences? get keyServerPreferences => - _getSubpacket(hashedSubpackets); - - PreferredKeyServer? get preferredKeyServer => - _getSubpacket(hashedSubpackets); - - PrimaryUserID? get primaryUserID => - _getSubpacket(hashedSubpackets); - - PolicyURI? get policyURI => _getSubpacket(hashedSubpackets); - - KeyFlags? get keyFlags => _getSubpacket(hashedSubpackets); - - SignerUserID? get signerUserID => - _getSubpacket(hashedSubpackets); - - RevocationReason? get revocationReason => - _getSubpacket(hashedSubpackets); - - Features? get features => _getSubpacket(hashedSubpackets); - - SignatureTarget? get signatureTarget => - _getSubpacket(hashedSubpackets); - - EmbeddedSignature? get embeddedSignature => - _getSubpacket(hashedSubpackets); - - IssuerFingerprint? get issuerFingerprint => - _getSubpacket(hashedSubpackets) ?? - _getSubpacket(unhashedSubpackets); - - bool get signatureNeverExpires => signatureExpirationTime == null; - - bool get keyNeverExpires => keyExpirationTime == null; - - factory SignaturePacket.fromByteData(final Uint8List bytes) { - var pos = 0; - - /// A one-octet version number (3 or 4 or 5). - final version = bytes[pos++]; - if (version != 4) { - throw UnsupportedError( - 'Version $version of the signature packet is unsupported.', - ); - } - - /// One-octet signature type. - final signatureType = SignatureType.values.firstWhere( - (type) => type.value == bytes[pos], - ); - pos++; - - /// One-octet public-key algorithm. - final keyAlgorithm = KeyAlgorithm.values.firstWhere( - (alg) => alg.value == bytes[pos], - ); - pos++; - - /// One-octet hash algorithm. - final hashAlgorithm = HashAlgorithm.values.firstWhere( - (alg) => alg.value == bytes[pos], - ); - pos++; - - /// read hashed subpackets - final hashedLength = bytes.sublist(pos, pos + 2).toUint16(); - pos += 2; - final hashedSubpackets = _readSubpackets( - bytes.sublist(pos, pos + hashedLength), - ); - pos += hashedLength; - - /// read unhashed subpackets - final unhashedLength = bytes.sublist(pos, pos + 2).toUint16(); - pos += 2; - final unhashedSubpackets = _readSubpackets( - bytes.sublist(pos, pos + unhashedLength), - ); - pos += unhashedLength; - - /// Two-octet field holding left 16 bits of signed hash value. - final signedHashValue = bytes.sublist(pos, pos + 2); - pos += 2; - final signature = bytes.sublist(pos); - - return SignaturePacket( - version, - signatureType, - keyAlgorithm, - hashAlgorithm, - signedHashValue, - signature, - hashedSubpackets: hashedSubpackets, - unhashedSubpackets: unhashedSubpackets, - ); - } - - static Future createSignature( - final SecretKeyPacket signKey, - final SignatureType signatureType, - final Uint8List dataToSign, { - final HashAlgorithm? preferredHash, - final List subpackets = const [], - final int keyExpirationTime = 0, - final DateTime? date, - }) async { - final version = signKey.version; - final keyAlgorithm = signKey.algorithm; - final hashAlgorithm = preferredHash ?? signKey.preferredHash; - - final hashedSubpackets = [ - SignatureCreationTime.fromTime(date ?? DateTime.now()), - IssuerFingerprint.fromKey(signKey), - IssuerKeyID(signKey.keyID.bytes), - NotationData.saltNotation(hashAlgorithm.saltSize), - ...subpackets, - ]; - if (keyExpirationTime > 0) { - hashedSubpackets.add(KeyExpirationTime.fromTime(keyExpirationTime)); - } - - final signatureData = Uint8List.fromList([ - version, - signatureType.value, - keyAlgorithm.value, - hashAlgorithm.value, - ..._writeSubpackets(hashedSubpackets), - ]); - final message = Uint8List.fromList([ - ...dataToSign, - ...signatureData, - ..._calculateTrailer( - version, - signatureData.lengthInBytes, - ) - ]); - return SignaturePacket( - version, - signatureType, - keyAlgorithm, - hashAlgorithm, - Helper.hashDigest(message, hashAlgorithm).sublist(0, 2), - await _signMessage(signKey, hashAlgorithm, message), - hashedSubpackets: hashedSubpackets, - ); - } - - static Future createSelfCertificate( - final SecretKeyPacket signKey, { - final HashAlgorithm? preferredHash, - final UserIDPacket? userID, - final UserAttributePacket? userAttribute, - final int keyExpirationTime = 0, - final DateTime? date, - }) async { - final bytes = userID?.writeForSign() ?? userAttribute?.writeForSign(); - if (bytes == null) { - throw ArgumentError( - 'Either a userID or userAttribute packet needs to be supplied for certification.', - ); - } - return SignaturePacket.createSignature( - signKey, - SignatureType.certGeneric, - Uint8List.fromList([ - ...signKey.writeForSign(), - ...bytes, - ]), - subpackets: [ - KeyFlags.fromFlags(KeyFlag.certifyKeys.value | KeyFlag.signData.value), - PreferredSymmetricAlgorithms(Uint8List.fromList([ - SymmetricAlgorithm.aes128.value, - SymmetricAlgorithm.aes192.value, - SymmetricAlgorithm.aes256.value, - ])), - PreferredHashAlgorithms(Uint8List.fromList([ - HashAlgorithm.sha256.value, - HashAlgorithm.sha512.value, - ])), - PreferredCompressionAlgorithms(Uint8List.fromList([ - CompressionAlgorithm.zlib.value, - CompressionAlgorithm.zip.value, - CompressionAlgorithm.uncompressed.value, - ])), - Features(Uint8List.fromList([ - SupportFeature.modificationDetection.value, - SupportFeature.aeadEncryptedData.value, - ])), - ], - preferredHash: preferredHash, - keyExpirationTime: keyExpirationTime, - date: date, - ); - } - - static Future createCertifySignature( - final SecretKeyPacket signKey, { - final HashAlgorithm? preferredHash, - final UserIDPacket? userID, - final UserAttributePacket? userAttribute, - final int keyExpirationTime = 0, - final DateTime? date, - }) async { - final bytes = userID?.writeForSign() ?? userAttribute?.writeForSign(); - if (bytes == null) { - throw ArgumentError( - 'Either a userID or userAttribute packet needs to be supplied for certification.', - ); - } - return SignaturePacket.createSignature( - signKey, - SignatureType.certGeneric, - Uint8List.fromList([ - ...signKey.writeForSign(), - ...bytes, - ]), - subpackets: [ - KeyFlags.fromFlags(KeyFlag.certifyKeys.value | KeyFlag.signData.value), - ], - preferredHash: preferredHash, - keyExpirationTime: keyExpirationTime, - date: date, - ); - } - - static Future createKeyBinding( - final SecretKeyPacket signKey, - final KeyPacket bindKey, { - final HashAlgorithm? preferredHash, - final int keyExpirationTime = 0, - final DateTime? date, - }) async { - return SignaturePacket.createSignature( - signKey, - SignatureType.keyBinding, - Uint8List.fromList([ - ...signKey.writeForSign(), - ...bindKey.writeForSign(), - ]), - preferredHash: preferredHash, - keyExpirationTime: keyExpirationTime, - date: date, - ); - } - - static Future createSubkeyBinding( - final SecretKeyPacket signKey, - final SecretSubkeyPacket subkey, { - final HashAlgorithm? preferredHash, - final int keyExpirationTime = 0, - final bool subkeySign = false, - final DateTime? date, - }) async { - final subpackets = []; - if (subkeySign) { - subpackets.add(KeyFlags.fromFlags(KeyFlag.signData.value)); - subpackets.add(EmbeddedSignature.fromSignature( - await SignaturePacket.createSignature( - subkey, - SignatureType.keyBinding, - Uint8List.fromList([ - ...signKey.writeForSign(), - ...subkey.writeForSign(), - ]), - keyExpirationTime: keyExpirationTime, - date: date, - ), - )); - } else { - subpackets.add( - KeyFlags.fromFlags( - KeyFlag.encryptCommunication.value | KeyFlag.encryptStorage.value, - ), - ); - } - return SignaturePacket.createSignature( - signKey, - SignatureType.subkeyBinding, - Uint8List.fromList([ - ...signKey.writeForSign(), - ...subkey.writeForSign(), - ]), - subpackets: subpackets, - preferredHash: preferredHash ?? subkey.preferredHash, - keyExpirationTime: keyExpirationTime, - date: date, - ); - } - - static Future createKeyRevocation( - final SecretKeyPacket signKey, { - final HashAlgorithm? preferredHash, - final RevocationReasonTag reason = RevocationReasonTag.noReason, - final String description = '', - final DateTime? date, - }) async { - return SignaturePacket.createSignature( - signKey, - SignatureType.keyRevocation, - Uint8List.fromList([ - ...signKey.writeForSign(), - ]), - preferredHash: preferredHash, - subpackets: [RevocationReason.fromRevocation(reason, description)], - date: date, - ); - } - - static Future createSubkeyRevocation( - final SecretKeyPacket signKey, - final SubkeyPacket subKey, { - final HashAlgorithm? preferredHash, - final RevocationReasonTag reason = RevocationReasonTag.noReason, - final String description = '', - final DateTime? date, - }) async { - return SignaturePacket.createSignature( - signKey, - SignatureType.subkeyRevocation, - Uint8List.fromList([ - ...signKey.writeForSign(), - ...subKey.writeForSign(), - ]), - preferredHash: preferredHash, - subpackets: [RevocationReason.fromRevocation(reason, description)], - date: date, - ); - } - - static Future createLiteralData( - final SecretKeyPacket signKey, - final LiteralDataPacket literalData, { - final HashAlgorithm? preferredHash, - final DateTime? date, - }) async { - final SignatureType signatureType; - switch (literalData.format) { - case LiteralFormat.text: - case LiteralFormat.utf8: - signatureType = SignatureType.text; - break; - default: - signatureType = SignatureType.binary; - } - return SignaturePacket.createSignature( - signKey, - signatureType, - literalData.writeForSign(), - preferredHash: preferredHash, - date: date, - ); - } - - @override - Uint8List toByteData() => Uint8List.fromList([ - ...signatureData, - ..._writeSubpackets(unhashedSubpackets), - ...signedHashValue, - ...signature, - ]); - - /// Verifies the signature packet. - Future verify( - final KeyPacket verifyKey, - final Uint8List dataToVerify, { - final DateTime? date, - }) async { - if (issuerKeyID.id != verifyKey.keyID.toString()) { - throw ArgumentError('Signature was not issued by the given public key.'); - } - if (keyAlgorithm != verifyKey.algorithm) { - throw ArgumentError( - 'Public key algorithm used to sign signature does not match issuer key algorithm.', - ); - } - if (signatureExpirationTime != null && - signatureExpirationTime!.expirationTime - .compareTo(date ?? DateTime.now()) < - 0) { - /// Signature is expired - return false; - } - - final message = Uint8List.fromList([ - ...dataToVerify, - ...signatureData, - ..._calculateTrailer( - version, - signatureData.lengthInBytes, - ) - ]); - final hash = Helper.hashDigest(message, hashAlgorithm); - if (signedHashValue[0] != hash[0] || signedHashValue[1] != hash[1]) { - throw StateError('Signed digest did not match'); - } - - final keyParams = verifyKey.publicParams; - if (keyParams is VerificationParams) { - return keyParams.verify(message, hashAlgorithm, signature); - } else { - throw UnsupportedError( - 'Unsupported public key algorithm for verification.', - ); - } - } - - Future verifyUserCertification( - final KeyPacket verifyKey, { - final UserIDPacket? userID, - final UserAttributePacket? userAttribute, - final DateTime? date, - }) async { - final bytes = userID?.writeForSign() ?? userAttribute?.writeForSign(); - if (bytes == null) { - throw ArgumentError( - 'Either a userID or userAttribute packet needs to be supplied for certification.', - ); - } - return verify( - verifyKey, - Uint8List.fromList([ - ...verifyKey.writeForSign(), - ...bytes, - ]), - date: date, - ); - } - - Future verifyLiteralData( - final KeyPacket verifyKey, - final LiteralDataPacket literalData, { - final DateTime? date, - }) async { - return verify( - verifyKey, - literalData.writeForSign(), - date: date, - ); - } - - static Uint8List _calculateTrailer( - final int version, - final int dataLength, - ) { - return Uint8List.fromList([ - version, - 0xff, - ...dataLength.pack32(), - ]); - } - - /// Signs provided data. This needs to be done prior to serialization. - static Future _signMessage( - final SecretKeyPacket key, - final HashAlgorithm hash, - final Uint8List message, - ) async { - switch (key.algorithm) { - case KeyAlgorithm.rsaEncryptSign: - case KeyAlgorithm.rsaSign: - return await (key.secretParams as RSASecretParams).sign(message, hash); - case KeyAlgorithm.dsa: - return await (key.secretParams as DSASecretParams).sign( - key.publicParams as DSAPublicParams, - message, - hash, - ); - case KeyAlgorithm.ecdsa: - return await (key.secretParams as ECSecretParams).sign( - key.publicParams as ECPublicParams, - message, - hash, - ); - case KeyAlgorithm.eddsa: - return await (key.secretParams as EdSecretParams).sign(message, hash); - default: - throw UnsupportedError( - 'Unsupported public key algorithm for signing.', - ); - } - } - - /// Creates list of bytes with subpacket data - static Uint8List _writeSubpackets( - final Iterable subpackets) { - final bytes = subpackets - .map((subpacket) => subpacket.encode()) - .expand((byte) => byte); - return Uint8List.fromList([...bytes.length.pack16(), ...bytes]); - } - - /// Reads V4 signature sub packets - static List _readSubpackets(final Uint8List bytes) { - final subpackets = []; - var offset = 0; - while (offset < bytes.length) { - final reader = SubpacketReader.read(bytes, offset); - offset = reader.offset; - final data = reader.data; - if (data.isNotEmpty) { - final critical = ((reader.type & 0x80) != 0); - final type = SignatureSubpacketType.values.firstWhere( - (type) => type.value == (reader.type & 0x7f), - ); - switch (type) { - case SignatureSubpacketType.signatureCreationTime: - subpackets.add(SignatureCreationTime( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.signatureExpirationTime: - subpackets.add(SignatureExpirationTime( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.exportableCertification: - subpackets.add(ExportableCertification( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.trustSignature: - subpackets.add(TrustSignature( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.regularExpression: - subpackets.add(RegularExpression( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.revocable: - subpackets.add(Revocable( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.keyExpirationTime: - subpackets.add(KeyExpirationTime( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.preferredSymmetricAlgorithms: - subpackets.add(PreferredSymmetricAlgorithms( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.revocationKey: - subpackets.add(RevocationKey( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.issuerKeyID: - subpackets.add(IssuerKeyID( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.notationData: - subpackets.add(NotationData( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.preferredHashAlgorithms: - subpackets.add(PreferredHashAlgorithms( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.preferredCompressionAlgorithms: - subpackets.add(PreferredCompressionAlgorithms( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.keyServerPreferences: - subpackets.add(KeyServerPreferences( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.preferredKeyServer: - subpackets.add(PreferredKeyServer( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.primaryUserID: - subpackets.add(PrimaryUserID(data, critical: critical)); - break; - case SignatureSubpacketType.policyURI: - subpackets.add(PolicyURI( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.keyFlags: - subpackets.add(KeyFlags( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.signerUserID: - subpackets.add(SignerUserID( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.revocationReason: - subpackets.add(RevocationReason( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.features: - subpackets.add(Features( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.signatureTarget: - subpackets.add(SignatureTarget( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.embeddedSignature: - subpackets.add(EmbeddedSignature( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.issuerFingerprint: - subpackets.add(IssuerFingerprint( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - case SignatureSubpacketType.preferredAeadAlgorithms: - subpackets.add(PreferredAeadAlgorithms( - data, - critical: critical, - isLong: reader.isLong, - )); - break; - default: - subpackets.add(SignatureSubpacket( - type, - data, - critical: critical, - isLong: reader.isLong, - )); - } - } - } - return subpackets; - } - - static T? _getSubpacket( - final List subpackets, - ) { - final typedSubpackets = subpackets.whereType(); - return typedSubpackets.isNotEmpty ? typedSubpackets.first : null; - } -} diff --git a/lib/src/packet/signature_subpacket.dart b/lib/src/packet/signature_subpacket.dart deleted file mode 100644 index 60f43dc8..00000000 --- a/lib/src/packet/signature_subpacket.dart +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../crypto/math/int_ext.dart'; -import '../enum/signature_subpacket_type.dart'; -export 'signature/embedded_signature.dart'; -export 'signature/exportable_certification.dart'; -export 'signature/features.dart'; -export 'signature/issuer_fingerprint.dart'; -export 'signature/issuer_key_id.dart'; -export 'signature/key_expiration_time.dart'; -export 'signature/key_flags.dart'; -export 'signature/notation_data.dart'; -export 'signature/preferred_aead_algorithms.dart'; -export 'signature/preferred_compression_algorithms.dart'; -export 'signature/preferred_hash_algorithms.dart'; -export 'signature/preferred_symmetric_algorithms.dart'; -export 'signature/primary_user_id.dart'; -export 'signature/revocable.dart'; -export 'signature/revocation_key.dart'; -export 'signature/revocation_reason.dart'; -export 'signature/signature_creation_time.dart'; -export 'signature/signature_expiration_time.dart'; -export 'signature/signature_target.dart'; -export 'signature/signer_user_id.dart'; -export 'signature/trust_signature.dart'; -export 'signature/policy_uri.dart'; -export 'signature/preferred_key_server.dart'; -export 'signature/regular_expression.dart'; -export 'signature/key_server_preferences.dart'; - -/// Author Nguyen Van Nguyen -class SignatureSubpacket { - final SignatureSubpacketType type; - - final bool critical; - - final bool isLong; - - final Uint8List data; - - SignatureSubpacket( - this.type, - this.data, { - this.critical = false, - this.isLong = false, - }); - - Uint8List encode() { - final List header; - final bodyLen = data.length + 1; - - if (isLong) { - header = [0xff, ...bodyLen.pack32()]; - } else { - if (bodyLen < 192) { - header = [bodyLen]; - } else if (bodyLen <= 8383) { - header = [(((bodyLen - 192) >> 8) & 0xff) + 192, bodyLen - 192]; - } else { - header = [0xff, ...bodyLen.pack32()]; - } - } - - return Uint8List.fromList([ - ...header, - critical ? type.value | 0x80 : type.value, - ...data, - ]); - } -} diff --git a/lib/src/packet/subkey_packet.dart b/lib/src/packet/subkey_packet.dart deleted file mode 100644 index 4d307227..00000000 --- a/lib/src/packet/subkey_packet.dart +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'key_packet.dart'; - -/// Author Nguyen Van Nguyen -abstract class SubkeyPacket extends KeyPacket {} diff --git a/lib/src/packet/subpacket_reader.dart b/lib/src/packet/subpacket_reader.dart index 99290f8b..cfdc8abc 100644 --- a/lib/src/packet/subpacket_reader.dart +++ b/lib/src/packet/subpacket_reader.dart @@ -1,14 +1,16 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; -import '../crypto/math/byte_ext.dart'; +import '../common/extensions.dart'; -/// Generic Sub Packet Data Reader function +/// Sub Packet Data Reader /// Author Nguyen Van Nguyen -class SubpacketReader { +final class SubpacketReader { final int type; final Uint8List data; @@ -44,7 +46,7 @@ class SubpacketReader { pos + length, ); } else if (header == 255) { - final length = bytes.sublist(pos, pos + 4).toUint32(); + final length = bytes.sublist(pos, pos + 4).unpack32(); pos += 4; return SubpacketReader( bytes[pos], diff --git a/lib/src/packet/sym_encrypted_data.dart b/lib/src/packet/sym_encrypted_data.dart index e023efad..7ef44088 100644 --- a/lib/src/packet/sym_encrypted_data.dart +++ b/lib/src/packet/sym_encrypted_data.dart @@ -1,42 +1,41 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. -import 'dart:typed_data'; +library; +import 'dart:typed_data'; import 'package:pointycastle/api.dart'; -import '../crypto/symmetric/base_cipher.dart'; -import '../enum/packet_tag.dart'; +import '../type/encrypted_data_packet.dart'; +import '../type/packet_list.dart'; +import '../common/config.dart'; +import '../common/helpers.dart'; +import '../cryptor/symmetric/buffered_cipher.dart'; +import '../enum/packet_type.dart'; import '../enum/symmetric_algorithm.dart'; -import '../helpers.dart'; -import 'contained_packet.dart'; +import 'base.dart'; import 'packet_list.dart'; -/// SymEncryptedData packet (tag 9) represents a Symmetrically Encrypted Data packet. +/// Implementation of the Symmetrically Encrypted Data Packet (Tag 9)) /// The encrypted contents will consist of more OpenPGP packets. -/// -/// See RFC 4880, sections 5.7 and 5.13. -/// The Symmetrically Encrypted Data packet contains data encrypted with a symmetric-key algorithm. -/// When it has been decrypted, it contains other packets (usually a literal data packet or compressed data packet, -/// but in theory other Symmetrically Encrypted Data packets or sequences of packets that form whole OpenPGP messages). /// Author Nguyen Van Nguyen -class SymEncryptedDataPacket extends ContainedPacket { - /// Encrypted secret-key data +class SymEncryptedDataPacket extends BasePacket implements EncryptedDataPacketInterface { + @override final Uint8List encrypted; - /// Decrypted packets contained within. - final PacketList? packets; + @override + final PacketListInterface? packetList; - SymEncryptedDataPacket(this.encrypted, {this.packets}) : super(PacketTag.symEncryptedData); + SymEncryptedDataPacket(this.encrypted, {this.packetList}) : super(PacketType.symEncryptedData); - factory SymEncryptedDataPacket.fromByteData(final Uint8List bytes) => SymEncryptedDataPacket(bytes); + factory SymEncryptedDataPacket.fromBytes(final Uint8List bytes) => SymEncryptedDataPacket(bytes); - static Future encryptPackets( + factory SymEncryptedDataPacket.encryptPackets( final Uint8List key, - final PacketList packets, { + final PacketListInterface packets, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - }) async { + }) { final cipher = BufferedCipher(symmetric.cfbCipherEngine) ..init( true, @@ -56,37 +55,34 @@ class SymEncryptedDataPacket extends ContainedPacket { ...prefix, ...cipher.process(packets.encode()), ]), - packets: packets, + packetList: packets, ); } @override - Uint8List toByteData() { - return encrypted; - } + Uint8List get data => encrypted; - /// Encrypt the symmetrically-encrypted packet data - Future encrypt( + @override + SymEncryptedDataPacket encrypt( final Uint8List key, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - }) async { - if (packets != null && packets!.isNotEmpty) { + }) { + if (packetList != null && packetList!.isNotEmpty) { return SymEncryptedDataPacket.encryptPackets( key, - packets!, + packetList!, symmetric: symmetric, ); } return this; } - /// Decrypt the symmetrically-encrypted packet data - Future decrypt( + @override + SymEncryptedDataPacket decrypt( final Uint8List key, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - final bool allowUnauthenticatedMessages = false, - }) async { - if (!allowUnauthenticatedMessages) { + }) { + if (!Config.allowUnauthenticated) { throw StateError('Message is not authenticated.'); } final blockSize = symmetric.blockSize; @@ -100,7 +96,7 @@ class SymEncryptedDataPacket extends ContainedPacket { ); return SymEncryptedDataPacket( encrypted, - packets: PacketList.packetDecode( + packetList: PacketList.decode( cipher.process(encrypted.sublist(blockSize + 2)), ), ); diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index 084c2aae..4b48fd12 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -1,137 +1,349 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; import 'package:pointycastle/api.dart'; -import '../crypto/math/byte_ext.dart'; -import '../crypto/symmetric/base_cipher.dart'; +import '../common/config.dart'; +import '../common/helpers.dart'; +import '../cryptor/symmetric/buffered_cipher.dart'; +import '../enum/aead_algorithm.dart'; import '../enum/hash_algorithm.dart'; -import '../enum/packet_tag.dart'; +import '../enum/packet_type.dart'; import '../enum/symmetric_algorithm.dart'; -import '../helpers.dart'; -import 'contained_packet.dart'; +import '../type/packet_list.dart'; +import 'base.dart'; import 'packet_list.dart'; /// Implementation of the Sym. Encrypted Integrity Protected Data Packet (Tag 18) -/// See RFC 4880, section 5.13. -/// -/// The Symmetrically Encrypted Integrity Protected Data packet is a variant of the Symmetrically Encrypted Data packet. -/// It is a new feature created for OpenPGP that addresses the problem of detecting a modification to encrypted data. -/// It is used in combination with a Modification Detection Code packet. /// Author Nguyen Van Nguyen -class SymEncryptedIntegrityProtectedDataPacket extends ContainedPacket { - static const version = 1; +class SymEncryptedIntegrityProtectedDataPacket extends BasePacket { + static const saltSize = 32; + static const mdcSuffix = [0xd3, 0x14]; + + final int version; - /// Encrypted data, the output of the selected symmetric-key cipher - /// operating in Cipher Feedback mode with shift amount equal to the - /// block size of the cipher (CFB-n where n is the block size). final Uint8List encrypted; - /// Decrypted packets contained within. - final PacketList? packets; + final PacketListInterface? packets; + + final SymmetricAlgorithm? symmetric; + + final AeadAlgorithm? aead; - SymEncryptedIntegrityProtectedDataPacket(this.encrypted, {this.packets}) - : super(PacketTag.symEncryptedIntegrityProtectedData); + final int chunkSize; - factory SymEncryptedIntegrityProtectedDataPacket.fromByteData( - final Uint8List bytes, - ) { - /// A one-octet version number. The only currently defined version is 1. - final seipVersion = bytes[0]; - if (seipVersion != version) { + final Uint8List? salt; + + SymEncryptedIntegrityProtectedDataPacket( + this.version, + this.encrypted, { + this.packets, + this.symmetric, + this.aead, + this.chunkSize = 0, + this.salt, + }) : super(PacketType.symEncryptedIntegrityProtectedData) { + if (version != 1 && version != 2) { throw UnsupportedError( - 'Version $seipVersion of the SEIP packet is unsupported.', + 'Version $version of the SEIPD packet is unsupported.', + ); + } + + if (symmetric != null) { + Helper.assertSymmetric(symmetric!); + } + + if (aead != null && version != 2) { + throw StateError( + 'Using AEAD with version $version SEIPD packet is not allowed.', + ); + } + + if (salt != null && salt!.length != saltSize) { + throw StateError( + 'Salt size must be $saltSize bytes.', ); } - return SymEncryptedIntegrityProtectedDataPacket(bytes.sublist(1)); } - static Future encryptPackets( + factory SymEncryptedIntegrityProtectedDataPacket.fromBytes(final Uint8List bytes) { + var pos = 0; + + /// A one-octet version number. + final version = bytes[pos++]; + + final SymmetricAlgorithm? symmetric; + final AeadAlgorithm? aead; + final int chunkSize; + final Uint8List? salt; + + if (version == 2) { + /// A one-octet cipher algorithm. + symmetric = SymmetricAlgorithm.values.firstWhere( + (algo) => algo.value == bytes[pos], + ); + pos++; + + /// A one-octet AEAD algorithm. + aead = AeadAlgorithm.values.firstWhere( + (algo) => algo.value == bytes[pos], + ); + pos++; + + /// A one-octet chunk size. + chunkSize = bytes[pos++]; + + /// Thirty-two octets of salt. + /// The salt is used to derive the message key and must be unique. + salt = bytes.sublist(pos, pos + saltSize); + pos += saltSize; + } else { + symmetric = null; + aead = null; + chunkSize = 0; + salt = null; + } + return SymEncryptedIntegrityProtectedDataPacket( + version, + bytes.sublist(pos), + symmetric: symmetric, + aead: aead, + chunkSize: chunkSize, + salt: salt, + ); + } + + factory SymEncryptedIntegrityProtectedDataPacket.encryptPackets( final Uint8List key, - final PacketList packets, { + final PacketListInterface packets, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - }) async { - final toHash = Uint8List.fromList([ - ...Helper.generatePrefix(symmetric), - ...packets.encode(), - 0xd3, - 0x14, - ]); - final plainText = Uint8List.fromList([ - ...toHash, - ...Helper.hashDigest(toHash, HashAlgorithm.sha1), - ]); + final AeadAlgorithm aead = AeadAlgorithm.gcm, + final bool aeadProtect = false, + }) { + Helper.assertSymmetric(symmetric); - final cipher = BufferedCipher(symmetric.cfbCipherEngine) - ..init( + final version = aeadProtect ? 2 : 1; + final salt = aeadProtect ? Helper.secureRandom().nextBytes(saltSize) : null; + final chunkSize = aeadProtect ? Config.aeadChunkSize : 0; + + final Uint8List encrypted; + if (aeadProtect) { + encrypted = _aeadCrypt( true, - ParametersWithIV( - KeyParameter(key), - Uint8List(symmetric.blockSize), - ), + key, + packets.encode(), + symmetric: symmetric, + aead: aead, + chunkSizeByte: chunkSize, + salt: salt, ); + } else { + final cipher = BufferedCipher(symmetric.cfbCipherEngine) + ..init( + true, + ParametersWithIV( + KeyParameter(key), + Uint8List(symmetric.blockSize), + ), + ); + final toHash = Uint8List.fromList([ + ...Helper.generatePrefix(symmetric), + ...packets.encode(), + ...mdcSuffix, + ]); + encrypted = cipher.process(Uint8List.fromList([ + ...toHash, + ...Helper.hashDigest(toHash, HashAlgorithm.sha1), + ])); + } + return SymEncryptedIntegrityProtectedDataPacket( - cipher.process(plainText), + version, + encrypted, packets: packets, + symmetric: symmetric, + aead: aead, + chunkSize: chunkSize, + salt: salt, ); } @override - Uint8List toByteData() { - return Uint8List.fromList([version, ...encrypted]); - } + Uint8List get data => Uint8List.fromList([ + version, + ...symmetric != null ? [symmetric!.value] : [], + ...aead != null ? [aead!.value] : [], + ...chunkSize > 0 ? [chunkSize] : [], + ...salt ?? [], + ...encrypted, + ]); /// Encrypt the payload in the packet. - Future encrypt( + SymEncryptedIntegrityProtectedDataPacket encrypt( final Uint8List key, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - }) async { + AeadAlgorithm aead = AeadAlgorithm.gcm, + bool aeadProtect = false, + }) { if (packets != null && packets!.isNotEmpty) { return SymEncryptedIntegrityProtectedDataPacket.encryptPackets( key, packets!, symmetric: symmetric, + aead: aead, + aeadProtect: aeadProtect, ); } return this; } - /// Decrypts the encrypted data contained in the packet. - Future decrypt( + SymEncryptedIntegrityProtectedDataPacket decrypt( final Uint8List key, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - }) async { - final cipher = BufferedCipher(symmetric.cfbCipherEngine) - ..init( - false, - ParametersWithIV( - KeyParameter(key), - Uint8List(symmetric.blockSize), - ), + }) { + if (packets != null && packets!.isNotEmpty) { + return this; + } else { + final cipherSymmetric = this.symmetric ?? symmetric; + final Uint8List packetBytes; + if (aead != null) { + final length = encrypted.length; + final data = encrypted.sublist(0, length - aead!.tagLength); + final authTag = encrypted.sublist(length - aead!.tagLength); + packetBytes = _aeadCrypt( + false, + key, + data, + finalChunk: authTag, + symmetric: cipherSymmetric, + aead: aead!, + chunkSizeByte: chunkSize, + salt: salt, + ); + } else { + final cipher = BufferedCipher(cipherSymmetric.cfbCipherEngine) + ..init( + false, + ParametersWithIV( + KeyParameter(key), + Uint8List(cipherSymmetric.blockSize), + ), + ); + final decrypted = cipher.process(encrypted); + final realHash = decrypted.sublist( + decrypted.length - HashAlgorithm.sha1.digestSize, + ); + final toHash = decrypted.sublist( + 0, + decrypted.length - HashAlgorithm.sha1.digestSize, + ); + final verifyHash = realHash.equals( + Helper.hashDigest(toHash, HashAlgorithm.sha1), + ); + if (!verifyHash) { + throw StateError('Modification detected.'); + } + packetBytes = toHash.sublist(cipherSymmetric.blockSize + 2, toHash.length - 2); + } + return SymEncryptedIntegrityProtectedDataPacket( + version, + encrypted, + packets: PacketList.decode(packetBytes), + symmetric: cipherSymmetric, + aead: aead, + chunkSize: chunkSize, + salt: salt, ); - final decrypted = cipher.process(encrypted); - final realHash = decrypted.sublist( - decrypted.length - HashAlgorithm.sha1.digestSize, - ); - final toHash = decrypted.sublist( - 0, - decrypted.length - HashAlgorithm.sha1.digestSize, + } + } + + static Uint8List _aeadCrypt( + final bool forEncryption, + final Uint8List key, + final Uint8List data, { + final Uint8List? finalChunk, + final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + AeadAlgorithm aead = AeadAlgorithm.gcm, + chunkSizeByte = 0, + final Uint8List? salt, + }) { + final dataLength = data.length; + final tagLength = forEncryption ? 0 : aead.tagLength; + final chunkSize = (1 << (chunkSizeByte + 6)) + tagLength; + + final aData = Uint8List.fromList([ + 0xc0 | PacketType.symEncryptedIntegrityProtectedData.value, + 2, + symmetric.value, + aead.value, + chunkSizeByte, + ]); + final keySize = symmetric.keySizeInByte; + final ivLength = aead.ivLength; + + final derivedKey = Helper.hkdf( + key, + keySize, + info: aData, + salt: salt, ); - final verifyHash = realHash.equals( - Helper.hashDigest(toHash, HashAlgorithm.sha1), + final kek = derivedKey.sublist(0, keySize); + final nonce = derivedKey.sublist(keySize, keySize + ivLength); + + /// The last 8 bytes of HKDF output are unneeded, but this avoids one copy. + nonce.setAll(ivLength - 8, List.filled(8, 0)); + + final processed = dataLength - tagLength * (dataLength / chunkSize).ceil(); + final crypted = Uint8List( + processed + (forEncryption ? aead.tagLength : 0), ); - if (!verifyHash) { - throw StateError('Modification detected.'); + final cipher = aead.cipherEngine(kek, symmetric); + var chunkData = Uint8List.fromList(data); + for (var chunkIndex = 0; chunkIndex == 0 || chunkData.isNotEmpty;) { + // Take a chunk of `data`, en/decrypt it, + // and shift `data` to the next chunk. + final size = chunkSize < chunkData.length ? chunkSize : chunkData.length; + crypted.setAll( + chunkIndex * size, + forEncryption + ? cipher.encrypt( + chunkData.sublist(0, size), + nonce, + aData, + ) + : cipher.decrypt( + chunkData.sublist(0, size), + nonce, + aData, + ), + ); + + chunkData = chunkData.sublist(size); + nonce.setAll(ivLength - 4, chunkIndex.pack32()); } - return SymEncryptedIntegrityProtectedDataPacket( - encrypted, - packets: PacketList.packetDecode( - toHash.sublist(symmetric.blockSize + 2, toHash.length - 2), - ), + /// For encryption: empty final chunk + /// For decryption: final authentication tag + final aDataTag = Uint8List.fromList( + [...aData, ...List.filled(8, 0)], ); + aDataTag.setAll(aDataTag.length - 4, processed.pack32()); + final finalCrypted = forEncryption + ? cipher.encrypt( + finalChunk ?? Uint8List(0), + nonce, + aDataTag, + ) + : cipher.decrypt( + finalChunk ?? Uint8List(0), + nonce, + aDataTag, + ); + return Uint8List.fromList([...crypted, ...finalCrypted]); } } diff --git a/lib/src/packet/sym_encrypted_session_key.dart b/lib/src/packet/sym_encrypted_session_key.dart index b0f3cc9f..7a248242 100644 --- a/lib/src/packet/sym_encrypted_session_key.dart +++ b/lib/src/packet/sym_encrypted_session_key.dart @@ -1,49 +1,42 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; import 'package:pointycastle/api.dart'; -import '../crypto/symmetric/base_cipher.dart'; +import '../common/argon2_s2k.dart'; +import '../common/generic_s2k.dart'; +import '../common/helpers.dart'; +import '../cryptor/symmetric/buffered_cipher.dart'; import '../enum/aead_algorithm.dart'; -import '../enum/hash_algorithm.dart'; -import '../enum/packet_tag.dart'; +import '../enum/packet_type.dart'; import '../enum/s2k_type.dart'; import '../enum/symmetric_algorithm.dart'; -import '../helpers.dart'; -import 'key/s2k.dart'; -import 'contained_packet.dart'; +import '../type/s2k.dart'; +import '../type/session_key.dart'; +import 'base.dart'; import 'key/session_key.dart'; -/// SymEncryptedSessionKey represents a Symmetric-Key Encrypted Session Key packet. -/// -/// See RFC 4880, section 5.3. -/// The Symmetric-Key Encrypted Session Key packet holds the -/// symmetric-key encryption of a session key used to encrypt a message. -/// Zero or more Public-Key Encrypted Session Key packets and/or -/// Symmetric-Key Encrypted Session Key packets may precede a -/// Symmetrically Encrypted Data packet that holds an encrypted message. -/// The message is encrypted with a session key, and the session key is -/// itself encrypted and stored in the Encrypted Session Key packet or -/// the Symmetric-Key Encrypted Session Key packet. +/// Implementation of the Symmetric Key Encrypted Session Key Packet (Tag 3) /// Author Nguyen Van Nguyen -class SymEncryptedSessionKeyPacket extends ContainedPacket { +class SymEncryptedSessionKeyPacket extends BasePacket { final int version; final SymmetricAlgorithm symmetric; final AeadAlgorithm? aead; - final S2K s2k; + final S2kInterface s2k; final Uint8List iv; final Uint8List encrypted; - /// Session key - final SessionKey? sessionKey; + final SessionKeyInterface? sessionKey; bool get isDecrypted => sessionKey != null; @@ -55,17 +48,32 @@ class SymEncryptedSessionKeyPacket extends ContainedPacket { this.symmetric = SymmetricAlgorithm.aes128, this.aead, this.sessionKey, - }) : super(PacketTag.symEncryptedSessionKey); + }) : super(PacketType.symEncryptedSessionKey) { + if (version != 4 && version != 5 && version != 6) { + throw UnsupportedError( + 'Version $version of the SKESK packet is unsupported.', + ); + } + if (version == 6) { + Helper.assertSymmetric(symmetric); + } + if (aead != null && version < 5) { + throw StateError( + 'Using AEAD with version $version SKESK packet is not allowed.', + ); + } + } - factory SymEncryptedSessionKeyPacket.fromByteData(final Uint8List bytes) { + factory SymEncryptedSessionKeyPacket.fromBytes(final Uint8List bytes) { var pos = 0; /// A one-octet version number. final version = bytes[pos++]; - if (version != 4 && version != 5) { - throw UnsupportedError( - 'Version $version of the SKESK packet is unsupported.', - ); + final isV6 = version == 6; + + if (isV6) { + /// A one-octet scalar octet count of the following 5 fields. + pos++; } /// A one-octet number describing the symmetric algorithm used. @@ -76,52 +84,63 @@ class SymEncryptedSessionKeyPacket extends ContainedPacket { final int ivLength; final AeadAlgorithm? aead; - if (version == 5) { + if (version >= 5) { /// A one-octet number describing the aead algorithm used. aead = AeadAlgorithm.values.firstWhere( (algo) => algo.value == bytes[pos], ); ivLength = aead.ivLength; pos++; + if (isV6) { + // A one-octet scalar octet count of the following field. + pos++; + } } else { ivLength = 0; aead = null; } /// A string-to-key (S2K) specifier, length as defined above. - final s2k = S2K.fromByteData(bytes.sublist(pos)); + final s2kType = S2kType.values.firstWhere( + (type) => type.value == bytes[pos], + ); + final s2k = switch (s2kType) { + S2kType.argon2 => Argon2S2k.fromBytes(bytes.sublist(pos)), + _ => GenericS2k.fromBytes(bytes.sublist(pos)), + }; pos += s2k.length; /// A starting initialization vector of size specified by the AEAD algorithm. final iv = bytes.sublist(pos, pos + ivLength); pos += ivLength; - final encrypted = bytes.sublist(pos); return SymEncryptedSessionKeyPacket( version, s2k, iv, - encrypted, + bytes.sublist(pos), symmetric: symmetric, aead: aead, ); } - static Future encryptSessionKey( + factory SymEncryptedSessionKeyPacket.encryptSessionKey( final String password, { - final SessionKey? sessionKey, + final SessionKeyInterface? sessionKey, final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - final AeadAlgorithm aead = AeadAlgorithm.ocb, + final AeadAlgorithm aead = AeadAlgorithm.gcm, final bool aeadProtect = false, - }) async { - final version = aeadProtect && sessionKey != null ? 5 : 4; - final s2k = S2K( - Helper.secureRandom().nextBytes(S2K.saltLength), - hash: HashAlgorithm.sha256, - type: S2kType.iterated, - ); + }) { + final version = aeadProtect && sessionKey != null ? 6 : 4; + final s2k = aeadProtect + ? Helper.stringToKey( + S2kType.argon2, + ) + : Helper.stringToKey( + S2kType.iterated, + ); - final key = await s2k.produceKey( + final key = s2k.produceKey( password, symmetric.keySizeInByte, ); @@ -131,14 +150,17 @@ class SymEncryptedSessionKeyPacket extends ContainedPacket { if (sessionKey != null) { if (aeadProtect) { final adata = Uint8List.fromList([ - 0xC0 | PacketTag.symEncryptedSessionKey.value, + 0xc0 | PacketType.symEncryptedSessionKey.value, version, symmetric.value, aead.value, ]); + + final kek = Helper.hkdf(key, symmetric.keySizeInByte, info: adata); + iv = Helper.secureRandom().nextBytes(aead.ivLength); - final cipher = aead.cipherEngine(key, symmetric); - encrypted = cipher.encrypt(sessionKey.key, iv, adata); + final cipher = aead.cipherEngine(kek, symmetric); + encrypted = cipher.encrypt(sessionKey.encryptionKey, iv, adata); } else { final cipher = BufferedCipher( symmetric.cfbCipherEngine, @@ -168,25 +190,34 @@ class SymEncryptedSessionKeyPacket extends ContainedPacket { ); } - Future decrypt(final String password) async { + SymEncryptedSessionKeyPacket decrypt(final String password) { if (isDecrypted) { return this; } else { - final key = await s2k.produceKey( + final key = s2k.produceKey( password, symmetric.keySizeInByte, ); final SessionKey sessionKey; if (encrypted.isNotEmpty) { - if (version == 5) { + if (aead != null) { final adata = Uint8List.fromList([ - 0xC0 | tag.value, + 0xC0 | type.value, version, symmetric.value, aead!.value, ]); - final cipher = aead!.cipherEngine(key, symmetric); + + final Uint8List kek = version == 6 + ? Helper.hkdf( + key, + symmetric.keySizeInByte, + info: adata, + ) + : key; + + final cipher = aead!.cipherEngine(kek, symmetric); final decrypted = cipher.decrypt(encrypted, iv, adata); sessionKey = SessionKey(decrypted, symmetric); } else { @@ -221,14 +252,14 @@ class SymEncryptedSessionKeyPacket extends ContainedPacket { } @override - Uint8List toByteData() { - return Uint8List.fromList([ - version, - symmetric.value, - ...aead != null ? [aead!.value] : [], - ...s2k.encode(), - ...iv, - ...encrypted, - ]); - } + Uint8List get data => Uint8List.fromList([ + version, + ...version == 6 ? [3 + s2k.length + iv.length] : [], + symmetric.value, + ...aead != null ? [aead!.value] : [], + ...version == 6 ? [s2k.length] : [], + ...s2k.toBytes, + ...iv, + ...encrypted, + ]); } diff --git a/lib/src/packet/trust.dart b/lib/src/packet/trust.dart new file mode 100644 index 00000000..e5da9fb3 --- /dev/null +++ b/lib/src/packet/trust.dart @@ -0,0 +1,30 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'package:dart_pg/src/enum/packet_type.dart'; + +import 'base.dart'; + +/// Implementation of the Trust Packet (Tag 12) +/// Author Nguyen Van Nguyen +class TrustPacket extends BasePacket { + final Uint8List levelAndTrustAmount; + + TrustPacket(this.levelAndTrustAmount) : super(PacketType.trust); + + factory TrustPacket.fromTrustCode(final int trustCode) => TrustPacket( + Uint8List.fromList([trustCode & 0xff]), + ); + + factory TrustPacket.fromBytes(final Uint8List bytes) => TrustPacket.fromTrustCode( + bytes[0], + ); + + @override + Uint8List get data => levelAndTrustAmount; +} diff --git a/lib/src/packet/trust_packet.dart b/lib/src/packet/trust_packet.dart deleted file mode 100644 index c81e84f1..00000000 --- a/lib/src/packet/trust_packet.dart +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../enum/packet_tag.dart'; -import 'contained_packet.dart'; - -/// Implementation of the Trust Packet (Tag 12) -/// -/// See https://tools.ietf.org/html/rfc4880#section-5.10 -/// The Trust packet is used only within keyrings and is not normally exported. -/// Trust packets contain data that record the user's specificationsof which key holders are trustworthy introducers, -/// along with other information that implementing software uses for trust information. -/// The format of Trust packets is defined by a given implementation. -/// Author Nguyen Van Nguyen -class TrustPacket extends ContainedPacket { - final Uint8List levelAndTrustAmount; - - TrustPacket(this.levelAndTrustAmount) : super(PacketTag.trust); - - factory TrustPacket.fromTrustCode(final int trustCode) => TrustPacket( - Uint8List.fromList([trustCode & 0xff]), - ); - - factory TrustPacket.fromByteData(final Uint8List bytes) => - TrustPacket.fromTrustCode( - bytes[0], - ); - - @override - Uint8List toByteData() { - return levelAndTrustAmount; - } -} diff --git a/lib/src/packet/user_attribute.dart b/lib/src/packet/user_attribute.dart index 7cb904c6..4a7650a3 100644 --- a/lib/src/packet/user_attribute.dart +++ b/lib/src/packet/user_attribute.dart @@ -1,53 +1,49 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; -import '../crypto/math/int_ext.dart'; -import '../enum/packet_tag.dart'; -import 'contained_packet.dart'; -import 'image_attribute.dart'; +import '../type/user_id_packet.dart'; +import '../common/extensions.dart'; +import '../enum/packet_type.dart'; +import 'base.dart'; +import 'image_user_attribute.dart'; import 'subpacket_reader.dart'; import 'user_attribute_subpacket.dart'; -/// Implementation of the User Attribute Packet (Tag 17) -/// -/// The User Attribute packet is a variation of the User ID packet. -/// It is capable of storing more types of data than the User ID packet, which is limited to text. -/// Like the User ID packet, a User Attribute packet may be certified by the key -/// owner ("self-signed") or any other key owner who cares to certify it. -/// Except as noted, a User Attribute packet may be used anywhere that a User ID packet may be used. +/// Implementation of the User Attribute Packet (Type 17) /// Author Nguyen Van Nguyen -class UserAttributePacket extends ContainedPacket { +class UserAttributePacket extends BasePacket implements UserIDPacketInterface { final List attributes; - UserAttributePacket(this.attributes) : super(PacketTag.userAttribute); - - ImageAttributeSubpacket? get userImage { - final attrs = attributes.whereType(); - return attrs.isNotEmpty ? attrs.first : null; - } + UserAttributePacket(this.attributes) : super(PacketType.userAttribute); - factory UserAttributePacket.fromByteData(final Uint8List bytes) => + factory UserAttributePacket.fromBytes( + final Uint8List bytes, + ) => UserAttributePacket( _readSubpackets(bytes), ); @override - Uint8List toByteData() => Uint8List.fromList(attributes - .map((attr) => attr.encode()) - .expand((byte) => byte) - .toList(growable: false)); + Uint8List get data => Uint8List.fromList( + attributes + .map( + (attr) => attr.encode(), + ) + .expand((byte) => byte) + .toList(growable: false), + ); - Uint8List writeForSign() { - final bytes = toByteData(); - return Uint8List.fromList([ - 0xd1, - ...bytes.lengthInBytes.pack32(), - ...bytes, - ]); - } + @override + Uint8List get signBytes => Uint8List.fromList([ + 0xd1, + ...data.length.pack32(), + ...data, + ]); static List _readSubpackets(final Uint8List bytes) { final attributes = []; @@ -57,8 +53,8 @@ class UserAttributePacket extends ContainedPacket { offset = reader.offset; if (reader.data.isNotEmpty) { switch (reader.type) { - case ImageAttributeSubpacket.jpeg: - attributes.add(ImageAttributeSubpacket( + case ImageUserAttribute.jpeg: + attributes.add(ImageUserAttribute( reader.data, isLong: reader.isLong, )); diff --git a/lib/src/packet/user_attribute_subpacket.dart b/lib/src/packet/user_attribute_subpacket.dart index 71eda451..2b96351d 100644 --- a/lib/src/packet/user_attribute_subpacket.dart +++ b/lib/src/packet/user_attribute_subpacket.dart @@ -1,11 +1,14 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:typed_data'; -import '../crypto/math/int_ext.dart'; +import '../common/extensions.dart'; +/// User attribute subpacket /// Author Nguyen Van Nguyen class UserAttributeSubpacket { final int type; diff --git a/lib/src/packet/user_id.dart b/lib/src/packet/user_id.dart index 003dc5ce..be5900d4 100644 --- a/lib/src/packet/user_id.dart +++ b/lib/src/packet/user_id.dart @@ -1,22 +1,20 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; import 'dart:convert'; import 'dart:typed_data'; -import '../crypto/math/int_ext.dart'; -import '../enum/packet_tag.dart'; -import '../helpers.dart'; -import 'contained_packet.dart'; +import '../common/helpers.dart'; +import '../enum/packet_type.dart'; +import '../type/user_id_packet.dart'; +import 'base.dart'; /// Implementation of the User ID Packet (Tag 13) -/// -/// A User ID packet consists of UTF-8 text that is intended to represent the name and email address of the key holder. -/// By convention, it includes an RFC2822 mail name-addr, but there are no restrictions on its content. -/// The packet length in the header specifies the length of the User ID. /// Author Nguyen Van Nguyen -class UserIDPacket extends ContainedPacket { +class UserIDPacket extends BasePacket implements UserIDPacketInterface { final String userID; final String name; @@ -29,25 +27,22 @@ class UserIDPacket extends ContainedPacket { : name = _extractName(userID), email = _extractEmail(userID), comment = _extractComment(userID), - super(PacketTag.userID); + super(PacketType.userID); - factory UserIDPacket.fromByteData(final Uint8List bytes) { - return UserIDPacket(utf8.decode(bytes)); - } + factory UserIDPacket.fromBytes( + final Uint8List bytes, + ) => + UserIDPacket(utf8.decode(bytes)); @override - Uint8List toByteData() { - return userID.stringToBytes(); - } + Uint8List get data => userID.toBytes(); - Uint8List writeForSign() { - final bytes = toByteData(); - return Uint8List.fromList([ - 0xb4, - ...bytes.lengthInBytes.pack32(), - ...bytes, - ]); - } + @override + Uint8List get signBytes => Uint8List.fromList([ + 0xb4, + ...data.lengthInBytes.pack32(), + ...data, + ]); static String _extractName(final String userID) { final name = []; diff --git a/lib/src/crypto/aead/base.dart b/lib/src/type/aead.dart similarity index 62% rename from lib/src/crypto/aead/base.dart rename to lib/src/type/aead.dart index f714ce54..06258275 100644 --- a/lib/src/crypto/aead/base.dart +++ b/lib/src/type/aead.dart @@ -1,28 +1,26 @@ -/// Copyright 2023-present by Dart Privacy Guard project. All rights reserved. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. /// For the full copyright and license information, please view the LICENSE /// file that was distributed with this source code. -import 'dart:typed_data'; +library; -export 'eax.dart'; -export 'ocb.dart'; -export 'gcm.dart'; +import 'dart:typed_data'; -/// AEAD Authenticated-Encryption base class +/// AEAD Authenticated-Encryption interface /// Author Nguyen Van Nguyen -abstract class Base { +abstract class AeadInterface { /// Encrypt plaintext input. Uint8List encrypt( - final Uint8List plaintext, + final Uint8List plainText, final Uint8List nonce, - final Uint8List adata, + final Uint8List aData, ); /// Decrypt ciphertext input. Uint8List decrypt( - final Uint8List ciphertext, + final Uint8List cipherText, final Uint8List nonce, - final Uint8List adata, + final Uint8List aData, ); /// Get aead nonce diff --git a/lib/src/type/armorable.dart b/lib/src/type/armorable.dart new file mode 100644 index 00000000..00bf09ab --- /dev/null +++ b/lib/src/type/armorable.dart @@ -0,0 +1,12 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +/// Armorable interface +/// Author Nguyen Van Nguyen +abstract class ArmorableInterface { + /// Return ASCII armored text + String armor(); +} diff --git a/lib/src/type/cleartext_message.dart b/lib/src/type/cleartext_message.dart deleted file mode 100644 index 01d5b6e4..00000000 --- a/lib/src/type/cleartext_message.dart +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import '../packet/literal_data.dart'; -import 'key.dart'; -import 'signature.dart'; -import 'verification.dart'; - -/// Class that represents a cleartext message. -/// Author Nguyen Van Nguyen -class CleartextMessage { - /// The cleartext of the message - final String _text; - - final List verifications; - - CleartextMessage( - final String text, [ - final Iterable verifications = const [], - ]) : _text = text.trimRight().replaceAll( - RegExp(r'\r?\n', multiLine: true), - '\r\n', - ), - verifications = verifications.toList(growable: false); - - String get text => _text; - - String get normalizeText => _text.replaceAll( - RegExp(r'\r\n', multiLine: true), - '\n', - ); - - /// Verify detached signature - /// Return cleartext message with verifications - Future verifySignature( - final Signature signature, - final Iterable verificationKeys, { - final DateTime? date, - }) async => - CleartextMessage( - text, - await Verification.createVerifications( - LiteralDataPacket.fromText(text), - signature.packets, - verificationKeys, - date: date, - ), - ); -} diff --git a/lib/src/type/encrypted_data_packet.dart b/lib/src/type/encrypted_data_packet.dart new file mode 100644 index 00000000..69cf3bb7 --- /dev/null +++ b/lib/src/type/encrypted_data_packet.dart @@ -0,0 +1,32 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../enum/symmetric_algorithm.dart'; +import 'packet_list.dart'; + +/// Encrypted data packet interface +/// Author Nguyen Van Nguyen +abstract class EncryptedDataPacketInterface { + /// Encrypted data + Uint8List get encrypted; + + /// Decrypted packets contained within. + PacketListInterface? get packetList; + + /// Encrypt the payload in the packet. + EncryptedDataPacketInterface encrypt( + final Uint8List key, { + final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + }); + + /// Decrypt the encrypted data contained in the packet. + EncryptedDataPacketInterface decrypt( + final Uint8List key, { + final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + }); +} diff --git a/lib/src/type/for_signing.dart b/lib/src/type/for_signing.dart new file mode 100644 index 00000000..35883cd4 --- /dev/null +++ b/lib/src/type/for_signing.dart @@ -0,0 +1,14 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +/// For signing interface +/// Author Nguyen Van Nguyen +abstract class ForSigningInterface { + /// Get bytes for sign + Uint8List get signBytes; +} diff --git a/lib/src/type/key.dart b/lib/src/type/key.dart deleted file mode 100644 index 3e831a06..00000000 --- a/lib/src/type/key.dart +++ /dev/null @@ -1,362 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import '../enum/key_algorithm.dart'; -import '../enum/packet_tag.dart'; -import '../enum/signature_type.dart'; -import '../packet/key/key_id.dart'; -import '../packet/key/key_params.dart'; -import '../packet/key_packet.dart'; -import '../packet/packet_list.dart'; -import '../packet/signature_packet.dart'; -import '../packet/user_attribute.dart'; -import '../packet/user_id.dart'; -import 'public_key.dart'; -import 'subkey.dart'; -import 'user.dart'; - -export 'public_key.dart'; -export 'private_key.dart'; - -/// Abstract class that represents an OpenPGP key. Must contain a primary key. -/// Can contain additional subkeys, signatures, user ids, user attributes. -/// Author Nguyen Van Nguyen -abstract class Key { - final KeyPacket keyPacket; - - final Iterable revocationSignatures; - - final Iterable directSignatures; - - late final List users; - - late final List subkeys; - - Key( - this.keyPacket, { - this.revocationSignatures = const [], - this.directSignatures = const [], - final Iterable users = const [], - final Iterable subkeys = const [], - }) { - this.users = users - .map((user) => User( - mainKey: this, - userID: user.userID, - userAttribute: user.userAttribute, - selfCertifications: user.selfCertifications, - otherCertifications: user.otherCertifications, - revocationSignatures: user.revocationSignatures, - )) - .toList(growable: false); - this.subkeys = subkeys - .map((subkey) => Subkey( - subkey.keyPacket, - mainKey: this, - bindingSignatures: subkey.bindingSignatures, - revocationSignatures: subkey.revocationSignatures, - )) - .toList(growable: false); - } - - DateTime get creationTime => keyPacket.creationTime; - - KeyAlgorithm get algorithm => keyPacket.algorithm; - - String get fingerprint => keyPacket.fingerprint; - - KeyID get keyID => keyPacket.keyID; - - KeyParams get publicParams => keyPacket.publicParams; - - int get keyStrength => keyPacket.keyStrength; - - bool get isPrivate => keyPacket.tag == PacketTag.secretKey; - - PublicKey get toPublic; - - bool get isEncryptionKey { - if (keyPacket.isEncryptionKey) { - for (final user in users) { - for (var signature in user.selfCertifications) { - if (signature.keyFlags != null && - !(signature.keyFlags!.isEncryptStorage || signature.keyFlags!.isEncryptCommunication)) { - return false; - } - } - } - } - return keyPacket.isEncryptionKey; - } - - bool get isSigningKey { - if (keyPacket.isSigningKey) { - for (final user in users) { - for (var signature in user.selfCertifications) { - if (signature.keyFlags != null && !signature.keyFlags!.isSignData) { - return false; - } - } - } - } - return keyPacket.isSigningKey; - } - - bool get aeadSupported { - for (final user in users) { - for (var signature in user.selfCertifications) { - if (signature.features != null && signature.features!.supportAeadEncryptedData) { - return true; - } - } - } - return false; - } - - /// Returns ASCII armored text of key - String armor(); - - /// Verify primary key. - /// Checks for revocation signatures, expiration time and valid self signature. - Future verifyPrimaryKey({ - final String userID = '', - final DateTime? date, - }) async { - if (await isRevoked(date: date)) { - return false; - } - final user = await getPrimaryUser(userID: userID, date: date); - if (!await user.verify(date: date)) { - return false; - } - for (final signature in directSignatures) { - if (!await signature.verify( - keyPacket, - keyPacket.writeForSign(), - date: date, - )) { - return false; - } - } - return true; - } - - Future getPrimaryUser({ - final String userID = '', - final DateTime? date, - }) async { - final validUsers = []; - for (final user in users) { - if (user.userID == null) { - continue; - } - final selfCertifications = user.selfCertifications - ..sort( - (a, b) => b.creationTime.creationTime.compareTo( - a.creationTime.creationTime, - ), - ); - if (await user.isRevoked( - date: date, - signature: selfCertifications.isNotEmpty ? selfCertifications[0] : null, - )) { - continue; - } - validUsers.add(user); - } - if (validUsers.isEmpty) { - throw StateError('Could not find primary user'); - } - for (final user in validUsers) { - if (userID.isNotEmpty && user.userID!.userID != userID) { - throw StateError('Could not find user that matches that user ID'); - } - } - return validUsers[0]; - } - - /// Checks if a signature on a key is revoked - Future isRevoked({ - final SignaturePacket? signature, - final DateTime? date, - }) async { - if (revocationSignatures.isNotEmpty) { - final revocationKeyIDs = []; - for (final revocation in revocationSignatures) { - if (signature == null || revocation.issuerKeyID.id == signature.issuerKeyID.id) { - if (await revocation.verify( - keyPacket, - keyPacket.writeForSign(), - date: date, - )) { - return true; - } - } - revocationKeyIDs.add(revocation.issuerKeyID.id); - } - return revocationKeyIDs.isNotEmpty; - } - return false; - } - - Future getExpirationTime() async { - DateTime? expirationTime; - final signatures = directSignatures.toList(growable: false) - ..sort( - (a, b) => b.creationTime.creationTime.compareTo( - a.creationTime.creationTime, - ), - ); - for (final signature in signatures) { - if (signature.keyExpirationTime != null) { - final keyExpirationTime = signature.keyExpirationTime!.time; - final creationTime = signature.creationTime.creationTime; - expirationTime = creationTime.add(Duration(seconds: keyExpirationTime)); - break; - } - } - if (expirationTime == null) { - final user = await getPrimaryUser(); - user.selfCertifications.sort( - (a, b) => b.creationTime.creationTime.compareTo( - a.creationTime.creationTime, - ), - ); - for (final signature in user.selfCertifications) { - if (signature.keyExpirationTime != null) { - final keyExpirationTime = signature.keyExpirationTime!.time; - final creationTime = signature.creationTime.creationTime; - expirationTime = creationTime.add( - Duration(seconds: keyExpirationTime), - ); - break; - } - } - } - return expirationTime; - } - - PacketList toPacketList() => PacketList([ - keyPacket, - ...revocationSignatures, - ...directSignatures, - ...users.map((user) => user.toPacketList()).expand((packet) => packet), - ...subkeys.map((subkey) => subkey.toPacketList()).expand((packet) => packet), - ]); - - static Map readPacketList(final PacketList packetList) { - final revocationSignatures = []; - final directSignatures = []; - final users = []; - final subkeys = []; - - KeyPacket? keyPacket; - Subkey? subkey; - User? user; - String? primaryKeyID; - for (final packet in packetList) { - switch (packet.tag) { - case PacketTag.publicKey: - case PacketTag.secretKey: - if (keyPacket != null) { - throw StateError('Key block contains multiple keys'); - } - if (packet is KeyPacket) { - keyPacket = packet; - primaryKeyID = packet.keyID.toString(); - } - break; - case PacketTag.publicSubkey: - case PacketTag.secretSubkey: - if (packet is SubkeyPacket) { - subkey = Subkey( - packet, - revocationSignatures: [], - bindingSignatures: [], - ); - subkeys.add(subkey); - } - user = null; - break; - case PacketTag.userID: - if (packet is UserIDPacket) { - user = User( - userID: packet, - selfCertifications: [], - otherCertifications: [], - revocationSignatures: [], - ); - users.add(user); - } - break; - case PacketTag.userAttribute: - if (packet is UserAttributePacket) { - user = User( - userAttribute: packet, - selfCertifications: [], - otherCertifications: [], - revocationSignatures: [], - ); - users.add(user); - } - break; - case PacketTag.signature: - if (packet is SignaturePacket) { - switch (packet.signatureType) { - case SignatureType.certGeneric: - case SignatureType.certPersona: - case SignatureType.certCasual: - case SignatureType.certPositive: - if (user != null) { - if (packet.issuerKeyID.id == primaryKeyID) { - user.selfCertifications.add(packet); - } else { - user.otherCertifications.add(packet); - } - } - break; - case SignatureType.certRevocation: - if (user != null) { - user.revocationSignatures.add(packet); - } else { - directSignatures.add(packet); - } - break; - case SignatureType.subkeyBinding: - if (subkey != null) { - subkey.bindingSignatures.add(packet); - } - break; - case SignatureType.subkeyRevocation: - if (subkey != null) { - subkey.revocationSignatures.add(packet); - } - break; - case SignatureType.key: - directSignatures.add(packet); - break; - case SignatureType.keyRevocation: - revocationSignatures.add(packet); - break; - default: - } - } - break; - default: - } - } - - if (keyPacket == null) { - throw StateError('Key packet not found in packet list'); - } - - return { - 'keyPacket': keyPacket, - 'users': users, - 'revocationSignatures': revocationSignatures, - 'directSignatures': directSignatures, - 'subkeys': subkeys, - }; - } -} diff --git a/lib/src/type/key_material.dart b/lib/src/type/key_material.dart new file mode 100644 index 00000000..304cf9f7 --- /dev/null +++ b/lib/src/type/key_material.dart @@ -0,0 +1,17 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +/// Key material interface +/// Author Nguyen Van Nguyen +abstract class KeyMaterialInterface { + /// Get key length + int get keyLength; + + /// Serialize key material to bytes + Uint8List get toBytes; +} diff --git a/lib/src/type/key_packet.dart b/lib/src/type/key_packet.dart new file mode 100644 index 00000000..0230f7ed --- /dev/null +++ b/lib/src/type/key_packet.dart @@ -0,0 +1,48 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../enum/key_algorithm.dart'; +import 'key_material.dart'; +import 'packet.dart'; + +/// Key packet interface +/// Author Nguyen Van Nguyen +abstract class KeyPacketInterface extends PacketInterface { + /// Get key version + int get keyVersion; + + /// Get creation time + DateTime get creationTime; + + /// Get key algorithm + KeyAlgorithm get keyAlgorithm; + + /// Get fingerprint + Uint8List get fingerprint; + + /// Get key ID + Uint8List get keyID; + + /// Get key strength + int get keyStrength; + + /// Return key packet is subkey + bool get isSubkey; + + /// Is signing key + bool get isSigningKey; + + /// Is encryption key + bool get isEncryptionKey; + + /// Get bytes for sign + Uint8List get signBytes; + + /// Get key material + KeyMaterialInterface get keyMaterial; +} diff --git a/lib/src/type/literal_data.dart b/lib/src/type/literal_data.dart new file mode 100644 index 00000000..4856baf3 --- /dev/null +++ b/lib/src/type/literal_data.dart @@ -0,0 +1,27 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'package:dart_pg/src/enum/literal_format.dart'; + +/// Literal data interface +/// Author Nguyen Van Nguyen +abstract class LiteralDataInterface { + LiteralFormat get format; + + String get filename; + + DateTime get time; + + Uint8List get binary; + + String get text; + + Uint8List get header; + + Uint8List get signBytes; +} diff --git a/lib/src/type/message.dart b/lib/src/type/message.dart deleted file mode 100644 index 918b09ce..00000000 --- a/lib/src/type/message.dart +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:developer'; -import 'dart:typed_data'; - -import '../armor/armor.dart'; -import '../enum/armor_type.dart'; -import '../enum/compression_algorithm.dart'; -import '../enum/literal_format.dart'; -import '../enum/packet_tag.dart'; -import '../enum/signature_type.dart'; -import '../enum/symmetric_algorithm.dart'; -import '../packet/aead_encrypted_data.dart'; -import '../packet/compressed_data.dart'; -import '../packet/contained_packet.dart'; -import '../packet/key/key_id.dart'; -import '../packet/key/session_key.dart'; -import '../packet/literal_data.dart'; -import '../packet/one_pass_signature.dart'; -import '../packet/packet_list.dart'; -import '../packet/public_key_encrypted_session_key.dart'; -import '../packet/signature_packet.dart'; -import '../packet/sym_encrypted_data.dart'; -import '../packet/sym_encrypted_integrity_protected_data.dart'; -import '../packet/sym_encrypted_session_key.dart'; -import 'key.dart'; -import 'signature.dart'; -import 'signed_message.dart'; -import 'verification.dart'; - -/// Class that represents an OpenPGP message. -/// Can be an encrypted message, signed message, compressed message or literal message -/// See {@link https://tools.ietf.org/html/rfc4880#section-11.3} -/// Author Nguyen Van Nguyen -class Message { - /// The packets that form this message - final PacketList packetList; - - final List verifications; - - Message( - this.packetList, [ - final Iterable verifications = const [], - ]) : verifications = verifications.toList(growable: false); - - factory Message.fromArmored(final String armored) { - final armor = Armor.decode(armored); - if (armor.type != ArmorType.message) { - throw ArgumentError('Armored text not of message type'); - } - return Message(PacketList.packetDecode(armor.data)); - } - - factory Message.fromSignedMessage(final SignedMessage signedMessage) { - final signatures = signedMessage.signature.packets.toList(growable: false); - return Message(PacketList([ - LiteralDataPacket.fromText(signedMessage.text), - ...signatures.map((packet) { - final index = signatures.indexOf(packet); - return OnePassSignaturePacket( - packet.signatureType, - packet.hashAlgorithm, - packet.keyAlgorithm, - packet.issuerKeyID.data, - (index == signatures.length - 1) ? 1 : 0, - ); - }), - ...signedMessage.signature.packets - ])); - } - - factory Message.createTextMessage( - final String text, { - final DateTime? time, - }) => - Message(PacketList([LiteralDataPacket.fromText(text, time: time)])); - - factory Message.createBinaryMessage( - final Uint8List data, { - final String filename = '', - final DateTime? time, - }) => - Message(PacketList([ - LiteralDataPacket( - data, - format: LiteralFormat.binary, - filename: filename, - time: time, - ) - ])); - - LiteralDataPacket? get literalData { - final packets = - unwrapCompressed().packetList.whereType(); - return packets.isNotEmpty ? packets.elementAt(0) : null; - } - - /// Gets the key IDs of the keys that signed the message - Iterable get signingKeyIDs { - final packetList = unwrapCompressed().packetList; - final onePassSignatures = packetList.whereType(); - if (onePassSignatures.isNotEmpty) { - return onePassSignatures.map((packet) => KeyID(packet.issuerKeyID)); - } - return packetList.whereType().map( - (packet) => KeyID(packet.issuerKeyID.data), - ); - } - - /// Gets the key IDs of the keys to which the session key is encrypted - Iterable get encryptionKeyIDs => unwrapCompressed() - .packetList - .whereType() - .map((packet) => packet.publicKeyID); - - Iterable get signaturePackets => - unwrapCompressed().packetList.whereType(); - - /// Returns ASCII armored text of message - String armor() => Armor.encode(ArmorType.message, packetList.encode()); - - /// Append signature to unencrypted message - Message appendSignature(final SignaturePacket signature) { - return Message(PacketList([...unwrapCompressed().packetList, signature])); - } - - /// Sign the message (the literal data packet of the message) - Future sign( - final Iterable signingKeys, { - final DateTime? date, - }) async { - if (signingKeys.isEmpty) { - throw ArgumentError('No signing keys provided'); - } - final literalDataPackets = packetList.whereType(); - if (literalDataPackets.isEmpty) { - throw StateError('No literal data packet to sign.'); - } - - final literalData = literalDataPackets.elementAt(0); - final SignatureType signatureType; - switch (literalData.format) { - case LiteralFormat.text: - case LiteralFormat.utf8: - signatureType = SignatureType.text; - break; - default: - signatureType = SignatureType.binary; - } - - final keyList = signingKeys.toList(growable: false); - return Message(PacketList([ - ...await Future.wait(keyList.map((key) async { - final index = keyList.indexOf(key); - final keyPacket = await key.getSigningKeyPacket(date: date); - return OnePassSignaturePacket( - signatureType, - keyPacket.preferredHash, - keyPacket.algorithm, - keyPacket.keyID.bytes, - (index == keyList.length - 1) ? 1 : 0, - ); - })), - literalData, - ...await Future.wait(keyList.map( - (key) async => SignaturePacket.createLiteralData( - await key.getSigningKeyPacket(), - literalData, - date: date, - ), - )), - ])); - } - - /// Create a detached signature for the message (the literal data packet of the message) - Future signDetached( - final List signingKeys, { - final DateTime? date, - }) async { - if (signingKeys.isEmpty) { - throw ArgumentError('No signing keys provided'); - } - final literalDataPackets = packetList.whereType(); - if (literalDataPackets.isEmpty) { - throw StateError('No literal data packet to sign.'); - } - return Signature( - PacketList( - await Future.wait(signingKeys.map( - (key) async => SignaturePacket.createLiteralData( - await key.getSigningKeyPacket(), - literalDataPackets.elementAt(0), - date: date, - ), - )), - ), - ); - } - - /// Verify message signatures - /// Return new message with verifications - Future verify( - final Iterable verificationKeys, { - final DateTime? date, - }) async { - final packets = unwrapCompressed().packetList; - final literalDataPackets = packets.whereType(); - if (literalDataPackets.isEmpty) { - throw StateError('No literal data packet to verify.'); - } - - return Message( - packetList, - await Verification.createVerifications( - literalDataPackets.elementAt(0), - packets.whereType(), - verificationKeys, - date: date, - ), - ); - } - - /// Verify detached message signature - /// Return new message with verifications - Future verifySignature( - final Signature signature, - final List verificationKeys, { - final DateTime? date, - }) async { - final literalDataPackets = - unwrapCompressed().packetList.whereType(); - if (literalDataPackets.isEmpty) { - throw StateError('No literal data packet to verify.'); - } - return Message( - packetList, - await Verification.createVerifications( - literalDataPackets.elementAt(0), - signature.packets, - verificationKeys, - date: date, - ), - ); - } - - /// Encrypt the message either with public keys, passwords, or both at once. - /// Return new message with encrypted content. - Future encrypt({ - final Iterable encryptionKeys = const [], - final Iterable passwords = const [], - final SymmetricAlgorithm sessionKeySymmetric = SymmetricAlgorithm.aes128, - final SymmetricAlgorithm encryptionKeySymmetric = SymmetricAlgorithm.aes128, - final bool aeadProtect = false, - }) async { - if (encryptionKeys.isEmpty && passwords.isEmpty) { - throw ArgumentError('No encryption keys or passwords provided'); - } - final sessionKey = SessionKey.produceKey(sessionKeySymmetric); - - final pkeskPackets = await Future.wait( - encryptionKeys.map( - (key) async => PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( - await key.getEncryptionKeyPacket(), - sessionKey, - ), - ), - ); - final skeskPackets = await Future.wait( - passwords.map( - (password) => SymEncryptedSessionKeyPacket.encryptSessionKey( - password, - sessionKey: sessionKey, - symmetric: encryptionKeySymmetric, - aeadProtect: aeadProtect, - ), - ), - ); - bool aeadSupported = true; - for (final key in encryptionKeys) { - if (!key.aeadSupported) { - aeadSupported = false; - } - } - final ContainedPacket encrypted; - if (aeadProtect && aeadSupported) { - encrypted = await AeadEncryptedData.encryptPackets( - sessionKey.key, - packetList, - symmetric: sessionKeySymmetric, - ); - } else { - encrypted = await SymEncryptedIntegrityProtectedDataPacket.encryptPackets( - sessionKey.key, - packetList, - symmetric: sessionKeySymmetric, - ); - } - - return Message(PacketList([ - ...pkeskPackets, - ...skeskPackets, - encrypted, - ])); - } - - /// Decrypt the message. One of `decryptionKeys` or `passwords` must be specified. - /// Return new message with decrypted content. - Future decrypt({ - final Iterable decryptionKeys = const [], - final Iterable passwords = const [], - final bool allowUnauthenticatedMessages = false, - }) async { - if (decryptionKeys.isEmpty && passwords.isEmpty) { - throw ArgumentError('No decryption keys or passwords provided'); - } - - final encryptedPackets = packetList.filterByTags([ - PacketTag.symEncryptedData, - PacketTag.symEncryptedIntegrityProtectedData, - PacketTag.aeadEncryptedData, - ]); - if (encryptedPackets.isEmpty) { - throw StateError('No encrypted data found'); - } - - final sessionKeys = await _decryptSessionKeys( - decryptionKeys: decryptionKeys, - passwords: passwords, - ); - final encryptedPacket = encryptedPackets[0]; - if (encryptedPacket is SymEncryptedIntegrityProtectedDataPacket) { - for (var sessionKey in sessionKeys) { - try { - final packets = await encryptedPacket - .decrypt(sessionKey.key, symmetric: sessionKey.symmetric) - .then((packet) => packet.packets); - if (packets != null) { - return Message(packets); - } - } on Error catch (e) { - log(e.toString(), error: e, stackTrace: e.stackTrace); - } - } - } else if (encryptedPacket is AeadEncryptedData) { - for (var sessionKey in sessionKeys) { - try { - final packets = await encryptedPacket - .decrypt(sessionKey.key, symmetric: sessionKey.symmetric) - .then((packet) => packet.packets); - if (packets != null) { - return Message(packets); - } - } on Error catch (e) { - log(e.toString(), error: e, stackTrace: e.stackTrace); - } - } - } else if (encryptedPacket is SymEncryptedDataPacket) { - for (var sessionKey in sessionKeys) { - try { - final packets = await encryptedPacket - .decrypt( - sessionKey.key, - symmetric: sessionKey.symmetric, - allowUnauthenticatedMessages: allowUnauthenticatedMessages, - ) - .then((packet) => packet.packets); - if (packets != null) { - return Message(packets); - } - } on Error catch (e) { - log(e.toString(), error: e, stackTrace: e.stackTrace); - } - } - } - throw StateError('Decryption failed'); - } - - /// Compress the message (the literal and -if signed- signature data packets of the message) - /// Return new message with compressed content. - Future compress([ - final CompressionAlgorithm algorithm = CompressionAlgorithm.uncompressed, - ]) async { - if (algorithm != CompressionAlgorithm.uncompressed) { - return Message(PacketList([ - CompressedDataPacket.fromPacketList( - packetList, - algorithm: algorithm, - ), - ])); - } - return this; - } - - /// Unwrap compressed message - Message unwrapCompressed() { - final compressedPackets = packetList.whereType(); - if (compressedPackets.isNotEmpty) { - return Message(compressedPackets.elementAt(0).packets); - } - return this; - } - - Future> _decryptSessionKeys({ - final Iterable decryptionKeys = const [], - final Iterable passwords = const [], - }) async { - final sessionKeys = []; - if (decryptionKeys.isNotEmpty) { - final pkeskPackets = - packetList.whereType(); - for (final pkesk in pkeskPackets) { - for (final key in decryptionKeys) { - final keyPacket = await key.getDecryptionKeyPacket(); - if (keyPacket.keyID == pkesk.publicKeyID) { - try { - final sessionKey = await pkesk - .decrypt( - await key.getDecryptionKeyPacket(), - ) - .then((pkesk) => pkesk.sessionKey); - if (sessionKey != null) { - sessionKeys.add(sessionKey); - } - } on Error catch (e) { - log(e.toString(), error: e, stackTrace: e.stackTrace); - } - } - } - } - } else if (passwords.isNotEmpty) { - final skeskPackets = packetList.whereType(); - for (final skesk in skeskPackets) { - for (final password in passwords) { - try { - final sessionKey = await skesk.decrypt(password).then( - (skesk) => skesk.sessionKey, - ); - if (sessionKey != null) { - sessionKeys.add(sessionKey); - } - } on Error catch (e) { - log(e.toString(), error: e, stackTrace: e.stackTrace); - } - } - } - } - if (sessionKeys.isEmpty) { - throw StateError('Session key decryption failed.'); - } - return sessionKeys; - } -} diff --git a/lib/src/type/packet.dart b/lib/src/type/packet.dart new file mode 100644 index 00000000..97be5f1c --- /dev/null +++ b/lib/src/type/packet.dart @@ -0,0 +1,22 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../enum/packet_type.dart'; + +/// Packet interface +/// Author Nguyen Van Nguyen +abstract class PacketInterface { + /// Get packet type + PacketType get type; + + /// Get packet data + Uint8List get data; + + /// Serialize packet to bytes + Uint8List encode(); +} diff --git a/lib/src/type/packet_list.dart b/lib/src/type/packet_list.dart new file mode 100644 index 00000000..e9d82f32 --- /dev/null +++ b/lib/src/type/packet_list.dart @@ -0,0 +1,19 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'packet.dart'; + +/// Packet list interface +/// Author Nguyen Van Nguyen +abstract class PacketListInterface extends Iterable { + /// Get packets + Iterable get packets; + + /// Serialize packets to bytes + Uint8List encode(); +} diff --git a/lib/src/type/private_key.dart b/lib/src/type/private_key.dart deleted file mode 100644 index 9fb7ce29..00000000 --- a/lib/src/type/private_key.dart +++ /dev/null @@ -1,386 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:developer'; - -import '../armor/armor.dart'; -import '../enum/armor_type.dart'; -import '../enum/curve_info.dart'; -import '../enum/dh_key_size.dart'; -import '../enum/hash_algorithm.dart'; -import '../enum/key_algorithm.dart'; -import '../enum/key_generation_type.dart'; -import '../enum/packet_tag.dart'; -import '../enum/rsa_key_size.dart'; -import '../enum/s2k_type.dart'; -import '../enum/s2k_usage.dart'; -import '../enum/symmetric_algorithm.dart'; -import '../packet/contained_packet.dart'; -import '../packet/key/key_params.dart'; -import '../packet/packet_list.dart'; -import '../packet/key_packet.dart'; -import '../packet/signature_packet.dart'; -import '../packet/user_id.dart'; -import 'key.dart'; -import 'subkey.dart'; - -/// Class that represents an OpenPGP Private Key -/// Author Nguyen Van Nguyen -class PrivateKey extends Key { - PrivateKey( - final SecretKeyPacket keyPacket, { - super.users, - super.revocationSignatures, - super.directSignatures, - super.subkeys, - }) : super(keyPacket); - - /// Reads an (optionally armored) OpenPGP private key and returns a PrivateKey object - factory PrivateKey.fromArmored(final String armored) { - final armor = Armor.decode(armored); - if (armor.type != ArmorType.privateKey) { - throw ArgumentError('Armored text not of private key type'); - } - return PrivateKey.fromPacketList(PacketList.packetDecode(armor.data)); - } - - factory PrivateKey.fromPacketList(final PacketList packetList) { - final keyMap = Key.readPacketList(packetList); - if (keyMap['keyPacket'] is! SecretKeyPacket) { - throw StateError('Key packet not of secret key type'); - } - return PrivateKey( - keyMap['keyPacket'] as SecretKeyPacket, - revocationSignatures: keyMap['revocationSignatures'], - directSignatures: keyMap['directSignatures'], - users: keyMap['users'], - subkeys: keyMap['subkeys'], - ); - } - - /// Generates a new OpenPGP key pair. Supports RSA and ECC keys. - /// By default, primary and subkeys will be of same type. - /// The generated primary key will have signing capabilities. - /// By default, one subkey with encryption capabilities is also generated. - static Future generate( - final Iterable userIDs, - final String passphrase, { - final KeyGenerationType type = KeyGenerationType.rsa, - final RSAKeySize rsaKeySize = RSAKeySize.s4096, - final DHKeySize dhKeySize = DHKeySize.l2048n224, - final CurveInfo curve = CurveInfo.secp521r1, - final int keyExpirationTime = 0, - final String? subkeyPassphrase, - final DateTime? date, - }) async { - if (userIDs.isEmpty || passphrase.isEmpty) { - throw ArgumentError( - 'UserIDs and passphrase are required for key generation', - ); - } - - final KeyAlgorithm keyAlgorithm; - final KeyAlgorithm subkeyAlgorithm; - switch (type) { - case KeyGenerationType.rsa: - keyAlgorithm = KeyAlgorithm.rsaEncryptSign; - subkeyAlgorithm = KeyAlgorithm.rsaEncryptSign; - break; - case KeyGenerationType.dsa: - keyAlgorithm = KeyAlgorithm.dsa; - subkeyAlgorithm = KeyAlgorithm.elgamal; - break; - case KeyGenerationType.ecdsa: - keyAlgorithm = KeyAlgorithm.ecdsa; - subkeyAlgorithm = KeyAlgorithm.ecdh; - break; - case KeyGenerationType.eddsa: - keyAlgorithm = KeyAlgorithm.eddsa; - subkeyAlgorithm = KeyAlgorithm.ecdh; - break; - } - - final secretKey = await SecretKeyPacket.generate( - keyAlgorithm, - rsaKeySize: rsaKeySize, - dhKeySize: dhKeySize, - curve: (type == KeyGenerationType.eddsa) ? CurveInfo.ed25519 : curve, - date: date, - ).then((secretKey) => secretKey.encrypt(passphrase)); - final secretSubkey = await SecretSubkeyPacket.generate( - subkeyAlgorithm, - rsaKeySize: rsaKeySize, - dhKeySize: dhKeySize, - curve: (type == KeyGenerationType.eddsa) ? CurveInfo.curve25519 : curve, - date: date, - ).then( - (secretSubkey) => secretSubkey.encrypt(subkeyPassphrase ?? passphrase), - ); - - final packets = [secretKey]; - - /// Wrap user id with certificate signature - for (final userID in userIDs) { - final userIDPacket = UserIDPacket(userID); - packets.addAll([ - userIDPacket, - await SignaturePacket.createSelfCertificate( - secretKey, - userID: userIDPacket, - keyExpirationTime: keyExpirationTime, - date: date, - ) - ]); - } - - /// Wrap secret subkey with binding signature - packets.addAll([ - secretSubkey, - await SignaturePacket.createSubkeyBinding( - secretKey, - secretSubkey, - keyExpirationTime: keyExpirationTime, - date: date, - ), - ]); - - return PrivateKey.fromPacketList(PacketList(packets)); - } - - @override - SecretKeyPacket get keyPacket => super.keyPacket as SecretKeyPacket; - - @override - PublicKey get toPublic { - final packetList = []; - final packets = toPacketList(); - for (final packet in packets) { - switch (packet.tag) { - case PacketTag.secretKey: - if (packet is SecretKeyPacket) { - packetList.add(packet.publicKey); - } - break; - case PacketTag.secretSubkey: - if (packet is SecretSubkeyPacket) { - packetList.add(packet.publicKey); - } - break; - default: - packetList.add(packet); - } - } - return PublicKey.fromPacketList((PacketList(packetList))); - } - - @override - String armor() => Armor.encode( - ArmorType.privateKey, - toPacketList().encode(), - ); - - Future getSigningKeyPacket({ - final String keyID = '', - final DateTime? date, - }) async { - if (!await verifyPrimaryKey(date: date)) { - throw StateError('Primary key is invalid'); - } - subkeys.sort( - (a, b) => b.keyPacket.creationTime.compareTo(a.keyPacket.creationTime), - ); - for (final subkey in subkeys) { - if (keyID.isEmpty || keyID == subkey.keyID.toString()) { - if (subkey.isSigningKey && await subkey.verify(date: date)) { - return subkey.keyPacket as SecretKeyPacket; - } - } - } - if (!isSigningKey || - (keyID.isNotEmpty && keyID != keyPacket.keyID.toString())) { - throw StateError('Could not find valid signing key packet.'); - } - return keyPacket; - } - - Future getDecryptionKeyPacket({ - final String keyID = '', - final DateTime? date, - }) async { - if (!await verifyPrimaryKey(date: date)) { - throw StateError('Primary key is invalid'); - } - subkeys.sort( - (a, b) => b.keyPacket.creationTime.compareTo(a.keyPacket.creationTime), - ); - for (final subkey in subkeys) { - if (keyID.isEmpty || keyID == subkey.keyID.toString()) { - if (!subkey.isSigningKey && await subkey.verify(date: date)) { - return subkey.keyPacket as SecretKeyPacket; - } - } - } - if (isSigningKey || - (keyID.isNotEmpty && keyID != keyPacket.keyID.toString())) { - throw StateError('Could not find valid decryption key packet.'); - } - return keyPacket; - } - - Future getPreferredHash({ - final String userID = '', - final DateTime? date, - }) async { - final keyPacket = await getSigningKeyPacket(date: date); - switch (keyPacket.algorithm) { - case KeyAlgorithm.ecdh: - case KeyAlgorithm.ecdsa: - case KeyAlgorithm.eddsa: - final oid = (keyPacket.publicParams as ECPublicParams).oid; - final curve = CurveInfo.values.firstWhere( - (info) => info.asn1Oid == oid, - orElse: () => CurveInfo.secp521r1, - ); - return curve.hashAlgorithm; - default: - try { - final user = await getPrimaryUser(userID: userID, date: date); - for (final cert in user.selfCertifications) { - if (cert.preferredHashAlgorithms != null && - cert.preferredHashAlgorithms!.preferences.isNotEmpty) { - return cert.preferredHashAlgorithms!.preferences[0]; - } - } - } on Error catch (e) { - log(e.toString(), error: e, stackTrace: e.stackTrace); - } - return HashAlgorithm.sha256; - } - } - - /// Lock a private key with the given passphrase. - /// This method does not change the original key. - Future encrypt( - final String passphrase, { - final Iterable subkeyPassphrases = const [], - final S2kUsage s2kUsage = S2kUsage.sha1, - final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - final HashAlgorithm hash = HashAlgorithm.sha1, - final S2kType type = S2kType.iterated, - }) async { - if (passphrase.isEmpty) { - throw ArgumentError('passphrase are required for key encryption'); - } - if (!keyPacket.isDecrypted) { - throw StateError('Private key must be decrypted before encrypting'); - } - return PrivateKey( - await keyPacket.encrypt( - passphrase, - s2kUsage: s2kUsage, - symmetric: symmetric, - hash: hash, - type: type, - ), - revocationSignatures: revocationSignatures, - directSignatures: directSignatures, - users: users, - subkeys: await Future.wait(subkeys.map((subkey) async { - final index = subkeys.indexOf(subkey); - final subkeyPassphrase = (index < subkeyPassphrases.length) - ? subkeyPassphrases.elementAt(index) - : passphrase; - if (subkeyPassphrase.isNotEmpty && - subkey.keyPacket is SecretSubkeyPacket) { - return Subkey( - await (subkey.keyPacket as SecretSubkeyPacket).encrypt( - subkeyPassphrase, - s2kUsage: s2kUsage, - symmetric: symmetric, - hash: hash, - type: type, - ), - revocationSignatures: subkey.revocationSignatures, - bindingSignatures: subkey.bindingSignatures, - ); - } else { - return subkey; - } - })), - ); - } - - /// Unlock a private key with the given passphrase. - /// This method does not change the original key. - Future decrypt( - final String passphrase, [ - final Iterable subkeyPassphrases = const [], - ]) async { - if (passphrase.isEmpty) { - throw ArgumentError('passphrase are required for key decryption'); - } - final secretKey = await keyPacket.decrypt(passphrase); - if (!secretKey.validate()) { - throw StateError('The key parameters are not consistent'); - } - return PrivateKey( - secretKey, - revocationSignatures: revocationSignatures, - directSignatures: directSignatures, - users: users, - subkeys: await Future.wait(subkeys.map((subkey) async { - final index = subkeys.indexOf(subkey); - final subkeyPassphrase = (index < subkeyPassphrases.length) - ? subkeyPassphrases.elementAt(index) - : passphrase; - if (subkeyPassphrase.isNotEmpty && - subkey.keyPacket is SecretSubkeyPacket) { - return Subkey( - await (subkey.keyPacket as SecretSubkeyPacket) - .decrypt(subkeyPassphrase), - revocationSignatures: subkey.revocationSignatures, - bindingSignatures: subkey.bindingSignatures, - ); - } else { - return subkey; - } - })), - ); - } - - /// Generates a new OpenPGP subkey, and returns a clone of the Key object with the new subkey added. - Future addSubkey( - final String passphrase, { - final KeyAlgorithm subkeyAlgorithm = KeyAlgorithm.rsaEncryptSign, - final RSAKeySize rsaKeySize = RSAKeySize.s4096, - final DHKeySize dhKeySize = DHKeySize.l2048n224, - final CurveInfo curve = CurveInfo.secp521r1, - final int keyExpirationTime = 0, - final bool subkeySign = false, - final DateTime? date, - }) async { - if (passphrase.isEmpty) { - throw ArgumentError('passphrase are required for key generation'); - } - final secretSubkey = await SecretSubkeyPacket.generate( - subkeyAlgorithm, - rsaKeySize: rsaKeySize, - dhKeySize: dhKeySize, - curve: curve, - date: date, - ).then((secretSubkey) => secretSubkey.encrypt(passphrase)); - - return PrivateKey.fromPacketList(PacketList([ - ...toPacketList(), - secretSubkey, - await SignaturePacket.createSubkeyBinding( - keyPacket, - secretSubkey, - keyExpirationTime: keyExpirationTime, - subkeySign: subkeySign, - date: date, - ) - ])); - } -} diff --git a/lib/src/type/public_key.dart b/lib/src/type/public_key.dart deleted file mode 100644 index 279c136b..00000000 --- a/lib/src/type/public_key.dart +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import '../armor/armor.dart'; -import '../enum/packet_tag.dart'; -import '../enum/armor_type.dart'; -import '../packet/packet_list.dart'; -import '../packet/key_packet.dart'; -import 'key.dart'; - -/// Class that represents an OpenPGP Public Key -/// Author Nguyen Van Nguyen -class PublicKey extends Key { - PublicKey( - final PublicKeyPacket keyPacket, { - super.revocationSignatures, - super.directSignatures, - super.users, - super.subkeys, - }) : super(keyPacket); - - factory PublicKey.fromArmored(final String armored) { - final armor = Armor.decode(armored); - if (armor.type != ArmorType.publicKey) { - throw ArgumentError('Armored text not of public key type'); - } - return PublicKey.fromPacketList(PacketList.packetDecode(armor.data)); - } - - factory PublicKey.fromPacketList(final PacketList packetList) { - final keyMap = Key.readPacketList(packetList); - if (keyMap['keyPacket'] is! PublicKeyPacket) { - throw StateError('Key packet not of secret key type'); - } - return PublicKey( - keyMap['keyPacket'] as PublicKeyPacket, - revocationSignatures: keyMap['revocationSignatures'], - directSignatures: keyMap['directSignatures'], - users: keyMap['users'], - subkeys: keyMap['subkeys'], - ); - } - - static List readPublicKeys(String armored) { - final armor = Armor.decode(armored); - if (armor.type != ArmorType.publicKey) { - throw ArgumentError('Armored text not of public key type'); - } - final publicKeys = []; - final packetList = PacketList.packetDecode(armor.data); - final indexes = packetList.indexOfTags([PacketTag.publicKey]); - for (var i = 0; i < indexes.length; i++) { - if (indexes.asMap().containsKey(i + 1)) { - publicKeys.add( - PublicKey.fromPacketList( - PacketList( - packetList.packets.sublist(indexes[i], indexes[i + 1]), - ), - ), - ); - } - } - return publicKeys; - } - - @override - PublicKeyPacket get keyPacket => super.keyPacket as PublicKeyPacket; - - @override - PublicKey get toPublic => this; - - @override - String armor() => Armor.encode(ArmorType.publicKey, toPacketList().encode()); - - Future getEncryptionKeyPacket({ - final String keyID = '', - final DateTime? date, - }) async { - if (!await verifyPrimaryKey(date: date)) { - throw StateError('Primary key is invalid'); - } - subkeys.sort( - (a, b) => b.keyPacket.creationTime.compareTo(a.keyPacket.creationTime), - ); - for (final subkey in subkeys) { - if (keyID.isEmpty || keyID == subkey.keyID.toString()) { - if (subkey.isEncryptionKey && await subkey.verify(date: date)) { - return subkey.keyPacket.publicKey; - } - } - } - if (isSigningKey || (keyID.isNotEmpty && keyID != keyPacket.keyID.toString())) { - throw StateError('Could not find valid encryption key packet.'); - } - return keyPacket.publicKey; - } - - Future getVerificationKeyPacket({ - final String keyID = '', - final DateTime? date, - }) async { - if (!await verifyPrimaryKey(date: date)) { - throw StateError('Primary key is invalid'); - } - subkeys.sort( - (a, b) => b.keyPacket.creationTime.compareTo(a.keyPacket.creationTime), - ); - for (final subkey in subkeys) { - if (keyID.isEmpty || keyID == subkey.keyID.toString()) { - if (!subkey.isEncryptionKey && await subkey.verify(date: date)) { - return subkey.keyPacket as PublicKeyPacket; - } - } - } - if (isEncryptionKey || (keyID.isNotEmpty && keyID != keyPacket.keyID.toString())) { - throw StateError('Could not find valid verification key packet.'); - } - return keyPacket; - } -} diff --git a/lib/src/type/public_key_material.dart b/lib/src/type/public_key_material.dart new file mode 100644 index 00000000..05d5077c --- /dev/null +++ b/lib/src/type/public_key_material.dart @@ -0,0 +1,20 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; +import '../enum/hash_algorithm.dart'; +import 'key_material.dart'; + +/// Public key material interface +/// Author Nguyen Van Nguyen +abstract class PublicKeyMaterialInterface extends KeyMaterialInterface { + /// Verify a signature with message + bool verify( + final Uint8List message, + final HashAlgorithm hash, + final Uint8List signature, + ); +} diff --git a/lib/src/type/s2k.dart b/lib/src/type/s2k.dart new file mode 100644 index 00000000..d652c186 --- /dev/null +++ b/lib/src/type/s2k.dart @@ -0,0 +1,28 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../enum/s2k_type.dart'; + +/// String-to-key interface +/// Author Nguyen Van Nguyen +abstract class S2kInterface { + /// Get S2K type + S2kType get type; + + /// Get salt + Uint8List get salt; + + /// Get length + int get length; + + /// Serialize s2k information to bytes + Uint8List get toBytes; + + /// Produce a key using the specified passphrase and the defined hash algorithm + Uint8List produceKey(final String passphrase, final int length); +} diff --git a/lib/src/type/secret_key_material.dart b/lib/src/type/secret_key_material.dart new file mode 100644 index 00000000..557630c9 --- /dev/null +++ b/lib/src/type/secret_key_material.dart @@ -0,0 +1,17 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'key_material.dart'; + +/// Secret key material interface +/// Author Nguyen Van Nguyen +abstract class SecretKeyMaterialInterface extends KeyMaterialInterface { + /// Get public key material + KeyMaterialInterface get publicMaterial; + + /// Validate with public key material + bool get isValid; +} diff --git a/lib/src/type/secret_key_packet.dart b/lib/src/type/secret_key_packet.dart new file mode 100644 index 00000000..43bbd4d4 --- /dev/null +++ b/lib/src/type/secret_key_packet.dart @@ -0,0 +1,37 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import '../enum/symmetric_algorithm.dart'; +import '../enum/aead_algorithm.dart'; +import 'key_packet.dart'; +import 'secret_key_material.dart'; + +/// Secret key packet interface +/// Author Nguyen Van Nguyen +abstract class SecretKeyPacketInterface extends KeyPacketInterface { + /// Get public key packet + KeyPacketInterface get publicKey; + + /// Get secret key material + SecretKeyMaterialInterface? get secretKeyMaterial; + + /// Get aead algorithm + AeadAlgorithm? get aead; + + /// Secret key packed is encrypted + bool get isEncrypted; + + /// Secret key packed is decrypted + bool get isDecrypted; + + SecretKeyPacketInterface encrypt( + String passphrase, + SymmetricAlgorithm symmetric, + AeadAlgorithm? aead, + ); + + SecretKeyPacketInterface decrypt(String passphrase); +} diff --git a/lib/src/type/session_key.dart b/lib/src/type/session_key.dart new file mode 100644 index 00000000..655ca65a --- /dev/null +++ b/lib/src/type/session_key.dart @@ -0,0 +1,26 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../enum/symmetric_algorithm.dart'; + +/// Session key interface +/// Author Nguyen Van Nguyen +abstract class SessionKeyInterface { + Uint8List get encryptionKey; + + SymmetricAlgorithm get symmetric; + + /// Checksum the encryption key + void checksum(Uint8List checksum); + + /// Compute checksum + Uint8List computeChecksum(); + + /// Serialize session key to bytes + Uint8List encode(); +} diff --git a/lib/src/type/session_key_cryptor.dart b/lib/src/type/session_key_cryptor.dart new file mode 100644 index 00000000..904d87cc --- /dev/null +++ b/lib/src/type/session_key_cryptor.dart @@ -0,0 +1,17 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'secret_key_material.dart'; + +/// Session key cryptor interface +/// Author Nguyen Van Nguyen +abstract class SessionKeyCryptorInterface { + Uint8List encode(); + + Uint8List decrypt(SecretKeyMaterialInterface key); +} diff --git a/lib/src/type/signature.dart b/lib/src/type/signature.dart deleted file mode 100644 index bf5a97e3..00000000 --- a/lib/src/type/signature.dart +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import '../armor/armor.dart'; -import '../enum/armor_type.dart'; -import '../packet/packet_list.dart'; -import '../packet/signature_packet.dart'; - -/// Class that represents an OpenPGP signature. -/// Author Nguyen Van Nguyen -class Signature { - final List packets; - - Signature(PacketList packetList) - : packets = - packetList.whereType().toList(growable: false); - - factory Signature.fromArmored(final String armored) { - final armor = Armor.decode(armored); - if (armor.type != ArmorType.signature) { - throw ArgumentError('Armored text not of signature type'); - } - return Signature(PacketList.packetDecode(armor.data)); - } - - List get signingKeyIDs => - packets.map((packet) => packet.issuerKeyID.id).toList(growable: false); - - /// Returns ASCII armored text of signature - String armor() => - Armor.encode(ArmorType.signature, PacketList(packets).encode()); -} diff --git a/lib/src/type/signed_message.dart b/lib/src/type/signed_message.dart deleted file mode 100644 index b8236393..00000000 --- a/lib/src/type/signed_message.dart +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import '../armor/armor.dart'; -import '../enum/armor_type.dart'; -import '../packet/literal_data.dart'; -import '../packet/packet_list.dart'; -import '../packet/signature_packet.dart'; -import 'cleartext_message.dart'; -import 'key.dart'; -import 'message.dart'; -import 'signature.dart'; -import 'verification.dart'; - -/// Class that represents an OpenPGP cleartext signed message. -/// See {@link https://tools.ietf.org/html/rfc4880#section-7} -/// Author Nguyen Van Nguyen -class SignedMessage extends CleartextMessage { - /// The detached signature or an empty signature for unsigned messages - final Signature signature; - - SignedMessage(super.text, this.signature, [super.verifications]); - - factory SignedMessage.fromArmored(final String armored) { - final armor = Armor.decode(armored); - if (armor.type != ArmorType.signedMessage) { - throw ArgumentError('Armored text not of signed message type'); - } - final packetList = PacketList.packetDecode(armor.data); - return SignedMessage(armor.text, Signature(packetList)); - } - - /// Sign a cleartext. - static Future signCleartext( - final String text, - final Iterable signingKeys, { - final DateTime? date, - }) async { - if (signingKeys.isEmpty) { - throw ArgumentError('No signing keys provided'); - } - return SignedMessage( - text, - Signature( - PacketList( - await Future.wait(signingKeys.map( - (key) async => SignaturePacket.createLiteralData( - await key.getSigningKeyPacket(), - LiteralDataPacket.fromText(text), - preferredHash: await key.getPreferredHash(date: date), - date: date, - ), - )), - ), - ), - ); - } - - List get signingKeyIDs => signature.signingKeyIDs; - - /// Returns ASCII armored text of signed signature - String armor() { - final hashes = signature.packets.map( - (packet) => packet.hashAlgorithm.name.toUpperCase(), - ); - return Armor.encode( - ArmorType.signedMessage, - PacketList(signature.packets).encode(), - text: text, - hashAlgo: hashes.isNotEmpty ? hashes.join(',') : '', - ); - } - - /// Verify signatures of cleartext signed message - /// Return signed message with verifications - Future verify( - final Iterable verificationKeys, { - final DateTime? date, - }) async { - return SignedMessage( - text, - signature, - await Verification.createVerifications( - LiteralDataPacket.fromText(text), - signature.packets, - verificationKeys, - date: date, - ), - ); - } - - Message toMessage() { - return Message.fromSignedMessage(this); - } -} diff --git a/lib/src/type/signing_key_material.dart b/lib/src/type/signing_key_material.dart new file mode 100644 index 00000000..8015beea --- /dev/null +++ b/lib/src/type/signing_key_material.dart @@ -0,0 +1,20 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'secret_key_material.dart'; +import '../enum/hash_algorithm.dart'; + +/// Signing key material interface +/// Author Nguyen Van Nguyen +abstract class SigningKeyMaterialInterface extends SecretKeyMaterialInterface { + /// Sign a message and return signature + Uint8List sign( + final Uint8List message, + final HashAlgorithm hash, + ); +} diff --git a/lib/src/type/subkey.dart b/lib/src/type/subkey.dart deleted file mode 100644 index e46b7269..00000000 --- a/lib/src/type/subkey.dart +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:typed_data'; - -import '../enum/key_algorithm.dart'; -import '../enum/revocation_reason_tag.dart'; -import '../packet/key/key_id.dart'; -import '../packet/key/key_params.dart'; -import '../packet/packet_list.dart'; -import '../packet/signature_packet.dart'; -import '../packet/subkey_packet.dart'; -import 'key.dart'; - -/// Class that represents a subkey packet and the relevant signatures. -/// Author Nguyen Van Nguyen -class Subkey { - /// subkey packet to hold in the Subkey - final SubkeyPacket keyPacket; - - final Key? mainKey; - - final List revocationSignatures; - - final List bindingSignatures; - - Subkey( - this.keyPacket, { - this.mainKey, - this.revocationSignatures = const [], - this.bindingSignatures = const [], - }); - - DateTime get creationTime => keyPacket.creationTime; - - KeyAlgorithm get algorithm => keyPacket.algorithm; - - String get fingerprint => keyPacket.fingerprint; - - KeyID get keyID => keyPacket.keyID; - - KeyParams get publicParams => keyPacket.publicParams; - - int get keyStrength => keyPacket.keyStrength; - - PacketList toPacketList() { - return PacketList([ - keyPacket, - ...revocationSignatures, - ...bindingSignatures, - ]); - } - - bool get isEncryptionKey { - if (keyPacket.isEncryptionKey) { - for (final signature in bindingSignatures) { - if (signature.keyFlags != null && - !(signature.keyFlags!.isEncryptStorage || signature.keyFlags!.isEncryptCommunication)) { - return false; - } - } - } - return keyPacket.isEncryptionKey; - } - - bool get isSigningKey { - if (keyPacket.isSigningKey) { - for (final signature in bindingSignatures) { - if (signature.keyFlags != null && !signature.keyFlags!.isSignData) { - return false; - } - } - } - return keyPacket.isSigningKey; - } - - Future verify({ - final DateTime? date, - }) async { - if (await isRevoked(date: date)) { - return false; - } - if (mainKey != null) { - for (final signature in bindingSignatures) { - if (!await signature.verify( - mainKey!.keyPacket, - Uint8List.fromList([ - ...mainKey!.keyPacket.writeForSign(), - ...keyPacket.writeForSign(), - ]), - date: date, - )) { - return false; - } - } - } - return true; - } - - Future isRevoked({ - final SignaturePacket? signature, - final DateTime? date, - }) async { - if (mainKey != null && revocationSignatures.isNotEmpty) { - final revocationKeyIDs = []; - for (final revocation in revocationSignatures) { - if (signature == null || revocation.issuerKeyID.id == signature.issuerKeyID.id) { - if (await revocation.verify( - mainKey!.keyPacket, - Uint8List.fromList([ - ...mainKey!.keyPacket.writeForSign(), - ...keyPacket.writeForSign(), - ]), - date: date, - )) { - return true; - } - } - revocationKeyIDs.add(revocation.issuerKeyID.id); - } - return revocationKeyIDs.isNotEmpty; - } - return false; - } - - Future revoke({ - RevocationReasonTag reason = RevocationReasonTag.noReason, - String description = '', - final DateTime? date, - }) async { - if (mainKey != null && mainKey is PrivateKey) { - return Subkey( - keyPacket, - mainKey: mainKey, - revocationSignatures: [ - await SignaturePacket.createSubkeyRevocation( - (mainKey as PrivateKey).keyPacket, - keyPacket, - reason: reason, - description: description, - date: date, - ) - ], - bindingSignatures: bindingSignatures, - ); - } - return this; - } - - DateTime? getExpirationTime() { - bindingSignatures.sort( - (a, b) => b.creationTime.creationTime.compareTo( - a.creationTime.creationTime, - ), - ); - for (final signature in bindingSignatures) { - if (signature.keyExpirationTime != null) { - final expirationTime = signature.keyExpirationTime!.time; - final creationTime = signature.creationTime.creationTime; - return creationTime.add(Duration(seconds: expirationTime)); - } - } - return null; - } -} diff --git a/lib/src/type/subkey_packet.dart b/lib/src/type/subkey_packet.dart new file mode 100644 index 00000000..c2fbdd3f --- /dev/null +++ b/lib/src/type/subkey_packet.dart @@ -0,0 +1,11 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'key_packet.dart'; + +/// Subkey packet interface +/// Author Nguyen Van Nguyen +abstract class SubkeyPacketInterface extends KeyPacketInterface {} diff --git a/lib/src/type/subpacket.dart b/lib/src/type/subpacket.dart new file mode 100644 index 00000000..23e94874 --- /dev/null +++ b/lib/src/type/subpacket.dart @@ -0,0 +1,23 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +/// Subpacket interface +/// Author Nguyen Van Nguyen +abstract class Subpacket { + /// Get sub-packet type + int get type; + + /// Get sub-packet data + Uint8List get data; + + /// Is long + bool get isLong; + + /// Serialize sub-packet to bytes + Uint8List encode(); +} diff --git a/lib/src/type/user.dart b/lib/src/type/user.dart deleted file mode 100644 index 0ac85fc4..00000000 --- a/lib/src/type/user.dart +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import '../packet/packet_list.dart'; -import '../packet/signature_packet.dart'; -import '../packet/user_attribute.dart'; -import '../packet/user_id.dart'; -import 'key.dart'; - -/// Class that represents an user ID and the relevant signatures. -/// Author Nguyen Van Nguyen -class User { - final Key? mainKey; - - final UserIDPacket? userID; - - final UserAttributePacket? userAttribute; - - final List selfCertifications; - - final List otherCertifications; - - final List revocationSignatures; - - User({ - this.mainKey, - this.userID, - this.userAttribute, - this.selfCertifications = const [], - this.otherCertifications = const [], - this.revocationSignatures = const [], - }); - - /// Checks if a given certificate of the user is revoked - Future isRevoked({ - final SignaturePacket? signature, - final DateTime? date, - }) async { - if (mainKey != null && revocationSignatures.isNotEmpty) { - final revocationKeyIDs = []; - for (var revocation in revocationSignatures) { - if (signature == null || revocation.issuerKeyID.id == signature.issuerKeyID.id) { - if (await revocation.verifyUserCertification( - mainKey!.keyPacket, - userID: userID, - userAttribute: userAttribute, - date: date, - )) { - return true; - } - } - revocationKeyIDs.add(revocation.issuerKeyID.id); - } - return revocationKeyIDs.isNotEmpty; - } - return false; - } - - Future verify({ - final DateTime? date, - }) async { - if (await isRevoked(date: date)) { - return false; - } - if (mainKey != null) { - for (final signature in selfCertifications) { - if (!await signature.verifyUserCertification( - mainKey!.keyPacket, - userID: userID, - userAttribute: userAttribute, - date: date, - )) { - return false; - } - } - } - return true; - } - - /// Generate third-party certifications over this user and its primary key - /// return new user with new certifications. - Future certify( - List signingKeys, { - final DateTime? date, - }) async { - if (signingKeys.isNotEmpty) { - return User( - mainKey: mainKey, - userID: userID, - userAttribute: userAttribute, - selfCertifications: selfCertifications, - otherCertifications: await Future.wait( - signingKeys.map( - (key) async => SignaturePacket.createCertifySignature( - await key.getSigningKeyPacket(date: date), - userID: userID, - userAttribute: userAttribute, - date: date, - ), - ), - ), - revocationSignatures: revocationSignatures, - ); - } - return this; - } - - PacketList toPacketList() { - return PacketList([ - userID ?? userAttribute!, - ...revocationSignatures, - ...selfCertifications, - ...otherCertifications, - ]); - } -} diff --git a/lib/src/type/user_id_packet.dart b/lib/src/type/user_id_packet.dart new file mode 100644 index 00000000..49152f3e --- /dev/null +++ b/lib/src/type/user_id_packet.dart @@ -0,0 +1,16 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'packet.dart'; + +/// User ID packet interface +/// Author Nguyen Van Nguyen +abstract class UserIDPacketInterface extends PacketInterface { + /// Get bytes for sign + Uint8List get signBytes; +} diff --git a/lib/src/type/verification.dart b/lib/src/type/verification.dart deleted file mode 100644 index 57ae2112..00000000 --- a/lib/src/type/verification.dart +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2022-present by Dart Privacy Guard project. All rights reserved. -// For the full copyright and license information, please view the LICENSE -// file that was distributed with this source code. - -import 'dart:developer'; - -import '../packet/literal_data.dart'; -import '../packet/packet_list.dart'; -import '../packet/signature_packet.dart'; -import 'key.dart'; -import 'signature.dart'; - -/// Class that represents validity of signature. -/// Author Nguyen Van Nguyen -class Verification { - final String keyID; - - final Signature signature; - - final bool verified; - - Verification(this.keyID, this.signature, this.verified); - - static Future> createVerifications( - final LiteralDataPacket literalData, - final Iterable signaturePackets, - final Iterable verificationKeys, { - final DateTime? date, - }) async { - if (verificationKeys.isEmpty) { - throw ArgumentError('No verification keys provided'); - } - final verifications = []; - for (var signaturePacket in signaturePackets) { - for (final key in verificationKeys) { - try { - final keyPacket = await key.getVerificationKeyPacket( - keyID: signaturePacket.issuerKeyID.id, - ); - verifications.add(Verification( - keyPacket.keyID.id, - Signature(PacketList([signaturePacket])), - await signaturePacket.verifyLiteralData( - keyPacket, - literalData, - date: date, - ), - )); - } on Error catch (e) { - log(e.toString(), error: e, stackTrace: e.stackTrace); - } - } - } - return verifications; - } -} diff --git a/licenses/LICENSE-BouncyCastle.html b/licenses/LICENSE-BouncyCastle.html deleted file mode 100644 index f536bb1f..00000000 --- a/licenses/LICENSE-BouncyCastle.html +++ /dev/null @@ -1,22 +0,0 @@ - - - -Copyright (c) 2000-2021 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) -

-Permission is hereby granted, free of charge, to any person obtaining a copy of this software -and associated documentation files (the "Software"), to deal in the Software without restriction, -including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: -

-The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. -

-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - - diff --git a/licenses/LICENSE-PineNaCl b/licenses/LICENSE-PineNaCl deleted file mode 100644 index 6e45c056..00000000 --- a/licenses/LICENSE-PineNaCl +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2018 Pal Dorogi - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/licenses/LICENSE-PointyCastle b/licenses/LICENSE-PointyCastle deleted file mode 100644 index 5928ef30..00000000 --- a/licenses/LICENSE-PointyCastle +++ /dev/null @@ -1,20 +0,0 @@ - -Copyright (c) 2000 - 2019 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/pubspec.yaml b/pubspec.yaml index 7bb02f0e..da5d5bcf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,21 +1,16 @@ name: dart_pg -description: Dart PG (Dart Privacy Guard) - The OpenPGP implementation in Dart language. -version: 1.5.2 -homepage: https://github.com/web-of-trust/dart-pg +description: Dart PG (Dart Privacy Guard) - The OpenPGP implementation in Dart language. +version: 2.0.0 +repository: https://github.com/web-of-trust/dart-pg environment: - sdk: ^3.2.0 + sdk: ^3.4.4 dependencies: pinenacl: ^0.6.0 pointycastle: ^3.9.1 + sign_dart: ^1.0.0 dev_dependencies: lints: ^3.0.0 - faker: ^2.0.0 test: ^1.24.0 - -false_secrets: - - /test/data/key_data.dart - - /test/data/s2k_data.dart - \ No newline at end of file diff --git a/test/armor/armor_test.dart b/test/armor/armor_test.dart deleted file mode 100644 index a6999599..00000000 --- a/test/armor/armor_test.dart +++ /dev/null @@ -1,158 +0,0 @@ -import 'dart:convert'; - -import 'package:dart_pg/src/armor/armor.dart'; -import 'package:dart_pg/src/enum/armor_type.dart'; -import 'package:dart_pg/src/enum/hash_algorithm.dart'; -import 'package:faker/faker.dart'; -import 'package:test/test.dart'; - -void main() { - group('armor tests', (() { - final faker = Faker(); - - test('armor multipart section test', (() { - final bytes = utf8.encoder.convert(faker.lorem.words(100).join(' ')); - final partIndex = faker.randomGenerator.integer(100); - final partTotal = faker.randomGenerator.integer(100); - - final armored = Armor.encode( - ArmorType.multipartSection, - bytes, - partIndex: partIndex, - partTotal: partTotal, - ); - final beginReg = RegExp(r'BEGIN PGP MESSAGE, PART \d+\/\d+'); - expect(beginReg.hasMatch(armored), true); - - final endReg = RegExp(r'END PGP MESSAGE, PART \d+\/\d+'); - expect(endReg.hasMatch(armored), true); - - final armor = Armor.decode(armored); - expect(armor.type, ArmorType.multipartSection); - expect(armor.data, bytes); - })); - - test('armor multipart last test', (() { - final bytes = utf8.encoder.convert(faker.lorem.words(100).join(' ')); - final partIndex = faker.randomGenerator.integer(100); - - final armored = Armor.encode( - ArmorType.multipartLast, - bytes, - partIndex: partIndex, - ); - - final beginReg = RegExp(r'BEGIN PGP MESSAGE, PART \d+'); - expect(beginReg.hasMatch(armored), true); - - final endReg = RegExp(r'END PGP MESSAGE, PART \d+'); - expect(endReg.hasMatch(armored), true); - - final armor = Armor.decode(armored); - expect(armor.type, ArmorType.multipartLast); - expect(armor.data, bytes); - })); - - test('armor signed message test', (() { - final text = faker.lorem.words(100).join(' '); - final bytes = utf8.encoder.convert(text); - - final armored = Armor.encode( - ArmorType.signedMessage, - bytes, - text: text, - hashAlgo: HashAlgorithm.sha256.digestName, - ); - - final beginReg = RegExp(r'BEGIN PGP SIGNED MESSAGE'); - expect(beginReg.hasMatch(armored), true); - - final beginSignReg = RegExp(r'BEGIN PGP SIGNATURE'); - expect(beginSignReg.hasMatch(armored), true); - - final endSignReg = RegExp(r'END PGP SIGNATURE'); - expect(endSignReg.hasMatch(armored), true); - - final armor = Armor.decode(armored); - expect(armor.type, ArmorType.signedMessage); - expect(armor.data, bytes); - expect(armor.text, text); - })); - - test('armor message test', (() { - final message = utf8.encoder.convert(faker.lorem.words(100).join(' ')); - - final armored = Armor.encode( - ArmorType.message, - message, - ); - - final beginReg = RegExp(r'BEGIN PGP MESSAGE'); - expect(beginReg.hasMatch(armored), true); - - final endReg = RegExp(r'END PGP MESSAGE'); - expect(endReg.hasMatch(armored), true); - - final armor = Armor.decode(armored); - expect(armor.type, ArmorType.message); - expect(armor.data, message); - })); - - test('armor public key test', (() { - final publicKey = utf8.encoder.convert(faker.lorem.words(100).join(' ')); - - final armored = Armor.encode( - ArmorType.publicKey, - publicKey, - ); - - final beginReg = RegExp(r'BEGIN PGP PUBLIC KEY BLOCK'); - expect(beginReg.hasMatch(armored), true); - - final endReg = RegExp(r'END PGP PUBLIC KEY BLOCK'); - expect(endReg.hasMatch(armored), true); - - final armor = Armor.decode(armored); - expect(armor.type, ArmorType.publicKey); - expect(armor.data, publicKey); - })); - - test('armor private key test', (() { - final privateKey = utf8.encoder.convert(faker.lorem.words(100).join(' ')); - - final armored = Armor.encode( - ArmorType.privateKey, - privateKey, - ); - - final beginReg = RegExp(r'BEGIN PGP PRIVATE KEY BLOCK'); - expect(beginReg.hasMatch(armored), true); - - final endReg = RegExp(r'END PGP PRIVATE KEY BLOCK'); - expect(endReg.hasMatch(armored), true); - - final armor = Armor.decode(armored); - expect(armor.type, ArmorType.privateKey); - expect(armor.data, privateKey); - })); - - test('armor signature test', (() { - final signature = utf8.encoder.convert(faker.lorem.words(100).join(' ')); - - final armored = Armor.encode( - ArmorType.signature, - signature, - ); - - final beginReg = RegExp(r'BEGIN PGP SIGNATURE'); - expect(beginReg.hasMatch(armored), true); - - final endReg = RegExp(r'END PGP SIGNATURE'); - expect(endReg.hasMatch(armored), true); - - final armor = Armor.decode(armored); - expect(armor.type, ArmorType.signature); - expect(armor.data, signature); - })); - })); -} diff --git a/test/common/s2k_test.dart b/test/common/s2k_test.dart new file mode 100644 index 00000000..a3ab527a --- /dev/null +++ b/test/common/s2k_test.dart @@ -0,0 +1,7 @@ +import 'package:test/test.dart'; + +void main() { + group('Generic S2k', () {}); + + group('Argon2 S2k', () {}); +} diff --git a/test/crypto/aead_test.dart b/test/crypto/aead_test.dart index b960b785..296236c6 100644 --- a/test/crypto/aead_test.dart +++ b/test/crypto/aead_test.dart @@ -1,14 +1,14 @@ import 'dart:typed_data'; -import 'package:dart_pg/src/crypto/aead/eax.dart'; -import 'package:dart_pg/src/crypto/aead/gcm.dart'; -import 'package:dart_pg/src/crypto/aead/ocb.dart'; +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/cryptor/aead/eax.dart'; +import 'package:dart_pg/src/cryptor/aead/gcm.dart'; +import 'package:dart_pg/src/cryptor/aead/ocb.dart'; import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; -import 'package:dart_pg/src/helpers.dart'; import 'package:test/test.dart'; void main() { - group('eax tests', () { + group('EAX', () { final testVectors = [ { 'name': 'Test Case 1', @@ -64,8 +64,7 @@ void main() { 'key': '7c77d6e813bed5ac98baa417477a2e7d', 'nonce': '1a8c98dcd73d38393b2bf1569deefc19', 'header': '65d2017990d62528', - 'cipher': - '02083e3979da014812f59f11d52630da30137327d10649b0aa6e1c181db617d7f2', + 'cipher': '02083e3979da014812f59f11d52630da30137327d10649b0aa6e1c181db617d7f2', }, { 'name': 'Test Case 8', @@ -73,8 +72,7 @@ void main() { 'key': '5fff20cafab119ca2fc73549e20f5b0d', 'nonce': 'dde59b97d722156d4d9aff2bc7559826', 'header': '54b9f04e6a09189a', - 'cipher': - '2ec47b2c4954a489afc7ba4897edcdae8cc33b60450599bd02c96382902aef7f832a', + 'cipher': '2ec47b2c4954a489afc7ba4897edcdae8cc33b60450599bd02c96382902aef7f832a', }, { 'name': 'Test Case 9', @@ -82,8 +80,7 @@ void main() { 'key': 'a4a4782bcffd3ec5e7ef6d8c34a56123', 'nonce': 'b781fcf2f75fa5a8de97a9ca48e522ec', 'header': '899a175897561d7e', - 'cipher': - '0de18fd0fdd91e7af19f1d8ee8733938b1e8e7f6d2231618102fdb7fe55ff1991700', + 'cipher': '0de18fd0fdd91e7af19f1d8ee8733938b1e8e7f6d2231618102fdb7fe55ff1991700', }, { 'name': 'Test Case 10', @@ -91,8 +88,7 @@ void main() { 'key': '8395fcf1e95bebd697bd010bc766aac3', 'nonce': '22e7add93cfc6393c57ec0b3c17d6b44', 'header': '126735fcc320d25a', - 'cipher': - 'cb8920f87a6c75cff39627b56e3ed197c552d295a7cfc46afc253b4652b1af3795b124ab6e', + 'cipher': 'cb8920f87a6c75cff39627b56e3ed197c552d295a7cfc46afc253b4652b1af3795b124ab6e', }, ]; for (var map in testVectors) { @@ -107,32 +103,26 @@ void main() { /// encryption test var ct = eax.encrypt(msg, nonce, header); - expect(ct, equals(cipher), - reason: 'encryption test $map["name"] did not match output'); + expect(ct, equals(cipher), reason: 'encryption test $map["name"] did not match output'); /// decryption test with verification var pt = eax.decrypt(cipher, nonce, header); - expect(pt, equals(msg), - reason: 'decryption test $map["name"] did not match output'); + expect(pt, equals(msg), reason: 'decryption test $map["name"] did not match output'); /// testing without additional data ct = eax.encrypt(msg, nonce, Uint8List(0)); pt = eax.decrypt(ct, nonce, Uint8List(0)); - expect(pt, equals(msg), - reason: 'test $map["name"] did not match output'); + expect(pt, equals(msg), reason: 'test $map["name"] did not match output'); /// testing with multiple additional data - ct = eax.encrypt( - msg, nonce, Uint8List.fromList([...header, ...header, ...header])); - pt = eax.decrypt( - ct, nonce, Uint8List.fromList([...header, ...header, ...header])); - expect(pt, equals(msg), - reason: 'test $map["name"] did not match output'); + ct = eax.encrypt(msg, nonce, Uint8List.fromList([...header, ...header, ...header])); + pt = eax.decrypt(ct, nonce, Uint8List.fromList([...header, ...header, ...header])); + expect(pt, equals(msg), reason: 'test $map["name"] did not match output'); }); } }); - group('ocb tests', () { + group('OCB', () { final key = '000102030405060708090a0b0c0d0e0f'.hexToBytes(); final testVectors = [ { @@ -189,8 +179,7 @@ void main() { 'N': 'bbaa99887766554433221107', 'A': '000102030405060708090a0b0c0d0e0f1011121314151617', 'P': '000102030405060708090a0b0c0d0e0f1011121314151617', - 'C': - '1ca2207308c87c010756104d8840ce1952f09673a448a122c92c62241051f57356d7f3c90bb0e07f', + 'C': '1ca2207308c87c010756104d8840ce1952f09673a448a122c92c62241051f57356d7f3c90bb0e07f', }, { 'name': 'Test Case 9', @@ -204,16 +193,14 @@ void main() { 'N': 'bbaa99887766554433221109', 'A': '', 'P': '000102030405060708090a0b0c0d0e0f1011121314151617', - 'C': - '221bd0de7fa6fe993eccd769460a0af2d6cded0c395b1c3ce725f32494b9f914d85c0b1eb38357ff', + 'C': '221bd0de7fa6fe993eccd769460a0af2d6cded0c395b1c3ce725f32494b9f914d85c0b1eb38357ff', }, { 'name': 'Test Case 11', 'N': 'bbaa9988776655443322110a', 'A': '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', 'P': '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', - 'C': - 'bd6f6c496201c69296c11efd138a467abd3c707924b964deaffc40319af5a48540fbba186c5553c68ad9f592a79a4240', + 'C': 'bd6f6c496201c69296c11efd138a467abd3c707924b964deaffc40319af5a48540fbba186c5553c68ad9f592a79a4240', }, { 'name': 'Test Case 12', @@ -227,24 +214,20 @@ void main() { 'N': 'bbaa9988776655443322110c', 'A': '', 'P': '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f', - 'C': - '2942bfc773bda23cabc6acfd9bfd5835bd300f0973792ef46040c53f1432bcdfb5e1dde3bc18a5f840b52e653444d5df', + 'C': '2942bfc773bda23cabc6acfd9bfd5835bd300f0973792ef46040c53f1432bcdfb5e1dde3bc18a5f840b52e653444d5df', }, { 'name': 'Test Case 14', 'N': 'bbaa9988776655443322110d', - 'A': - '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627', - 'P': - '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627', + 'A': '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627', + 'P': '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627', 'C': 'd5ca91748410c1751ff8a2f618255b68a0a12e093ff454606e59f9c1d0ddc54b65e8628e568bad7aed07ba06a4a69483a7035490c5769e60', }, { 'name': 'Test Case 15', 'N': 'bbaa9988776655443322110e', - 'A': - '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627', + 'A': '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627', 'P': '', 'C': 'c5cd9d1850c141e358649994ee701b68', }, @@ -252,8 +235,7 @@ void main() { 'name': 'Test Case 16', 'N': 'bbaa9988776655443322110f', 'A': '', - 'P': - '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627', + 'P': '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627', 'C': '4412923493c57d5de0d700f753cce0d1d2d95060122e9f15a5ddbfc5787e50b5cc55ee507bcb084e479ad363ac366b95a98ca5f3000b1479', }, @@ -269,32 +251,26 @@ void main() { /// encryption test var ct = ocb.encrypt(msg, nonce, header); - expect(ct, equals(cipher), - reason: 'encryption test $map["name"] did not match output'); + expect(ct, equals(cipher), reason: 'encryption test $map["name"] did not match output'); /// decryption test with verification var pt = ocb.decrypt(cipher, nonce, header); - expect(pt, equals(msg), - reason: 'decryption test $map["name"] did not match output'); + expect(pt, equals(msg), reason: 'decryption test $map["name"] did not match output'); /// testing without additional data ct = ocb.encrypt(msg, nonce, Uint8List(0)); pt = ocb.decrypt(ct, nonce, Uint8List(0)); - expect(pt, equals(msg), - reason: 'test $map["name"] did not match output'); + expect(pt, equals(msg), reason: 'test $map["name"] did not match output'); /// testing with multiple additional data - ct = ocb.encrypt( - msg, nonce, Uint8List.fromList([...header, ...header, ...header])); - pt = ocb.decrypt( - ct, nonce, Uint8List.fromList([...header, ...header, ...header])); - expect(pt, equals(msg), - reason: 'test $map["name"] did not match output'); + ct = ocb.encrypt(msg, nonce, Uint8List.fromList([...header, ...header, ...header])); + pt = ocb.decrypt(ct, nonce, Uint8List.fromList([...header, ...header, ...header])); + expect(pt, equals(msg), reason: 'test $map["name"] did not match output'); }); } }); - group('gcm tests', () { + group('GCM', () { final testVectors = [ { 'name': 'Test Case 1', @@ -424,8 +400,7 @@ void main() { }, { 'name': 'Test Case 13', - 'key': - '0000000000000000000000000000000000000000000000000000000000000000', + 'key': '0000000000000000000000000000000000000000000000000000000000000000', 'iv': '000000000000000000000000', 'aad': '', 'input': '', @@ -434,8 +409,7 @@ void main() { }, { 'name': 'Test Case 14', - 'key': - '0000000000000000000000000000000000000000000000000000000000000000', + 'key': '0000000000000000000000000000000000000000000000000000000000000000', 'iv': '000000000000000000000000', 'aad': '', 'input': '00000000000000000000000000000000', @@ -444,8 +418,7 @@ void main() { }, { 'name': 'Test Case 15', - 'key': - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', + 'key': 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', 'iv': 'cafebabefacedbaddecaf888', 'aad': '', 'input': @@ -456,8 +429,7 @@ void main() { }, { 'name': 'Test Case 16', - 'key': - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', + 'key': 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', 'iv': 'cafebabefacedbaddecaf888', 'aad': 'feedfacedeadbeeffeedfacedeadbeefabaddad2', 'input': @@ -468,8 +440,7 @@ void main() { }, { 'name': 'Test Case 17', - 'key': - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', + 'key': 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', 'iv': 'cafebabefacedbad', 'aad': 'feedfacedeadbeeffeedfacedeadbeefabaddad2', 'input': @@ -480,8 +451,7 @@ void main() { }, { 'name': 'Test Case 18', - 'key': - 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', + 'key': 'feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308', 'iv': '9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b', 'aad': 'feedfacedeadbeeffeedfacedeadbeefabaddad2', @@ -514,21 +484,17 @@ void main() { /// decryption test with verification var pt = gcm.decrypt(Uint8List.fromList([...output, ...mac]), iv, aad); - expect(pt, equals(input), - reason: 'decryption test $map["name"] did not match output'); + expect(pt, equals(input), reason: 'decryption test $map["name"] did not match output'); /// testing without additional data ct = gcm.encrypt(input, iv, Uint8List(0)); pt = gcm.decrypt(ct, iv, Uint8List(0)); - expect(pt, equals(input), - reason: 'test $map["name"] did not match output'); + expect(pt, equals(input), reason: 'test $map["name"] did not match output'); /// testing with multiple additional data - ct = gcm.encrypt( - input, iv, Uint8List.fromList([...aad, ...aad, ...aad])); + ct = gcm.encrypt(input, iv, Uint8List.fromList([...aad, ...aad, ...aad])); pt = gcm.decrypt(ct, iv, Uint8List.fromList([...aad, ...aad, ...aad])); - expect(pt, equals(input), - reason: 'test $map["name"] did not match output'); + expect(pt, equals(input), reason: 'test $map["name"] did not match output'); }); } }); diff --git a/test/crypto/asymmetric_test.dart b/test/crypto/asymmetric_test.dart index fb28fca0..16ecc685 100644 --- a/test/crypto/asymmetric_test.dart +++ b/test/crypto/asymmetric_test.dart @@ -1,103 +1,26 @@ import 'dart:typed_data'; -import 'package:dart_pg/src/crypto/asymmetric/elgamal.dart'; -import 'package:dart_pg/src/crypto/math/big_int.dart'; -import 'package:dart_pg/src/crypto/signer/dsa.dart'; -import 'package:dart_pg/src/helpers.dart'; -import 'package:dart_pg/src/openpgp.dart'; +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/cryptor/asymmetric/dsa.dart'; +import 'package:dart_pg/src/cryptor/asymmetric/elgamal.dart'; import 'package:pointycastle/api.dart'; -import 'package:faker/faker.dart'; import 'package:test/test.dart'; void main() { - group('ElGamal', () { - test('encryption test', () { - final prime = BigInt.parse( - '21842708581829896181355246474716153471799584702398145343781873612858268458790012658568509171208714431649208343296936349116172973580334664598762798393358559705621580042818388996849377434152861440023221391670968030831872591041152546055355390395164610169584848575897862985569697158568543863240507089993891556886557196474202323417190832644888994692005921518845406236886593318056471289387692729870131076001447553511077011608675277948641532603949929873743962633410696917376288603162134431896218895079026006444970460740888015606154190820808415464316451512185142037759825776982215354150775475714608377003555330213945294287583'); - final generator = BigInt.from(5); - final publicExponent = BigInt.parse( - '15590699273124096367845758349645226104138190024888407784287357837260359983637081328343487075505090749437978508505177973032496352672432943366064714761944186683268687737839702679962914617907849880272896027951360632795432278109768129193340345955504697749295122222309114553683024697759013607172925522796547740584824687858596370069179394322422986612767141828757141197590339291313205573787292371971283021600112532974909997630543606784003003770779526568284167901082742375947463384656907527559426381569970468966330515531538944216482677858434221351520007191681768077185259988739987157075690073689280587056931667283700771424191'); - final secretExponent = BigInt.parse( - '1446296390097566101617671091884237397227201126182287254457502825594360365391017793839243843109598388819'); - - final publicKey = ElGamalPublicKey(publicExponent, prime, generator); - final privateKey = ElGamalPrivateKey(secretExponent, prime, generator); - - final engine = ElGamalEngine(); - engine.init(true, PublicKeyParameter(publicKey)); - expect(engine.outputBlockSize, 2048 ~/ 4, - reason: "2048 outputBlockSize on encryption failed."); - - final message = - faker.randomGenerator.string(prime.byteLength).stringToBytes(); - final plainText = message; - final cipherText = Uint8List(engine.outputBlockSize); - engine.processBlock(plainText, 0, plainText.length, cipherText, 0); - - engine.init(false, PrivateKeyParameter(privateKey)); - engine.processBlock(cipherText, 0, cipherText.length, plainText, 0); - - expect(engine.outputBlockSize, (2048 ~/ 8) - 1, - reason: "2048 outputBlockSize on decryption failed."); - expect(message, equals(plainText), reason: '2048 bit test failed'); - }); - - test('key generator test', () { - final keyGen = ElGamalKeyGenerator() - ..init( - ParametersWithRandom( - ElGamalKeyGeneratorParameters( - DHKeySize.l2048n224.lSize, - DHKeySize.l2048n224.nSize, - 64, - ), - Helper.secureRandom(), - ), - ); - final keyPair = keyGen.generateKeyPair(); - - final engine = ElGamalEngine(); - engine.init( - true, PublicKeyParameter(keyPair.publicKey)); - expect(engine.outputBlockSize, 2048 ~/ 4, - reason: "2048 outputBlockSize on encryption failed."); - - final message = faker.randomGenerator - .string( - (keyPair.publicKey as ElGamalPublicKey).prime.byteLength, - ) - .stringToBytes(); - final plainText = message; - final cipherText = Uint8List(engine.outputBlockSize); - engine.processBlock(plainText, 0, plainText.length, cipherText, 0); - - engine.init( - false, PrivateKeyParameter(keyPair.privateKey)); - expect(engine.outputBlockSize, (2048 ~/ 8) - 1, - reason: "2048 outputBlockSize on decryption failed."); - - engine.processBlock(cipherText, 0, cipherText.length, plainText, 0); - expect(message, equals(plainText), reason: '2048 bit test failed'); - }); - }); - - group('DSA signer', (() { + group('DSA', () { final prime = BigInt.parse( '18162137922021319783039084688936650943756676913936359487644230524033744038416716640315362119531957065117691808427264479820376952164824666951768158261158011465383622961646140102588814345836301228952436368478471605675244965240663395560393520043446488959085100446039711212792538929022066114201273802300832755863321968834472697137828335693635804943342266260626637109532050720555091308772127451653755872557451491248990382251482443887099369600464777831608990878007068814783464418617901906104198108074206903909793174142273612557335586051547777907089455850194383049226681257881252609518716935491786310874981455522347755965263'); - final order = BigInt.parse( - '66237724685660121164013399122464209600863963990328366006106224772467420697001'); + final order = BigInt.parse('66237724685660121164013399122464209600863963990328366006106224772467420697001'); final generator = BigInt.parse( '693117913754860276426473281664845283847827874078831837566781765224888221249721170562868171267423414589469577529462100295651372077651432728133000631159241493543615314643538727517688679127996156948227084734117835362992668110067565260029784027890436946325120986252996031434727021897912364504906315612769347582107563243931645185214720978427405505592726492013000567393825423372958968113840944031967516897982342279432489751673785605929305317306465162868047750314617256208362522435739505165126345703258213171855603629264914838294962758085762547804458344916991647240641414079945072645196179749686064721950595476461703347596'); final publicExponent = BigInt.parse( '4782172781034192998693423575547001875121450860285384286800905429000791264609187331114128879953098324124068264308502771574321383159125468535909140265000623111180145376619101796653939164726433685123758969904269839090078039075889777192894408978360938352224532502867949812284119229251938382586523438070582120724642233025483830096315195183698731083960541920397786806884931581765685611516570402002856528710913800628319231592500299988215432415250617341963426900030521545457294059283280813544106638008667172693314074865970063708048659226032286932981816054377197606962836326219108126026753202594298484748732919893514434857817'); - final secretExponent = BigInt.parse( - '4058353653331808916569051257818485824406724876273357155551230247524105887159'); + final secretExponent = BigInt.parse('4058353653331808916569051257818485824406724876273357155551230247524105887159'); final privateKey = DSAPrivateKey(secretExponent, prime, order, generator); final publicKey = DSAPublicKey(publicExponent, prime, order, generator); - final message = - faker.randomGenerator.string(prime.byteLength).stringToBytes(); + final message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit ........".toBytes(); test('With sha1 test', (() { final signer = DSASigner(Digest('SHA-1')); @@ -116,48 +39,35 @@ void main() { signer.init(false, PublicKeyParameter(publicKey)); expect(signer.verifySignature(message, signature), true); })); + }); - test('key generator test', () { - final signer = DSASigner(Digest('SHA-256')); - final keyGen = DSAKeyGenerator() - ..init( - ParametersWithRandom( - DSAKeyGeneratorParameters( - DHKeySize.l2048n224.lSize, - DHKeySize.l2048n224.nSize, - 64, - ), - Helper.secureRandom(), - ), - ); - final keyPair = keyGen.generateKeyPair(); + group('ElGamal', () { + test('encryption', () { + final prime = BigInt.parse( + '21842708581829896181355246474716153471799584702398145343781873612858268458790012658568509171208714431649208343296936349116172973580334664598762798393358559705621580042818388996849377434152861440023221391670968030831872591041152546055355390395164610169584848575897862985569697158568543863240507089993891556886557196474202323417190832644888994692005921518845406236886593318056471289387692729870131076001447553511077011608675277948641532603949929873743962633410696917376288603162134431896218895079026006444970460740888015606154190820808415464316451512185142037759825776982215354150775475714608377003555330213945294287583'); + final generator = BigInt.from(5); + final publicExponent = BigInt.parse( + '15590699273124096367845758349645226104138190024888407784287357837260359983637081328343487075505090749437978508505177973032496352672432943366064714761944186683268687737839702679962914617907849880272896027951360632795432278109768129193340345955504697749295122222309114553683024697759013607172925522796547740584824687858596370069179394322422986612767141828757141197590339291313205573787292371971283021600112532974909997630543606784003003770779526568284167901082742375947463384656907527559426381569970468966330515531538944216482677858434221351520007191681768077185259988739987157075690073689280587056931667283700771424191'); + final secretExponent = BigInt.parse( + '1446296390097566101617671091884237397227201126182287254457502825594360365391017793839243843109598388819'); - signer.init(true, PrivateKeyParameter(keyPair.privateKey)); - final signature = signer.generateSignature(message); - signer.init(false, PublicKeyParameter(keyPair.publicKey)); - expect(signer.verifySignature(message, signature), true); - }); - })); + final publicKey = ElGamalPublicKey(publicExponent, prime, generator); + final privateKey = ElGamalPrivateKey(secretExponent, prime, generator); - test('Diffie Hellman key agreement test', (() { - final prime = BigInt.parse( - 'ffffffffffffffffc90fdaa22168c234c4c6628b80dc1cd129024e088a67cc74020bbea63b139b22514a08798e3404ddef9519b3cd3a431b302b0a6df25f14374fe1356d6d51c245e485b576625e7ec6f44c42e9a637ed6b0bff5cb6f406b7edee386bfb5a899fa5ae9f24117c4b1fe649286651ece45b3dc2007cb8a163bf0598da48361c55d39a69163fa8fd24cf5f83655d23dca3ad961c62f356208552bb9ed529077096966d670c354e4abc9804f1746c08ca18217c32905e462e36ce3be39e772c180e86039b2783a2ec07a28fb5c55df06f4c52c9de2bcbf6955817183995497cea956ae515d2261898fa051015728e5a8aacaa68ffffffffffffffff', - radix: 16); - final generator = BigInt.two; - final alicePrivate = BigInt.parse( - '22606eda7960458bc9d65f46dd96f114f9a004f0493c1f262139d2c8063b733162e876182ca3bf063ab1a167abdb7f03e0a225a6205660439f6ce46d252069ff', - radix: 16); - final bobPrivate = BigInt.parse( - '6e3efa13a96025d63e4b0d88a09b3a46ddfe9dd3bc9d16554898c02b4ac181f0ceb4e818664b12f02c71a07215c400f988352a4779f3e88836f7c3d3b3c739de', - radix: 16); + final engine = ElGamalEngine(); + engine.init(true, PublicKeyParameter(publicKey)); + expect(engine.outputBlockSize, 2048 ~/ 4, reason: "2048 outputBlockSize on encryption failed."); + + final message = "En un lugar de La Mancha, de cuyo nombre no quiero acordarme ...".toBytes(); + final plainText = message; + final cipherText = Uint8List(engine.outputBlockSize); + engine.processBlock(plainText, 0, plainText.length, cipherText, 0); - final alicePublic = generator.modPow(alicePrivate, prime); - final bobPublic = generator.modPow(bobPrivate, prime); + engine.init(false, PrivateKeyParameter(privateKey)); + engine.processBlock(cipherText, 0, cipherText.length, plainText, 0); - final aliceShared = bobPublic.modPow(alicePrivate, prime); - final bobShared = alicePublic.modPow(bobPrivate, prime); - expect(aliceShared, bobShared, - reason: - 'Failed asserting that Alice and Bob share the same BigInteger.'); - })); + expect(engine.outputBlockSize, (2048 ~/ 8) - 1, reason: "2048 outputBlockSize on decryption failed."); + expect(message, equals(plainText), reason: '2048 bit test failed'); + }); + }); } diff --git a/test/crypto/ecc_test.dart b/test/crypto/ecc_test.dart new file mode 100644 index 00000000..a246922a --- /dev/null +++ b/test/crypto/ecc_test.dart @@ -0,0 +1,324 @@ +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/cryptor/ecc/x448.dart'; +import 'package:test/test.dart'; + +void main() { + group('Test X448 ECDH', () { + checkECDHVector( + "9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b", + "9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0", + "1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d", + "3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609", + "07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d", + "ECDH Vector #1", + ); + + test('Test ECDH', () { + final random = Helper.secureRandom(); + for (var i = 0; i < 100; i++) { + /// Generate ephemeral private keys + final kA = random.nextBytes(X448.payloadSize); + final kB = random.nextBytes(X448.payloadSize); + + /// Publish their public keys + final qA = X448.scalarMultBase(kA); + final qB = X448.scalarMultBase(kB); + + /// Compute the shared secrets + final sA = X448.scalarMult(kA, qB); + final sB = X448.scalarMult(kB, qA); + + expect(sA, equals(sB), reason: 'Shared secrets did not match'); + } + }); + }); + + group('Test X448 Vector', () { + checkX448Vector( + "3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3", + "06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086", + "ce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239fe14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f", + "Vector #1", + ); + + checkX448Vector( + "203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c538345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f", + "0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db", + "884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d", + "Vector #2", + ); + + checkX448Vector( + "9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b", + "0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0", + "Vector #3", + ); + + checkX448Vector( + "1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d", + "0500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609", + "Vector #4", + ); + + checkX448Vector( + "9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b", + "3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609", + "07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d", + "Vector #5", + ); + + checkX448Vector( + "1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d", + "9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0", + "07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d", + "Vector #6", + ); + }); + + group('Test X448 Regression', () { + checkX448Vector( + "c05bd19c61d1c2c0e79414345cfb9c138eed88054fac8f74b2c4b5e1e817aaad629d159903bef40c10c85a8b90b8433c7f35248d72bea2d1", + "fefffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "856b8707b16e1b21dfc547fdb04c61a4aed9f9001f3f26404901e9ba30933cdd7ca9e2a0e57700588eb8576312ead8ee5791a8ecff32efaa", + "Regression #1", + ); + + checkX448Vector( + "24ba9df56ef036b4bcde7b0138b7983ae0fe3d2fd4b9d13ef0b8b0998c8394364d7dcb25a3885e571374f91615275440db0645ee7c0a6feb", + "0000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "f1ef9174222c422cb3a6194da91dbdab62b0688179e77f47019cc9eb7c38da86f6f51fc250e8a46dd4b3341cc5f71f1d8daf0b28e873d818", + "Regression #2", + ); + + checkX448Vector( + "40670a1efa7072a65c279f9618263a9e266fe12d82ff53c29b99d5e265e1fc7c32345227d6699a6d6b5517cf33b43ab156ee20df4878798e", + "0000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000", + "f27f02b452f9a5e95f08092e7e4058ae560732a4ffd5e4c4cc497af9d8e0d77f3d94d07dea932f0a79fa63c852a1cf03b60ab5a5201748ef", + "Regression #3", + ); + + checkX448Vector( + "8c37fb35eac1dbda6a3b5bf492c1f642c761be3adf0ab7617a66002576c45bba8202970bae6c5e05f645f5439ca2f42b89dacace1a5d0e82", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040", + "60c468df97e2e4427f27420cc6bc9eebaa2bceb827eb55a187fc5c29555e72a663243f6af4095641d72caeacb369720ea18cadd6efdbece6", + "Regression #4", + ); + + checkX448Vector( + "e8761598ba212a4e9724eaab2f3c225b0cc019595fa702ae0361bf3d348d9d6f7a04352424a5fd3026650f2a04574499daebc71f4c6d0fd9", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffbf", + "2521c283651396fb03bf074e3ce6d08d7b393de5fa85e9ac633cef328ac54576f6005f34c795425c56db62e8ceddf807d68e37646afb1184", + "Regression #5", + ); + checkX448Vector( + "5410735bd95cd0640fc1e2e11a028803f1cb4344f4efee75ae0b9eb9db5627d6e2a4b6dbad4af3fee986cce934bed60a0e8698204638b5a9", + "fffffffffffffffefffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "8951b4fc362ccd60cb560fde65fa126158a9727a3d577c507566fa5b4a79c2ac6bfd6c69defeb9eb29830cc4aaf6427f2ae66b2cd320159d", + "Regression #6", + ); + + checkX448Vector( + "08353724fbc03927b17359a88c121276ad697991ee89868e48890e95d1b03e603bcb51fdf6f296f1f1d10f5df10e00b8a25c9809f9aa1a94", + "0000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "30b1d98154768a2a2af568e2fa3577a042a5c7e5f9ac91b100655ea332b42db568034b15fdf75c693d8c2d0c2de54fb9d6d17efa316aa543", + "Regression #7", + ); + + checkX448Vector( + "98c6f36e1cb74528763f3aa11196ef9449c67be360e25e40ab06f1e39b742615a7dde3b29415ed827c68f07d4a47a4d9595c40c7fccb92a3", + "ffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000", + "c3c7aec97786a43b79f8a053cf185363112f04411a8ef3d3283f61eeac59a1f2918e10f54937932f5ac1e3b72fdbea57f34274598b17d768", + "Regression #8", + ); + + checkX448Vector( + "4804afd055ec05f335b7e3eebbde2a6de010ce7495e6c0d02173fa8c48bb495375b7d149c67b92d2885acb8d8bbb51a317453788671efa9f", + "fffffffffffffffffffffffffffffffffefffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "d6a2724dc9ca1579d94a8cc8815a3ed392b9e10b39f57c747f7b2d84f54969062c8b86929a1a12f466d3ef9598f1904773a4ee938f0f5df3", + "Regression #9", + ); + + checkX448Vector( + "bc7bce37434c0a1d05eff428034f75ed7454ede6b2a6e34ed4fcedc050349c866c40a27c27898afec41b3b477f0df5c5356e57d7562cdda5", + "fffffefffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "963ff8e5ea534178e1922cc06c638367c2c4690aba7e4aaab8342210b9035727062762631f79e709d2baf0646f0d9a37df02d531791bc940", + "Regression #10", + ); + + checkX448Vector( + "c05bd19c61d1c2c0e79414345cfb9c138eed88054fac8f74b2c4b5e1e817aaad629d159903bef40c10c85a8b90b8433c7f35248d72bea2d1", + "fefffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "856b8707b16e1b21dfc547fdb04c61a4aed9f9001f3f26404901e9ba30933cdd7ca9e2a0e57700588eb8576312ead8ee5791a8ecff32efaa", + "Regression #11", + ); + + checkX448Vector( + "742b8c0b0ab0104a9ef9634c0aad023f35e41fb953717121ce4c2aebc1a128e7c0431cd1347a6241685f7174a8512d417ebaaa46ee780a8a", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "d92b02d7bb9952f4ca826b2b51f1a3d4de1fd4459f0d019853f3a960d54f3354d8e40fb28d1be65637bb7dba0571ff83797b7106c7497459", + "Regression #12", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Regression #13", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "9fe4a6f810098a4faf078cdc888988bae53234d9dac49e0c39186789d8ce4b35530bfbe4e8a5520b84028f3c6d2234f6bf2e07375e927e48", + "Regression #14", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "0000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "c51d92750f8491e669eabb9c30fc8d4d16bfd6a214fd8f1a884e6130f9d5121aef3ac1cb7eac7c128473d38fbdedc584c477575de332ad93", + "Regression #15", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "ffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "a1840a0bd418dab5529353787f71042303ed65df615340845ba39e48c82b70022c3e10c3afbaec9f3c1559d5164a4c123672f308f55f5cb3", + "Regression #16", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "0000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000", + "c9c399826f4fdf4898f8abbccb7401541ca6084cf53e6f809d87d1fb614e867d6ff956058275944351917fe41675bce2f642f8aadf01a7bb", + "Regression #17", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "0000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000", + "b4209095fd21eb70a3e60b380191c43a85ca96a03f079d4493b215567af08514560fff03f9f6280bc0b357919c533686d0c02019f4866b2e", + "Regression #18", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000", + "8c46b8a63d37ea4ff2603b6ee0b72fd37f5d4be4c9076b0841d07540dc1f28c2d15ce01c5bccb8ba284b4f077b9d0f554d49bb1f5f9ebf7e", + "Regression #19", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "0000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000", + "8c46b8a63d37ea4ff2603b6ee0b72fd37f5d4be4c9076b0841d07540dc1f28c2d15ce01c5bccb8ba284b4f077b9d0f554d49bb1f5f9ebf7e", + "Regression #20", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040", + "bbcb71fe09d61a0d78a310d6a8f97f15457e9c3e020a4b70f9fcdee93a897505494fbb1437a0eacaf79f526fce66c8a24ee4a0e75891af4c", + "Regression #21", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "b21e0e2701e415d4ec3a46fecf167ffec7bb335cf96e902f1f8a63e90ef956381014149e86f3a6838a40a33ea6947b9955899cb622812ca1", + "Regression #22", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080", + "ad4be2907f519cc9e87006bb1b1fbe71475db52680fe7f27707b455e70c74a0e2600c6ff6de69365be551fcde234f9a25b4c269255174c5a", + "Regression #23", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "00000000000000000000000000000000000000000000000000000000feffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "663c7b4c123648242185c9f88352304f4476bf46580297c41714917eda81efa5e0ce2cf48529b587a7e7cd1afcff9d2afa887d0feab6109b", + "Regression #24", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffeffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0b3587f884ca5ac777ac98bbb203dbddf3fda14f351488d0dcf60a9c13c2fec4b8595922a9ec09cfd5d9d20ac639f9f74369b34646288965", + "Regression #25", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "fefffffffffffffffffffffffffffefffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "3921df4603cce60a982403d04a034160e3bde6b6a496f5be2c927b37dcab5137de990cc00a589b63ee12c9ee7e944bc1500d1b3ded48622c", + "Regression #26", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "0000000000000000000000000000fffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "51ede7991c611ef33c0676584d1e8897cedf32cccf14e262515c043a7642048e01f2cc3ae392c40063459c34414d4cc809fd37253d7ee801", + "Regression #27", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "ffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "07538cec452af2888f2bc565f31a5a6e73489857752304e21a1907ff62f63874ef091a0c11d8514a3ed7a15af77efee84f6eec8fa0792423", + "Regression #28", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "fdfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "5116bf8a4860bbfa28c2c8c96a1105cc9c139130417ed2226f2fe29a3d0b39096b0456faf34dab950bf430bb58c8c8b9320f39c7766c9fc1", + "Regression #29", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "fefffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "Regression #30", + ); + + checkX448Vector( + "244534ab5f22108381d4a35f308d51b19e20ce997d7e9dff253e1d5faf1a8ad6ab73ff586d1eee6dac0fe7b5593c3123c6f1424800fa9b88", + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "006bc6af5644e8f5824ab7a1b1411d1b2f91d8038b545efcfcb9917c40a3ef48d698f342c1db57118936a18a2608c0427772c70bd0aae479", + "Regression #31", + ); + }); +} + +void checkX448Vector(String sk, String su, String se, String text) { + test(text, () { + final k = sk.hexToBytes(); + final u = su.hexToBytes(); + final sr = X448.scalarMult(k, u); + expect(sr, equals(se.hexToBytes()), reason: 'Vector did not match'); + }); +} + +void checkECDHVector(String sA, String sAPub, String sB, String sBPub, String sK, String text) { + test(text, () { + final a = sA.hexToBytes(); + final aPub = X448.scalarMultBase(a); + expect(aPub, equals(sAPub.hexToBytes()), reason: 'Public key did not match'); + + final b = sB.hexToBytes(); + final bPub = X448.scalarMultBase(b); + expect(bPub, equals(sBPub.hexToBytes()), reason: 'Public key did not match'); + + final aK = X448.scalarMult(a, bPub); + expect(aK, equals(sK.hexToBytes()), reason: 'Shared secret did not match'); + + final bK = X448.scalarMult(b, aPub); + expect(bK, equals(sK.hexToBytes()), reason: 'Shared secret did not match'); + }); +} diff --git a/test/crypto/symmetric_test.dart b/test/crypto/symmetric_test.dart index 807be35f..b5d7315b 100644 --- a/test/crypto/symmetric_test.dart +++ b/test/crypto/symmetric_test.dart @@ -1,336 +1,280 @@ -import 'package:dart_pg/src/crypto/symmetric/blowfish.dart'; -import 'package:dart_pg/src/crypto/symmetric/buffered_cipher.dart'; -import 'package:dart_pg/src/crypto/symmetric/camellia.dart'; -import 'package:dart_pg/src/crypto/symmetric/camellia_light.dart'; -import 'package:dart_pg/src/crypto/symmetric/cast5.dart'; -import 'package:dart_pg/src/crypto/symmetric/idea.dart'; -import 'package:dart_pg/src/crypto/symmetric/twofish.dart'; +import 'dart:typed_data'; + +import 'package:dart_pg/src/cryptor/symmetric/blowfish.dart'; +import 'package:dart_pg/src/cryptor/symmetric/camellia.dart'; +import 'package:dart_pg/src/cryptor/symmetric/cast5.dart'; +import 'package:dart_pg/src/cryptor/symmetric/idea.dart'; +import 'package:dart_pg/src/cryptor/symmetric/twofish.dart'; import 'package:pointycastle/export.dart'; import 'package:test/test.dart'; -import 'package:dart_pg/src/helpers.dart'; void main() { - group('Block cipher tests', (() { - test('IDEA test', (() { - _blockCipherVectorTest( - 0, - IDEAEngine(), - _kp('00112233445566778899aabbccddeeff'), - '000102030405060708090a0b0c0d0e0f', - 'ed732271a7b39f475b4b2b6719f194bf', - ); - - _blockCipherVectorTest( - 1, - IDEAEngine(), - _kp('00112233445566778899aabbccddeeff'), - 'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff', - 'b8bc6ed5c899265d2bcfad1fc6d4287d', - ); - })); - - test('CAST5 test', (() { - _blockCipherVectorTest( - 0, - CAST5Engine(), - _kp('0123456712345678234567893456789a'), - '0123456789abcdef', - '238b4fe5847e44b2', - ); - - _blockCipherVectorTest( - 1, - CAST5Engine(), - _kp('01234567123456782345'), - '0123456789abcdef', - 'eb6a711a2c02271b', - ); - - _blockCipherVectorTest( - 2, - CAST5Engine(), - _kp('0123456712'), - '0123456789abcdef', - '7ac816d16e9b302e', - ); - })); - - test('Blowfish test', (() { - _blockCipherVectorTest( - 0, - BlowfishEngine(), - _kp('0000000000000000'), - '0000000000000000', - '4ef997456198dd78', - ); - - _blockCipherVectorTest( - 1, - BlowfishEngine(), - _kp('ffffffffffffffff'), - 'ffffffffffffffff', - '51866fd5b85ecb8a', - ); - - _blockCipherVectorTest( - 2, - BlowfishEngine(), - _kp('3000000000000000'), - '1000000000000001', - '7d856f9a613063f2', - ); - - _blockCipherVectorTest( - 3, - BlowfishEngine(), - _kp('1111111111111111'), - '1111111111111111', - '2466dd878b963c9d', - ); - - _blockCipherVectorTest( - 4, - BlowfishEngine(), - _kp('0123456789abcdef'), - '1111111111111111', - '61f9c3802281b096', - ); - - _blockCipherVectorTest( - 5, - BlowfishEngine(), - _kp('fedcba9876543210'), - '0123456789abcdef', - '0aceab0fc6a0a28d', - ); - - _blockCipherVectorTest( - 6, - BlowfishEngine(), - _kp('7ca110454a1a6e57'), - '01a1d6d039776742', - '59c68245eb05282b', - ); - - _blockCipherVectorTest( - 7, - BlowfishEngine(), - _kp('0131d9619dc1376e'), - '5cd54ca83def57da', - 'b1b8cc0b250f09a0', - ); - })); - - test('Twofish test', (() { - final input = '000102030405060708090a0b0c0d0e0f'; - - _blockCipherVectorTest( - 0, - TwofishEngine(), - _kp('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'), - input, - '8ef0272c42db838bcf7b07af0ec30f38', - ); - - _blockCipherVectorTest( - 1, - TwofishEngine(), - _kp('000102030405060708090a0b0c0d0e0f1011121314151617'), - input, - '95accc625366547617f8be4373d10cd7', - ); - - _blockCipherVectorTest( - 2, - TwofishEngine(), - _kp('000102030405060708090a0b0c0d0e0f'), - input, - '9fb63337151be9c71306d159ea7afaa4', - ); - - _blockCipherVectorTest( - 3, - CBCBlockCipher(TwofishEngine()), - _kpWithIV('0123456789abcdef1234567890abcdef', - '1234567890abcdef0123456789abcdef'), - input, - 'd6bfdbb2090562e960273783127e2658', - ); - })); - - test('Camellia test', (() { - _blockCipherVectorTest( - 0, - CamelliaEngine(), - _kp('00000000000000000000000000000000'), - '80000000000000000000000000000000', - '07923a39eb0a817d1c4d87bdb82d1f1c', - ); - - _blockCipherVectorTest( - 1, - CamelliaEngine(), - _kp('80000000000000000000000000000000'), - '00000000000000000000000000000000', - '6c227f749319a3aa7da235a9bba05a2c', - ); - - _blockCipherVectorTest( - 2, - CamelliaEngine(), - _kp('0123456789abcdeffedcba9876543210'), - '0123456789abcdeffedcba9876543210', - '67673138549669730857065648eabe43', - ); - - /// 192 bit - _blockCipherVectorTest( - 3, - CamelliaEngine(), - _kp('0123456789abcdeffedcba98765432100011223344556677'), - '0123456789abcdeffedcba9876543210', - 'b4993401b3e996f84ee5cee7d79b09b9', - ); - - _blockCipherVectorTest( - 4, - CamelliaEngine(), - _kp('000000000000000000000000000000000000000000000000'), - '00040000000000000000000000000000', - '9BCA6C88B928C1B0F57F99866583A9BC', - ); - - _blockCipherVectorTest( - 5, - CamelliaEngine(), - _kp('949494949494949494949494949494949494949494949494'), - '636eb22d84b006381235641bcf0308d2', - '94949494949494949494949494949494', - ); - - /// 256 bit - _blockCipherVectorTest( - 6, - CamelliaEngine(), - _kp('0123456789abcdeffedcba987654321000112233445566778899aabbccddeeff'), - '0123456789abcdeffedcba9876543210', - '9acc237dff16d76c20ef7c919e3a7509', - ); - - _blockCipherVectorTest( - 7, - CamelliaEngine(), - _kp('4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A'), - '057764fe3a500edbd988c5c3b56cba9a', - '4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a', - ); - - _blockCipherVectorTest( - 8, - CamelliaEngine(), - _kp('0303030303030303030303030303030303030303030303030303030303030303'), - '7968b08aba92193f2295121ef8d75c8a', - '03030303030303030303030303030303', - ); - })); - - test('Camellia light test', (() { - _blockCipherVectorTest( - 0, - CamelliaLightEngine(), - _kp('00000000000000000000000000000000'), - '80000000000000000000000000000000', - '07923a39eb0a817d1c4d87bdb82d1f1c', - ); - - _blockCipherVectorTest( - 1, - CamelliaLightEngine(), - _kp('80000000000000000000000000000000'), - '00000000000000000000000000000000', - '6c227f749319a3aa7da235a9bba05a2c', - ); - - _blockCipherVectorTest( - 2, - CamelliaLightEngine(), - _kp('0123456789abcdeffedcba9876543210'), - '0123456789abcdeffedcba9876543210', - '67673138549669730857065648eabe43', - ); - - /// 192 bit - _blockCipherVectorTest( - 3, - CamelliaLightEngine(), - _kp('0123456789abcdeffedcba98765432100011223344556677'), - '0123456789abcdeffedcba9876543210', - 'b4993401b3e996f84ee5cee7d79b09b9', - ); - - _blockCipherVectorTest( - 4, - CamelliaLightEngine(), - _kp('000000000000000000000000000000000000000000000000'), - '00040000000000000000000000000000', - '9BCA6C88B928C1B0F57F99866583A9BC', - ); - - _blockCipherVectorTest( - 5, - CamelliaLightEngine(), - _kp('949494949494949494949494949494949494949494949494'), - '636eb22d84b006381235641bcf0308d2', - '94949494949494949494949494949494', - ); - - /// 256 bit - _blockCipherVectorTest( - 6, - CamelliaLightEngine(), - _kp('0123456789abcdeffedcba987654321000112233445566778899aabbccddeeff'), - '0123456789abcdeffedcba9876543210', - '9acc237dff16d76c20ef7c919e3a7509', - ); - - _blockCipherVectorTest( - 7, - CamelliaLightEngine(), - _kp('4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A'), - '057764fe3a500edbd988c5c3b56cba9a', - '4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a', - ); - - _blockCipherVectorTest( - 8, - CamelliaLightEngine(), - _kp('0303030303030303030303030303030303030303030303030303030303030303'), - '7968b08aba92193f2295121ef8d75c8a', - '03030303030303030303030303030303', - ); - })); - })); + group('Blowfish', () { + _blockCipherTest( + 0, + BlowfishEngine(), + _kp('0000000000000000'), + '0000000000000000', + '4ef997456198dd78', + ); + + _blockCipherTest( + 1, + BlowfishEngine(), + _kp('ffffffffffffffff'), + 'ffffffffffffffff', + '51866fd5b85ecb8a', + ); + + _blockCipherTest( + 2, + BlowfishEngine(), + _kp('3000000000000000'), + '1000000000000001', + '7d856f9a613063f2', + ); + + _blockCipherTest( + 3, + BlowfishEngine(), + _kp('1111111111111111'), + '1111111111111111', + '2466dd878b963c9d', + ); + + _blockCipherTest( + 4, + BlowfishEngine(), + _kp('0123456789abcdef'), + '1111111111111111', + '61f9c3802281b096', + ); + + _blockCipherTest( + 5, + BlowfishEngine(), + _kp('fedcba9876543210'), + '0123456789abcdef', + '0aceab0fc6a0a28d', + ); + + _blockCipherTest( + 6, + BlowfishEngine(), + _kp('7ca110454a1a6e57'), + '01a1d6d039776742', + '59c68245eb05282b', + ); + + _blockCipherTest( + 7, + BlowfishEngine(), + _kp('0131d9619dc1376e'), + '5cd54ca83def57da', + 'b1b8cc0b250f09a0', + ); + }); + + group('Camellia', () { + _blockCipherTest( + 0, + CamelliaEngine(), + _kp('00000000000000000000000000000000'), + '80000000000000000000000000000000', + '07923a39eb0a817d1c4d87bdb82d1f1c', + ); + + _blockCipherTest( + 1, + CamelliaEngine(), + _kp('80000000000000000000000000000000'), + '00000000000000000000000000000000', + '6c227f749319a3aa7da235a9bba05a2c', + ); + + _blockCipherTest( + 2, + CamelliaEngine(), + _kp('0123456789abcdeffedcba9876543210'), + '0123456789abcdeffedcba9876543210', + '67673138549669730857065648eabe43', + ); + + _blockCipherTest( + 3, + CamelliaEngine(), + _kp('0123456789abcdeffedcba98765432100011223344556677'), + '0123456789abcdeffedcba9876543210', + 'b4993401b3e996f84ee5cee7d79b09b9', + ); + + _blockCipherTest( + 4, + CamelliaEngine(), + _kp('000000000000000000000000000000000000000000000000'), + '00040000000000000000000000000000', + '9bca6c88b928c1b0f57f99866583a9bc', + ); + + _blockCipherTest( + 5, + CamelliaEngine(), + _kp('949494949494949494949494949494949494949494949494'), + '636eb22d84b006381235641bcf0308d2', + '94949494949494949494949494949494', + ); + + _blockCipherTest( + 6, + CamelliaEngine(), + _kp('0123456789abcdeffedcba987654321000112233445566778899aabbccddeeff'), + '0123456789abcdeffedcba9876543210', + '9acc237dff16d76c20ef7c919e3a7509', + ); + + _blockCipherTest(7, CamelliaEngine(), _kp('4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a'), + '057764fe3a500edbd988c5c3b56cba9a', '4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a'); + + _blockCipherTest( + 8, + CamelliaEngine(), + _kp('0303030303030303030303030303030303030303030303030303030303030303'), + '7968b08aba92193f2295121ef8d75c8a', + '03030303030303030303030303030303', + ); + }); + + group('CAST5', () { + _blockCipherTest( + 0, + CAST5Engine(), + _kp('0123456712345678234567893456789a'), + '0123456789abcdef', + '238b4fe5847e44b2', + ); + + _blockCipherTest( + 1, + CAST5Engine(), + _kp('01234567123456782345'), + '0123456789abcdef', + 'eb6a711a2c02271b', + ); + + _blockCipherTest( + 2, + CAST5Engine(), + _kp('0123456712'), + '0123456789abcdef', + '7ac816d16e9b302e', + ); + }); + + group('IDEA', () { + _blockCipherTest( + 0, + IDEAEngine(), + _kp('00112233445566778899aabbccddeeff'), + '000102030405060708090a0b0c0d0e0f', + 'ed732271a7b39f475b4b2b6719f194bf', + ); + + _blockCipherTest( + 1, + IDEAEngine(), + _kp('00112233445566778899aabbccddeeff'), + 'f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff', + 'b8bc6ed5c899265d2bcfad1fc6d4287d', + ); + }); + + group('Twofish', () { + final input = '000102030405060708090a0b0c0d0e0f'; + + _blockCipherTest( + 0, + TwofishEngine(), + _kp('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'), + input, + '8ef0272c42db838bcf7b07af0ec30f38', + ); + + _blockCipherTest( + 1, + TwofishEngine(), + _kp('000102030405060708090a0b0c0d0e0f1011121314151617'), + input, + '95accc625366547617f8be4373d10cd7', + ); + + _blockCipherTest( + 2, + TwofishEngine(), + _kp('000102030405060708090a0b0c0d0e0f'), + input, + '9fb63337151be9c71306d159ea7afaa4', + ); + + _blockCipherTest( + 3, + CBCBlockCipher(TwofishEngine()), + _kpWithIV( + '0123456789abcdef1234567890abcdef', + '1234567890abcdef0123456789abcdef', + ), + input, + 'd6bfdbb2090562e960273783127e2658', + ); + }); } KeyParameter _kp(String key) { - return KeyParameter(key.hexToBytes()); + return KeyParameter(_uint8ListFromHex(key)); } ParametersWithIV _kpWithIV(String key, String iv) { - return ParametersWithIV(_kp(key), iv.hexToBytes()); + return ParametersWithIV(_kp(key), _uint8ListFromHex(iv)); } -void _blockCipherVectorTest(int id, BlockCipher engine, CipherParameters params, - String input, String output) { - final inBytes = input.hexToBytes(); - final outBytes = output.hexToBytes(); +void _blockCipherTest( + int id, + BlockCipher cipher, + CipherParameters parameters, + String input, + String output, +) { + test('BlockCipher Test: $id ', () { + var input0 = _uint8ListFromHex(input); + var output0 = _uint8ListFromHex(output); + + cipher.init(true, parameters); + var out = Uint8List(input0.length); + var p = 0; + while (p < input0.length) { + p += cipher.processBlock(input0, p, out, p); + } + + expect(output0, equals(out), reason: '$id did not match output'); + + cipher.init(false, parameters); + out = Uint8List(output0.length); + p = 0; + while (p < output0.length) { + p += cipher.processBlock(output0, p, out, p); + } + + expect(input0, equals(out), reason: '$id did not match input'); + }); +} - final cipher = BufferedCipher(engine); - cipher.init(true, params); - expect(outBytes, equals(cipher.process(inBytes)), - reason: '${cipher.algorithmName} test $id did not match output'); +Uint8List _uint8ListFromHex(String hex) { + hex = hex.replaceAll(RegExp(r'\s'), ''); // remove all whitespace, if any - cipher.init(false, params); - expect(inBytes, equals(cipher.process(outBytes)), - reason: '${cipher.algorithmName} test $id did not match input'); + var result = Uint8List(hex.length ~/ 2); + for (var i = 0; i < hex.length; i += 2) { + var num = hex.substring(i, i + 2); + var byte = int.parse(num, radix: 16); + result[i ~/ 2] = byte; + } + return result; } diff --git a/test/dart_pg_test.dart b/test/dart_pg_test.dart new file mode 100644 index 00000000..153be31c --- /dev/null +++ b/test/dart_pg_test.dart @@ -0,0 +1,16 @@ +import 'package:dart_pg/dart_pg.dart'; +import 'package:test/test.dart'; + +void main() { + group('A group of tests', () { + final awesome = OpenPGP(); + + setUp(() { + // Additional setup goes here. + }); + + test('First Test', () { + expect(awesome.isAwesome, isTrue); + }); + }); +} diff --git a/test/data/key_data.dart b/test/data/key_data.dart deleted file mode 100644 index 1740352e..00000000 --- a/test/data/key_data.dart +++ /dev/null @@ -1,613 +0,0 @@ -const passphrase = 'password'; - -const rsaPublicKeyPacket = ''' -BGPcua0BDAC9sldAErPNUMBjH8wcA/om0YqZmRpTMTqEttqSAqgc8CCIF2nW8KwsLz5hyPQEKYPh -3Ka1ZxLMwHC0oJMqlR2uiDMPwNmx9oy4WOr7rltWxQFhlh6+GYw+eiMDXJZEswseg1+q6SIRHoG4 -0H8sawIHToVNS+9GjvJsfZwdkrILAZyLfrPeHB22td89qE7Lckpj9HBACalaSR7RxMYEzfX2Qyoy -tReJ0zciFPrqpr24dPM/E+aECEaxWbLGPWMJXCjWP380RxmOreoab5t7H7u2r0pjDnFy05TFGrps -yB6Qn59OrMUGpthzJDimG9sz4PvMQpAn3udXYzmuom4xcCWhAxs3/vH7TB3x8d5ieqRojOUr2mBa -xgzZPearsSoNp0i0ME/7JUWw7EXKzfUDH8rDxjA2Rv4FaIPkgc9SitJq5YYvzpXF0KGkn4d8/NQo -Lae6E7igkhn8cNhiCfuis1M3DcIMx5N2saD5Vg9pe3leWU9odbM3LycHb4ry2KWNLvsAEQEAAQ== -'''; - -const rsaPublicSubkeyPacket = ''' -BGPcua0BDADKGeCxetVYpbkZ8wCjTG9FfNYhkqdtrpJCqQ+AksMM7menq1c6CkPk0roqIpQeat58 -rYFH/6RUHbTCm7FfDa8u3bI/7tola/r2CPURtH6X+F2Q/dC/bzDSAaAfG7lZWL5ufxVN86Y0B3yf -+MJPPLO39zdV5nRoQH9YBmgnhry0ihVBpJuNJ/5h0eCDXvS2PcosmLOwOY8DNNQHU+uiyiQV5kSV -S6KP+kQy6dgOe9XRMgaEVRxeBHKsXSuwAa906Ku+RAQ2/YF0GRejljQxOPTKafY26zye9MBhSZGA -EfJtkpfbT8TnTuMevd/Rz4BAlFIEwKj11W1oZSs2edIefBHsr+dPjo1PDkVol+JuK/3LF7cbJtXg -WhPcv516ybXGriJ7JTz8oGC5gPbeuq/3AcwCtNIVtDcIOCtKVcCvxn9p9kPCsoD6O6Ao7XmAU8pF -PpGtwD4IcPE8xBa7Nk/Ii2gsEEJjT3kooaV+F2RjlVYaWCT3AzONLr8iCs2x/FINfOUAEQEAAQ== -'''; - -const rsaPublicKey = '''-----BEGIN PGP PUBLIC KEY BLOCK----- - -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 -ZCBbTaFD6c0mYFI= -=eghN ------END PGP PUBLIC KEY BLOCK----- -'''; - -const rsaSecretKeyPacket = ''' -BGPcua0BDAC9sldAErPNUMBjH8wcA/om0YqZmRpTMTqEttqSAqgc8CCIF2nW8KwsLz5hyPQEKYPh -3Ka1ZxLMwHC0oJMqlR2uiDMPwNmx9oy4WOr7rltWxQFhlh6+GYw+eiMDXJZEswseg1+q6SIRHoG4 -0H8sawIHToVNS+9GjvJsfZwdkrILAZyLfrPeHB22td89qE7Lckpj9HBACalaSR7RxMYEzfX2Qyoy -tReJ0zciFPrqpr24dPM/E+aECEaxWbLGPWMJXCjWP380RxmOreoab5t7H7u2r0pjDnFy05TFGrps -yB6Qn59OrMUGpthzJDimG9sz4PvMQpAn3udXYzmuom4xcCWhAxs3/vH7TB3x8d5ieqRojOUr2mBa -xgzZPearsSoNp0i0ME/7JUWw7EXKzfUDH8rDxjA2Rv4FaIPkgc9SitJq5YYvzpXF0KGkn4d8/NQo -Lae6E7igkhn8cNhiCfuis1M3DcIMx5N2saD5Vg9pe3leWU9odbM3LycHb4ry2KWNLvsAEQEAAf4H -AwKI71Gy9KByI/9psKvpCajf2McCBNAtgqbAg0fTAldwinHdJIne18M7U/0sqLgbZlTFUfM4mGhN -uFhhJxyX2JYGD/FQ8B6dj52lFjCRDi4BM78HajEAldzP4+5I/ezmUjq+1Jvm8YcwhzaUjdQr2PJg -ww0MQoCcg6PZlm8SJ6FRhxnsmoMoJxxZUI0w05tMlsv+jii+ikz/PQkc6Ki57Oh23CbW8WY+LKV9 -r3VCVS/PmYVzz+9zWnbmeaDLHD5ctspL+k2Lc1bipbJ29shS2xrXjrUKwwVgI75cQ2pXFwIcDWSE -l2h8oQKQ7CG3GmFzn6HCHd0XIU/yt3Gu5mr2szqb7a8MBnTk5l3vbbiobWlU+WBIMrt9mDPgyxNi -aC3AdPIilZN26Cxd+wNaVdX5MPkKfJGb/nJ6Em5BM0v7xJPjwoiJLeN+Uts4kb6M07zIILXqqMh2 -APYXzRraie/8YXwg9FTsIUpSuw6teupzA9HqwFMhzCXqhfQ/oJPI7QhvwZCV/h3B3VCi6f5XUCZJ -F8A++zGlpSL3v0lZX0yQZNYGRIMIYSIlOCLDvD/mtI+NFonL4s9uG1PkSoFiJav+uRTKS4e0daqD -V/2oauLHZbcUKFdd68ZmfzmyViaLLLv1ATt3XkJknjns2nntFujGKEdajkzbU1reOb9Jlergigyw -iVzCvu2QzkB8C9C1sO0mMfgqMvDpWDxMGU7ILWAeR4H/n/Kg5vJ/piZ8K8qpffllgJ/ooVAaChGx -XhUXcK5Jg68GRHyo2iFrh4qH6I5zvH/GtgtZGlaMEmVI9GRwWOyqMvmMcVO7lDAeXqiP4YstahsG -mmlEE6Uf+yBgaWR2TuB4ZiV+dhZHp1P46YBqCZFFa2dgcJmSaMRn1LF0NDKCKxypjdsxaPeaXiGL -s07cmMrNkJD6HHoLuAzKi0wWdUQjglE86xEIMjRrf+qbE0tjoiJJ26qMlzEj+39TajRiz87ptND2 -SCFwLz6Pyo9xgqEBCVq530IHjQLD+692vx+L6BRfSKmb4zUYgG8fTyh7B0CfBeNTBC82iyI4a9m4 -ug32/Lb454WHoASOa4tSjjunHnL+PjqfhDHr8wuq9Z4lT47il9dfye/Ffwg8GISb4qCAhiVpNxN2 -WcRPR2o4sN75riXLBJliK/6XOBNGyihF2KT7CJIeiFJeAfk+cSOx8ptQ3V2GRV9L7M4qiwatnv7N -+LXENAod2SK1tKb0+lyYDwg9PE32DoWJWBynB+G+vPkOhPzHxpeSvg0rWf5rqnZb7RAbvyjxB8oL -dspzcB22aQPGegWxww9SU5zi6f/3BZIeRjhxFmjyrUnYyh9SHkrMNK8XxhqFtA== -'''; - -const rsaSecretSubkeyPacket = ''' -BGPcua0BDADKGeCxetVYpbkZ8wCjTG9FfNYhkqdtrpJCqQ+AksMM7menq1c6CkPk0roqIpQeat58 -rYFH/6RUHbTCm7FfDa8u3bI/7tola/r2CPURtH6X+F2Q/dC/bzDSAaAfG7lZWL5ufxVN86Y0B3yf -+MJPPLO39zdV5nRoQH9YBmgnhry0ihVBpJuNJ/5h0eCDXvS2PcosmLOwOY8DNNQHU+uiyiQV5kSV -S6KP+kQy6dgOe9XRMgaEVRxeBHKsXSuwAa906Ku+RAQ2/YF0GRejljQxOPTKafY26zye9MBhSZGA -EfJtkpfbT8TnTuMevd/Rz4BAlFIEwKj11W1oZSs2edIefBHsr+dPjo1PDkVol+JuK/3LF7cbJtXg -WhPcv516ybXGriJ7JTz8oGC5gPbeuq/3AcwCtNIVtDcIOCtKVcCvxn9p9kPCsoD6O6Ao7XmAU8pF -PpGtwD4IcPE8xBa7Nk/Ii2gsEEJjT3kooaV+F2RjlVYaWCT3AzONLr8iCs2x/FINfOUAEQEAAf4H -AwJvsFqDoU0YRv9Ft4cK8NBKUilj2Y654UK4CNykpOIW50SZq3tdp4tLmVKnkMjH2CINd9AIhSek -noWBk5qqNtnvVhuqI1LTMtlVDXCpPMcQjiRfiXYqYvNEhhY4Wj8OVlhvGgJW2XjkmAz75xr9wi0g -+0Mj1nIhxI+/wOc7bxYin+8D/WToZkabOe19/I2YrsM62akF0VMZnaErLp9HOLGHOjj6NzRNpkS4 -R1GN6wv5xhR6Uma+qQULW5YQMh7L583u/eGm48roKAz9Vrj47U6kDV8fwhZVK8+1hYP3xi9vFpng -41Kl2M0MraygyCw6dpIm/UQRI4lMEAXf9Gpp4mRghtVBrfqoA5xC7S9GfF1gGyT1SP7a8S9jM0KY -znBGQX5tfR+rq1igTWe4GGWIvnkBu7t6UeSCvYcZuaFB7t1vdhlYKTtQZ9JNJGQFR77/vbruR1dG -HhYEwHT9wdlMpfSFRoKjVxx8Gb08nCOSE4XQy9IeakyjYR/OgMcoBcR9T1xsP4t89HuZQmypzSAc -MXGFEu4xiYdKceD4nv7Leql3/oHKZGr8Pz+VxODuIpalNAOrLtVAEmoGSFkH74LLS1P+2Ivpq3/B -Ny8p1ZrRdAqZO0/irOObngiwrA1yriBQULHvFPs1m9X8mTMXVNFWftDWsg3VAx1Nel9ueLQ+fFC7 -TP9Zv/A4W84cwj55S9I94ToUDsPZYjUgvOvecPXT7atYaIdAtj47hQrQQP4TfuGkPBSLoEoNUNpp -+r5p4/KW9Mnx4k3COO748xynmCRDG3FmKpMGD3M1kmamqrSzcDepovPfEGEqC5/u83QyNLnN0O81 -8tMGZwR8wvgeECcYlpaj7KWTS/1pAhou7wLzag0P4qtu6TxXsoxchk/aXvtlTwPE+h2jY7axgsNK -JU6V0Aa1ZO2a0Vy+LdxPpDvO2SA3z0g75EA0CRLrWIWyD75XRl0n0te+iiptPPh/GWgvwn/xDtXc -BDysTjcySrHOwLRkmisQfilxI+8hlnHB505jkCfhMBG5ZvcxH9RE2pr+0hKo1x+A+G1oTUMiowFC -KtQ4/APfwtWTsrfSDlSZaGnJ2zZeabcoRTQ4RlRuxolF8vSquE3kFZbTado0eMWgQzB6nMCh2vUn -kcoEoarUaEyN5Xhr5VXvSqVg34lVzxYB8lomVHo8Ph6AvPy6TLF/LwqckHnJhunO5qq5Y3bZFj8u -hP0RSTZaAgsnMpZIkw3+tTcUvYtac9zOsa5FvUn2xzVIPXNDzvCaooNQjOLq75YY8K0PSGZ+C9ZJ -OQA9W5gOjm3oERdQwIiVv0Ce4TMVRFKq8dLke6XDGl4Q+UoyY1GXkZA0b3ee -'''; - -const rsaPrivateKey = '''-----BEGIN PGP PRIVATE KEY BLOCK----- - -lQWGBGPcua0BDAC9sldAErPNUMBjH8wcA/om0YqZmRpTMTqEttqSAqgc8CCIF2nW -8KwsLz5hyPQEKYPh3Ka1ZxLMwHC0oJMqlR2uiDMPwNmx9oy4WOr7rltWxQFhlh6+ -GYw+eiMDXJZEswseg1+q6SIRHoG40H8sawIHToVNS+9GjvJsfZwdkrILAZyLfrPe -HB22td89qE7Lckpj9HBACalaSR7RxMYEzfX2QyoytReJ0zciFPrqpr24dPM/E+aE -CEaxWbLGPWMJXCjWP380RxmOreoab5t7H7u2r0pjDnFy05TFGrpsyB6Qn59OrMUG -pthzJDimG9sz4PvMQpAn3udXYzmuom4xcCWhAxs3/vH7TB3x8d5ieqRojOUr2mBa -xgzZPearsSoNp0i0ME/7JUWw7EXKzfUDH8rDxjA2Rv4FaIPkgc9SitJq5YYvzpXF -0KGkn4d8/NQoLae6E7igkhn8cNhiCfuis1M3DcIMx5N2saD5Vg9pe3leWU9odbM3 -LycHb4ry2KWNLvsAEQEAAf4HAwKI71Gy9KByI/9psKvpCajf2McCBNAtgqbAg0fT -AldwinHdJIne18M7U/0sqLgbZlTFUfM4mGhNuFhhJxyX2JYGD/FQ8B6dj52lFjCR -Di4BM78HajEAldzP4+5I/ezmUjq+1Jvm8YcwhzaUjdQr2PJgww0MQoCcg6PZlm8S -J6FRhxnsmoMoJxxZUI0w05tMlsv+jii+ikz/PQkc6Ki57Oh23CbW8WY+LKV9r3VC -VS/PmYVzz+9zWnbmeaDLHD5ctspL+k2Lc1bipbJ29shS2xrXjrUKwwVgI75cQ2pX -FwIcDWSEl2h8oQKQ7CG3GmFzn6HCHd0XIU/yt3Gu5mr2szqb7a8MBnTk5l3vbbio -bWlU+WBIMrt9mDPgyxNiaC3AdPIilZN26Cxd+wNaVdX5MPkKfJGb/nJ6Em5BM0v7 -xJPjwoiJLeN+Uts4kb6M07zIILXqqMh2APYXzRraie/8YXwg9FTsIUpSuw6teupz -A9HqwFMhzCXqhfQ/oJPI7QhvwZCV/h3B3VCi6f5XUCZJF8A++zGlpSL3v0lZX0yQ -ZNYGRIMIYSIlOCLDvD/mtI+NFonL4s9uG1PkSoFiJav+uRTKS4e0daqDV/2oauLH -ZbcUKFdd68ZmfzmyViaLLLv1ATt3XkJknjns2nntFujGKEdajkzbU1reOb9Jlerg -igywiVzCvu2QzkB8C9C1sO0mMfgqMvDpWDxMGU7ILWAeR4H/n/Kg5vJ/piZ8K8qp -ffllgJ/ooVAaChGxXhUXcK5Jg68GRHyo2iFrh4qH6I5zvH/GtgtZGlaMEmVI9GRw -WOyqMvmMcVO7lDAeXqiP4YstahsGmmlEE6Uf+yBgaWR2TuB4ZiV+dhZHp1P46YBq -CZFFa2dgcJmSaMRn1LF0NDKCKxypjdsxaPeaXiGLs07cmMrNkJD6HHoLuAzKi0wW -dUQjglE86xEIMjRrf+qbE0tjoiJJ26qMlzEj+39TajRiz87ptND2SCFwLz6Pyo9x -gqEBCVq530IHjQLD+692vx+L6BRfSKmb4zUYgG8fTyh7B0CfBeNTBC82iyI4a9m4 -ug32/Lb454WHoASOa4tSjjunHnL+PjqfhDHr8wuq9Z4lT47il9dfye/Ffwg8GISb -4qCAhiVpNxN2WcRPR2o4sN75riXLBJliK/6XOBNGyihF2KT7CJIeiFJeAfk+cSOx -8ptQ3V2GRV9L7M4qiwatnv7N+LXENAod2SK1tKb0+lyYDwg9PE32DoWJWBynB+G+ -vPkOhPzHxpeSvg0rWf5rqnZb7RAbvyjxB8oLdspzcB22aQPGegWxww9SU5zi6f/3 -BZIeRjhxFmjyrUnYyh9SHkrMNK8XxhqFtLQccnNhIHBncCBrZXkgPHRlc3RAZHVt -bXkuY29tPokB0QQTAQgAOxYhBETr+ebcZkfWHFVt4npoa1oQcJVZBQJj3LmtAhsD -BQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAAAoJEHpoa1oQcJVZWQUMAJZK58S4 -7bZT/e+WqKL6m/M+IpKzT4jKkZ4XIuYs/hM6vqu2az4OFJZKGmOA6bsYZ63AJEOf -06YjjKmJM9wrsxnw4G8S/0PHuYu93kOhPloGt6VEiEm3IVye+sDRMryuq9LQuMOa -BcqeS4+ICY9kGqdMh0Vip3cpgnmF5l4YFpWUrSaaq1jvoy5TLxX5+WNLjyYZ/vtN -+xgQRAS93E3sV8c4ezFpiZ9/rceC2UEQ4knQ5pLZiyJ9zLecgsJ30psisBoiA7pX -+fqg+7pP2V/e9cDP9xNeSGPgtfCUan5gg+Xun4X9ZPrz76zCcG6d0rTKVvz+6lZR -mEHcFpVS5wVeg3T3sVvZ+aCm79tof2H4h2dIOAiHqd9VAGPIdNxhg8dwGUDn6+9M -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 -+u1d9MHDKqlTocsb+y7fwF1kIFtNoUPpzSZgUg== -=Qo7R ------END PGP PRIVATE KEY BLOCK----- -'''; - -const dsaPublicKeyPacket = ''' -BGPcrdcRCACP3zFGGehAuVweMBiB/sxiDzVqdT0WKaWplttrjUtAtnKGZMizfFNdquS+MDjrwAjv -CQq+2F47P4tVFCx4yiZDiEhfdaShQ8Fpb3kaJ6tJHMOjL+kax8o9QfBvtbk0U8qIwVuivxPGQ63L -40oTkUEuEA5XA0JbABe9wVTMWFrtkxwsi00H2nAKRszdv3WmVOkGH7L/3RPz/aXPdgDWWWVv/lTi -uw6t/L2CvPdVf+r6suUMC/22UHbHp8ZwZopC8575bc6NL7FeuENcBzhbg+1P3LSPHEAeiqIcQ16j -Dvkc47ijyww10i0GyilkWNrUcSiZI5uYlW6rYrjbO3PzswtPAQCScTk+Hav+aiZVdMSvHDMffVq6 -mRdOLH2lIi2buQ4RqQf7BX2UyFm6FIlzdSOniQ6rGPmP4ywVYEfED66uRwFNqin7ZvgPN5K/Lf7W -5OZP0el6rEPzYJ8dqrJ6QvlIF5rmkP1DbOhiJxhBOaq78iF28ysgGiO/8z5ooLdHSjGpZZyuwXlN -hRJBPy1LVNCr58ZQqvTdUV0CJpMyctj4Oag7z7VIed/ArbAv6XFDvd1++JRz8NIEUDoywn7u1oC/ -5leM85/0VKfLciXDyjFfvACJbbh10ZkShfnyePid7PppdP45W+u8m35j7pH9nP2u4ovSYH6vw31j -7TmY8EssnBmJXdZxGafD4w7eZDowHKxpnDxOU7+yW9Tl3rMRtg1gOac9jAf+JeHRqbu6ZoYydv8s -itLxzPFG+XGbb8FdbxtkWu1TT0xIxXiNgBF4fu9qAljo85dS6xNIFlXZbNxJMrJL1Oe8O5NYjClk -8FlLFpzEol69GDD2Gdi00bXh3Iu0xVIPbynwYiC+UpnDVk9iqs/E8nrlz4RuG1UkbQS3dVu0LQdJ -5ThonHLD+lWip3Gv1IjJ2QwI/aY1BzTWffdTfQveVNIs2EwbN7KRVO+BydeVhJ8wABXif5H7VySy -MeXDcuzFHy3lmBqpJGUaZ2zkFAJ+hqNQAYF8aFPd/YMJAla6nizS9VSP9zhBZyy4/G9rX4dwQz0e -BtB1vw9Zrar8xzzHIINLWQ== -'''; - -const elgamalPublicSubkeyPacket = ''' -BGPcrdcQCACtBw+SPmoRzcoyIRNUZQucGtwDZlmHGhpda57vLwfmn1dudGTJ1f9Y60sGUf83B9h2 -UAdnu6m/sC2XOOAtfkLrdl5qLJ+iopMMzLbuRWCy52JGJ130O00U7FaeeXcKPoGyH3EK/RRxjdBq -4QkW/C0PJFD1nWY8/vN0FYJsnua3Acsd4jjKUh2pGkPQB71Li/Mp/3+g63nOp8vP9wrxg96fbtey -sAqPre9crvRWE0fZz7XKcDCLw38E8r+mWlcJ9pmfBqId757fkMK7dcFBpd/31iEFqBCOQwkoFSss -Q3jFDusEZuicFAuophtwqAcHD7PHeUqDsrujKz1E/pCH0XLfAAMFB/97gItyjbrhICY3iMyEMFV4 -JF7u6Of8prs7D5slzgOOq++CcGXY52P7OJ9I+LI1TPXj8yBQVx4QuX4mh/6JsJhV2aVsxj/ujjof -kSkMCY3vm6YzNi0JZiq53k+D6kpo68raSRuew855v09quqP4mXme7xk0bFfn/NeXNwHDZyvc6IBC -CXRKhU26of99LoWbftbo1m/5JzEhAz3NR2hZ0mXm4SSnF3iM5//HODcWvUtBpnLLYFj7q2jTCeck -E6ac1pTi50mRsChrmzVLZdgVg/G8NttGrBBZ82JSivbtydW1O7+mEhA52TM+NMzJxEESsoLd6fXV -GVpb7mkSUw4rj4e/ -'''; - -const dsaPublicKey = '''-----BEGIN PGP PUBLIC KEY BLOCK----- - -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 -yrq4mgOb5sN3qzRQkSSevjGwPDYaWw== -=Rts4 ------END PGP PUBLIC KEY BLOCK----- -'''; - -const dsaSecretKeyPacket = ''' -BGPcrdcRCACP3zFGGehAuVweMBiB/sxiDzVqdT0WKaWplttrjUtAtnKGZMizfFNdquS+MDjrwAjv -CQq+2F47P4tVFCx4yiZDiEhfdaShQ8Fpb3kaJ6tJHMOjL+kax8o9QfBvtbk0U8qIwVuivxPGQ63L -40oTkUEuEA5XA0JbABe9wVTMWFrtkxwsi00H2nAKRszdv3WmVOkGH7L/3RPz/aXPdgDWWWVv/lTi -uw6t/L2CvPdVf+r6suUMC/22UHbHp8ZwZopC8575bc6NL7FeuENcBzhbg+1P3LSPHEAeiqIcQ16j -Dvkc47ijyww10i0GyilkWNrUcSiZI5uYlW6rYrjbO3PzswtPAQCScTk+Hav+aiZVdMSvHDMffVq6 -mRdOLH2lIi2buQ4RqQf7BX2UyFm6FIlzdSOniQ6rGPmP4ywVYEfED66uRwFNqin7ZvgPN5K/Lf7W -5OZP0el6rEPzYJ8dqrJ6QvlIF5rmkP1DbOhiJxhBOaq78iF28ysgGiO/8z5ooLdHSjGpZZyuwXlN -hRJBPy1LVNCr58ZQqvTdUV0CJpMyctj4Oag7z7VIed/ArbAv6XFDvd1++JRz8NIEUDoywn7u1oC/ -5leM85/0VKfLciXDyjFfvACJbbh10ZkShfnyePid7PppdP45W+u8m35j7pH9nP2u4ovSYH6vw31j -7TmY8EssnBmJXdZxGafD4w7eZDowHKxpnDxOU7+yW9Tl3rMRtg1gOac9jAf+JeHRqbu6ZoYydv8s -itLxzPFG+XGbb8FdbxtkWu1TT0xIxXiNgBF4fu9qAljo85dS6xNIFlXZbNxJMrJL1Oe8O5NYjClk -8FlLFpzEol69GDD2Gdi00bXh3Iu0xVIPbynwYiC+UpnDVk9iqs/E8nrlz4RuG1UkbQS3dVu0LQdJ -5ThonHLD+lWip3Gv1IjJ2QwI/aY1BzTWffdTfQveVNIs2EwbN7KRVO+BydeVhJ8wABXif5H7VySy -MeXDcuzFHy3lmBqpJGUaZ2zkFAJ+hqNQAYF8aFPd/YMJAla6nizS9VSP9zhBZyy4/G9rX4dwQz0e -BtB1vw9Zrar8xzzHIINLWf4HAwJ4wOH3neEBpP9iSUVLz8CprwNq+unWJaPlbj29s7V8Q0PocqB5 -cva6eygqD2Oc8iOTQ03jZCKAAvdmZi1h9CB3sGDG755si/IDdta7kI5b -'''; - -const elgamalSecretKeyPacket = ''' -BGPcrdcQCACtBw+SPmoRzcoyIRNUZQucGtwDZlmHGhpda57vLwfmn1dudGTJ1f9Y60sGUf83B9h2 -UAdnu6m/sC2XOOAtfkLrdl5qLJ+iopMMzLbuRWCy52JGJ130O00U7FaeeXcKPoGyH3EK/RRxjdBq -4QkW/C0PJFD1nWY8/vN0FYJsnua3Acsd4jjKUh2pGkPQB71Li/Mp/3+g63nOp8vP9wrxg96fbtey -sAqPre9crvRWE0fZz7XKcDCLw38E8r+mWlcJ9pmfBqId757fkMK7dcFBpd/31iEFqBCOQwkoFSss -Q3jFDusEZuicFAuophtwqAcHD7PHeUqDsrujKz1E/pCH0XLfAAMFB/97gItyjbrhICY3iMyEMFV4 -JF7u6Of8prs7D5slzgOOq++CcGXY52P7OJ9I+LI1TPXj8yBQVx4QuX4mh/6JsJhV2aVsxj/ujjof -kSkMCY3vm6YzNi0JZiq53k+D6kpo68raSRuew855v09quqP4mXme7xk0bFfn/NeXNwHDZyvc6IBC -CXRKhU26of99LoWbftbo1m/5JzEhAz3NR2hZ0mXm4SSnF3iM5//HODcWvUtBpnLLYFj7q2jTCeck -E6ac1pTi50mRsChrmzVLZdgVg/G8NttGrBBZ82JSivbtydW1O7+mEhA52TM+NMzJxEESsoLd6fXV -GVpb7mkSUw4rj4e//gcDAux7i0fK3ufZ//hX8kQR7EhnTAyflG9Gumil1u4N8bLmlzwoDXGk+1Io -CLY8rJrxmlPWUxM1ZUsAHvVYD9+4R3QCsPVQNYaAwzX0MhBoeC6AleOzZhUBQB7Mmw== -'''; - -const dsaPrivateKey = '''-----BEGIN PGP PRIVATE KEY BLOCK----- - -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 -vjGwPDYaWw== -=gdau ------END PGP PRIVATE KEY BLOCK----- -'''; - -const ecdsaPublicKeyPacket = ''' -BGPaCO4TBSuBBAAiAwMEaCwglSTnZUUOj8V9uoKtK2vwUDpBB+05c03zYhpCSgAFneAlMgXjEqRt -wMYJqmaqEAua2urwtf3PSfL1OUCrhpihucGMsNZNEbg9E+4QMQa7hKYf2mhNKGvTyGT5/3y0 -'''; - -const ecdhPublicSubkeyPacket = ''' -BGPaCO4SBSuBBAAiAwMEddfhchBcGf7wiC31hNqs4zsHGhZ//746qM/KdCPStq3ts/R9j5QUUYWQ -lrvw1tOaCKFuTrCsxVq6yE09MXb/8K5ihc6veMpjgnITAiG9XtwIjPi4rCUdxKxKF70KyRFnAwEJ -CQ== -'''; - -const eccPublicKey = '''-----BEGIN PGP PUBLIC KEY BLOCK----- - -mG8EY9oI7hMFK4EEACIDAwRoLCCVJOdlRQ6PxX26gq0ra/BQOkEH7TlzTfNiGkJK -AAWd4CUyBeMSpG3AxgmqZqoQC5ra6vC1/c9J8vU5QKuGmKG5wYyw1k0RuD0T7hAx -BruEph/aaE0oa9PIZPn/fLS0HGVjYyBwZ3Aga2V5IDx0ZXN0QGR1bW15LmNvbT6I -swQTEwkAOxYhBC2Erhd8G+0IfLmQPN7vzHZuIq7fBQJj2gjuAhsDBQsJCAcCAiIC -BhUKCQgLAgQWAgMBAh4HAheAAAoJEN7vzHZuIq7fVUIBf3UbGkLqYDEEIuuSGn6x -a5ZZ3E5v052aRYmfZwIKg9i6915DT7YfbeTi8rlguBAD7gGAjmTR8m0w0NYa/9Dm -kSDrseXp68EbDQ2ZGdCrQSlvea7ifG1hCK6RPaAEWV1JpFXMuHMEY9oI7hIFK4EE -ACIDAwR11+FyEFwZ/vCILfWE2qzjOwcaFn//vjqoz8p0I9K2re2z9H2PlBRRhZCW -u/DW05oIoW5OsKzFWrrITT0xdv/wrmKFzq94ymOCchMCIb1e3AiM+LisJR3ErEoX -vQrJEWcDAQkJiJgEGBMJACAWIQQthK4XfBvtCHy5kDze78x2biKu3wUCY9oI7gIb -DAAKCRDe78x2biKu30/OAYDekhwLCs2P3dZdOcWVeiDFAe6DB9EebUA7E+o2uUnj -+GqFelOcwUZ7fja3Y2dsl1wBfiV99aDsKdzfjaTCAOzEjKftKlasiHmG+he0Wtrq -keqzoAN0xcrpBBpl+pHaKFXeQQ== -=SBDa ------END PGP PUBLIC KEY BLOCK----- -'''; - -const ecdsaSecretKeyPacket = ''' -BGPaCO4TBSuBBAAiAwMEaCwglSTnZUUOj8V9uoKtK2vwUDpBB+05c03zYhpCSgAFneAlMgXjEqRt -wMYJqmaqEAua2urwtf3PSfL1OUCrhpihucGMsNZNEbg9E+4QMQa7hKYf2mhNKGvTyGT5/3y0/gcD -Ap94vYLzodEc/7ekZAEQhyr57DPyYFxzu8NtFd3aHp82+VOyn7L+5sm42pHrZzH1qHlz7ZW4De8+ -69rHxFV01KdA1OuXLLgywYo7z4qsmf4g7J40VJ7w/+1wogw6olum -'''; - -const ecdhSecretKeyPacket = ''' -BGPaCO4SBSuBBAAiAwMEddfhchBcGf7wiC31hNqs4zsHGhZ//746qM/KdCPStq3ts/R9j5QUUYWQ -lrvw1tOaCKFuTrCsxVq6yE09MXb/8K5ihc6veMpjgnITAiG9XtwIjPi4rCUdxKxKF70KyRFnAwEJ -Cf4HAwIdlMbeADzxG/+b/ExzmBtHRbcbfnG/lalx6516vVccJHkDkcxUHCufBATfzLvJ62FdWItU -w+3jIYOekJyerqJoSf80zYieR+4cFEEVFKG62+PdVhjMLirmLwbNN59pag== -'''; - -const eccPrivateKey = '''-----BEGIN PGP PRIVATE KEY BLOCK----- - -lNIEY9oI7hMFK4EEACIDAwRoLCCVJOdlRQ6PxX26gq0ra/BQOkEH7TlzTfNiGkJK -AAWd4CUyBeMSpG3AxgmqZqoQC5ra6vC1/c9J8vU5QKuGmKG5wYyw1k0RuD0T7hAx -BruEph/aaE0oa9PIZPn/fLT+BwMCn3i9gvOh0Rz/t6RkARCHKvnsM/JgXHO7w20V -3doenzb5U7Kfsv7mybjaketnMfWoeXPtlbgN7z7r2sfEVXTUp0DU65csuDLBijvP -iqyZ/iDsnjRUnvD/7XCiDDqiW6a0HGVjYyBwZ3Aga2V5IDx0ZXN0QGR1bW15LmNv -bT6IswQTEwkAOxYhBC2Erhd8G+0IfLmQPN7vzHZuIq7fBQJj2gjuAhsDBQsJCAcC -AiICBhUKCQgLAgQWAgMBAh4HAheAAAoJEN7vzHZuIq7fVUIBf3UbGkLqYDEEIuuS -Gn6xa5ZZ3E5v052aRYmfZwIKg9i6915DT7YfbeTi8rlguBAD7gGAjmTR8m0w0NYa -/9DmkSDrseXp68EbDQ2ZGdCrQSlvea7ifG1hCK6RPaAEWV1JpFXMnNYEY9oI7hIF -K4EEACIDAwR11+FyEFwZ/vCILfWE2qzjOwcaFn//vjqoz8p0I9K2re2z9H2PlBRR -hZCWu/DW05oIoW5OsKzFWrrITT0xdv/wrmKFzq94ymOCchMCIb1e3AiM+LisJR3E -rEoXvQrJEWcDAQkJ/gcDAh2Uxt4APPEb/5v8THOYG0dFtxt+cb+VqXHrnXq9Vxwk -eQORzFQcK58EBN/Mu8nrYV1Yi1TD7eMhg56QnJ6uomhJ/zTNiJ5H7hwUQRUUobrb -491WGMwuKuYvBs03n2lqiJgEGBMJACAWIQQthK4XfBvtCHy5kDze78x2biKu3wUC -Y9oI7gIbDAAKCRDe78x2biKu30/OAYDekhwLCs2P3dZdOcWVeiDFAe6DB9EebUA7 -E+o2uUnj+GqFelOcwUZ7fja3Y2dsl1wBfiV99aDsKdzfjaTCAOzEjKftKlasiHmG -+he0WtrqkeqzoAN0xcrpBBpl+pHaKFXeQQ== -=dVVi ------END PGP PRIVATE KEY BLOCK----- -'''; - -const curve25519PublicKeyPacket = ''' -BGPaCaoWCSsGAQQB2kcPAQEHQHKwCPnd4DNSVMyyreIK8mdXeYSL4ftctnT8HqyLvJUd -'''; - -const curve25519PublicSubkeyPacket = ''' -BGPaCaoSCisGAQQBl1UBBQEBB0C75cltt8fQBQ29kOTo1j/4NELxvDyiftR8uAWgsY8ycgMBCAc= -'''; - -const curve25519PublicKey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -mDMEY9oJqhYJKwYBBAHaRw8BAQdAcrAI+d3gM1JUzLKt4gryZ1d5hIvh+1y2dPwe -rIu8lR20JGN1cnZlIDI1NTE5IHBncCBrZXkgPHRlc3RAZHVtbXkuY29tPoiTBBMW -CgA7FiEEZyh8xjdnRuaD/SRnVlTlVNcvz0cFAmPaCaoCGwMFCwkIBwICIgIGFQoJ -CAsCBBYCAwECHgcCF4AACgkQVlTlVNcvz0cdrgD+MyEQdojCKeOtsxT5gKhewd1l -OhxyJKkjxMD3NderkRoBAND1d+suwA94LwgzGDa5uz4x2ZwpFXdiis5j+Na2+asK -uDgEY9oJqhIKKwYBBAGXVQEFAQEHQLvlyW23x9AFDb2Q5OjWP/g0QvG8PKJ+1Hy4 -BaCxjzJyAwEIB4h4BBgWCgAgFiEEZyh8xjdnRuaD/SRnVlTlVNcvz0cFAmPaCaoC -GwwACgkQVlTlVNcvz0dKFwD/XoNSHoehWu373DzAiGodPtFgCnh1Zu+daJOS/CUo -fjwA/R6+n8bBdnWpWW5eTpuJv8/VP5avUVf5dj0llCyLzWoL -=ApRX ------END PGP PUBLIC KEY BLOCK----- -'''; - -const curve25519SecretKeyPacket = ''' -BGPaCaoWCSsGAQQB2kcPAQEHQHKwCPnd4DNSVMyyreIK8mdXeYSL4ftctnT8HqyLvJUd/gcDAhky -iNQzaq4l/8e4wZnyha5lUs804vMECQ0WLakYmDx/KJ2No/H/PXHZBNwhA9yoOpFHvzojgj5vob/K -T4aLEoXiFr4h5ElYRvoB322qoiU= -'''; - -const curve25519SecretSubkeyPacket = ''' -BGPaCaoSCisGAQQBl1UBBQEBB0C75cltt8fQBQ29kOTo1j/4NELxvDyiftR8uAWgsY8ycgMBCAf+ -BwMC9SNt+Vthssv/5XaZE5a6HaeTC9nYCrJEAtQTJdqwe9dKkb69D+OM641M2vduimckQBCWZKoq -2dvc11YWlXwkvwMZGsdiHtZV9UL01t5GVA== -'''; - -const curve25519PrivateKey = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- - -lIYEY9oJqhYJKwYBBAHaRw8BAQdAcrAI+d3gM1JUzLKt4gryZ1d5hIvh+1y2dPwe -rIu8lR3+BwMCGTKI1DNqriX/x7jBmfKFrmVSzzTi8wQJDRYtqRiYPH8onY2j8f89 -cdkE3CED3Kg6kUe/OiOCPm+hv8pPhosSheIWviHkSVhG+gHfbaqiJbQkY3VydmUg -MjU1MTkgcGdwIGtleSA8dGVzdEBkdW1teS5jb20+iJMEExYKADsWIQRnKHzGN2dG -5oP9JGdWVOVU1y/PRwUCY9oJqgIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIX -gAAKCRBWVOVU1y/PRx2uAP4zIRB2iMIp462zFPmAqF7B3WU6HHIkqSPEwPc116uR -GgEA0PV36y7AD3gvCDMYNrm7PjHZnCkVd2KKzmP41rb5qwqciwRj2gmqEgorBgEE -AZdVAQUBAQdAu+XJbbfH0AUNvZDk6NY/+DRC8bw8on7UfLgFoLGPMnIDAQgH/gcD -AvUjbflbYbLL/+V2mROWuh2nkwvZ2AqyRALUEyXasHvXSpG+vQ/jjOuNTNr3bopn -JEAQlmSqKtnb3NdWFpV8JL8DGRrHYh7WVfVC9NbeRlSIeAQYFgoAIBYhBGcofMY3 -Z0bmg/0kZ1ZU5VTXL89HBQJj2gmqAhsMAAoJEFZU5VTXL89HShcA/16DUh6HoVrt -+9w8wIhqHT7RYAp4dWbvnWiTkvwlKH48AP0evp/GwXZ1qVluXk6bib/P1T+Wr1FX -+XY9JZQsi81qCw== -=qDmh ------END PGP PRIVATE KEY BLOCK----- -'''; - -const secretKeyPacket = ''' -BGPc3wYBCADPiGAZfRj0a7kmYHrVZwetSscgDIm+kcYPDRBBclWji2qFshEJCnzdf85pQkEqSLr3 -T1w2R7WZ56t6x4JZBduyVAyoTLHJJD6FWaAjL1zv/lVq9aVumII2sC6SCIcoaNMMGvEDbaCjgJRC -pI5v8j/o/dFE3mZ1kvn54Zplmw6khybnFGU2NrieKaO0wwp43vFHCc+P0Bt0shOUhKQ0y+dOiVOy -JppwGRauZhljQMLx5CeqhfRhnT0xFF5VnccA/OwmAANuXYr0j1H70bdiRwDPS4ZOQt0xVNL3L+Ph -Yk+PjJi7w1IxwDQOoRKsiidSVm+C9ifi7QpFY83CzpDObqiZABEBAAEAB/0XHw0B2fedR6Elnusg -em2XB3U+41a3VhtYUu7EM95Jrb8s7QVL2hDGRrZy8LA860R1SDCwVXXskyx+LdseWvI6oHWWjJDk -JXcXuUAHm3/BtzbCB2MqerBLMn7Fw1B3lpIoz5mZda3HeYxLDnVWEInBRgH6J8MHh1v1VIa0mj1b -5AVAUesTnbY30RkL2oAHiUom+Z6ZAUS+7CzL/pPEbFVoclT3aEmEH9Y41aGCPLHKSUvffZ+Nxacg -mgjMrMun7HrudQj4Z70QvsnBCkA4OZ9V1IdYH+MOFV9rorRedGJ3NjW0WK/oTffRMZQ6k79SsY2K -0/rgOa7Hm1TXbb88telHBAD9gHxFhP7gLpyBMTpio5S0hM5GcXI0wQUjFl5TkLcC21bKu/wIHSLq -CwQYwVXJlw0qbenhvb9C++G5VYtscvPz+NsK/zSMrRYKADS/EJtNECSPkgM0U14TD4EvwyPvS7oi -tGPJMWlwoiokyIvOF7f6ZHOQxm68XRPOnaCC1Ph+OwQA0ZPsK1lTsJ9mHcy9esK2vtOqgIY0m5OL -bkBtvkKSl9BP0HFzTxRh41YqXZ6h7EKXhc7VOJ16jA3IhHk9fCvA43US5h3l9QYJeIOCszJQXYhb -LnW7eOd8TEXIVU6Mmb6ZwJopO93lRAXYBEWR6FDwmQssTBumXJaHM9VOo2O+ozsD/26vPilF+znF -4DZuF3A6Y42c1oTO84D/N+T0YKJ784GdCRQuj+7vXoWeO7CzN+sK9sdgyclZgFfKGRDwu5WlYKFQ -aFnqSO6V9veESC8F6+OpuDXB80tsgh9dicMLVuJMC9hOKPrbC5ITQF0RiggZgaDh2c3KGWEfIFGh -t/ibT4MDPg8= -'''; - -const secretSubkeyPacket = ''' -BGPc3wYBCADhqaXRrGOxkv2Dc0r7uzcxuI7cEAYn24bu1EYh4DAmxRC5ucP/tguhnx5BpVyXeBMf -kDDfqlE0U7oIl1e2OGU/Hn8g8MBKRqXnqqb/Qi8oBzY+NP7YgzxqYTB2yYhSnFuX2wUsyTiaVejh -atK/wNmRU4j9f0AMJoE2qI8DPGiYs8cyos1O9o6ewJOgI8+WshL2w35HlXNtVyI9Hs5FbOCUZOgq -tgH53n+xAVoA4hKWFLkckyW3c5S1boHOZoepmkk8jjgcacLIt+gSc4V/1c5uNiwMePz149m7pZqj -dwTFF6ttglVgh8/dWJRO2h7625MJX7BNBCQfTN0St2CyC3ZdABEBAAEAB/0f5Li9YhZy/fnbjrtt -yZ965QE35AHitzmiXpkM56EWkaqT/UfQx7tsG8+MaqEj/UICp3/o1KsGcMDtvuoUJHNW0OoZzzz2 -IncHP4y7rfB5QU+uyhGsqxuqU1MkrsYYGuYtF7TrCAN6eKLh3RWtKUX2rq5BWa9FKFoBzh/sTqjf -z+DchCAWUP3U4rBaGRBAoX4REQ5qmzQFzGt5QKBP7oiXFgOGOhNbmkam5X36uVxUU9+NP3ks0Ps6 -IfBiOI9nm2cggyV/6JgbfLMFa3MXs3aduX+m0Onh5yUCwDp8c7BPSkZ54BmD+3C/Rq7D+HX+32C0 -IFLAT/vGINoeFualOi1RBAD6EkCQXrU8+sfPrmJC81KEPj7hdOlAg6DvbX5cOGZbHMkziyCrpvQ7 -nAwHP4+Y4YNh92eWoqOb6+HH7bZAkO57K/9SuScFEsCmH3ZOvldazLXnl8k1Flp+jOkaap/JFHuA -XxVVD9ax/8vp2Pp6jI5hEmAR7peOEa869AJ1ZvAIKQQA5wNA2RxH5IqniyrU32jbLaO3fsaMyMuo -GGztcbCigu9cuoR7MGKmxCAexTacHcLg+EJstF8H4gmuCyvhVRAbyfn/bNd2pTiTe6ebPFWpVmz/ -tg3wegHc04lbFUG4vajh/TNAc4X3SLZxdGxcGT+08oVc36ypamxJkIQVZeS50xUD/jykyyW6hk50 -d1nKc1hV08CpfTM8O/A1ulDLd1/OCzDzEbueItYNNyIUtYbA2jilfxJyu31fH4CZ5f2gzdnTkVDF -e9isvP0r6W+JHgLEkj5tElwqFXaVvDOR0YBzUTGOzFfXgWP8itLIjs+07VHpFaelgWLYkQflG3tv -k05ZIy1dRqM= -'''; - -const privateKey = '''-----BEGIN PGP PRIVATE KEY BLOCK----- - -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 -iTQl -=KyHf ------END PGP PRIVATE KEY BLOCK----- -'''; - -const publicKey = '''-----BEGIN PGP PUBLIC KEY BLOCK----- - -xsBNBGPc3wYBCADPiGAZfRj0a7kmYHrVZwetSscgDIm+kcYPDRBBclWji2qF -shEJCnzdf85pQkEqSLr3T1w2R7WZ56t6x4JZBduyVAyoTLHJJD6FWaAjL1zv -/lVq9aVumII2sC6SCIcoaNMMGvEDbaCjgJRCpI5v8j/o/dFE3mZ1kvn54Zpl -mw6khybnFGU2NrieKaO0wwp43vFHCc+P0Bt0shOUhKQ0y+dOiVOyJppwGRau -ZhljQMLx5CeqhfRhnT0xFF5VnccA/OwmAANuXYr0j1H70bdiRwDPS4ZOQt0x -VNL3L+PhYk+PjJi7w1IxwDQOoRKsiidSVm+C9ifi7QpFY83CzpDObqiZABEB -AAHNHHJzYSBwZ3Aga2V5IDx0ZXN0QGR1bW15LmNvbT7CwHUEEAEIACkFAmPc -3wYGCwkHCAMCCRAiE96TkTdJUAQVCAoCAxYCAQIZAQIbAwIeAQAAsIwIAKmg -HxdnroWM/ccxOJi4v7n9Ls8VcC1IlRwof/u9Ax6n14ThPK6PDC8Ur3Fw2BF3 -Moisa1Qzq8hCynBb/rkGWb5vWLfMtkYfPahgvhsmg6TXG/W1LBkpyTOFii+1 -Pci0o2bg3HKU+8GV++R9vfQQOnCVYx+mJc37Q6F4HSz6wX+/rAPY/30X++lt -w/3vgkQZI0b0NHUgjdJwzDHRixmsUtjXRWxcZ4cWsG+93VtjD7oqwJB4Yp2r -JhQP4gTR7yc1OXPAJ6T40J+iDa5zZi4rc5/qTrKJrEXFbeYjeml57r2huX03 -t0vEW2+6lobu/4KSd9Uni+dn65HEnl8xGlnG6VXOwE0EY9zfBgEIAOGppdGs -Y7GS/YNzSvu7NzG4jtwQBifbhu7URiHgMCbFELm5w/+2C6GfHkGlXJd4Ex+Q -MN+qUTRTugiXV7Y4ZT8efyDwwEpGpeeqpv9CLygHNj40/tiDPGphMHbJiFKc -W5fbBSzJOJpV6OFq0r/A2ZFTiP1/QAwmgTaojwM8aJizxzKizU72jp7Ak6Aj -z5ayEvbDfkeVc21XIj0ezkVs4JRk6Cq2Afnef7EBWgDiEpYUuRyTJbdzlLVu -gc5mh6maSTyOOBxpwsi36BJzhX/Vzm42LAx4/PXj2bulmqN3BMUXq22CVWCH -z91YlE7aHvrbkwlfsE0EJB9M3RK3YLILdl0AEQEAAcLAXwQYAQgAEwUCY9zf -BgkQIhPek5E3SVACGwwAAOGHCAC7AgulhsAZId2BctmcF6U/XOM0l/D0in0W -7m3RQsftoNTSRVuD1YrAD6RPxVjSA9JGPIuY7CdjL9rfeX7VTyJetJFgYg2U -Wsq5x9BxaROITygVtngrhzP2eW2iApZXuspwJDwE8oyye0NHwnxtLfJ5YXKE -S0sUNhOio/W7Z+c+nq6emzbtrjiwgwFc2FU+AnQL59Ni99i0EUKAIHx5Xjbd -GmaUoeHZr6OQoVnaBGBjO9+/a6N97pe4fbegYiP9j8N0FpolaODkvQRmfYmA -+3ZZ+8xT7uivGp4Dgqbz4Q20p7ppHWTuX+3S3TtLlAxI0k1h/gDo839hTUIs -c5THiTQl -=wB2d ------END PGP PUBLIC KEY BLOCK----- -'''; diff --git a/test/data/s2k_data.dart b/test/data/s2k_data.dart deleted file mode 100644 index 3445eee8..00000000 --- a/test/data/s2k_data.dart +++ /dev/null @@ -1,10 +0,0 @@ -const mode0Password1234 = 'jAQECQAC'; -const mode1Password123456 = 'jAwECQECqEKnqVn6Qio='; -const mode1PasswordFoobar = 'jAwECQECvJVYRYE8fDc='; -const mode3Aes128Password13Times0123456789 = 'jA0EBwMCBuRhXKRI+d3u'; -const mode3Aes192Password123 = 'jA0ECAMCj4F0xdlhx3nu'; -const mode3EncryptedKeyPasswordBgtyhn = - 'jC4EBwMCglmgbpjalBzuCM5Wxz59Z2/MekL482qgQu4ZSP0MmZ9OiPAOHkSr7OjG'; -const mode3Password9876 = 'jA0ECQMCuWfqllPbasgr'; -const mode3PasswordQwerty = 'jA0ECQMCeEXwW1X3tJ7x'; -const mode3TwofishPassword13Times0123456789 = 'jA0ECgMCUe38FUVAZazu'; diff --git a/test/packet/compression_test.dart b/test/packet/compression_test.dart deleted file mode 100644 index 40ce311d..00000000 --- a/test/packet/compression_test.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:dart_pg/src/enum/compression_algorithm.dart'; -import 'package:dart_pg/src/packet/compressed_data.dart'; -import 'package:dart_pg/src/packet/literal_data.dart'; -import 'package:dart_pg/src/packet/packet_list.dart'; -import 'package:faker/faker.dart'; -import 'package:test/test.dart'; - -void main() { - group('Compression', () { - final literalData = - LiteralDataPacket.fromText(faker.randomGenerator.string(10000)); - - test('zip test', () { - final compressedPacket = CompressedDataPacket.fromPacketList( - PacketList([literalData]), - algorithm: CompressionAlgorithm.zip, - ); - - final compressedData = compressedPacket.toByteData(); - final decompressedPacket = - CompressedDataPacket.fromByteData(compressedData); - - expect(compressedPacket.algorithm, equals(decompressedPacket.algorithm)); - expect( - compressedPacket.compressed, equals(decompressedPacket.compressed)); - expect(compressedPacket.packets[0].toByteData(), - equals(literalData.toByteData())); - }); - - test('zlib test', () { - final compressedPacket = CompressedDataPacket.fromPacketList( - PacketList([literalData]), - algorithm: CompressionAlgorithm.zlib, - ); - - final compressedData = compressedPacket.toByteData(); - final decompressedPacket = - CompressedDataPacket.fromByteData(compressedData); - - expect(compressedPacket.algorithm, equals(decompressedPacket.algorithm)); - expect( - compressedPacket.compressed, equals(decompressedPacket.compressed)); - expect(compressedPacket.packets[0].toByteData(), - equals(literalData.toByteData())); - }); - - test('bzip2 test', () { - expect( - () => CompressedDataPacket.fromPacketList( - PacketList([literalData]), - algorithm: CompressionAlgorithm.bzip2, - ), - throwsUnsupportedError, - ); - }); - }); -} diff --git a/test/packet/encryption_test.dart b/test/packet/encryption_test.dart deleted file mode 100644 index 8a590ad7..00000000 --- a/test/packet/encryption_test.dart +++ /dev/null @@ -1,304 +0,0 @@ -import 'dart:convert'; - -import 'package:dart_pg/src/enum/aead_algorithm.dart'; -import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; -import 'package:dart_pg/src/helpers.dart'; -import 'package:dart_pg/src/packet/aead_encrypted_data.dart'; -import 'package:dart_pg/src/packet/key/session_key.dart'; -import 'package:dart_pg/src/packet/key_packet.dart'; -import 'package:dart_pg/src/packet/literal_data.dart'; -import 'package:dart_pg/src/packet/packet_list.dart'; -import 'package:dart_pg/src/packet/public_key_encrypted_session_key.dart'; -import 'package:dart_pg/src/packet/sym_encrypted_data.dart'; -import 'package:dart_pg/src/packet/sym_encrypted_integrity_protected_data.dart'; -import 'package:dart_pg/src/packet/sym_encrypted_session_key.dart'; -import 'package:faker/faker.dart'; -import 'package:test/test.dart'; - -import '../data/key_data.dart'; - -void main() { - group('symmetrically', () { - final literalData = - LiteralDataPacket.fromText(faker.randomGenerator.string(10000)); - final packets = PacketList([literalData]); - final key = Helper.generateEncryptionKey(); // encryption key - final kek = faker.randomGenerator.string(10); // key encryption key - - test('encrypted data test', () async { - final encrypted = - await SymEncryptedDataPacket.encryptPackets(key, packets); - final encrypt = - SymEncryptedDataPacket.fromByteData(encrypted.toByteData()); - final decrypted = - await encrypt.decrypt(key, allowUnauthenticatedMessages: true); - final ldPacket = decrypted.packets![0]; - expect(ldPacket.toByteData(), equals(literalData.toByteData())); - - expect( - () => encrypt.decrypt(key), - throwsStateError, - ); - }); - - test('encrypted integrity protected test', () async { - final encrypted = - await SymEncryptedIntegrityProtectedDataPacket.encryptPackets( - key, packets); - final decrypted = - await SymEncryptedIntegrityProtectedDataPacket.fromByteData( - encrypted.toByteData()) - .decrypt(key); - final ldPacket = decrypted.packets![0]; - expect(ldPacket.toByteData(), equals(literalData.toByteData())); - }); - - test('password protected session key test', () async { - final skesk = await SymEncryptedSessionKeyPacket.encryptSessionKey(kek, - sessionKey: SessionKey.produceKey()); - final seip = - await SymEncryptedIntegrityProtectedDataPacket.encryptPackets( - skesk.sessionKey!.key, - packets, - symmetric: skesk.sessionKey!.symmetric, - ); - - final encryptedList = PacketList([skesk, seip]); - final packetList = PacketList.packetDecode(encryptedList.encode()); - - final decryptedSkesk = await packetList - .whereType() - .elementAt(0) - .decrypt(kek); - expect(skesk.sessionKey!.symmetric, - equals(decryptedSkesk.sessionKey!.symmetric)); - expect(skesk.sessionKey!.key, equals(decryptedSkesk.sessionKey!.key)); - - final decryptedSeip = await packetList - .whereType() - .elementAt(0) - .decrypt( - decryptedSkesk.sessionKey!.key, - symmetric: decryptedSkesk.sessionKey!.symmetric, - ); - final ldPacket = decryptedSeip.packets![0]; - expect(ldPacket.toByteData(), equals(literalData.toByteData())); - }); - }); - - group('public key protected', () { - final literalData = - LiteralDataPacket.fromText(faker.randomGenerator.string(100)); - final packets = PacketList([literalData]); - - test('rsa test', () async { - final secretKey = await SecretSubkeyPacket.fromByteData( - base64.decode(rsaSecretKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - - final pkesk = await PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( - secretKey.publicKey, SessionKey.produceKey()); - final seip = - await SymEncryptedIntegrityProtectedDataPacket.encryptPackets( - pkesk.sessionKey!.key, - packets, - symmetric: pkesk.sessionKey!.symmetric, - ); - - final encryptedList = PacketList([pkesk, seip]); - final packetList = PacketList.packetDecode(encryptedList.encode()); - - final decryptedPkesk = await packetList - .whereType() - .elementAt(0) - .decrypt(secretKey); - expect(pkesk.sessionKey!.symmetric, - equals(decryptedPkesk.sessionKey!.symmetric)); - expect(pkesk.sessionKey!.key, equals(decryptedPkesk.sessionKey!.key)); - - final decryptedSeip = await packetList - .whereType() - .elementAt(0) - .decrypt( - decryptedPkesk.sessionKey!.key, - symmetric: decryptedPkesk.sessionKey!.symmetric, - ); - final ldPacket = decryptedSeip.packets![0]; - expect(ldPacket.toByteData(), equals(literalData.toByteData())); - }); - - test('elgamal test', () async { - final secretKey = await SecretSubkeyPacket.fromByteData( - base64.decode(elgamalSecretKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - - final pkesk = await PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( - secretKey.publicKey, SessionKey.produceKey()); - final seip = - await SymEncryptedIntegrityProtectedDataPacket.encryptPackets( - pkesk.sessionKey!.key, - packets, - symmetric: pkesk.sessionKey!.symmetric, - ); - - final encryptedList = PacketList([pkesk, seip]); - final packetList = PacketList.packetDecode(encryptedList.encode()); - - final decryptedPkesk = await packetList - .whereType() - .elementAt(0) - .decrypt(secretKey); - expect(pkesk.sessionKey!.symmetric, - equals(decryptedPkesk.sessionKey!.symmetric)); - expect(pkesk.sessionKey!.key, equals(decryptedPkesk.sessionKey!.key)); - - final decryptedSeip = await packetList - .whereType() - .elementAt(0) - .decrypt( - decryptedPkesk.sessionKey!.key, - symmetric: decryptedPkesk.sessionKey!.symmetric, - ); - final ldPacket = decryptedSeip.packets![0]; - expect(ldPacket.toByteData(), equals(literalData.toByteData())); - }); - - test('ecdh test', () async { - final secretKey = await SecretSubkeyPacket.fromByteData( - base64.decode(ecdhSecretKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - final publicKey = PublicSubkeyPacket.fromByteData( - base64.decode(ecdhPublicSubkeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ); - - final pkesk = await PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( - publicKey, SessionKey.produceKey()); - final seip = - await SymEncryptedIntegrityProtectedDataPacket.encryptPackets( - pkesk.sessionKey!.key, - packets, - symmetric: pkesk.sessionKey!.symmetric, - ); - - final encryptedList = PacketList([pkesk, seip]); - final packetList = PacketList.packetDecode(encryptedList.encode()); - - final decryptedPkesk = await packetList - .whereType() - .elementAt(0) - .decrypt(secretKey); - expect(pkesk.sessionKey!.symmetric, - equals(decryptedPkesk.sessionKey!.symmetric)); - expect(pkesk.sessionKey!.key, equals(decryptedPkesk.sessionKey!.key)); - - final decryptedSeip = await packetList - .whereType() - .elementAt(0) - .decrypt( - decryptedPkesk.sessionKey!.key, - symmetric: decryptedPkesk.sessionKey!.symmetric, - ); - final ldPacket = decryptedSeip.packets![0]; - expect(ldPacket.toByteData(), equals(literalData.toByteData())); - }); - - test('curve25519 test', () async { - final secretKey = await SecretSubkeyPacket.fromByteData( - base64.decode(curve25519SecretSubkeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - final publicKey = PublicSubkeyPacket.fromByteData( - base64.decode(curve25519PublicSubkeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ); - - final pkesk = await PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( - publicKey, SessionKey.produceKey()); - final seip = - await SymEncryptedIntegrityProtectedDataPacket.encryptPackets( - pkesk.sessionKey!.key, - packets, - symmetric: pkesk.sessionKey!.symmetric, - ); - - final encryptedList = PacketList([pkesk, seip]); - final packetList = PacketList.packetDecode(encryptedList.encode()); - - final decryptedPkesk = await packetList - .whereType() - .elementAt(0) - .decrypt(secretKey); - expect(pkesk.sessionKey!.symmetric, - equals(decryptedPkesk.sessionKey!.symmetric)); - expect(pkesk.sessionKey!.key, equals(decryptedPkesk.sessionKey!.key)); - - final decryptedSeip = await packetList - .whereType() - .elementAt(0) - .decrypt( - decryptedPkesk.sessionKey!.key, - symmetric: decryptedPkesk.sessionKey!.symmetric, - ); - final ldPacket = decryptedSeip.packets![0]; - expect(ldPacket.toByteData(), equals(literalData.toByteData())); - }); - }); - - group('aead encrypted data', () { - test('test eax decrypt', () async { - final key = '86f1efb86952329f24acd3bfd0e5346d'.hexToBytes(); - final iv = 'b732379f73c4928de25facfe6517ec10'.hexToBytes(); - final bytes = - '0107010eb732379f73c4928de25facfe6517ec105dc11a81dc0cb8a2f6f3d90016384a56fc821ae11ae8dbcb49862655dea88d06a81486801b0ff387bd2eab013de1259586906eab2476' - .hexToBytes(); - - final eax = await AeadEncryptedData.fromByteData(bytes).decrypt(key); - expect(eax.symmetric, SymmetricAlgorithm.aes128); - expect(eax.aead, AeadAlgorithm.eax); - expect(eax.chunkSize, 14); - expect(eax.iv, equals(iv)); - - final ldPacket = eax.packets![0] as LiteralDataPacket; - expect(utf8.decode(ldPacket.data), "Hello, world!\n"); - }); - - test('test ocb decrypt', () async { - final key = 'd1f01ba30e130aa7d2582c16e050ae44'.hexToBytes(); - final iv = '5ed2bc1e470abe8f1d644c7a6c8a56'.hexToBytes(); - final bytes = - '0107020e5ed2bc1e470abe8f1d644c7a6c8a567b0f7701196611a154ba9c2574cd056284a8ef68035c623d93cc708a43211bb6eaf2b27f7c18d571bcd83b20add3a08b73af15b9a098' - .hexToBytes(); - - final ocb = await AeadEncryptedData.fromByteData(bytes).decrypt(key); - expect(ocb.symmetric, SymmetricAlgorithm.aes128); - expect(ocb.aead, AeadAlgorithm.ocb); - expect(ocb.chunkSize, 14); - expect(ocb.iv, equals(iv)); - - final ldPacket = ocb.packets![0] as LiteralDataPacket; - expect(utf8.decode(ldPacket.data), "Hello, world!\n"); - }); - - test('test encrypt', () async { - final literalData = - LiteralDataPacket.fromText(faker.randomGenerator.string(10000)); - final packets = PacketList([literalData]); - final key = Helper.generateEncryptionKey(SymmetricAlgorithm.aes256); - - final encrypted = await AeadEncryptedData.encryptPackets(key, packets); - expect(encrypted.symmetric, SymmetricAlgorithm.aes128); - expect(encrypted.aead, AeadAlgorithm.ocb); - expect(encrypted.chunkSize, 12); - - final decrypted = - await AeadEncryptedData.fromByteData(encrypted.toByteData()) - .decrypt(key); - final ldPacket = decrypted.packets![0]; - expect(ldPacket.toByteData(), equals(literalData.toByteData())); - }); - }); -} diff --git a/test/packet/key_packet_test.dart b/test/packet/key_packet_test.dart deleted file mode 100644 index 81356ee6..00000000 --- a/test/packet/key_packet_test.dart +++ /dev/null @@ -1,248 +0,0 @@ -import 'dart:convert'; - -import 'package:dart_pg/src/enum/key_algorithm.dart'; -import 'package:dart_pg/src/enum/s2k_usage.dart'; -import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; - -import 'package:dart_pg/src/packet/key/ec_secret_params.dart'; -import 'package:dart_pg/src/packet/key/ecdh_public_params.dart'; -import 'package:dart_pg/src/packet/key/ed_secret_params.dart'; -import 'package:dart_pg/src/packet/key/eddsa_public_params.dart'; -import 'package:dart_pg/src/packet/key/rsa_public_params.dart'; -import 'package:dart_pg/src/packet/key/rsa_secret_params.dart'; -import 'package:dart_pg/src/packet/public_key.dart'; -import 'package:dart_pg/src/packet/public_subkey.dart'; -import 'package:dart_pg/src/packet/secret_key.dart'; -import 'package:dart_pg/src/packet/secret_subkey.dart'; -import 'package:test/test.dart'; - -import '../data/key_data.dart'; - -void main() { - group('public key packet', () { - test('rsa test', () { - final publicKey = PublicKeyPacket.fromByteData(base64.decode( - rsaPublicKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), ''))); - expect(publicKey.fingerprint, '44ebf9e6dc6647d61c556de27a686b5a10709559'); - expect(publicKey.algorithm, KeyAlgorithm.rsaEncryptSign); - - final publicSubkey = PublicSubkeyPacket.fromByteData(base64.decode( - rsaPublicSubkeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), ''))); - expect( - publicSubkey.fingerprint, '8da510f6630e613b4e4b627a1500062542172d9c'); - expect(publicSubkey.algorithm, KeyAlgorithm.rsaEncryptSign); - }); - - test('dsa elgamal test', () { - final publicKey = PublicKeyPacket.fromByteData(base64.decode( - dsaPublicKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), ''))); - expect(publicKey.fingerprint, 'd7143f20460ecd568e1ed6cd76c0caec8769a8a7'); - expect(publicKey.algorithm, KeyAlgorithm.dsa); - - final publicSubkey = PublicSubkeyPacket.fromByteData(base64.decode( - elgamalPublicSubkeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), ''))); - expect( - publicSubkey.fingerprint, 'cabe81ea1ab72a92e1c0c65c16e7d1ac9c6620c8'); - expect(publicSubkey.algorithm, KeyAlgorithm.elgamal); - }); - - test('ecc test', () { - final publicKey = PublicKeyPacket.fromByteData(base64.decode( - ecdsaPublicKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), ''))); - expect(publicKey.fingerprint, '2d84ae177c1bed087cb9903cdeefcc766e22aedf'); - expect(publicKey.algorithm, KeyAlgorithm.ecdsa); - - final publicSubkey = PublicSubkeyPacket.fromByteData(base64.decode( - ecdhPublicSubkeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), ''))); - expect( - publicSubkey.fingerprint, '7a2da9aa8c176411d6ed1d2f24373aaf7d84b6be'); - expect(publicSubkey.algorithm, KeyAlgorithm.ecdh); - }); - - test('curve25519 test', () { - final publicKey = PublicKeyPacket.fromByteData(base64.decode( - curve25519PublicKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), ''))); - expect(publicKey.fingerprint, '67287cc6376746e683fd24675654e554d72fcf47'); - expect(publicKey.algorithm, KeyAlgorithm.eddsa); - - final publicSubkey = PublicSubkeyPacket.fromByteData(base64.decode( - curve25519PublicSubkeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), ''))); - expect( - publicSubkey.fingerprint, '38460d0ea0f3da56ccf63e9d0a4e826effaf48a4'); - expect(publicSubkey.algorithm, KeyAlgorithm.ecdh); - }); - }); - - group('secret key packet', () { - test('rsa test', (() async { - final secretKey = await SecretKeyPacket.fromByteData( - base64.decode(rsaSecretKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - expect(secretKey.fingerprint, '44ebf9e6dc6647d61c556de27a686b5a10709559'); - expect(secretKey.algorithm, KeyAlgorithm.rsaEncryptSign); - expect(secretKey.validate(), isTrue); - - final secretSubkey = await SecretSubkeyPacket.fromByteData( - base64.decode(rsaSecretSubkeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - expect( - secretSubkey.fingerprint, '8da510f6630e613b4e4b627a1500062542172d9c'); - expect(secretSubkey.algorithm, KeyAlgorithm.rsaEncryptSign); - expect(secretSubkey.validate(), isTrue); - })); - - test('dsa elgamal test', () async { - final secretKey = await SecretKeyPacket.fromByteData( - base64.decode(dsaSecretKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - expect(secretKey.fingerprint, 'd7143f20460ecd568e1ed6cd76c0caec8769a8a7'); - expect(secretKey.algorithm, KeyAlgorithm.dsa); - expect(secretKey.validate(), isTrue); - - final secretSubkey = await SecretSubkeyPacket.fromByteData( - base64.decode(elgamalSecretKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - expect( - secretSubkey.fingerprint, 'cabe81ea1ab72a92e1c0c65c16e7d1ac9c6620c8'); - expect(secretSubkey.algorithm, KeyAlgorithm.elgamal); - expect(secretSubkey.validate(), isTrue); - }); - - test('ecc test', () async { - final secretKey = await SecretKeyPacket.fromByteData( - base64.decode(ecdsaSecretKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - expect(secretKey.fingerprint, '2d84ae177c1bed087cb9903cdeefcc766e22aedf'); - expect(secretKey.algorithm, KeyAlgorithm.ecdsa); - expect(secretKey.validate(), isTrue); - - final secretSubkey = await SecretSubkeyPacket.fromByteData( - base64.decode(ecdhSecretKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - expect( - secretSubkey.fingerprint, '7a2da9aa8c176411d6ed1d2f24373aaf7d84b6be'); - expect(secretSubkey.algorithm, KeyAlgorithm.ecdh); - expect(secretSubkey.validate(), isTrue); - }); - - test('curve25519 test', () async { - final secretKey = await SecretKeyPacket.fromByteData( - base64.decode(curve25519SecretKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - expect(secretKey.fingerprint, '67287cc6376746e683fd24675654e554d72fcf47'); - expect(secretKey.algorithm, KeyAlgorithm.eddsa); - expect(secretKey.validate(), isTrue); - expect(secretKey.publicKey.publicParams, isA()); - expect(secretKey.secretParams, isA()); - - final secretSubkey = await SecretSubkeyPacket.fromByteData( - base64.decode(curve25519SecretSubkeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - expect( - secretSubkey.fingerprint, '38460d0ea0f3da56ccf63e9d0a4e826effaf48a4'); - expect(secretSubkey.algorithm, KeyAlgorithm.ecdh); - expect(secretSubkey.validate(), isTrue); - expect(secretSubkey.publicKey.publicParams, isA()); - expect(secretSubkey.secretParams, isA()); - }); - - test('encrypt test', (() async { - final secretKey = SecretKeyPacket.fromByteData( - base64.decode( - secretKeyPacket.replaceAll(RegExp(r'\r?\n', multiLine: true), '')), - ); - final publicParams = secretKey.publicKey.publicParams as RSAPublicParams; - final secretParams = secretKey.secretParams as RSASecretParams; - - expect(secretKey.fingerprint, '93456c517e3eddb679bb510c2213de9391374950'); - expect(secretKey.algorithm, KeyAlgorithm.rsaEncryptSign); - expect(secretParams.coefficient, - secretParams.primeP.modInverse(secretParams.primeQ)); - expect(publicParams.modulus, secretParams.modulus); - - expect(secretKey.isDecrypted, true); - expect(secretKey.s2kUsage, S2kUsage.none); - expect(secretKey.symmetric, SymmetricAlgorithm.plaintext); - expect(secretKey.iv, isNull); - expect(secretKey.s2k, isNull); - - final encryptedKey = await secretKey.encrypt(passphrase); - expect(encryptedKey.fingerprint, secretKey.fingerprint); - expect(encryptedKey.secretParams, secretKey.secretParams); - - expect(encryptedKey.s2kUsage, S2kUsage.sha1); - expect(encryptedKey.symmetric, SymmetricAlgorithm.aes128); - expect(encryptedKey.iv, isNotNull); - expect(encryptedKey.s2k, isNotNull); - - final decryptedKey = - await SecretKeyPacket.fromByteData(encryptedKey.toByteData()) - .decrypt(passphrase); - final decryptedParams = decryptedKey.secretParams as RSASecretParams; - - expect(decryptedKey.fingerprint, secretKey.fingerprint); - expect(decryptedParams.exponent, secretParams.exponent); - expect(decryptedParams.primeP, secretParams.primeP); - expect(decryptedParams.primeQ, secretParams.primeQ); - expect(decryptedParams.coefficient, secretParams.coefficient); - - final secretSubkey = SecretSubkeyPacket.fromByteData( - base64.decode(secretSubkeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ); - final subkeyPublicParams = - secretSubkey.publicKey.publicParams as RSAPublicParams; - final subkeySecretParams = secretSubkey.secretParams as RSASecretParams; - - expect( - secretSubkey.fingerprint, 'c503083b150f47a5d6fdb661c865808a31866def'); - expect(secretSubkey.algorithm, KeyAlgorithm.rsaEncryptSign); - expect(subkeySecretParams.coefficient, - subkeySecretParams.primeP.modInverse(subkeySecretParams.primeQ)); - expect(subkeyPublicParams.modulus, subkeySecretParams.modulus); - - expect(secretSubkey.isDecrypted, true); - expect(secretSubkey.s2kUsage, S2kUsage.none); - expect(secretSubkey.symmetric, SymmetricAlgorithm.plaintext); - expect(secretSubkey.iv, isNull); - expect(secretSubkey.s2k, isNull); - - final subkeyEncryptedKey = await secretSubkey.encrypt(passphrase); - expect(subkeyEncryptedKey.fingerprint, secretSubkey.fingerprint); - expect(subkeyEncryptedKey.secretParams, secretSubkey.secretParams); - - expect(subkeyEncryptedKey.s2kUsage, S2kUsage.sha1); - expect(subkeyEncryptedKey.symmetric, SymmetricAlgorithm.aes128); - expect(subkeyEncryptedKey.iv, isNotNull); - expect(subkeyEncryptedKey.s2k, isNotNull); - - final subkeyDecryptedKey = await SecretKeyPacket.fromByteData( - subkeyEncryptedKey.toByteData(), - ).decrypt(passphrase); - final subkeyDecryptedParams = - subkeyDecryptedKey.secretParams as RSASecretParams; - - expect(subkeyDecryptedKey.fingerprint, secretSubkey.fingerprint); - expect(subkeyDecryptedParams.exponent, subkeySecretParams.exponent); - expect(subkeyDecryptedParams.primeP, subkeySecretParams.primeP); - expect(subkeyDecryptedParams.primeQ, subkeySecretParams.primeQ); - expect(subkeyDecryptedParams.coefficient, subkeySecretParams.coefficient); - })); - }); -} diff --git a/test/packet/key_wrap_test.dart b/test/packet/key_wrap_test.dart deleted file mode 100644 index 7ae40908..00000000 --- a/test/packet/key_wrap_test.dart +++ /dev/null @@ -1,382 +0,0 @@ -import 'dart:typed_data'; - -import 'package:dart_pg/src/packet/key/aes_key_wrap.dart'; -import 'package:dart_pg/src/helpers.dart'; -import 'package:dart_pg/src/packet/key/camellia_key_wrap.dart'; -import 'package:test/test.dart'; - -void main() { - group('Key wrap & unwrap', () { - final key128 = Uint8List.fromList(List.generate(16, (index) => index)); - final key192 = Uint8List.fromList(List.generate(24, (index) => index)); - final key256 = Uint8List.fromList(List.generate(32, (index) => index)); - - final keyData128 = Uint8List.fromList([ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // 0 - 7 - 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, - ]); - final keyData192 = Uint8List.fromList([ - 0x00, - 0x11, - 0x22, - 0x33, - 0x44, - 0x55, - 0x66, - 0x77, - 0x88, - 0x99, - 0xaa, - 0xbb, - 0xcc, - 0xdd, - 0xee, - 0xff, - 0x00, - 0x01, - 0x02, - 0x03, - 0x04, - 0x05, - 0x06, - 0x07, - ]); - final keyData256 = Uint8List.fromList([ - 0x00, - 0x11, - 0x22, - 0x33, - 0x44, - 0x55, - 0x66, - 0x77, - 0x88, - 0x99, - 0xaa, - 0xbb, - 0xcc, - 0xdd, - 0xee, - 0xff, - 0x00, - 0x01, - 0x02, - 0x03, - 0x04, - 0x05, - 0x06, - 0x07, - 0x08, - 0x09, - 0x0a, - 0x0b, - 0x0c, - 0x0d, - 0x0e, - 0x0f, - ]); - - test('aes 128-bit test', () async { - final aes = AesKeyWrap(16); - - final wrappedKey128128 = Uint8List.fromList([ - 0x1f, - 0xa6, - 0x8b, - 0x0a, - 0x81, - 0x12, - 0xb4, - 0x47, - 0xae, - 0xf3, - 0x4b, - 0xd8, - 0xfb, - 0x5a, - 0x7b, - 0x82, - 0x9d, - 0x3e, - 0x86, - 0x23, - 0x71, - 0xd2, - 0xcf, - 0xe5, - ]); - final wrappedKey128 = await aes.wrap(key128, keyData128); - final unwrappedKey128 = await aes.unwrap(key128, wrappedKey128); - - expect(wrappedKey128, equals(wrappedKey128128)); - expect(unwrappedKey128, equals(keyData128)); - - final key = Helper.secureRandom().nextBytes(16); - final keyData = Helper.secureRandom().nextBytes(32); - final wrappedKey = await aes.wrap(key, keyData); - final unwrappedKey = await aes.unwrap(key, wrappedKey); - expect(unwrappedKey, equals(keyData)); - }); - - test('aes 192-bit test', () async { - final aes = AesKeyWrap(24); - final wrappedKey128192 = Uint8List.fromList([ - 0x96, - 0x77, - 0x8b, - 0x25, - 0xae, - 0x6c, - 0xa4, - 0x35, - 0xf9, - 0x2b, - 0x5b, - 0x97, - 0xc0, - 0x50, - 0xae, - 0xd2, - 0x46, - 0x8a, - 0xb8, - 0xa1, - 0x7a, - 0xd8, - 0x4e, - 0x5d, - ]); - final wrappedKey192192 = Uint8List.fromList([ - 0x03, - 0x1d, - 0x33, - 0x26, - 0x4e, - 0x15, - 0xd3, - 0x32, - 0x68, - 0xf2, - 0x4e, - 0xc2, - 0x60, - 0x74, - 0x3e, - 0xdc, - 0xe1, - 0xc6, - 0xc7, - 0xdd, - 0xee, - 0x72, - 0x5a, - 0x93, - 0x6b, - 0xa8, - 0x14, - 0x91, - 0x5c, - 0x67, - 0x62, - 0xd2 - ]); - - final wrappedKey128 = await aes.wrap(key192, keyData128); - final unwrappedKey128 = await aes.unwrap(key192, wrappedKey128); - expect(wrappedKey128, equals(wrappedKey128192)); - expect(unwrappedKey128, equals(keyData128)); - - final wrappedKey192 = await aes.wrap(key192, keyData192); - final unwrappedKey192 = await aes.unwrap(key192, wrappedKey192); - expect(wrappedKey192, equals(wrappedKey192192)); - expect(unwrappedKey192, equals(keyData192)); - - final key = Helper.secureRandom().nextBytes(24); - final keyData = Helper.secureRandom().nextBytes(32); - final wrappedKey = await aes.wrap(key, keyData); - final unwrappedKey = await aes.unwrap(key, wrappedKey); - expect(unwrappedKey, equals(keyData)); - }); - - test('aes 256-bit test', () async { - final aes = AesKeyWrap(32); - final wrappedKey128256 = Uint8List.fromList([ - 0x64, - 0xe8, - 0xc3, - 0xf9, - 0xce, - 0x0f, - 0x5b, - 0xa2, - 0x63, - 0xe9, - 0x77, - 0x79, - 0x05, - 0x81, - 0x8a, - 0x2a, - 0x93, - 0xc8, - 0x19, - 0x1e, - 0x7d, - 0x6e, - 0x8a, - 0xe7, - ]); - final wrappedKey192256 = Uint8List.fromList([ - 0xa8, - 0xf9, - 0xbc, - 0x16, - 0x12, - 0xc6, - 0x8b, - 0x3f, - 0xf6, - 0xe6, - 0xf4, - 0xfb, - 0xe3, - 0x0e, - 0x71, - 0xe4, - 0x76, - 0x9c, - 0x8b, - 0x80, - 0xa3, - 0x2c, - 0xb8, - 0x95, - 0x8c, - 0xd5, - 0xd1, - 0x7d, - 0x6b, - 0x25, - 0x4d, - 0xa1, - ]); - final wrappedKey256256 = Uint8List.fromList([ - 0x28, - 0xc9, - 0xf4, - 0x04, - 0xc4, - 0xb8, - 0x10, - 0xf4, - 0xcb, - 0xcc, - 0xb3, - 0x5c, - 0xfb, - 0x87, - 0xf8, - 0x26, - 0x3f, - 0x57, - 0x86, - 0xe2, - 0xd8, - 0x0e, - 0xd3, - 0x26, - 0xcb, - 0xc7, - 0xf0, - 0xe7, - 0x1a, - 0x99, - 0xf4, - 0x3b, - 0xfb, - 0x98, - 0x8b, - 0x9b, - 0x7a, - 0x02, - 0xdd, - 0x21, - ]); - - final wrappedKey128 = await aes.wrap(key256, keyData128); - final unwrappedKey128 = await aes.unwrap(key256, wrappedKey128); - expect(wrappedKey128, equals(wrappedKey128256)); - expect(unwrappedKey128, equals(keyData128)); - - final wrappedKey192 = await aes.wrap(key256, keyData192); - final unwrappedKey192 = await aes.unwrap(key256, wrappedKey192); - expect(wrappedKey192, equals(wrappedKey192256)); - expect(unwrappedKey192, equals(keyData192)); - - final wrappedKey256 = await aes.wrap(key256, keyData256); - final unwrappedKey256 = await aes.unwrap(key256, wrappedKey256); - expect(wrappedKey256, equals(wrappedKey256256)); - expect(unwrappedKey256, equals(keyData256)); - - final key = Helper.secureRandom().nextBytes(32); - final keyData = Helper.secureRandom().nextBytes(32); - final wrappedKey = await aes.wrap(key, keyData); - final unwrappedKey = await aes.unwrap(key, wrappedKey); - expect(unwrappedKey, equals(keyData)); - }); - - test('camellia 128-bit test', () async { - final camellia = CamelliaKeyWrap(16); - - final wrappedKey128 = await camellia.wrap(key128, keyData128); - final unwrappedKey128 = await camellia.unwrap(key128, wrappedKey128); - expect(unwrappedKey128, equals(keyData128)); - - final key = Helper.secureRandom().nextBytes(16); - final keyData = Helper.secureRandom().nextBytes(32); - final wrappedKey = await camellia.wrap(key, keyData); - final unwrappedKey = await camellia.unwrap(key, wrappedKey); - expect(unwrappedKey, equals(keyData)); - }); - - test('camellia 192-bit test', () async { - final camellia = CamelliaKeyWrap(24); - - final wrappedKey128 = await camellia.wrap(key192, keyData128); - final unwrappedKey128 = await camellia.unwrap(key192, wrappedKey128); - expect(unwrappedKey128, equals(keyData128)); - - final wrappedKey192 = await camellia.wrap(key192, keyData192); - final unwrappedKey192 = await camellia.unwrap(key192, wrappedKey192); - expect(unwrappedKey192, equals(keyData192)); - - final key = Helper.secureRandom().nextBytes(24); - final keyData = Helper.secureRandom().nextBytes(32); - final wrappedKey = await camellia.wrap(key, keyData); - final unwrappedKey = await camellia.unwrap(key, wrappedKey); - expect(unwrappedKey, equals(keyData)); - }); - - test('camellia 256-bit test', () async { - final camellia = CamelliaKeyWrap(32); - - final wrappedKey128 = await camellia.wrap(key256, keyData128); - final unwrappedKey128 = await camellia.unwrap(key256, wrappedKey128); - expect(unwrappedKey128, equals(keyData128)); - - final wrappedKey192 = await camellia.wrap(key256, keyData192); - final unwrappedKey192 = await camellia.unwrap(key256, wrappedKey192); - expect(unwrappedKey192, equals(keyData192)); - - final wrappedKey256 = await camellia.wrap(key256, keyData256); - final unwrappedKey256 = await camellia.unwrap(key256, wrappedKey256); - expect(unwrappedKey256, equals(keyData256)); - - final key = Helper.secureRandom().nextBytes(32); - final keyData = Helper.secureRandom().nextBytes(32); - final wrappedKey = await camellia.wrap(key, keyData); - final unwrappedKey = await camellia.unwrap(key, wrappedKey); - expect(unwrappedKey, equals(keyData)); - }); - }); -} diff --git a/test/packet/s2k_test.dart b/test/packet/s2k_test.dart deleted file mode 100644 index 8ab041d9..00000000 --- a/test/packet/s2k_test.dart +++ /dev/null @@ -1,200 +0,0 @@ -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:dart_pg/src/crypto/math/byte_ext.dart'; -import 'package:dart_pg/src/enum/hash_algorithm.dart'; -import 'package:dart_pg/src/enum/s2k_type.dart'; -import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; -import 'package:dart_pg/src/packet/key/s2k.dart'; -import 'package:dart_pg/src/packet/packet_list.dart'; -import 'package:dart_pg/src/packet/sym_encrypted_session_key.dart'; -import 'package:test/test.dart'; - -import '../data/s2k_data.dart'; - -void main() { - group('s2k', () { - test('mode-0-password-1234 test', () async { - const passphrase = '1234'; - final salt = Uint8List.fromList([]); - - final packets = PacketList.packetDecode(base64.decode(mode0Password1234)); - final skesk = packets[0] as SymEncryptedSessionKeyPacket; - expect(skesk.s2k.type, S2kType.simple); - expect(skesk.s2k.hash, HashAlgorithm.sha1); - expect(skesk.s2k.salt, equals(salt)); - expect(skesk.symmetric, SymmetricAlgorithm.aes256); - - final s2k = S2K(salt, type: skesk.s2k.type, hash: skesk.s2k.hash); - final key = - await s2k.produceKey(passphrase, (skesk.symmetric.keySize + 7) >> 3); - expect(key.toHexadecimal(), - '7110eda4d09e062aa5e4a390b0a572ac0d2c0220f352b0d292b65164c2a67301'); - }); - - test('mode-1-password-123456 test', () async { - const passphrase = '123456'; - final salt = - Uint8List.fromList([0xa8, 0x42, 0xa7, 0xa9, 0x59, 0xfa, 0x42, 0x2a]); - - final packets = - PacketList.packetDecode(base64.decode(mode1Password123456)); - final skesk = packets[0] as SymEncryptedSessionKeyPacket; - expect(skesk.s2k.type, S2kType.salted); - expect(skesk.s2k.hash, HashAlgorithm.sha1); - expect(skesk.s2k.salt, equals(salt)); - expect(skesk.symmetric, SymmetricAlgorithm.aes256); - - final s2k = S2K(salt, type: skesk.s2k.type, hash: skesk.s2k.hash); - final key = - await s2k.produceKey(passphrase, (skesk.symmetric.keySize + 7) >> 3); - expect(key.toHexadecimal(), - '8b79077ca448f6fb3d3ad2a264d3b938d357c9fb3e41219fd962df960a9afa08'); - }); - - test('mode-1-password-foobar test', () async { - const passphrase = 'foobar'; - final salt = - Uint8List.fromList([0xbc, 0x95, 0x58, 0x45, 0x81, 0x3c, 0x7c, 0x37]); - - final packets = - PacketList.packetDecode(base64.decode(mode1PasswordFoobar)); - final skesk = packets[0] as SymEncryptedSessionKeyPacket; - expect(skesk.s2k.type, S2kType.salted); - expect(skesk.s2k.hash, HashAlgorithm.sha1); - expect(skesk.s2k.salt, equals(salt)); - expect(skesk.symmetric, SymmetricAlgorithm.aes256); - - final s2k = S2K(salt, type: skesk.s2k.type, hash: skesk.s2k.hash); - final key = - await s2k.produceKey(passphrase, (skesk.symmetric.keySize + 7) >> 3); - expect(key.toHexadecimal(), - 'b7d48aae9b943b22a4d390083e8460b5edfa118fe1688bf0c473b8094d1a8d10'); - }); - - test('mode-3-password-qwerty test', () async { - const itCount = 241; - const passphrase = 'qwerty'; - final salt = - Uint8List.fromList([0x78, 0x45, 0xf0, 0x5b, 0x55, 0xf7, 0xb4, 0x9e]); - - final packets = - PacketList.packetDecode(base64.decode(mode3PasswordQwerty)); - final skesk = packets[0] as SymEncryptedSessionKeyPacket; - expect(skesk.s2k.type, S2kType.iterated); - expect(skesk.s2k.hash, HashAlgorithm.sha1); - expect(skesk.s2k.itCount, itCount); - expect(skesk.s2k.salt, equals(salt)); - expect(skesk.symmetric, SymmetricAlgorithm.aes256); - - final s2k = S2K(salt, - type: skesk.s2k.type, - hash: skesk.s2k.hash, - itCount: skesk.s2k.itCount); - final key = - await s2k.produceKey(passphrase, (skesk.symmetric.keySize + 7) >> 3); - expect(key.toHexadecimal(), - '575ad156187a3f8cec11108309236eb499f1e682f0d1afadfac4ecf97613108a'); - }); - - test('mode-3-password-9876 test', () async { - const itCount = 43; - const passphrase = '9876'; - final salt = - Uint8List.fromList([0xb9, 0x67, 0xea, 0x96, 0x53, 0xdb, 0x6a, 0xc8]); - - final packets = PacketList.packetDecode(base64.decode(mode3Password9876)); - final skesk = packets[0] as SymEncryptedSessionKeyPacket; - expect(skesk.s2k.type, S2kType.iterated); - expect(skesk.s2k.hash, HashAlgorithm.sha1); - expect(skesk.s2k.itCount, itCount); - expect(skesk.s2k.salt, equals(salt)); - expect(skesk.symmetric, SymmetricAlgorithm.aes256); - - final s2k = S2K(salt, - type: skesk.s2k.type, - hash: skesk.s2k.hash, - itCount: skesk.s2k.itCount); - final key = - await s2k.produceKey(passphrase, (skesk.symmetric.keySize + 7) >> 3); - expect(key.toHexadecimal(), - '736c226b8c64e4e6d0325c6c552ef7c0738f98f48fed65fd8c93265103efa23a'); - }); - - test('mode-3-aes192-password-123 test', () async { - const itCount = 238; - const passphrase = '123'; - final salt = - Uint8List.fromList([0x8f, 0x81, 0x74, 0xc5, 0xd9, 0x61, 0xc7, 0x79]); - - final packets = - PacketList.packetDecode(base64.decode(mode3Aes192Password123)); - final skesk = packets[0] as SymEncryptedSessionKeyPacket; - expect(skesk.s2k.type, S2kType.iterated); - expect(skesk.s2k.hash, HashAlgorithm.sha1); - expect(skesk.s2k.itCount, itCount); - expect(skesk.s2k.salt, equals(salt)); - expect(skesk.symmetric, SymmetricAlgorithm.aes192); - - final s2k = S2K(salt, - type: skesk.s2k.type, - hash: skesk.s2k.hash, - itCount: skesk.s2k.itCount); - final key = - await s2k.produceKey(passphrase, (skesk.symmetric.keySize + 7) >> 3); - expect(key.toHexadecimal(), - '915e96fc694e7f90a6850b740125ea005199c725f3bd27e3'); - }); - - test('mode-3-twofish-password-13-times-0123456789 test', () async { - const itCount = 238; - const passphrase = - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'; - final salt = - Uint8List.fromList([0x51, 0xed, 0xfc, 0x15, 0x45, 0x40, 0x65, 0xac]); - - final packets = PacketList.packetDecode( - base64.decode(mode3TwofishPassword13Times0123456789)); - final skesk = packets[0] as SymEncryptedSessionKeyPacket; - expect(skesk.s2k.type, S2kType.iterated); - expect(skesk.s2k.hash, HashAlgorithm.sha1); - expect(skesk.s2k.itCount, itCount); - expect(skesk.s2k.salt, equals(salt)); - expect(skesk.symmetric, SymmetricAlgorithm.twofish); - - final s2k = S2K(salt, - type: skesk.s2k.type, - hash: skesk.s2k.hash, - itCount: skesk.s2k.itCount); - final key = - await s2k.produceKey(passphrase, (skesk.symmetric.keySize + 7) >> 3); - expect(key.toHexadecimal(), - 'ea264fada5a859c40d88a159b344ecf1f51ff327fdb3c558b0a7dc299777173e'); - }); - - test('mode-3-aes128-password-13-times-0123456789 test', () async { - const itCount = 238; - const passphrase = - '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'; - final salt = - Uint8List.fromList([0x06, 0xe4, 0x61, 0x5c, 0xa4, 0x48, 0xf9, 0xdd]); - - final packets = PacketList.packetDecode( - base64.decode(mode3Aes128Password13Times0123456789)); - final skesk = packets[0] as SymEncryptedSessionKeyPacket; - expect(skesk.s2k.type, S2kType.iterated); - expect(skesk.s2k.hash, HashAlgorithm.sha1); - expect(skesk.s2k.itCount, itCount); - expect(skesk.s2k.salt, equals(salt)); - expect(skesk.symmetric, SymmetricAlgorithm.aes128); - - final s2k = S2K(salt, - type: skesk.s2k.type, - hash: skesk.s2k.hash, - itCount: skesk.s2k.itCount); - final key = - await s2k.produceKey(passphrase, (skesk.symmetric.keySize + 7) >> 3); - expect(key.toHexadecimal(), 'f3d0ce52ed6143637443e3399437fd0f'); - }); - }); -} diff --git a/test/packet/signing_test.dart b/test/packet/signing_test.dart deleted file mode 100644 index 7202be0a..00000000 --- a/test/packet/signing_test.dart +++ /dev/null @@ -1,202 +0,0 @@ -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:dart_pg/src/enum/key_flag.dart'; -import 'package:dart_pg/src/enum/signature_subpacket_type.dart'; -import 'package:dart_pg/src/enum/signature_type.dart'; -import 'package:dart_pg/src/enum/support_feature.dart'; -import 'package:dart_pg/src/helpers.dart'; -import 'package:dart_pg/src/packet/key_packet.dart'; -import 'package:dart_pg/src/packet/signature_packet.dart'; - -import 'package:dart_pg/src/packet/signature_subpacket.dart'; -import 'package:dart_pg/src/packet/subpacket_reader.dart'; -import 'package:dart_pg/src/packet/user_id.dart'; -import 'package:faker/faker.dart'; -import 'package:test/test.dart'; - -import '../data/key_data.dart'; - -void main() { - group('sub packet', () { - test('key flag test', () { - final keyFlags = KeyFlags.fromFlags( - KeyFlag.certifyKeys.value | - KeyFlag.signData.value | - KeyFlag.encryptCommunication.value | - KeyFlag.encryptStorage.value | - KeyFlag.splitPrivateKey.value | - KeyFlag.authentication.value | - KeyFlag.sharedPrivateKey.value, - ); - for (final flag in KeyFlag.values) { - expect(keyFlags.flags & flag.value, flag.value); - } - }); - - test('features test', () { - final features = Features.fromFeatures( - SupportFeature.modificationDetection.value | - SupportFeature.aeadEncryptedData.value | - SupportFeature.version5PublicKey.value); - expect(features.supprtModificationDetection, true); - expect(features.supportAeadEncryptedData, true); - expect(features.supportVersion5PublicKey, true); - }); - - test('write & read test', () { - final random = Helper.secureRandom(); - final initSubpackets = SignatureSubpacketType.values - .map((type) => SignatureSubpacket(type, random.nextBytes(10))) - .toList(); - - final bytes = Uint8List.fromList( - initSubpackets - .map((subpacket) => subpacket.encode()) - .expand((byte) => byte) - .toList(), - ); - final subpackets = []; - var offset = 0; - while (offset < bytes.length) { - final reader = SubpacketReader.read(bytes, offset); - offset = reader.offset; - final data = reader.data; - if (data.isNotEmpty) { - final critical = ((reader.type & 0x80) != 0); - final type = SignatureSubpacketType.values - .firstWhere((type) => type.value == (reader.type & 0x7f)); - subpackets.add(SignatureSubpacket( - type, - data, - critical: critical, - isLong: reader.isLong, - )); - } - } - - expect(initSubpackets.length, subpackets.length); - for (final subpacket in initSubpackets) { - final index = initSubpackets.indexOf(subpacket); - expect(subpacket.type, subpackets[index].type); - expect(subpacket.data, equals(subpackets[index].data)); - } - }); - }); - - group('signing', () { - final name = faker.person.name(); - final email = faker.internet.email().replaceAll("'", ''); - final comment = faker.lorem.words(3).join(' '); - final dataToSign = Helper.secureRandom().nextBytes(1000); - - test('rsa test', () async { - final secretKey = await SecretKeyPacket.fromByteData(base64.decode( - rsaSecretKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), ''))) - .decrypt(passphrase); - final publicKey = PublicKeyPacket.fromByteData(base64.decode( - rsaPublicKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), ''))); - final signature = await SignaturePacket.createSignature( - secretKey, SignatureType.standalone, dataToSign); - - expect(await signature.verify(publicKey, dataToSign), isTrue); - - final userID = UserIDPacket([name, '($comment)', '<$email>'].join(' ')); - final selfCertificate = await SignaturePacket.createSelfCertificate( - secretKey, - userID: userID); - expect( - await selfCertificate.verifyUserCertification(publicKey, - userID: userID), - isTrue); - expect(selfCertificate.keyFlags!.flags & KeyFlag.certifyKeys.value, - KeyFlag.certifyKeys.value); - expect(selfCertificate.keyFlags!.flags & KeyFlag.signData.value, - KeyFlag.signData.value); - }); - - test('dsa test', () async { - final secretKey = await SecretKeyPacket.fromByteData( - base64.decode(dsaSecretKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - final publicKey = PublicKeyPacket.fromByteData(base64.decode( - dsaPublicKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), ''))); - final signature = await SignaturePacket.createSignature( - secretKey, SignatureType.standalone, dataToSign); - - expect(await signature.verify(publicKey, dataToSign), isTrue); - - final userID = UserIDPacket([name, '($comment)', '<$email>'].join(' ')); - final selfCertificate = await SignaturePacket.createSelfCertificate( - secretKey, - userID: userID); - expect( - await selfCertificate.verifyUserCertification(publicKey, - userID: userID), - isTrue); - expect(selfCertificate.keyFlags!.flags & KeyFlag.certifyKeys.value, - KeyFlag.certifyKeys.value); - expect(selfCertificate.keyFlags!.flags & KeyFlag.signData.value, - KeyFlag.signData.value); - }); - - test('ecdsa test', () async { - final secretKey = await SecretKeyPacket.fromByteData( - base64.decode(ecdsaSecretKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - final publicKey = PublicKeyPacket.fromByteData(base64.decode( - ecdsaPublicKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), ''))); - final signature = await SignaturePacket.createSignature( - secretKey, SignatureType.standalone, dataToSign); - - expect(await signature.verify(publicKey, dataToSign), isTrue); - - final userID = UserIDPacket([name, '($comment)', '<$email>'].join(' ')); - final selfCertificate = await SignaturePacket.createSelfCertificate( - secretKey, - userID: userID); - expect( - await selfCertificate.verifyUserCertification(publicKey, - userID: userID), - isTrue); - expect(selfCertificate.keyFlags!.flags & KeyFlag.certifyKeys.value, - KeyFlag.certifyKeys.value); - expect(selfCertificate.keyFlags!.flags & KeyFlag.signData.value, - KeyFlag.signData.value); - }); - - test('curve25519 test', () async { - final secretKey = await SecretKeyPacket.fromByteData( - base64.decode(curve25519SecretKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), '')), - ).decrypt(passphrase); - final publicKey = PublicKeyPacket.fromByteData(base64.decode( - curve25519PublicKeyPacket.replaceAll( - RegExp(r'\r?\n', multiLine: true), ''))); - final signature = await SignaturePacket.createSignature( - secretKey, SignatureType.standalone, dataToSign); - - expect(await signature.verify(publicKey, dataToSign), isTrue); - - final userID = UserIDPacket([name, '($comment)', '<$email>'].join(' ')); - final selfCertificate = await SignaturePacket.createSelfCertificate( - secretKey, - userID: userID); - - expect( - await selfCertificate.verifyUserCertification(publicKey, - userID: userID), - isTrue); - expect(selfCertificate.keyFlags!.flags & KeyFlag.certifyKeys.value, - KeyFlag.certifyKeys.value); - expect(selfCertificate.keyFlags!.flags & KeyFlag.signData.value, - KeyFlag.signData.value); - }); - }); -} diff --git a/test/packet/user_packet_test.dart b/test/packet/user_packet_test.dart deleted file mode 100644 index 16b4ac67..00000000 --- a/test/packet/user_packet_test.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:dart_pg/src/packet/image_attribute.dart'; -import 'package:dart_pg/src/packet/user_attribute.dart'; -import 'package:dart_pg/src/packet/user_attribute_subpacket.dart'; -import 'package:dart_pg/src/packet/user_id.dart'; -import 'package:faker/faker.dart'; -import 'package:test/test.dart'; - -void main() { - group('User packet', (() { - test('user id test', (() { - final name = faker.person.name(); - final email = faker.internet.email().replaceAll("'", ''); - final comment = faker.lorem.words(3).join(' '); - - final userID = UserIDPacket([name, '($comment)', '<$email>'].join(' ')); - expect(userID.name, name); - expect(userID.email, email); - expect(userID.comment, comment); - - final cloneUserId = UserIDPacket.fromByteData(userID.toByteData()); - expect(userID.name, cloneUserId.name); - expect(userID.email, cloneUserId.email); - expect(userID.comment, cloneUserId.comment); - })); - - test('user attribute test', (() { - final imageData = - Uint8List.fromList(faker.randomGenerator.numbers(255, 100)); - final subpacketType = faker.randomGenerator.integer(100); - final subpacketData = - utf8.encoder.convert(faker.lorem.words(100).join(' ')); - - final userAttr = UserAttributePacket.fromByteData(UserAttributePacket([ - ImageAttributeSubpacket.fromImageData(imageData), - UserAttributeSubpacket(subpacketType, subpacketData), - ]).toByteData()); - final imageAttr = userAttr.attributes[0] as ImageAttributeSubpacket; - final subpacket = userAttr.attributes[1]; - - expect(imageAttr.version, 0x01); - expect(imageAttr.encoding, ImageAttributeSubpacket.jpeg); - expect(imageAttr.imageData, imageData); - - expect(subpacket.type, subpacketType); - expect(subpacket.data, subpacketData); - })); - })); -} diff --git a/test/type/key_test.dart b/test/type/key_test.dart deleted file mode 100644 index c28ecd28..00000000 --- a/test/type/key_test.dart +++ /dev/null @@ -1,534 +0,0 @@ -import 'package:dart_pg/src/enum/curve_info.dart'; -import 'package:dart_pg/src/enum/key_algorithm.dart'; -import 'package:dart_pg/src/enum/key_generation_type.dart'; -import 'package:dart_pg/src/packet/key/key_params.dart'; -import 'package:dart_pg/src/type/key.dart'; -import 'package:faker/faker.dart'; -import 'package:test/test.dart'; - -import '../data/key_data.dart'; - -void main() { - group('Read public key', () { - test('rsa test', () async { - final publicKey = PublicKey.fromArmored(rsaPublicKey); - expect(publicKey.fingerprint, '44ebf9e6dc6647d61c556de27a686b5a10709559'); - expect(publicKey.algorithm, KeyAlgorithm.rsaEncryptSign); - expect(publicKey.isPrivate, false); - - final user = publicKey.users[0]; - expect(user.userID!.name, 'rsa pgp key'); - expect(user.userID!.email, 'test@dummy.com'); - expect(await user.verify(), isTrue); - - final subkey = publicKey.subkeys[0]; - expect(subkey.fingerprint, '8da510f6630e613b4e4b627a1500062542172d9c'); - expect(subkey.algorithm, KeyAlgorithm.rsaEncryptSign); - expect(await subkey.verify(), isTrue); - }); - - test('dsa elgamal test', () async { - final publicKey = PublicKey.fromArmored(dsaPublicKey); - expect(publicKey.fingerprint, 'd7143f20460ecd568e1ed6cd76c0caec8769a8a7'); - expect(publicKey.algorithm, KeyAlgorithm.dsa); - expect(publicKey.isPrivate, isFalse); - - final user = publicKey.users[0]; - expect(user.userID!.name, 'dsa elgamal pgp key'); - expect(user.userID!.email, 'test@dummy.com'); - expect(await user.verify(), isTrue); - - final subkey = publicKey.subkeys[0]; - expect(subkey.fingerprint, 'cabe81ea1ab72a92e1c0c65c16e7d1ac9c6620c8'); - expect(subkey.algorithm, KeyAlgorithm.elgamal); - expect(await subkey.verify(), isTrue); - }); - - test('ecc test', () async { - final publicKey = PublicKey.fromArmored(eccPublicKey); - expect(publicKey.fingerprint, '2d84ae177c1bed087cb9903cdeefcc766e22aedf'); - expect(publicKey.algorithm, KeyAlgorithm.ecdsa); - expect(publicKey.isPrivate, isFalse); - - final user = publicKey.users[0]; - expect(user.userID!.name, 'ecc pgp key'); - expect(user.userID!.email, 'test@dummy.com'); - expect(await user.verify(), isTrue); - - final subkey = publicKey.subkeys[0]; - expect(subkey.fingerprint, '7a2da9aa8c176411d6ed1d2f24373aaf7d84b6be'); - expect(subkey.algorithm, KeyAlgorithm.ecdh); - expect(await subkey.verify(), isTrue); - }); - - test('curve25519 test', () async { - final publicKey = PublicKey.fromArmored(curve25519PublicKey); - expect(publicKey.fingerprint, '67287cc6376746e683fd24675654e554d72fcf47'); - expect(publicKey.algorithm, KeyAlgorithm.eddsa); - expect(publicKey.isPrivate, isFalse); - - final user = publicKey.users[0]; - expect(user.userID!.name, 'curve 25519 pgp key'); - expect(user.userID!.email, 'test@dummy.com'); - expect(await user.verify(), isTrue); - - final subkey = publicKey.subkeys[0]; - expect(subkey.fingerprint, '38460d0ea0f3da56ccf63e9d0a4e826effaf48a4'); - expect(subkey.algorithm, KeyAlgorithm.ecdh); - expect(await subkey.verify(), isTrue); - }); - }); - - group('Read private key', () { - test('rsa test', () async { - final privateKey = - await PrivateKey.fromArmored(rsaPrivateKey).decrypt(passphrase); - expect( - privateKey.fingerprint, '44ebf9e6dc6647d61c556de27a686b5a10709559'); - expect(privateKey.algorithm, KeyAlgorithm.rsaEncryptSign); - expect(privateKey.isPrivate, isTrue); - expect(privateKey.keyPacket.isDecrypted, isTrue); - expect(privateKey.keyPacket.validate(), isTrue); - - final user = privateKey.users[0]; - expect(user.userID!.name, 'rsa pgp key'); - expect(user.userID!.email, 'test@dummy.com'); - expect(await user.verify(), isTrue); - - final subkey = privateKey.subkeys[0]; - expect(subkey.fingerprint, '8da510f6630e613b4e4b627a1500062542172d9c'); - expect(subkey.algorithm, KeyAlgorithm.rsaEncryptSign); - expect(await subkey.verify(), isTrue); - }); - - test('dsa elgamal test', () async { - final privateKey = - await PrivateKey.fromArmored(dsaPrivateKey).decrypt(passphrase); - expect( - privateKey.fingerprint, 'd7143f20460ecd568e1ed6cd76c0caec8769a8a7'); - expect(privateKey.algorithm, KeyAlgorithm.dsa); - expect(privateKey.isPrivate, isTrue); - expect(privateKey.keyPacket.isDecrypted, isTrue); - expect(privateKey.keyPacket.validate(), isTrue); - - final user = privateKey.users[0]; - expect(user.userID!.name, 'dsa elgamal pgp key'); - expect(user.userID!.email, 'test@dummy.com'); - expect(await user.verify(), isTrue); - - final subkey = privateKey.subkeys[0]; - expect(subkey.fingerprint, 'cabe81ea1ab72a92e1c0c65c16e7d1ac9c6620c8'); - expect(subkey.algorithm, KeyAlgorithm.elgamal); - expect(await subkey.verify(), isTrue); - }); - - test('ecc test', () async { - final privateKey = - await PrivateKey.fromArmored(eccPrivateKey).decrypt(passphrase); - expect( - privateKey.fingerprint, '2d84ae177c1bed087cb9903cdeefcc766e22aedf'); - expect(privateKey.algorithm, KeyAlgorithm.ecdsa); - expect(privateKey.isPrivate, isTrue); - expect(privateKey.keyPacket.isDecrypted, isTrue); - expect(privateKey.keyPacket.validate(), isTrue); - - final user = privateKey.users[0]; - expect(user.userID!.name, 'ecc pgp key'); - expect(user.userID!.email, 'test@dummy.com'); - expect(await user.verify(), isTrue); - - final subkey = privateKey.subkeys[0]; - expect(subkey.fingerprint, '7a2da9aa8c176411d6ed1d2f24373aaf7d84b6be'); - expect(subkey.algorithm, KeyAlgorithm.ecdh); - expect(await subkey.verify(), isTrue); - }); - - test('curve25519 test', () async { - final privateKey = await PrivateKey.fromArmored(curve25519PrivateKey) - .decrypt(passphrase); - expect( - privateKey.fingerprint, '67287cc6376746e683fd24675654e554d72fcf47'); - expect(privateKey.algorithm, KeyAlgorithm.eddsa); - expect(privateKey.isPrivate, isTrue); - expect(privateKey.keyPacket.isDecrypted, isTrue); - expect(privateKey.keyPacket.validate(), isTrue); - - final user = privateKey.users[0]; - expect(user.userID!.name, 'curve 25519 pgp key'); - expect(user.userID!.email, 'test@dummy.com'); - expect(await user.verify(), isTrue); - - final subkey = privateKey.subkeys[0]; - expect(subkey.fingerprint, '38460d0ea0f3da56ccf63e9d0a4e826effaf48a4'); - expect(subkey.algorithm, KeyAlgorithm.ecdh); - expect(await subkey.verify(), isTrue); - }); - }); - - group('Generate key', () { - final name = faker.person.name(); - final email = faker.internet.email().replaceAll("'", ''); - final comment = faker.lorem.words(3).join(' '); - final userID = [name, '($comment)', '<$email>'].join(' '); - final passphrase = faker.internet.password(); - - test('rsa', () async { - final privateKey = await PrivateKey.generate( - [userID], - passphrase, - type: KeyGenerationType.rsa, - ); - expect(privateKey.algorithm, KeyAlgorithm.rsaEncryptSign); - expect(privateKey.isPrivate, isTrue); - expect(privateKey.keyStrength, 4096); - expect(privateKey.keyPacket.validate(), isTrue); - - final user = privateKey.users[0]; - expect(user.userID!.name, name); - expect(user.userID!.email, email); - expect(user.userID!.comment, comment); - expect(await user.verify(), isTrue); - - final subkey = privateKey.subkeys[0]; - expect(subkey.algorithm, KeyAlgorithm.rsaEncryptSign); - expect(await subkey.verify(), isTrue); - - final bindingSignature = subkey.bindingSignatures[0]; - expect(bindingSignature.keyFlags!.isEncryptCommunication, isTrue); - expect(bindingSignature.keyFlags!.isEncryptStorage, isTrue); - - privateKey.addSubkey(passphrase).then((privateKey) async { - final subkey = privateKey.subkeys[1]; - expect(subkey.algorithm, KeyAlgorithm.rsaEncryptSign); - expect(await subkey.verify(), isTrue); - }); - }); - - test('dsa elGamal', () async { - final privateKey = await PrivateKey.generate( - [userID], - passphrase, - type: KeyGenerationType.dsa, - ); - expect(privateKey.algorithm, KeyAlgorithm.dsa); - expect(privateKey.isPrivate, isTrue); - expect(privateKey.keyStrength, 2048); - expect(privateKey.keyPacket.validate(), isTrue); - - final user = privateKey.users[0]; - expect(user.userID!.name, name); - expect(user.userID!.email, email); - expect(user.userID!.comment, comment); - expect(await user.verify(), isTrue); - - final subkey = privateKey.subkeys[0]; - expect(subkey.algorithm, KeyAlgorithm.elgamal); - expect(await subkey.verify(), isTrue); - - final bindingSignature = subkey.bindingSignatures[0]; - expect(bindingSignature.keyFlags!.isEncryptCommunication, isTrue); - expect(bindingSignature.keyFlags!.isEncryptStorage, isTrue); - - privateKey - .addSubkey(passphrase, subkeyAlgorithm: KeyAlgorithm.elgamal) - .then((privateKey) async { - final subkey = privateKey.subkeys[1]; - expect(subkey.algorithm, KeyAlgorithm.elgamal); - expect(await subkey.verify(), isTrue); - }); - }); - - test('prime256v1 curve', () async { - final privateKey = await PrivateKey.generate( - [userID], - passphrase, - type: KeyGenerationType.ecdsa, - curve: CurveInfo.prime256v1, - ); - final publicParams = - privateKey.keyPacket.publicParams as ECDSAPublicParams; - expect(privateKey.algorithm, KeyAlgorithm.ecdsa); - expect(privateKey.isPrivate, isTrue); - expect(privateKey.keyStrength, 256); - expect(privateKey.keyPacket.validate(), isTrue); - expect(publicParams.curve, CurveInfo.prime256v1); - - final user = privateKey.users[0]; - expect(user.userID!.name, name); - expect(user.userID!.email, email); - expect(user.userID!.comment, comment); - expect(await user.verify(), isTrue); - - final subkey = privateKey.subkeys[0]; - expect(subkey.algorithm, KeyAlgorithm.ecdh); - expect(await subkey.verify(), isTrue); - expect(subkey.keyStrength, 256); - - final subkeyPublicParams = - subkey.keyPacket.publicParams as ECDHPublicParams; - expect(subkeyPublicParams.curve, CurveInfo.prime256v1); - expect(subkeyPublicParams.kdfHash, CurveInfo.prime256v1.hashAlgorithm); - expect(subkeyPublicParams.kdfSymmetric, - CurveInfo.prime256v1.symmetricAlgorithm); - }); - - test('secp256k1 curve', () async { - final privateKey = await PrivateKey.generate( - [userID], - passphrase, - type: KeyGenerationType.ecdsa, - curve: CurveInfo.secp256k1, - ); - final publicParams = - privateKey.keyPacket.publicParams as ECDSAPublicParams; - expect(privateKey.algorithm, KeyAlgorithm.ecdsa); - expect(privateKey.isPrivate, isTrue); - expect(privateKey.keyStrength, 256); - expect(privateKey.keyPacket.validate(), isTrue); - expect(publicParams.curve, CurveInfo.secp256k1); - - final user = privateKey.users[0]; - expect(user.userID!.name, name); - expect(user.userID!.email, email); - expect(user.userID!.comment, comment); - expect(await user.verify(), isTrue); - - final subkey = privateKey.subkeys[0]; - expect(subkey.algorithm, KeyAlgorithm.ecdh); - expect(await subkey.verify(), isTrue); - expect(subkey.keyStrength, 256); - - final subkeyPublicParams = - subkey.keyPacket.publicParams as ECDHPublicParams; - expect(subkeyPublicParams.curve, CurveInfo.secp256k1); - expect(subkeyPublicParams.kdfHash, CurveInfo.secp256k1.hashAlgorithm); - expect(subkeyPublicParams.kdfSymmetric, - CurveInfo.secp256k1.symmetricAlgorithm); - }); - - test('secp384r1 curve', () async { - final privateKey = await PrivateKey.generate( - [userID], - passphrase, - type: KeyGenerationType.ecdsa, - curve: CurveInfo.secp384r1, - ); - final publicParams = privateKey.keyPacket.publicParams as ECPublicParams; - expect(privateKey.algorithm, KeyAlgorithm.ecdsa); - expect(privateKey.isPrivate, isTrue); - expect(privateKey.keyStrength, 384); - expect(privateKey.keyPacket.validate(), isTrue); - expect(publicParams.curve, CurveInfo.secp384r1); - - final user = privateKey.users[0]; - expect(user.userID!.name, name); - expect(user.userID!.email, email); - expect(user.userID!.comment, comment); - expect(await user.verify(), isTrue); - - final subkey = privateKey.subkeys[0]; - expect(subkey.algorithm, KeyAlgorithm.ecdh); - expect(await subkey.verify(), isTrue); - expect(subkey.keyStrength, 384); - - final subkeyPublicParams = - subkey.keyPacket.publicParams as ECDHPublicParams; - expect(subkeyPublicParams.curve, CurveInfo.secp384r1); - expect(subkeyPublicParams.kdfHash, CurveInfo.secp384r1.hashAlgorithm); - expect(subkeyPublicParams.kdfSymmetric, - CurveInfo.secp384r1.symmetricAlgorithm); - }); - - test('secp521r1 curve', () async { - final privateKey = await PrivateKey.generate( - [userID], - passphrase, - type: KeyGenerationType.ecdsa, - curve: CurveInfo.secp521r1, - ); - final publicParams = privateKey.keyPacket.publicParams as ECPublicParams; - expect(privateKey.algorithm, KeyAlgorithm.ecdsa); - expect(privateKey.isPrivate, isTrue); - expect(privateKey.keyStrength, 521); - expect(privateKey.keyPacket.validate(), isTrue); - expect(publicParams.curve, CurveInfo.secp521r1); - - final user = privateKey.users[0]; - expect(user.userID!.name, name); - expect(user.userID!.email, email); - expect(user.userID!.comment, comment); - expect(await user.verify(), isTrue); - - final subkey = privateKey.subkeys[0]; - expect(subkey.algorithm, KeyAlgorithm.ecdh); - expect(await subkey.verify(), isTrue); - expect(subkey.keyStrength, 521); - - final subkeyPublicParams = - subkey.keyPacket.publicParams as ECDHPublicParams; - expect(subkeyPublicParams.curve, CurveInfo.secp521r1); - expect(subkeyPublicParams.kdfHash, CurveInfo.secp521r1.hashAlgorithm); - expect(subkeyPublicParams.kdfSymmetric, - CurveInfo.secp521r1.symmetricAlgorithm); - }); - - test('brainpoolp256r1 curve', () async { - final privateKey = await PrivateKey.generate( - [userID], - passphrase, - type: KeyGenerationType.ecdsa, - curve: CurveInfo.brainpoolP256r1, - ); - final publicParams = privateKey.keyPacket.publicParams as ECPublicParams; - expect(privateKey.algorithm, KeyAlgorithm.ecdsa); - expect(privateKey.isPrivate, isTrue); - expect(privateKey.keyStrength, 256); - expect(privateKey.keyPacket.validate(), isTrue); - expect(publicParams.curve, CurveInfo.brainpoolP256r1); - - final user = privateKey.users[0]; - expect(user.userID!.name, name); - expect(user.userID!.email, email); - expect(user.userID!.comment, comment); - expect(await user.verify(), isTrue); - - final subkey = privateKey.subkeys[0]; - expect(subkey.algorithm, KeyAlgorithm.ecdh); - expect(await subkey.verify(), isTrue); - expect(subkey.keyStrength, 256); - - final subkeyPublicParams = - subkey.keyPacket.publicParams as ECDHPublicParams; - expect(subkeyPublicParams.curve, CurveInfo.brainpoolP256r1); - expect( - subkeyPublicParams.kdfHash, CurveInfo.brainpoolP256r1.hashAlgorithm); - expect(subkeyPublicParams.kdfSymmetric, - CurveInfo.brainpoolP256r1.symmetricAlgorithm); - }); - - test('brainpoolp384r1 curve', () async { - final privateKey = await PrivateKey.generate( - [userID], - passphrase, - type: KeyGenerationType.ecdsa, - curve: CurveInfo.brainpoolP384r1, - ); - final publicParams = privateKey.keyPacket.publicParams as ECPublicParams; - expect(privateKey.algorithm, KeyAlgorithm.ecdsa); - expect(privateKey.isPrivate, isTrue); - expect(privateKey.keyStrength, 384); - expect(privateKey.keyPacket.validate(), isTrue); - expect(publicParams.curve, CurveInfo.brainpoolP384r1); - - final user = privateKey.users[0]; - expect(user.userID!.name, name); - expect(user.userID!.email, email); - expect(user.userID!.comment, comment); - expect(await user.verify(), isTrue); - - final subkey = privateKey.subkeys[0]; - expect(subkey.algorithm, KeyAlgorithm.ecdh); - expect(await subkey.verify(), isTrue); - expect(subkey.keyStrength, 384); - - final subkeyPublicParams = - subkey.keyPacket.publicParams as ECDHPublicParams; - expect(subkeyPublicParams.curve, CurveInfo.brainpoolP384r1); - expect( - subkeyPublicParams.kdfHash, CurveInfo.brainpoolP384r1.hashAlgorithm); - expect(subkeyPublicParams.kdfSymmetric, - CurveInfo.brainpoolP384r1.symmetricAlgorithm); - }); - - test('brainpoolp512r1 curve', () async { - final privateKey = await PrivateKey.generate( - [userID], - passphrase, - type: KeyGenerationType.ecdsa, - curve: CurveInfo.brainpoolP512r1, - ); - final publicParams = privateKey.keyPacket.publicParams as ECPublicParams; - expect(privateKey.algorithm, KeyAlgorithm.ecdsa); - expect(privateKey.isPrivate, isTrue); - expect(privateKey.keyStrength, 512); - expect(privateKey.keyPacket.validate(), isTrue); - expect(publicParams.curve, CurveInfo.brainpoolP512r1); - - final user = privateKey.users[0]; - expect(user.userID!.name, name); - expect(user.userID!.email, email); - expect(user.userID!.comment, comment); - expect(await user.verify(), isTrue); - - final subkey = privateKey.subkeys[0]; - expect(subkey.algorithm, KeyAlgorithm.ecdh); - expect(await subkey.verify(), isTrue); - expect(subkey.keyStrength, 512); - - final subkeyPublicParams = - subkey.keyPacket.publicParams as ECDHPublicParams; - expect(subkeyPublicParams.curve, CurveInfo.brainpoolP512r1); - expect( - subkeyPublicParams.kdfHash, CurveInfo.brainpoolP512r1.hashAlgorithm); - expect(subkeyPublicParams.kdfSymmetric, - CurveInfo.brainpoolP512r1.symmetricAlgorithm); - - privateKey - .addSubkey(passphrase, - subkeyAlgorithm: KeyAlgorithm.ecdh, curve: CurveInfo.secp521r1) - .then((privateKey) async { - final subkey = privateKey.subkeys[1]; - expect(subkey.algorithm, KeyAlgorithm.ecdh); - expect(await subkey.verify(), isTrue); - - final publicParams = subkey.publicParams as ECDHPublicParams; - expect(publicParams.curve, CurveInfo.secp521r1); - }); - }); - - test('curve25519', () async { - final privateKey = await PrivateKey.generate( - [userID], - passphrase, - type: KeyGenerationType.eddsa, - ); - final publicParams = - privateKey.keyPacket.publicParams as EdDSAPublicParams; - expect(privateKey.algorithm, KeyAlgorithm.eddsa); - expect(privateKey.isPrivate, isTrue); - expect(privateKey.keyStrength, 255); - expect(privateKey.keyPacket.validate(), isTrue); - expect(publicParams.curve, CurveInfo.ed25519); - - final user = privateKey.users[0]; - expect(user.userID!.name, name); - expect(user.userID!.email, email); - expect(user.userID!.comment, comment); - expect(await user.verify(), isTrue); - - final subkey = privateKey.subkeys[0]; - expect(subkey.algorithm, KeyAlgorithm.ecdh); - expect(await subkey.verify(), isTrue); - expect(subkey.keyStrength, 255); - - final subkeyPublicParams = - subkey.keyPacket.publicParams as ECDHPublicParams; - expect(subkeyPublicParams.curve, CurveInfo.curve25519); - expect(subkeyPublicParams.kdfHash, CurveInfo.curve25519.hashAlgorithm); - expect(subkeyPublicParams.kdfSymmetric, - CurveInfo.curve25519.symmetricAlgorithm); - - privateKey - .addSubkey(passphrase, - subkeyAlgorithm: KeyAlgorithm.ecdh, curve: CurveInfo.curve25519) - .then((privateKey) async { - final subkey = privateKey.subkeys[1]; - expect(subkey.algorithm, KeyAlgorithm.ecdh); - expect(await subkey.verify(), isTrue); - - final publicParams = subkey.publicParams as ECDHPublicParams; - expect(publicParams.curve, CurveInfo.curve25519); - }); - }); - }); -} diff --git a/test/type/message_test.dart b/test/type/message_test.dart deleted file mode 100644 index 97758d11..00000000 --- a/test/type/message_test.dart +++ /dev/null @@ -1,394 +0,0 @@ -import 'dart:convert'; - -import 'package:dart_pg/src/enum/compression_algorithm.dart'; -import 'package:dart_pg/src/packet/aead_encrypted_data.dart'; -import 'package:dart_pg/src/packet/compressed_data.dart'; -import 'package:dart_pg/src/packet/public_key_encrypted_session_key.dart'; -import 'package:dart_pg/src/packet/sym_encrypted_integrity_protected_data.dart'; -import 'package:dart_pg/src/packet/sym_encrypted_session_key.dart'; -import 'package:dart_pg/src/type/cleartext_message.dart'; -import 'package:dart_pg/src/type/key.dart'; -import 'package:dart_pg/src/type/message.dart'; -import 'package:dart_pg/src/type/signed_message.dart'; -import 'package:faker/faker.dart'; -import 'package:test/test.dart'; - -import '../data/key_data.dart'; - -void main() { - group('Cleartext signing', () { - final signingKey = PrivateKey.fromArmored(privateKey); - final verificationKey = PublicKey.fromArmored(publicKey); - final text = faker.randomGenerator.string(1000); - - test('atached test', () async { - final signedMessage = - await SignedMessage.signCleartext(text, [signingKey]); - final verifiedMessage = await signedMessage.verify([verificationKey]); - final signature = signedMessage.signature; - - expect(signedMessage.verifications.isEmpty, isTrue); - expect(verifiedMessage.verifications.isNotEmpty, isTrue); - - for (final verification in verifiedMessage.verifications) { - expect(verification.keyID, verificationKey.keyID.id); - expect(verification.verified, isTrue); - - expect( - signature.packets.elementAt(0).signatureData, - equals(verification.signature.packets.elementAt(0).signatureData), - ); - } - }); - - test('detached test', () async { - final signature = await SignedMessage.signCleartext(text, [signingKey]) - .then((signedMessage) => signedMessage.signature); - final cleartextMessage = CleartextMessage(text); - final verifiedMessage = - await cleartextMessage.verifySignature(signature, [verificationKey]); - - expect(verifiedMessage.verifications.isNotEmpty, isTrue); - for (final verification in verifiedMessage.verifications) { - expect(verification.keyID, verificationKey.keyID.id); - expect(verification.verified, isTrue); - - expect( - signature.packets.elementAt(0).signatureData, - equals(verification.signature.packets.elementAt(0).signatureData), - ); - } - }); - }); - - group('Message signing', () { - final signingKey = PrivateKey.fromArmored(privateKey); - final verificationKey = PublicKey.fromArmored(publicKey); - final text = faker.randomGenerator.string(1000); - - test('atached test', () async { - final signedMessage = - await Message.createTextMessage(text).sign([signingKey]); - expect(signedMessage.signingKeyIDs.elementAt(0).id, signingKey.keyID.id); - expect(signedMessage.verifications.isEmpty, isTrue); - - final verifiedMessage = await signedMessage.verify([verificationKey]); - final signaturePackets = signedMessage.signaturePackets; - expect(verifiedMessage.verifications.isNotEmpty, isTrue); - for (final verification in verifiedMessage.verifications) { - expect(verification.keyID, verificationKey.keyID.id); - expect(verification.verified, isTrue); - - expect( - signaturePackets.elementAt(0).signatureData, - equals(verification.signature.packets.elementAt(0).signatureData), - ); - } - }); - - test('detached test', () async { - final message = Message.createTextMessage(text); - final signature = await message.signDetached([signingKey]); - final verifiedMessage = - await message.verifySignature(signature, [verificationKey]); - - expect(verifiedMessage.verifications.isNotEmpty, isTrue); - for (final verification in verifiedMessage.verifications) { - expect(verification.keyID, verificationKey.keyID.id); - expect(verification.verified, isTrue); - - expect( - signature.packets.elementAt(0).signatureData, - equals(verification.signature.packets.elementAt(0).signatureData), - ); - } - }); - }); - - group('Message compression', () { - final text = faker.randomGenerator.string(1000); - - test('zip test', () async { - final compressedMessage = await Message.createTextMessage(text) - .compress(CompressionAlgorithm.zip); - expect(compressedMessage.packetList.length, 1); - expect( - compressedMessage.packetList - .whereType() - .elementAt(0) - .algorithm, - CompressionAlgorithm.zip); - - final message = compressedMessage.unwrapCompressed(); - expect(message.packetList.whereType(), isEmpty); - expect(message.literalData, isNotNull); - expect(message.literalData!.text, text); - }); - - test('zlib test', () async { - final compressedMessage = await Message.createTextMessage(text) - .compress(CompressionAlgorithm.zlib); - expect(compressedMessage.packetList.length, 1); - expect( - compressedMessage.packetList - .whereType() - .elementAt(0) - .algorithm, - CompressionAlgorithm.zlib); - - final message = compressedMessage.unwrapCompressed(); - expect(message.packetList.whereType(), isEmpty); - expect(message.literalData, isNotNull); - expect(message.literalData!.text, text); - }); - - test('bzip2 test', () { - expect( - () => Message.createTextMessage(text) - .compress(CompressionAlgorithm.bzip2), - throwsUnsupportedError, - ); - }); - }); - - group('Message encryption', () { - final signingKey = PrivateKey.fromArmored(privateKey); - final verificationKey = PublicKey.fromArmored(publicKey); - final encryptionKeys = [ - PublicKey.fromArmored(rsaPublicKey), - PublicKey.fromArmored(dsaPublicKey), - PublicKey.fromArmored(eccPublicKey), - PublicKey.fromArmored(curve25519PublicKey), - ]; - final password = faker.randomGenerator.string(100); - - final text = faker.randomGenerator.string(1000); - final createTextMessage = Message.createTextMessage(text); - final signedMessage = createTextMessage.sign([signingKey]); - final encryptedMessage = signedMessage.then( - (signedMessage) => signedMessage - .encrypt(encryptionKeys: encryptionKeys, passwords: [password]), - ); - - test('encrypted test', () { - encryptedMessage.then((message) { - expect(message.literalData, isNull); - expect( - message.packetList - .whereType() - .length, - encryptionKeys.length); - expect( - message.packetList.whereType().length, - 1); - expect( - message.packetList - .whereType(), - isNotEmpty); - }); - }); - - test('password only test', () async { - final encryptedMessage = - await createTextMessage.encrypt(passwords: [password]); - expect(encryptedMessage.literalData, isNull); - expect( - encryptedMessage.packetList.whereType(), - isNotEmpty); - expect( - encryptedMessage.packetList - .whereType(), - isNotEmpty); - - final decryptedMessage = - await encryptedMessage.decrypt(passwords: [password]); - expect(decryptedMessage.literalData, isNotNull); - expect(decryptedMessage.literalData!.text, text); - }); - - test('password decrypt test', () async { - final decryptedMessage = await encryptedMessage - .then((message) => message.decrypt(passwords: [password])); - expect(decryptedMessage.literalData, isNotNull); - expect(decryptedMessage.literalData!.text, text); - - final verifiedMessage = await decryptedMessage.verify([verificationKey]); - final signaturePackets = await signedMessage - .then((signedMessage) => signedMessage.signaturePackets); - expect(verifiedMessage.verifications.isNotEmpty, isTrue); - for (final verification in verifiedMessage.verifications) { - expect(verification.keyID, verificationKey.keyID.id); - expect(verification.verified, isTrue); - - expect( - signaturePackets.elementAt(0).signatureData, - equals(verification.signature.packets.elementAt(0).signatureData), - ); - } - }); - - test('rsa decrypt test', () async { - final decryptionKey = - await PrivateKey.fromArmored(rsaPrivateKey).decrypt(passphrase); - final decryptedMessage = await encryptedMessage - .then((message) => message.decrypt(decryptionKeys: [decryptionKey])); - expect(decryptedMessage.literalData, isNotNull); - expect(decryptedMessage.literalData!.text, text); - - final verifiedMessage = await decryptedMessage.verify([verificationKey]); - final signaturePackets = await signedMessage - .then((signedMessage) => signedMessage.signaturePackets); - expect(verifiedMessage.verifications.isNotEmpty, isTrue); - for (final verification in verifiedMessage.verifications) { - expect(verification.keyID, verificationKey.keyID.id); - expect(verification.verified, isTrue); - - expect( - signaturePackets.elementAt(0).signatureData, - equals(verification.signature.packets.elementAt(0).signatureData), - ); - } - }); - - test('elgamal decrypt test', () async { - final decryptionKey = - await PrivateKey.fromArmored(dsaPrivateKey).decrypt(passphrase); - final decryptedMessage = await encryptedMessage - .then((message) => message.decrypt(decryptionKeys: [decryptionKey])); - expect(decryptedMessage.literalData, isNotNull); - expect(decryptedMessage.literalData!.text, text); - - final verifiedMessage = await decryptedMessage.verify([verificationKey]); - final signaturePackets = await signedMessage - .then((signedMessage) => signedMessage.signaturePackets); - expect(verifiedMessage.verifications.isNotEmpty, isTrue); - for (final verification in verifiedMessage.verifications) { - expect(verification.keyID, verificationKey.keyID.id); - expect(verification.verified, isTrue); - - expect( - signaturePackets.elementAt(0).signatureData, - equals(verification.signature.packets.elementAt(0).signatureData), - ); - } - }); - - test('ecc decrypt test', () async { - final decryptionKey = - await PrivateKey.fromArmored(eccPrivateKey).decrypt(passphrase); - final decryptedMessage = await encryptedMessage - .then((message) => message.decrypt(decryptionKeys: [decryptionKey])); - expect(decryptedMessage.literalData, isNotNull); - expect(decryptedMessage.literalData!.text, text); - - final verifiedMessage = await decryptedMessage.verify([verificationKey]); - final signaturePackets = await signedMessage - .then((signedMessage) => signedMessage.signaturePackets); - expect(verifiedMessage.verifications.isNotEmpty, isTrue); - for (final verification in verifiedMessage.verifications) { - expect(verification.keyID, verificationKey.keyID.id); - expect(verification.verified, isTrue); - - expect( - signaturePackets.elementAt(0).signatureData, - equals(verification.signature.packets.elementAt(0).signatureData), - ); - } - }); - - test('curve25519 decrypt test', () async { - final decryptionKey = await PrivateKey.fromArmored(curve25519PrivateKey) - .decrypt(passphrase); - final decryptedMessage = await encryptedMessage - .then((message) => message.decrypt(decryptionKeys: [decryptionKey])); - expect(decryptedMessage.literalData, isNotNull); - expect(decryptedMessage.literalData!.text, text); - - final verifiedMessage = await decryptedMessage.verify([verificationKey]); - final signaturePackets = await signedMessage - .then((signedMessage) => signedMessage.signaturePackets); - expect(verifiedMessage.verifications.isNotEmpty, isTrue); - for (final verification in verifiedMessage.verifications) { - expect(verification.keyID, verificationKey.keyID.id); - expect(verification.verified, isTrue); - - expect( - signaturePackets.elementAt(0).signatureData, - equals(verification.signature.packets.elementAt(0).signatureData), - ); - } - }); - - test('aead decrypt message test', () async { - const encryptedMessageData = ''' ------BEGIN PGP MESSAGE----- - -jD0FBwIDCBt851vLHmcX/6vdNoPTtUfAzChVu81xM5XTozdap+0i087ojXBfNnI+ -E8PyCSTHDlgN4vWJzBS+1E0BBwIQCicPh3seliAmtmPxL4M/qEj7bIX0kgtygRa3 -g68tHRXcVN6USJbvuTWuIwy8eaCwrRdG3pF3b5BADyHl3nsINR9KysPKuM1AaQ== -=STC7 ------END PGP MESSAGE----- -'''; - final decryptedMessage = await Message.fromArmored(encryptedMessageData) - .decrypt(passwords: ['password']); - expect( - utf8.decode(decryptedMessage.literalData!.data), "Hello Dart PG\n"); - }); - - test('aead encrypt message test', () async { - final createTextMessage = Message.createTextMessage(text); - final signedMessage = await createTextMessage.sign([signingKey]); - final encryptedMessage = await signedMessage.encrypt( - encryptionKeys: encryptionKeys, - passwords: [password], - aeadProtect: true, - ); - - expect(encryptedMessage.literalData, isNull); - expect( - encryptedMessage.packetList - .whereType() - .length, - encryptionKeys.length); - expect( - encryptedMessage.packetList - .whereType() - .length, - 1); - expect(encryptedMessage.packetList.whereType(), - isNotEmpty); - - var decryptedMessage = - await encryptedMessage.decrypt(passwords: [password]); - expect(utf8.decode(decryptedMessage.literalData!.data), text); - - var decryptionKey = - await PrivateKey.fromArmored(rsaPrivateKey).decrypt(passphrase); - decryptedMessage = - await encryptedMessage.decrypt(decryptionKeys: [decryptionKey]); - expect(decryptedMessage.literalData, isNotNull); - expect(decryptedMessage.literalData!.text, text); - - decryptionKey = - await PrivateKey.fromArmored(dsaPrivateKey).decrypt(passphrase); - decryptedMessage = - await encryptedMessage.decrypt(decryptionKeys: [decryptionKey]); - expect(decryptedMessage.literalData, isNotNull); - expect(decryptedMessage.literalData!.text, text); - - decryptionKey = - await PrivateKey.fromArmored(eccPrivateKey).decrypt(passphrase); - decryptedMessage = - await encryptedMessage.decrypt(decryptionKeys: [decryptionKey]); - expect(decryptedMessage.literalData, isNotNull); - expect(decryptedMessage.literalData!.text, text); - - decryptionKey = await PrivateKey.fromArmored(curve25519PrivateKey) - .decrypt(passphrase); - decryptedMessage = - await encryptedMessage.decrypt(decryptionKeys: [decryptionKey]); - expect(decryptedMessage.literalData, isNotNull); - expect(decryptedMessage.literalData!.text, text); - }); - }); -} From 216f38b6f6a948e50dff7a1d927fa88c945e5480 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 19 Nov 2024 11:05:06 +0700 Subject: [PATCH 004/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/packet_reader.dart | 2 +- .../public_key_encrypted_session_key.dart | 2 +- ...ym_encrypted_integrity_protected_data.dart | 23 ++++++++++++------- lib/src/packet/sym_encrypted_session_key.dart | 2 +- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/src/packet/packet_reader.dart b/lib/src/packet/packet_reader.dart index 4fb9c2f5..369e940b 100644 --- a/lib/src/packet/packet_reader.dart +++ b/lib/src/packet/packet_reader.dart @@ -22,7 +22,7 @@ final class PacketReader { factory PacketReader.read(final Uint8List bytes, [final int offset = 0]) { if (bytes.length <= offset || bytes.sublist(offset).length < 2 || (bytes[offset] & 0x80) == 0) { - throw StateError( + throw ArgumentError( 'Error during parsing. This data probably does not conform to a valid OpenPGP format.', ); } diff --git a/lib/src/packet/public_key_encrypted_session_key.dart b/lib/src/packet/public_key_encrypted_session_key.dart index 6c656354..0313ad2e 100644 --- a/lib/src/packet/public_key_encrypted_session_key.dart +++ b/lib/src/packet/public_key_encrypted_session_key.dart @@ -49,7 +49,7 @@ class PublicKeyEncryptedSessionKeyPacket extends BasePacket { ); } if (version == 6 && keyAlgorithm == KeyAlgorithm.elgamal) { - throw UnsupportedError( + throw ArgumentError( 'Key algorithm ${keyAlgorithm.name} cannot be used with version {$version} PKESK packet.', ); } diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index 4b48fd12..8ef547d6 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -58,14 +58,21 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket { Helper.assertSymmetric(symmetric!); } - if (aead != null && version != 2) { - throw StateError( - 'Using AEAD with version $version SEIPD packet is not allowed.', - ); + if (aead != null) { + if (version != 2) { + throw ArgumentError( + 'Using AEAD with version $version SEIPD packet is not allowed.', + ); + } + if (chunkSize <= 0) { + throw ArgumentError( + 'Chunk size must be greater than zero.', + ); + } } if (salt != null && salt!.length != saltSize) { - throw StateError( + throw ArgumentError( 'Salt size must be $saltSize bytes.', ); } @@ -127,7 +134,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket { }) { Helper.assertSymmetric(symmetric); - final version = aeadProtect ? 2 : 1; + final version = aeadProtect || Config.useV6Key ? 2 : 1; final salt = aeadProtect ? Helper.secureRandom().nextBytes(saltSize) : null; final chunkSize = aeadProtect ? Config.aeadChunkSize : 0; @@ -166,8 +173,8 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket { version, encrypted, packets: packets, - symmetric: symmetric, - aead: aead, + symmetric: version == 2 ? symmetric : null, + aead: aeadProtect ? aead : null, chunkSize: chunkSize, salt: salt, ); diff --git a/lib/src/packet/sym_encrypted_session_key.dart b/lib/src/packet/sym_encrypted_session_key.dart index 7a248242..402e3411 100644 --- a/lib/src/packet/sym_encrypted_session_key.dart +++ b/lib/src/packet/sym_encrypted_session_key.dart @@ -58,7 +58,7 @@ class SymEncryptedSessionKeyPacket extends BasePacket { Helper.assertSymmetric(symmetric); } if (aead != null && version < 5) { - throw StateError( + throw ArgumentError( 'Using AEAD with version $version SKESK packet is not allowed.', ); } From 93f452ab620dafb981ddadfaba22adde0e1f9beb Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 19 Nov 2024 11:08:15 +0700 Subject: [PATCH 005/202] WIP Signed-off-by: Nguyen Van Nguyen --- README.md | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 8a707851..978c4349 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,33 @@ Dart PG (Dart Privacy Guard) - The OpenPGP library in Dart language =================================================================== Dart PG is an implementation of the OpenPGP standard in Dart language. -It implements [RFC4880](https://www.rfc-editor.org/rfc/rfc4880), [RFC5581](https://www.rfc-editor.org/rfc/rfc5581), [RFC6637](https://www.rfc-editor.org/rfc/rfc6637), -parts of [RFC4880bis](https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-rfc4880bis). +It implements [RFC 9580](https://www.rfc-editor.org/rfc/rfc9580) and +provides encryption with public key or symmetric cryptographic algorithms, +digital signatures, compression, and key management. ## Features * Support data signing & encryption. * Support key management: key generation, key reading, key decryption. -* Support public-key algorithms: [RSA](https://en.wikipedia.org/wiki/RSA_(cryptosystem)), - [DSA](https://en.wikipedia.org/wiki/Digital_Signature_Algorithm), - [ElGamal](https://en.wikipedia.org/wiki/ElGamal_encryption), - [ECDSA](https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm), - [EdDSA](https://en.wikipedia.org/wiki/EdDSA) +* Support public-key algorithms: [RSA](https://www.rfc-editor.org/rfc/rfc3447), + [ECDSA](https://www.rfc-editor.org/rfc/rfc6979), + [EdDSA](https://www.rfc-editor.org/rfc/rfc8032) and [ECDH](https://en.wikipedia.org/wiki/Elliptic-curve_Diffie%E2%80%93Hellman). -* Support symmetric ciphers: 3DES, IDEA, CAST5, Blowfish, Twofish, - [AES](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard), - [Camellia](https://en.wikipedia.org/wiki/Camellia_(cipher)). -* Support AEAD algorithms: [EAX](https://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf), [OCB](https://tools.ietf.org/html/rfc7253), [GCM](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf). -* Support hash algorithms: MD5, SHA-1, RIPEMD-160, SHA-256, SHA-384, SHA-512, SHA-224. -* Support compression algorithms: ZIP, ZLIB. +* Support symmetric ciphers: Blowfish, Twofish, + [AES](https://www.rfc-editor.org/rfc/rfc3394), + [Camellia](https://www.rfc-editor.org/rfc/rfc3713). +* Support AEAD ciphers: [EAX](https://seclab.cs.ucdavis.edu/papers/eax.pdf), + [OCB](https://tools.ietf.org/html/rfc7253), + [GCM](https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf). +* Support hash algorithms: SHA-256, SHA-384, SHA-512, SHA-224, SHA3-256, SHA3-512. +* Support compression algorithms: Zip, Zlib, BZip2. * Support [ECC](https://en.wikipedia.org/wiki/Elliptic-curve_cryptography) curves: - [secP256k1, secP384r1, secP521r1](https://www.rfc-editor.org/rfc/rfc6090), + [secp256r1, secp384r1, secp521r1](https://www.rfc-editor.org/rfc/rfc6090), [brainpoolP256r1, brainpoolP384r1, brainpoolP512r1](https://www.rfc-editor.org/rfc/rfc5639), - [curve25519](https://www.rfc-editor.org/rfc/rfc7748), [ed25519](https://www.rfc-editor.org/rfc/rfc8032), - [prime256v1](https://www.secg.org/sec2-v2.pdf). + [Curve25519, Curve448](https://www.rfc-editor.org/rfc/rfc7748), + [Ed25519, Ed448](https://www.rfc-editor.org/rfc/rfc8032). +* Support public-key algorithms, symmetric ciphers & hash algorithms + for signature verification & message decryption (backward compatibility): + DSA, ElGamal, TripleDES, IDEA, CAST5, MD5, SHA-1, RIPEMD-160. ## Getting started In `Dart` or `Flutter` project add the dependency: From 5455a048220aa0011b93782d7cc23d18ebe955b1 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 19 Nov 2024 11:14:36 +0700 Subject: [PATCH 006/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/enum/ecc.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/src/enum/ecc.dart b/lib/src/enum/ecc.dart index 2cb2d41c..96dde56e 100644 --- a/lib/src/enum/ecc.dart +++ b/lib/src/enum/ecc.dart @@ -13,8 +13,7 @@ import 'symmetric_algorithm.dart'; /// See https://www.rfc-editor.org/rfc/rfc9580#section-9.2 /// Author Nguyen Van Nguyen enum Ecc { - prime256v1([1, 2, 840, 10045, 3, 1, 7], '1.2.840.10045.3.1.7'), - secp256k1([1, 3, 132, 0, 10], '1.3.132.0.10'), + secp256r1([1, 2, 840, 10045, 3, 1, 7], '1.2.840.10045.3.1.7'), secp384r1([1, 3, 132, 0, 34], '1.3.132.0.34'), secp521r1([1, 3, 132, 0, 35], '1.3.132.0.35'), brainpoolP256r1([1, 3, 36, 3, 3, 2, 8, 1, 1, 7], '1.3.36.3.3.2.8.1.1.7'), @@ -32,13 +31,13 @@ enum Ecc { ASN1ObjectIdentifier get asn1Oid => ASN1ObjectIdentifier(identifier); HashAlgorithm get hashAlgorithm => switch (this) { - brainpoolP256r1 || curve25519 || prime256v1 || secp256k1 => HashAlgorithm.sha256, + brainpoolP256r1 || curve25519 || secp256r1 => HashAlgorithm.sha256, brainpoolP384r1 || secp384r1 => HashAlgorithm.sha384, brainpoolP512r1 || ed25519 || secp521r1 => HashAlgorithm.sha512, }; SymmetricAlgorithm get symmetricAlgorithm => switch (this) { - brainpoolP256r1 || curve25519 || ed25519 || prime256v1 || secp256k1 => SymmetricAlgorithm.aes128, + brainpoolP256r1 || curve25519 || ed25519 || secp256r1 => SymmetricAlgorithm.aes128, brainpoolP384r1 || secp384r1 => SymmetricAlgorithm.aes192, brainpoolP512r1 || secp521r1 => SymmetricAlgorithm.aes256, }; From 989eb94d94dd235e0dfa22936a80cc538ea2352f Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 19 Nov 2024 14:02:26 +0700 Subject: [PATCH 007/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/enum/key_flag.dart | 34 +++++ lib/src/enum/revocation_key_tag.dart | 16 +++ lib/src/enum/revocation_reason_tag.dart | 28 ++++ lib/src/enum/signature_subpacket_type.dart | 44 ++++++ lib/src/enum/signature_type.dart | 128 ++++++++++++++++++ lib/src/enum/support_feature.dart | 25 ++++ lib/src/packet/signature.dart | 19 +++ .../packet/signature/embedded_signature.dart | 20 +++ .../signature/exportable_certification.dart | 33 +++++ lib/src/packet/signature/features.dart | 37 +++++ .../packet/signature/issuer_fingerprint.dart | 32 +++++ lib/src/packet/signature/issuer_key_id.dart | 30 ++++ .../packet/signature/key_expiration_time.dart | 35 +++++ lib/src/packet/signature/key_flags.dart | 58 ++++++++ .../signature/key_server_preferences.dart | 36 +++++ lib/src/packet/signature/notation_data.dart | 102 ++++++++++++++ lib/src/packet/signature/policy_uri.dart | 31 +++++ .../signature/preferred_aead_algorithms.dart | 27 ++++ .../preferred_compression_algorithms.dart | 28 ++++ .../signature/preferred_hash_algorithms.dart | 25 ++++ .../signature/preferred_key_server.dart | 30 ++++ .../preferred_symmetric_algorithms.dart | 25 ++++ lib/src/packet/signature/primary_user_id.dart | 26 ++++ .../packet/signature/regular_expression.dart | 28 ++++ lib/src/packet/signature/revocable.dart | 32 +++++ lib/src/packet/signature/revocation_key.dart | 52 +++++++ .../packet/signature/revocation_reason.dart | 45 ++++++ .../signature/signature_creation_time.dart | 30 ++++ .../signature/signature_expiration_time.dart | 31 +++++ .../packet/signature/signature_target.dart | 51 +++++++ lib/src/packet/signature/signer_user_id.dart | 26 ++++ lib/src/packet/signature/trust_signature.dart | 32 +++++ lib/src/packet/signature_subpacket.dart | 52 +++++++ 33 files changed, 1248 insertions(+) create mode 100644 lib/src/enum/key_flag.dart create mode 100644 lib/src/enum/revocation_key_tag.dart create mode 100644 lib/src/enum/revocation_reason_tag.dart create mode 100644 lib/src/enum/signature_subpacket_type.dart create mode 100644 lib/src/enum/signature_type.dart create mode 100644 lib/src/enum/support_feature.dart create mode 100644 lib/src/packet/signature.dart create mode 100644 lib/src/packet/signature/embedded_signature.dart create mode 100644 lib/src/packet/signature/exportable_certification.dart create mode 100644 lib/src/packet/signature/features.dart create mode 100644 lib/src/packet/signature/issuer_fingerprint.dart create mode 100644 lib/src/packet/signature/issuer_key_id.dart create mode 100644 lib/src/packet/signature/key_expiration_time.dart create mode 100644 lib/src/packet/signature/key_flags.dart create mode 100644 lib/src/packet/signature/key_server_preferences.dart create mode 100644 lib/src/packet/signature/notation_data.dart create mode 100644 lib/src/packet/signature/policy_uri.dart create mode 100644 lib/src/packet/signature/preferred_aead_algorithms.dart create mode 100644 lib/src/packet/signature/preferred_compression_algorithms.dart create mode 100644 lib/src/packet/signature/preferred_hash_algorithms.dart create mode 100644 lib/src/packet/signature/preferred_key_server.dart create mode 100644 lib/src/packet/signature/preferred_symmetric_algorithms.dart create mode 100644 lib/src/packet/signature/primary_user_id.dart create mode 100644 lib/src/packet/signature/regular_expression.dart create mode 100644 lib/src/packet/signature/revocable.dart create mode 100644 lib/src/packet/signature/revocation_key.dart create mode 100644 lib/src/packet/signature/revocation_reason.dart create mode 100644 lib/src/packet/signature/signature_creation_time.dart create mode 100644 lib/src/packet/signature/signature_expiration_time.dart create mode 100644 lib/src/packet/signature/signature_target.dart create mode 100644 lib/src/packet/signature/signer_user_id.dart create mode 100644 lib/src/packet/signature/trust_signature.dart create mode 100644 lib/src/packet/signature_subpacket.dart diff --git a/lib/src/enum/key_flag.dart b/lib/src/enum/key_flag.dart new file mode 100644 index 00000000..cc511095 --- /dev/null +++ b/lib/src/enum/key_flag.dart @@ -0,0 +1,34 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +/// Key flag enum +/// Author Nguyen Van Nguyen +enum KeyFlag { + /// 0x01 - This key may be used to certify other keys. + certifyKeys(1), + + /// 0x02 - This key may be used to sign data. + signData(2), + + /// 0x04 - This key may be used to encrypt communications. + encryptCommunication(4), + + /// 0x08 - This key may be used to encrypt storage. + encryptStorage(8), + + /// 0x10 - The private component of this key may have been split by a secret-sharing mechanism. + splitPrivateKey(16), + + /// 0x20 - This key may be used for authentication. + authentication(32), + + /// 0x80 - The private component of this key may be in the possession of more than one person. + sharedPrivateKey(128); + + final int value; + + const KeyFlag(this.value); +} diff --git a/lib/src/enum/revocation_key_tag.dart b/lib/src/enum/revocation_key_tag.dart new file mode 100644 index 00000000..41fbc3d4 --- /dev/null +++ b/lib/src/enum/revocation_key_tag.dart @@ -0,0 +1,16 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +/// Revocation key tag enum +/// Author Nguyen Van Nguyen +enum RevocationKeyTag { + classDefault(128), + classSensitive(64); + + final int value; + + const RevocationKeyTag(this.value); +} diff --git a/lib/src/enum/revocation_reason_tag.dart b/lib/src/enum/revocation_reason_tag.dart new file mode 100644 index 00000000..1186a097 --- /dev/null +++ b/lib/src/enum/revocation_reason_tag.dart @@ -0,0 +1,28 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +/// Reason for Revocation +/// Author Nguyen Van Nguyen +enum RevocationReasonTag { + /// No reason specified (key revocations or cert revocations) + noReason(0), + + /// Key is superseded (key revocations) + keySuperseded(1), + + /// Key material has been compromised (key revocations) + keyCompromised(2), + + /// Key is retired and no longer used (key revocations) + keyRetired(3), + + /// User ID information is no longer valid (cert revocations) + userIDInvalid(32); + + final int value; + + const RevocationReasonTag(this.value); +} diff --git a/lib/src/enum/signature_subpacket_type.dart b/lib/src/enum/signature_subpacket_type.dart new file mode 100644 index 00000000..fb4570e4 --- /dev/null +++ b/lib/src/enum/signature_subpacket_type.dart @@ -0,0 +1,44 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +/// Signature subpacket type enum +/// Author Nguyen Van Nguyen +enum SignatureSubpacketType { + signatureCreationTime(2), + signatureExpirationTime(3), + exportableCertification(4), + trustSignature(5), + regularExpression(6), + revocable(7), + keyExpirationTime(9), + placeholderBackwardCompatibility(10), + preferredSymmetricAlgorithms(11), + revocationKey(12), + issuerKeyID(16), + notationData(20), + preferredHashAlgorithms(21), + preferredCompressionAlgorithms(22), + keyServerPreferences(23), + preferredKeyServer(24), + primaryUserID(25), + policyURI(26), + keyFlags(27), + signerUserID(28), + revocationReason(29), + features(30), + signatureTarget(31), + embeddedSignature(32), + issuerFingerprint(33), + preferredAeadAlgorithms(34), + intendedRecipientFingerprint(35), + attestedCertifications(37), + keyBlock(38), + preferredAeadCiphers(39); + + final int value; + + const SignatureSubpacketType(this.value); +} diff --git a/lib/src/enum/signature_type.dart b/lib/src/enum/signature_type.dart new file mode 100644 index 00000000..ca71fb06 --- /dev/null +++ b/lib/src/enum/signature_type.dart @@ -0,0 +1,128 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +///Signature type enum +/// Author Nguyen Van Nguyen +enum SignatureType { + /// Signature of a binary document. + binary(0), + + /// Signature of a canonical text document. + /// + /// Canonicalyzing the document by converting line endings. + text(1), + + /// Standalone signature. + /// + /// This signature is a signature of only its own subpacket contents. + /// It is calculated identically to a signature over a zero-lengh binary document. + /// Note that it doesn't make sense to have a V3 standalone signature. + standalone(2), + + /// Generic certification of a User ID and Public-Key packet. + /// + /// The issuer of this certification does not make any particular + /// assertion as to how well the certifier has checked that the owner + /// of the key is in fact the person described by the User ID. + certGeneric(16), + + /// Persona certification of a User ID and Public-Key packet. + /// + /// The issuer of this certification has not done any verification of + /// the claim that the owner of this key is the User ID specified. + certPersona(17), + + /// Casual certification of a User ID and Public-Key packet. + /// + /// The issuer of this certification has done some casual verification of the claim of identity. + certCasual(18), + + /// Positive certification of a User ID and Public-Key packet. + /// + /// The issuer of this certification has done substantial verification of the claim of identity. + /// Most OpenPGP implementations make their "key signatures" as 0x10 certifications. + /// Some implementations can issue 0x11-0x13 certifications, but few differentiate between the types. + certPositive(19), + + /// Certification revocation signature. + /// + /// This signature revokes an earlier User ID certification signature + /// (signature class 0x10 through 0x13) or direct-key signature (0x1F). + /// It should be issued by the same key that issued the revoked signature or an authorized revocation key. + /// The signature is computed over the same data as the certificate that it + /// revokes, and should have a later creation date than that certificate. + certRevocation(48), + + /// Subkey binding signature. + /// + /// This signature is a statement by the top-level signing key that indicates that it owns the subkey. + /// This signature is calculated directly on the primary key and subkey, + /// and not on any User ID or other packets. + /// A signature that binds a signing subkey MUST have an Embedded Signature subpacket in this binding signature + /// that contains a 0x19 signature made by the signing subkey on the primary key and subkey. + subkeyBinding(24), + + /// Primary Key Binding Signature + /// + /// This signature is a statement by a signing subkey, indicating + /// that it is owned by the primary key and subkey. + /// This signature is calculated the same way as a 0x18 signature: directly on the + /// primary key and subkey, and not on any User ID or other packets. + /// + /// When a signature is made over a key, the hash data starts with the octet 0x99, + /// followed by a two-octet length of the key, and then body of the key packet. + /// (Note that this is an old-style packet header for a key packet with two-octet length.) + /// A subkey binding signature (type 0x18) or primary key binding signature (type 0x19) then hashes + /// the subkey using the same format as the main key (also using 0x99 as the first octet). + keyBinding(25), + + /// Signature directly on a key + /// + /// This signature is calculated directly on a key. + /// It binds the information in the Signature subpackets to the key, + /// and is appropriate to be used for subpackets that provide information + /// about the key, such as the Revocation Key subpacket. + /// It is also appropriate for statements that non-self certifiers want to make + /// about the key itself, rather than the binding between a key and a name. + key(31), + + /// Key revocation signature + /// + /// The signature is calculated directly on the key being revoked. + /// A revoked key is not to be used. + /// Only revocation signatures by the key being revoked, or by an authorized revocation key, + /// should be considered valid revocation signatures. + keyRevocation(32), + + /// Subkey revocation signature + /// + /// The signature is calculated directly on the subkey being revoked. + /// A revoked subkey is not to be used. Only revocation signatures + /// by the top-level signature key that is bound to this subkey, or + /// by an authorized revocation key, should be considered valid revocation signatures. + /// Key revocation signatures (types 0x20 and 0x28) hash only the key being revoked. + subkeyRevocation(40), + + /// Timestamp signature. + /// + /// This signature is only meaningful for the timestamp contained in it. + timestamp(64), + + /// Third-Party Confirmation signature. + /// + /// This signature is a signature over some other OpenPGP Signature packet(s). + /// It is analogous to a notary seal on the signed data. + /// A third-party signature SHOULD include Signature Target + /// subpacket(s) to give easy identification. Note that we really do + /// mean SHOULD. There are plausible uses for this (such as a blind + /// party that only sees the signature, not the key or source + /// document) that cannot include a target subpacket. + thirdParty(80); + + final int value; + + const SignatureType(this.value); +} diff --git a/lib/src/enum/support_feature.dart b/lib/src/enum/support_feature.dart new file mode 100644 index 00000000..54551984 --- /dev/null +++ b/lib/src/enum/support_feature.dart @@ -0,0 +1,25 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +/// Support Feature +/// Author Nguyen Van Nguyen +enum SupportFeature { + /// 0x01 - Modification Detection (packets 18 and 19) + version1SEIPD(1), + + /// 0x02 - AEAD Encrypted Data Packet (packet 20) and version 5 Symmetric-Key Encrypted Session Key Packets (packet 3) + aeadEncrypted(2), + + /// 0x04 - Version 5 Public-Key Packet format and corresponding new fingerprint format + version5PublicKey(4), + + /// 0x08 - VeVersion 2 Symmetrically Encrypted and Integrity Protected Data packet + version2SEIPD(8); + + final int value; + + const SupportFeature(this.value); +} diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart new file mode 100644 index 00000000..b97f2c73 --- /dev/null +++ b/lib/src/packet/signature.dart @@ -0,0 +1,19 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../enum/packet_type.dart'; +import 'base.dart'; + +/// Implementation of the Signature Packet (Tag 2) +/// Author Nguyen Van Nguyen +class SignaturePacket extends BasePacket { + SignaturePacket() : super(PacketType.signature); + + @override + Uint8List get data => Uint8List.fromList([]); +} diff --git a/lib/src/packet/signature/embedded_signature.dart b/lib/src/packet/signature/embedded_signature.dart new file mode 100644 index 00000000..e304844d --- /dev/null +++ b/lib/src/packet/signature/embedded_signature.dart @@ -0,0 +1,20 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../enum/signature_subpacket_type.dart'; +import '../signature.dart'; +import '../signature_subpacket.dart'; + +/// This subpacket contains a complete Signature packet body specified +/// Author Nguyen Van Nguyen +class EmbeddedSignature extends SignatureSubpacket { + EmbeddedSignature(final Uint8List data, {super.critical, super.isLong}) + : super(SignatureSubpacketType.embeddedSignature, data); + + factory EmbeddedSignature.fromSignature(final SignaturePacket signature) => EmbeddedSignature(signature.data); +} diff --git a/lib/src/packet/signature/exportable_certification.dart b/lib/src/packet/signature/exportable_certification.dart new file mode 100644 index 00000000..8eb74093 --- /dev/null +++ b/lib/src/packet/signature/exportable_certification.dart @@ -0,0 +1,33 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// This subpacket denotes whether a certification signature is +/// "exportable", to be used by other users than the signature's issuer. +/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.11 +/// Author Nguyen Van Nguyen +class ExportableCertification extends SignatureSubpacket { + ExportableCertification( + final Uint8List data, { + super.critical, + super.isLong, + }) : super(SignatureSubpacketType.exportableCertification, data); + + factory ExportableCertification.fromExportable( + final bool exportable, { + final bool critical = false, + }) => + ExportableCertification( + Uint8List.fromList([exportable ? 1 : 0]), + critical: critical, + ); + + bool get isExportable => data[0] != 0; +} diff --git a/lib/src/packet/signature/features.dart b/lib/src/packet/signature/features.dart new file mode 100644 index 00000000..9c3d0f32 --- /dev/null +++ b/lib/src/packet/signature/features.dart @@ -0,0 +1,37 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../enum/signature_subpacket_type.dart'; +import '../../enum/support_feature.dart'; +import '../signature_subpacket.dart'; + +/// The Features subpacket denotes which advanced OpenPGP features a +/// user's implementation supports. +/// Author Nguyen Van Nguyen +class Features extends SignatureSubpacket { + Features( + final Uint8List data, { + super.critical, + super.isLong, + }) : super(SignatureSubpacketType.features, data); + + factory Features.fromFeatures( + final int features, { + final bool critical = false, + }) => + Features(Uint8List.fromList([features]), critical: critical); + + bool get supprtVersion1SEIPD => (data[0] & SupportFeature.version1SEIPD.value) == SupportFeature.version1SEIPD.value; + + bool get supportAeadEncrypted => (data[0] & SupportFeature.aeadEncrypted.value) == SupportFeature.aeadEncrypted.value; + + bool get supportVersion5PublicKey => + (data[0] & SupportFeature.version5PublicKey.value) == SupportFeature.version5PublicKey.value; + + bool get supportVersion2SEIPD => (data[0] & SupportFeature.version2SEIPD.value) == SupportFeature.version2SEIPD.value; +} diff --git a/lib/src/packet/signature/issuer_fingerprint.dart b/lib/src/packet/signature/issuer_fingerprint.dart new file mode 100644 index 00000000..b7a4736b --- /dev/null +++ b/lib/src/packet/signature/issuer_fingerprint.dart @@ -0,0 +1,32 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'package:dart_pg/src/common/extensions.dart'; +import 'package:dart_pg/src/type/key_packet.dart'; + +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// The OpenPGP Key fingerprint of the key issuing the signature. +/// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-rfc4880bis#section-5.2.3.28 +/// Author Nguyen Van Nguyen +class IssuerFingerprint extends SignatureSubpacket { + IssuerFingerprint(final Uint8List data, {super.critical, super.isLong}) + : super(SignatureSubpacketType.issuerFingerprint, data); + + factory IssuerFingerprint.fromKey( + final KeyPacketInterface key, + ) => + IssuerFingerprint( + Uint8List.fromList([key.keyVersion, ...key.fingerprint]), + ); + + int get keyVersion => data[0]; + + String get fingerprint => data.sublist(1).toHexadecimal(); +} diff --git a/lib/src/packet/signature/issuer_key_id.dart b/lib/src/packet/signature/issuer_key_id.dart new file mode 100644 index 00000000..bb94d3b1 --- /dev/null +++ b/lib/src/packet/signature/issuer_key_id.dart @@ -0,0 +1,30 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../common/extensions.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// The OpenPGP Key ID of the key issuing the signature. +/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.5 +/// Author Nguyen Van Nguyen +class IssuerKeyID extends SignatureSubpacket { + IssuerKeyID(final Uint8List data, {super.critical, super.isLong}) : super(SignatureSubpacketType.issuerKeyID, data); + + factory IssuerKeyID.fromString( + final String id, { + final bool critical = false, + }) => + IssuerKeyID(id.hexToBytes(), critical: critical); + + factory IssuerKeyID.wildcard() => IssuerKeyID( + Uint8List.fromList(List.filled(8, 0, growable: false)), + ); + + String get id => data.toHexadecimal(); +} diff --git a/lib/src/packet/signature/key_expiration_time.dart b/lib/src/packet/signature/key_expiration_time.dart new file mode 100644 index 00000000..ff2009c3 --- /dev/null +++ b/lib/src/packet/signature/key_expiration_time.dart @@ -0,0 +1,35 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'package:dart_pg/src/common/extensions.dart'; + +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// The validity period of the key. This is the number of seconds after +/// the key creation time that the key expires. If this is not present +/// or has a value of zero, the key never expires. This is found only on +/// a self-signature. +/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.6 +/// Author Nguyen Van Nguyen +class KeyExpirationTime extends SignatureSubpacket { + KeyExpirationTime( + final Uint8List data, { + super.critical, + super.isLong, + }) : super(SignatureSubpacketType.keyExpirationTime, data); + + factory KeyExpirationTime.fromTime( + final int seconds, { + final bool critical = false, + }) => + KeyExpirationTime(seconds.pack32(), critical: critical); + + /// Return the number of seconds after creation time a key is valid for. + int get time => data.unpack32(); +} diff --git a/lib/src/packet/signature/key_flags.dart b/lib/src/packet/signature/key_flags.dart new file mode 100644 index 00000000..8889a88b --- /dev/null +++ b/lib/src/packet/signature/key_flags.dart @@ -0,0 +1,58 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../enum/key_flag.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// This subpacket contains a list of binary flags that hold information about a key. +/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.21 +/// Author Nguyen Van Nguyen +class KeyFlags extends SignatureSubpacket { + KeyFlags( + final Uint8List data, { + super.critical, + super.isLong, + }) : super(SignatureSubpacketType.keyFlags, data); + + factory KeyFlags.fromFlags( + final int flags, { + final bool critical = false, + }) => + KeyFlags(_flagsToBytes(flags), critical: critical); + + /// Return the flag values contained in the first 4 octets + /// (note: at the moment the standard only uses the first one). + int get flags { + var value = 0; + for (var i = 0; i != data.length; i++) { + value |= data[i] << (i * 8); + } + return value; + } + + bool get isCertifyKeys => flags & KeyFlag.certifyKeys.value == KeyFlag.certifyKeys.value; + + bool get isSignData => flags & KeyFlag.signData.value == KeyFlag.signData.value; + + bool get isEncryptCommunication => flags & KeyFlag.encryptCommunication.value == KeyFlag.encryptCommunication.value; + + bool get isEncryptStorage => flags & KeyFlag.encryptStorage.value == KeyFlag.encryptStorage.value; + + static Uint8List _flagsToBytes(final int flags) { + final bytes = Uint8List(4); + var size = 0; + for (int i = 0; i != 4; i++) { + bytes[i] = (flags >> (i * 8)) & 0xff; + if (bytes[i] != 0) { + size = i; + } + } + return bytes.sublist(0, size + 1); + } +} diff --git a/lib/src/packet/signature/key_server_preferences.dart b/lib/src/packet/signature/key_server_preferences.dart new file mode 100644 index 00000000..ee3c3187 --- /dev/null +++ b/lib/src/packet/signature/key_server_preferences.dart @@ -0,0 +1,36 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:convert'; +import 'dart:typed_data'; + +import '../../common/extensions.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// This is a list of one-bit flags that indicate preferences that the +/// key holder has about how the key is handled on a key server. +/// All undefined flags MUST be zero. +/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.17 +/// Author Nguyen Van Nguyen +class KeyServerPreferences extends SignatureSubpacket { + KeyServerPreferences( + final Uint8List data, { + super.critical, + super.isLong, + }) : super(SignatureSubpacketType.keyServerPreferences, data); + + factory KeyServerPreferences.fromServerPreferences( + final String serverPreferences, { + final bool critical = false, + }) => + KeyServerPreferences( + serverPreferences.toBytes(), + critical: critical, + ); + + String get serverPreferences => utf8.decode(data); +} diff --git a/lib/src/packet/signature/notation_data.dart b/lib/src/packet/signature/notation_data.dart new file mode 100644 index 00000000..3b5d7720 --- /dev/null +++ b/lib/src/packet/signature/notation_data.dart @@ -0,0 +1,102 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:convert'; +import 'dart:math'; +import 'dart:typed_data'; + +import '../../common/helpers.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// This subpacket describes a "notation" on the signature that the issuer wishes to make. +/// The notation has a name and a value, each of which are strings of octets. +/// Author Nguyen Van Nguyen +class NotationData extends SignatureSubpacket { + static const saltName = "salt@notations.dart-pg.org"; + + static const headerFlagLength = 4; + static const headerNameLength = 2; + static const headerValueLength = 2; + + NotationData(final Uint8List data, {super.critical, super.isLong}) : super(SignatureSubpacketType.notationData, data); + + factory NotationData.fromNotation( + final bool humanReadable, + final String notationName, + final String notationValue, { + final bool critical = false, + }) => + NotationData( + _notationToBytes(humanReadable, notationName, notationValue), + critical: critical, + ); + + factory NotationData.saltNotation( + int saltSize, { + final bool critical = false, + }) { + final salt = Helper.secureRandom().nextBytes(saltSize); + final nameData = utf8.encode(saltName); + final nameLength = min(nameData.length, 0xffff); + return NotationData( + Uint8List.fromList([ + ...[0, 0, 0, 0], + (nameLength >> 8) & 0xff, + (nameLength >> 0) & 0xff, + (salt.length >> 8) & 0xff, + (salt.length >> 0) & 0xff, + ...nameData, + ...salt, + ]), + critical: critical, + ); + } + + bool get isHumanReadable => data[0] == 0x80; + + String get notationName { + final nameLength = (((data[headerFlagLength] & 0xff) << 8) + (data[headerFlagLength + 1] & 0xff)); + final nameOffset = headerFlagLength + headerNameLength + headerValueLength; + return utf8.decode(data.sublist(nameOffset, nameOffset + nameLength)); + } + + String get notationValue { + final nameLength = (((data[headerFlagLength] & 0xff) << 8) + (data[headerFlagLength + 1] & 0xff)); + final valueLength = (((data[headerFlagLength + headerNameLength] & 0xff) << 8) + + (data[headerFlagLength + headerNameLength + 1] & 0xff)); + final valueOffset = headerFlagLength + headerNameLength + headerValueLength + nameLength; + return utf8.decode(data.sublist(valueOffset, valueOffset + valueLength)); + } + + static Uint8List _notationToBytes( + final bool humanReadable, + final String notationName, + final String notationValue, + ) { + final nameData = utf8.encode(notationName); + final nameLength = min(nameData.length, 0xffff); + if (nameLength != nameData.length) { + throw ArgumentError('notationName exceeds maximum length.'); + } + + final valueData = utf8.encode(notationValue); + final valueLength = min(valueData.length, 0xffff); + if (valueLength != valueData.length) { + throw ArgumentError('notationValue exceeds maximum length.'); + } + + return Uint8List.fromList([ + ...[humanReadable ? 0x80 : 0, 0, 0, 0], + (nameLength >> 8) & 0xff, + (nameLength >> 0) & 0xff, + (valueLength >> 8) & 0xff, + (valueLength >> 0) & 0xff, + ...nameData.sublist(0, nameLength), + ...valueData.sublist(0, valueLength), + ]); + } +} diff --git a/lib/src/packet/signature/policy_uri.dart b/lib/src/packet/signature/policy_uri.dart new file mode 100644 index 00000000..2993d91b --- /dev/null +++ b/lib/src/packet/signature/policy_uri.dart @@ -0,0 +1,31 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:convert'; +import 'dart:typed_data'; + +import '../../common/extensions.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// This subpacket contains a URI of a document that describes the policy +/// under which the signature was issued. +/// Author Nguyen Van Nguyen +class PolicyURI extends SignatureSubpacket { + PolicyURI( + final Uint8List data, { + super.critical, + super.isLong, + }) : super(SignatureSubpacketType.policyURI, data); + + factory PolicyURI.fromURI( + final String uri, { + final bool critical = false, + }) => + PolicyURI(uri.toBytes(), critical: critical); + + String get uri => utf8.decode(data); +} diff --git a/lib/src/packet/signature/preferred_aead_algorithms.dart b/lib/src/packet/signature/preferred_aead_algorithms.dart new file mode 100644 index 00000000..a888e65c --- /dev/null +++ b/lib/src/packet/signature/preferred_aead_algorithms.dart @@ -0,0 +1,27 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../enum/aead_algorithm.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// PreferredAeadAlgorithms sub-packet class +/// Author Nguyen Van Nguyen +class PreferredAeadAlgorithms extends SignatureSubpacket { + PreferredAeadAlgorithms( + final Uint8List data, { + super.critical, + super.isLong, + }) : super( + SignatureSubpacketType.preferredAeadAlgorithms, + data, + ); + + List get preferences => + data.map((pref) => AeadAlgorithm.values.firstWhere((alg) => alg.value == pref)).toList(); +} diff --git a/lib/src/packet/signature/preferred_compression_algorithms.dart b/lib/src/packet/signature/preferred_compression_algorithms.dart new file mode 100644 index 00000000..6266273b --- /dev/null +++ b/lib/src/packet/signature/preferred_compression_algorithms.dart @@ -0,0 +1,28 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../enum/compression_algorithm.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// Compression algorithm numbers that indicate which algorithms the key +/// holder prefers to use. +/// Author Nguyen Van Nguyen +class PreferredCompressionAlgorithms extends SignatureSubpacket { + PreferredCompressionAlgorithms( + final Uint8List data, { + super.critical, + super.isLong, + }) : super( + SignatureSubpacketType.preferredCompressionAlgorithms, + data, + ); + + List get preferences => + data.map((pref) => CompressionAlgorithm.values.firstWhere((alg) => alg.value == pref)).toList(); +} diff --git a/lib/src/packet/signature/preferred_hash_algorithms.dart b/lib/src/packet/signature/preferred_hash_algorithms.dart new file mode 100644 index 00000000..d441d247 --- /dev/null +++ b/lib/src/packet/signature/preferred_hash_algorithms.dart @@ -0,0 +1,25 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../enum/hash_algorithm.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// Message digest algorithm numbers that indicate which algorithms the +/// key holder prefers to receive. +/// Author Nguyen Van Nguyen +class PreferredHashAlgorithms extends SignatureSubpacket { + PreferredHashAlgorithms( + final Uint8List data, { + super.critical, + super.isLong, + }) : super(SignatureSubpacketType.preferredHashAlgorithms, data); + + List get preferences => + data.map((pref) => HashAlgorithm.values.firstWhere((alg) => alg.value == pref)).toList(); +} diff --git a/lib/src/packet/signature/preferred_key_server.dart b/lib/src/packet/signature/preferred_key_server.dart new file mode 100644 index 00000000..d8a84a72 --- /dev/null +++ b/lib/src/packet/signature/preferred_key_server.dart @@ -0,0 +1,30 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:convert'; +import 'dart:typed_data'; + +import '../../common/extensions.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// This is a URI of a key server that the key holder prefers be used for updates. +/// Author Nguyen Van Nguyen +class PreferredKeyServer extends SignatureSubpacket { + PreferredKeyServer( + final Uint8List data, { + super.critical, + super.isLong, + }) : super(SignatureSubpacketType.preferredKeyServer, data); + + factory PreferredKeyServer.fromKeyServer( + final String keyServer, { + final bool critical = false, + }) => + PreferredKeyServer(keyServer.toBytes(), critical: critical); + + String get keyServer => utf8.decode(data); +} diff --git a/lib/src/packet/signature/preferred_symmetric_algorithms.dart b/lib/src/packet/signature/preferred_symmetric_algorithms.dart new file mode 100644 index 00000000..ba09daeb --- /dev/null +++ b/lib/src/packet/signature/preferred_symmetric_algorithms.dart @@ -0,0 +1,25 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../enum/signature_subpacket_type.dart'; +import '../../enum/symmetric_algorithm.dart'; +import '../signature_subpacket.dart'; + +/// Symmetric algorithm numbers that indicate which algorithms the key +/// holder prefers to use. +/// Author Nguyen Van Nguyen +class PreferredSymmetricAlgorithms extends SignatureSubpacket { + PreferredSymmetricAlgorithms( + final Uint8List data, { + super.critical, + super.isLong, + }) : super(SignatureSubpacketType.preferredSymmetricAlgorithms, data); + + List get preferences => + data.map((pref) => SymmetricAlgorithm.values.firstWhere((alg) => alg.value == pref)).toList(growable: false); +} diff --git a/lib/src/packet/signature/primary_user_id.dart b/lib/src/packet/signature/primary_user_id.dart new file mode 100644 index 00000000..a1625889 --- /dev/null +++ b/lib/src/packet/signature/primary_user_id.dart @@ -0,0 +1,26 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// This is a flag in a User ID's self-signature that states whether this +/// User ID is the main User ID for this key. +/// Author Nguyen Van Nguyen +class PrimaryUserID extends SignatureSubpacket { + PrimaryUserID(final Uint8List data, {super.critical, super.isLong}) + : super(SignatureSubpacketType.primaryUserID, data); + + factory PrimaryUserID.fromIsPrimary( + final bool isPrimary, { + final bool critical = false, + }) => + PrimaryUserID(Uint8List.fromList([isPrimary ? 1 : 0]), critical: critical); + + bool get isPrimary => data[0] != 0; +} diff --git a/lib/src/packet/signature/regular_expression.dart b/lib/src/packet/signature/regular_expression.dart new file mode 100644 index 00000000..ed6794c9 --- /dev/null +++ b/lib/src/packet/signature/regular_expression.dart @@ -0,0 +1,28 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:convert'; +import 'dart:typed_data'; + +import '../../common/extensions.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// Used in conjunction with trust Signature packets (of level > 0) to +/// limit the scope of trust that is extended. +/// Author Nguyen Van Nguyen +class RegularExpression extends SignatureSubpacket { + RegularExpression(final Uint8List data, {super.critical, super.isLong}) + : super(SignatureSubpacketType.revocable, data); + + factory RegularExpression.fromExpression( + final String expression, { + final bool critical = false, + }) => + RegularExpression(expression.toBytes(), critical: critical); + + String get expression => utf8.decode(data); +} diff --git a/lib/src/packet/signature/revocable.dart b/lib/src/packet/signature/revocable.dart new file mode 100644 index 00000000..c0b3c6f3 --- /dev/null +++ b/lib/src/packet/signature/revocable.dart @@ -0,0 +1,32 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// Signature's revocability status. The packet body contains a +/// Boolean flag indicating whether the signature is revocable. +/// Author Nguyen Van Nguyen +class Revocable extends SignatureSubpacket { + Revocable( + final Uint8List data, { + super.critical, + super.isLong, + }) : super(SignatureSubpacketType.revocable, data); + + factory Revocable.fromRevocable( + final bool isRevocable, { + final bool critical = false, + }) => + Revocable( + Uint8List.fromList([isRevocable ? 1 : 0]), + critical: critical, + ); + + bool get isRevocable => data[0] != 0; +} diff --git a/lib/src/packet/signature/revocation_key.dart b/lib/src/packet/signature/revocation_key.dart new file mode 100644 index 00000000..f40e8eac --- /dev/null +++ b/lib/src/packet/signature/revocation_key.dart @@ -0,0 +1,52 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../common/extensions.dart'; +import '../../enum/key_algorithm.dart'; +import '../../enum/revocation_key_tag.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// Authorizes the specified key to issue revocation signatures for this key. +/// Author Nguyen Van Nguyen +class RevocationKey extends SignatureSubpacket { + RevocationKey(final Uint8List data, {super.critical, super.isLong}) + : super(SignatureSubpacketType.revocationKey, data); + + factory RevocationKey.fromRevocation( + final RevocationKeyTag signatureClass, + final KeyAlgorithm keyAlgorithm, + final Uint8List fingerprint, { + final bool critical = false, + }) => + RevocationKey( + _revocationToBytes(signatureClass, keyAlgorithm, fingerprint), + critical: critical, + ); + + RevocationKeyTag get signatureClass => RevocationKeyTag.values.firstWhere( + (tag) => tag.value == data[0], + ); + + KeyAlgorithm get keyAlgorithm => KeyAlgorithm.values.firstWhere( + (alg) => alg.value == data[1], + ); + + String get fingerprint => data.sublist(2).toHexadecimal(); + + static Uint8List _revocationToBytes( + final RevocationKeyTag signatureClass, + final KeyAlgorithm keyAlgorithm, + final Uint8List fingerprint, + ) => + Uint8List.fromList([ + signatureClass.value, + keyAlgorithm.value, + ...fingerprint, + ]); +} diff --git a/lib/src/packet/signature/revocation_reason.dart b/lib/src/packet/signature/revocation_reason.dart new file mode 100644 index 00000000..7e070236 --- /dev/null +++ b/lib/src/packet/signature/revocation_reason.dart @@ -0,0 +1,45 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:convert'; +import 'dart:typed_data'; + +import '../../enum/revocation_reason_tag.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// This subpacket is used only in key revocation and certification +/// revocation signatures. +/// Author Nguyen Van Nguyen +class RevocationReason extends SignatureSubpacket { + RevocationReason(final Uint8List data, {super.critical, super.isLong}) + : super(SignatureSubpacketType.revocationReason, data); + + factory RevocationReason.fromRevocation( + final RevocationReasonTag reason, + final String description, { + final bool critical = false, + }) => + RevocationReason( + _revocationToBytes(reason, description), + critical: critical, + ); + + RevocationReasonTag get reason => RevocationReasonTag.values.firstWhere( + (reason) => reason.value == data[0], + ); + + String get description => utf8.decode(data.sublist(1)); + + static Uint8List _revocationToBytes( + final RevocationReasonTag reason, + final String description, + ) => + Uint8List.fromList([ + reason.value, + ...utf8.encode(description), + ]); +} diff --git a/lib/src/packet/signature/signature_creation_time.dart b/lib/src/packet/signature/signature_creation_time.dart new file mode 100644 index 00000000..7d7597e7 --- /dev/null +++ b/lib/src/packet/signature/signature_creation_time.dart @@ -0,0 +1,30 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../common/extensions.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// The time the signature was made. +/// MUST be present in the hashed area. +/// Author Nguyen Van Nguyen +class SignatureCreationTime extends SignatureSubpacket { + SignatureCreationTime( + final Uint8List data, { + super.critical, + super.isLong, + }) : super(SignatureSubpacketType.signatureCreationTime, data); + + factory SignatureCreationTime.fromTime( + final DateTime time, { + final bool critical = false, + }) => + SignatureCreationTime(time.toBytes(), critical: critical); + + DateTime get creationTime => data.toDateTime(); +} diff --git a/lib/src/packet/signature/signature_expiration_time.dart b/lib/src/packet/signature/signature_expiration_time.dart new file mode 100644 index 00000000..30a04c66 --- /dev/null +++ b/lib/src/packet/signature/signature_expiration_time.dart @@ -0,0 +1,31 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../common/extensions.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// The validity period of the signature. This is the number of seconds +/// after the signature creation time that the signature expires. +/// If this is not present or has a value of zero, it never expires. +/// Author Nguyen Van Nguyen +class SignatureExpirationTime extends SignatureSubpacket { + SignatureExpirationTime( + final Uint8List data, { + super.critical, + super.isLong, + }) : super(SignatureSubpacketType.signatureExpirationTime, data); + + factory SignatureExpirationTime.fromExpirationTime( + final DateTime time, { + final bool critical = false, + }) => + SignatureExpirationTime(time.toBytes(), critical: critical); + + DateTime get expirationTime => data.toDateTime(); +} diff --git a/lib/src/packet/signature/signature_target.dart b/lib/src/packet/signature/signature_target.dart new file mode 100644 index 00000000..09aeed5c --- /dev/null +++ b/lib/src/packet/signature/signature_target.dart @@ -0,0 +1,51 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../enum/hash_algorithm.dart'; +import '../../enum/key_algorithm.dart'; +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// This subpacket identifies a specific target signature to which a signature refers. +/// Author Nguyen Van Nguyen +class SignatureTarget extends SignatureSubpacket { + SignatureTarget(final Uint8List data, {super.critical, super.isLong}) + : super(SignatureSubpacketType.signatureTarget, data); + + factory SignatureTarget.fromHashData( + final KeyAlgorithm keyAlgorithm, + final HashAlgorithm hashAlgorithm, + final Uint8List hashData, { + final bool critical = false, + }) => + SignatureTarget( + _hashDataBytes(keyAlgorithm, hashAlgorithm, hashData), + critical: critical, + ); + + KeyAlgorithm get keyAlgorithm => KeyAlgorithm.values.firstWhere( + (alg) => alg.value == data[0], + ); + + HashAlgorithm get hashAlgorithm => HashAlgorithm.values.firstWhere( + (alg) => alg.value == data[1], + ); + + Uint8List get hashData => data.sublist(2); + + static Uint8List _hashDataBytes( + final KeyAlgorithm keyAlgorithm, + final HashAlgorithm hashAlgorithm, + final Uint8List hashData, + ) => + Uint8List.fromList([ + keyAlgorithm.value, + hashAlgorithm.value, + ...hashData, + ]); +} diff --git a/lib/src/packet/signature/signer_user_id.dart b/lib/src/packet/signature/signer_user_id.dart new file mode 100644 index 00000000..6e9e2b15 --- /dev/null +++ b/lib/src/packet/signature/signer_user_id.dart @@ -0,0 +1,26 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:convert'; +import 'dart:typed_data'; + +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// This subpacket allows a keyholder to state which User ID is +/// responsible for the signing. +/// Author Nguyen Van Nguyen +class SignerUserID extends SignatureSubpacket { + SignerUserID(final Uint8List data, {super.critical, super.isLong}) : super(SignatureSubpacketType.signerUserID, data); + + factory SignerUserID.fromUserID( + final String userID, { + final bool critical = false, + }) => + SignerUserID(utf8.encoder.convert(userID), critical: critical); + + String get userID => utf8.decode(data); +} diff --git a/lib/src/packet/signature/trust_signature.dart b/lib/src/packet/signature/trust_signature.dart new file mode 100644 index 00000000..047a4aa7 --- /dev/null +++ b/lib/src/packet/signature/trust_signature.dart @@ -0,0 +1,32 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// Signer asserts that the key is not only valid but also trustworthy at +/// the specified level. +/// Author Nguyen Van Nguyen +class TrustSignature extends SignatureSubpacket { + TrustSignature(final Uint8List data, {super.critical, super.isLong}) + : super(SignatureSubpacketType.trustSignature, data); + + factory TrustSignature.fromTrust( + final int trustLevel, + final int trustAmount, { + final bool critical = false, + }) => + TrustSignature( + Uint8List.fromList([trustLevel, trustAmount]), + critical: critical, + ); + + int get trustLevel => data[0]; + + int get trustAmount => data[1]; +} diff --git a/lib/src/packet/signature_subpacket.dart b/lib/src/packet/signature_subpacket.dart new file mode 100644 index 00000000..9d4cee23 --- /dev/null +++ b/lib/src/packet/signature_subpacket.dart @@ -0,0 +1,52 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../common/extensions.dart'; +import '../enum/signature_subpacket_type.dart'; + +/// Signature subpacket class +/// Author Nguyen Van Nguyen +class SignatureSubpacket { + final SignatureSubpacketType type; + + final bool critical; + + final bool isLong; + + final Uint8List data; + + SignatureSubpacket( + this.type, + this.data, { + this.critical = false, + this.isLong = false, + }); + + Uint8List encode() { + final List header; + final bodyLen = data.length + 1; + + if (isLong) { + header = [0xff, ...bodyLen.pack32()]; + } else { + if (bodyLen < 192) { + header = [bodyLen]; + } else if (bodyLen <= 8383) { + header = [(((bodyLen - 192) >> 8) & 0xff) + 192, bodyLen - 192]; + } else { + header = [0xff, ...bodyLen.pack32()]; + } + } + + return Uint8List.fromList([ + ...header, + critical ? type.value | 0x80 : type.value, + ...data, + ]); + } +} From f1b94da0fc84c6299132d6758fbea34fed7f023c Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 19 Nov 2024 15:09:14 +0700 Subject: [PATCH 008/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/signature_subpacket.dart | 11 ++-- lib/src/type/signature_packet.dart | 68 +++++++++++++++++++++++++ lib/src/type/subpacket.dart | 6 ++- 3 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 lib/src/type/signature_packet.dart diff --git a/lib/src/packet/signature_subpacket.dart b/lib/src/packet/signature_subpacket.dart index 9d4cee23..c58e6129 100644 --- a/lib/src/packet/signature_subpacket.dart +++ b/lib/src/packet/signature_subpacket.dart @@ -8,18 +8,22 @@ import 'dart:typed_data'; import '../common/extensions.dart'; import '../enum/signature_subpacket_type.dart'; +import '../type/subpacket.dart'; /// Signature subpacket class /// Author Nguyen Van Nguyen -class SignatureSubpacket { +class SignatureSubpacket implements SubpacketInterface { + @override final SignatureSubpacketType type; - final bool critical; - + @override final bool isLong; + @override final Uint8List data; + final bool critical; + SignatureSubpacket( this.type, this.data, { @@ -27,6 +31,7 @@ class SignatureSubpacket { this.isLong = false, }); + @override Uint8List encode() { final List header; final bodyLen = data.length + 1; diff --git a/lib/src/type/signature_packet.dart b/lib/src/type/signature_packet.dart new file mode 100644 index 00000000..3f46068e --- /dev/null +++ b/lib/src/type/signature_packet.dart @@ -0,0 +1,68 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../enum/hash_algorithm.dart'; +import '../enum/key_algorithm.dart'; +import '../enum/signature_type.dart'; +import 'key_packet.dart'; +import 'packet.dart'; +import 'subpacket.dart'; + +/// Signature packet interface +/// Author Nguyen Van Nguyen +abstract class SignaturePacketInterface extends PacketInterface { + int get version; + + SignatureType get signatureType; + + KeyAlgorithm get keyAlgorithm; + + HashAlgorithm get hashAlgorithm; + + Iterable get hashedSubpackets; + + Iterable get unhashedSubpackets; + + Uint8List get signatureData; + + Uint8List get signedHashValue; + + Uint8List get salt; + + Uint8List get signature; + + DateTime? get creationTime; + + DateTime? get expirationTime; + + Uint8List get issuerKeyID; + + Uint8List get issuerFingerprint; + + bool get isPrimaryUserID; + + bool get isCertification; + + bool get isCertRevocation; + + bool get isDirectKey; + + bool get isKeyRevocation; + + bool get isSubkeyBinding; + + bool get isSubkeyRevocation; + + bool isExpired([DateTime? time]); + + bool verify( + KeyPacketInterface verifyKey, + Uint8List dataToVerify, [ + DateTime? time, + ]); +} diff --git a/lib/src/type/subpacket.dart b/lib/src/type/subpacket.dart index 23e94874..76f1d86d 100644 --- a/lib/src/type/subpacket.dart +++ b/lib/src/type/subpacket.dart @@ -6,11 +6,13 @@ library; import 'dart:typed_data'; +import '../enum/signature_subpacket_type.dart'; + /// Subpacket interface /// Author Nguyen Van Nguyen -abstract class Subpacket { +abstract class SubpacketInterface { /// Get sub-packet type - int get type; + SignatureSubpacketType get type; /// Get sub-packet data Uint8List get data; From 36c81c17d4ed7dc20b2432abdf0285e9c953334f Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 19 Nov 2024 16:19:20 +0700 Subject: [PATCH 009/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/enum/hash_algorithm.dart | 5 +- lib/src/enum/signature_type.dart | 2 +- lib/src/packet/public_key.dart | 6 +- .../public_key_encrypted_session_key.dart | 9 +- lib/src/packet/signature.dart | 162 +++++++++++++++++- .../packet/signature/issuer_fingerprint.dart | 18 +- lib/src/packet/signature/issuer_key_id.dart | 12 +- lib/src/packet/signature_subpacket.dart | 26 +++ 8 files changed, 218 insertions(+), 22 deletions(-) diff --git a/lib/src/enum/hash_algorithm.dart b/lib/src/enum/hash_algorithm.dart index 76657afc..2574e363 100644 --- a/lib/src/enum/hash_algorithm.dart +++ b/lib/src/enum/hash_algorithm.dart @@ -37,12 +37,11 @@ enum HashAlgorithm { int get digestSize => switch (this) { md5 => 16, - sha1 => 20, - ripemd160 => 20, + sha1 || ripemd160 => 20, + sha224 => 28, sha256 || sha3_256 => 32, sha384 => 48, sha512 || sha3_512 => 64, - sha224 => 28, }; int get saltSize => switch (this) { diff --git a/lib/src/enum/signature_type.dart b/lib/src/enum/signature_type.dart index ca71fb06..b70eda54 100644 --- a/lib/src/enum/signature_type.dart +++ b/lib/src/enum/signature_type.dart @@ -87,7 +87,7 @@ enum SignatureType { /// about the key, such as the Revocation Key subpacket. /// It is also appropriate for statements that non-self certifiers want to make /// about the key itself, rather than the binding between a key and a name. - key(31), + directKey(31), /// Key revocation signature /// diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index e4d741a1..70885620 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -22,6 +22,8 @@ import 'key/public_material.dart'; /// Implementation of the Public Key Packet (Type 6) /// Author Nguyen Van Nguyen class PublicKeyPacket extends BasePacket implements KeyPacketInterface { + static const keyIDSize = 8; + @override final int keyVersion; @@ -117,12 +119,12 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { _fingerprint = Uint8List.fromList( Helper.hashDigest(signBytes, HashAlgorithm.sha256), ); - _keyID = _fingerprint.sublist(0, 12); + _keyID = _fingerprint.sublist(0, keyIDSize); } else { _fingerprint = Uint8List.fromList( Helper.hashDigest(signBytes, HashAlgorithm.sha1), ); - _keyID = _fingerprint.sublist(12, 20); + _keyID = _fingerprint.sublist(12, 12 + keyIDSize); } } diff --git a/lib/src/packet/public_key_encrypted_session_key.dart b/lib/src/packet/public_key_encrypted_session_key.dart index 0313ad2e..6ace64ea 100644 --- a/lib/src/packet/public_key_encrypted_session_key.dart +++ b/lib/src/packet/public_key_encrypted_session_key.dart @@ -16,6 +16,7 @@ import '../type/session_key_cryptor.dart'; import 'base.dart'; import 'key/public_material.dart'; import 'key/session_key_cryptor.dart'; +import 'public_key.dart'; /// Implementation of the Public-Key Encrypted Session Key (PKESK) Packet (Type 1) /// Author Nguyen Van Nguyen @@ -67,12 +68,14 @@ class PublicKeyEncryptedSessionKeyPacket extends BasePacket { keyVersion = bytes[pos++]; keyFingerprint = bytes.sublist(pos, pos + length - 1); pos += length - 1; - keyID = keyVersion == 6 ? keyFingerprint.sublist(0, 8) : keyFingerprint.sublist(12, 20); + keyID = keyVersion == 6 + ? keyFingerprint.sublist(0, PublicKeyPacket.keyIDSize) + : keyFingerprint.sublist(12, 12 + PublicKeyPacket.keyIDSize); } else { keyVersion = 0; keyFingerprint = Uint8List(0); - keyID = bytes.sublist(pos, pos + 8); - pos += 8; + keyID = bytes.sublist(pos, pos + PublicKeyPacket.keyIDSize); + pos += PublicKeyPacket.keyIDSize; } final keyAlgorithm = KeyAlgorithm.values.firstWhere( (algo) => algo.value == bytes[pos], diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index b97f2c73..cee62136 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -6,14 +6,172 @@ library; import 'dart:typed_data'; +import 'package:dart_pg/src/type/key_packet.dart'; + +import '../common/helpers.dart'; +import '../enum/hash_algorithm.dart'; +import '../enum/key_algorithm.dart'; import '../enum/packet_type.dart'; +import '../enum/signature_type.dart'; +import '../type/signature_packet.dart'; +import '../type/subpacket.dart'; import 'base.dart'; +import 'public_key.dart'; +import 'signature_subpacket.dart'; /// Implementation of the Signature Packet (Tag 2) /// Author Nguyen Van Nguyen -class SignaturePacket extends BasePacket { - SignaturePacket() : super(PacketType.signature); +class SignaturePacket extends BasePacket implements SignaturePacketInterface { + @override + final int version; + + @override + final SignatureType signatureType; + + @override + final KeyAlgorithm keyAlgorithm; + + @override + final HashAlgorithm hashAlgorithm; + + @override + final Uint8List signedHashValue; + + @override + final Uint8List salt; + + @override + final Uint8List signature; + + @override + final Iterable hashedSubpackets; + + @override + final Iterable unhashedSubpackets; + + @override + final Uint8List signatureData; + + SignaturePacket( + this.version, + this.signatureType, + this.keyAlgorithm, + this.hashAlgorithm, + this.signedHashValue, + this.salt, + this.signature, { + this.hashedSubpackets = const [], + this.unhashedSubpackets = const [], + }) : signatureData = Uint8List.fromList([ + version, + signatureType.value, + keyAlgorithm.value, + hashAlgorithm.value, + ..._encodeSubpackets(hashedSubpackets), + ]), + super(PacketType.signature); @override Uint8List get data => Uint8List.fromList([]); + + @override + DateTime? get creationTime => _getSubpacket( + hashedSubpackets, + )?.creationTime; + + @override + DateTime? get expirationTime => _getSubpacket( + hashedSubpackets, + )?.expirationTime; + + @override + bool get isCertRevocation => signatureType == SignatureType.certRevocation; + + @override + bool get isCertification => switch (signatureType) { + SignatureType.certGeneric || + SignatureType.certPersona || + SignatureType.certCasual || + SignatureType.certPositive => + true, + _ => false + }; + + @override + bool get isDirectKey => signatureType == SignatureType.directKey; + + @override + bool get isKeyRevocation => signatureType == SignatureType.keyRevocation; + + @override + bool get isPrimaryUserID => + _getSubpacket( + hashedSubpackets, + )?.isPrimary ?? + false; + + @override + bool get isSubkeyBinding => signatureType == SignatureType.subkeyBinding; + + @override + bool get isSubkeyRevocation => signatureType == SignatureType.subkeyRevocation; + + @override + Uint8List get issuerFingerprint { + final subpacket = _getSubpacket( + hashedSubpackets, + ) ?? + _getSubpacket( + unhashedSubpackets, + ); + return subpacket?.fingerprint ?? Uint8List(version == 6 ? 32 : 20); + } + + @override + Uint8List get issuerKeyID { + final subpacket = _getSubpacket( + hashedSubpackets, + ) ?? + _getSubpacket( + unhashedSubpackets, + ); + if (subpacket != null) { + return subpacket.keyID; + } else { + return version == 6 + ? issuerFingerprint.sublist(0, PublicKeyPacket.keyIDSize) + : issuerFingerprint.sublist(12, 12 + PublicKeyPacket.keyIDSize); + } + } + + @override + bool isExpired([DateTime? time]) { + // TODO: implement isExpired + throw UnimplementedError(); + } + + @override + bool verify(KeyPacketInterface verifyKey, Uint8List dataToVerify, [DateTime? time]) { + // TODO: implement verify + throw UnimplementedError(); + } + + static T? _getSubpacket( + final Iterable subpackets, + ) { + final typedSubpackets = subpackets.whereType(); + return typedSubpackets.isNotEmpty ? typedSubpackets.first : null; + } + + /// Encode subpacket to bytes + static Uint8List _encodeSubpackets( + final Iterable subpackets, + ) { + final bytes = subpackets + .map( + (subpacket) => subpacket.encode(), + ) + .expand((byte) => byte); + return Uint8List.fromList([...bytes.length.pack16(), ...bytes]); + } } diff --git a/lib/src/packet/signature/issuer_fingerprint.dart b/lib/src/packet/signature/issuer_fingerprint.dart index b7a4736b..f315bd6c 100644 --- a/lib/src/packet/signature/issuer_fingerprint.dart +++ b/lib/src/packet/signature/issuer_fingerprint.dart @@ -6,7 +6,6 @@ library; import 'dart:typed_data'; -import 'package:dart_pg/src/common/extensions.dart'; import 'package:dart_pg/src/type/key_packet.dart'; import '../../enum/signature_subpacket_type.dart'; @@ -16,17 +15,26 @@ import '../signature_subpacket.dart'; /// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-rfc4880bis#section-5.2.3.28 /// Author Nguyen Van Nguyen class IssuerFingerprint extends SignatureSubpacket { - IssuerFingerprint(final Uint8List data, {super.critical, super.isLong}) - : super(SignatureSubpacketType.issuerFingerprint, data); + IssuerFingerprint( + final Uint8List data, { + super.critical, + super.isLong, + }) : super( + SignatureSubpacketType.issuerFingerprint, + data, + ); factory IssuerFingerprint.fromKey( final KeyPacketInterface key, ) => IssuerFingerprint( - Uint8List.fromList([key.keyVersion, ...key.fingerprint]), + Uint8List.fromList([ + key.keyVersion, + ...key.fingerprint, + ]), ); int get keyVersion => data[0]; - String get fingerprint => data.sublist(1).toHexadecimal(); + Uint8List get fingerprint => data.sublist(1); } diff --git a/lib/src/packet/signature/issuer_key_id.dart b/lib/src/packet/signature/issuer_key_id.dart index bb94d3b1..427e3b89 100644 --- a/lib/src/packet/signature/issuer_key_id.dart +++ b/lib/src/packet/signature/issuer_key_id.dart @@ -14,7 +14,11 @@ import '../signature_subpacket.dart'; /// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.5 /// Author Nguyen Van Nguyen class IssuerKeyID extends SignatureSubpacket { - IssuerKeyID(final Uint8List data, {super.critical, super.isLong}) : super(SignatureSubpacketType.issuerKeyID, data); + IssuerKeyID( + final Uint8List data, { + super.critical, + super.isLong, + }) : super(SignatureSubpacketType.issuerKeyID, data); factory IssuerKeyID.fromString( final String id, { @@ -22,9 +26,5 @@ class IssuerKeyID extends SignatureSubpacket { }) => IssuerKeyID(id.hexToBytes(), critical: critical); - factory IssuerKeyID.wildcard() => IssuerKeyID( - Uint8List.fromList(List.filled(8, 0, growable: false)), - ); - - String get id => data.toHexadecimal(); + Uint8List get keyID => data; } diff --git a/lib/src/packet/signature_subpacket.dart b/lib/src/packet/signature_subpacket.dart index c58e6129..62ad7599 100644 --- a/lib/src/packet/signature_subpacket.dart +++ b/lib/src/packet/signature_subpacket.dart @@ -10,6 +10,32 @@ import '../common/extensions.dart'; import '../enum/signature_subpacket_type.dart'; import '../type/subpacket.dart'; +export 'signature/embedded_signature.dart'; +export 'signature/exportable_certification.dart'; +export 'signature/features.dart'; +export 'signature/issuer_fingerprint.dart'; +export 'signature/issuer_key_id.dart'; +export 'signature/key_expiration_time.dart'; +export 'signature/key_flags.dart'; +export 'signature/key_server_preferences.dart'; +export 'signature/notation_data.dart'; +export 'signature/policy_uri.dart'; +export 'signature/preferred_aead_algorithms.dart'; +export 'signature/preferred_compression_algorithms.dart'; +export 'signature/preferred_hash_algorithms.dart'; +export 'signature/preferred_key_server.dart'; +export 'signature/preferred_symmetric_algorithms.dart'; +export 'signature/primary_user_id.dart'; +export 'signature/regular_expression.dart'; +export 'signature/revocable.dart'; +export 'signature/revocation_key.dart'; +export 'signature/revocation_reason.dart'; +export 'signature/signature_creation_time.dart'; +export 'signature/signature_expiration_time.dart'; +export 'signature/signature_target.dart'; +export 'signature/signer_user_id.dart'; +export 'signature/trust_signature.dart'; + /// Signature subpacket class /// Author Nguyen Van Nguyen class SignatureSubpacket implements SubpacketInterface { From 80b3775c2865afd4b0edfbdf4f520f4c3f39cc94 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 19 Nov 2024 16:34:34 +0700 Subject: [PATCH 010/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/signature.dart | 40 ++++++++---------------------- lib/src/type/signature_packet.dart | 2 ++ 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index cee62136..e6578ad0 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -75,14 +75,10 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { Uint8List get data => Uint8List.fromList([]); @override - DateTime? get creationTime => _getSubpacket( - hashedSubpackets, - )?.creationTime; + DateTime? get creationTime => getSubpacket()?.creationTime; @override - DateTime? get expirationTime => _getSubpacket( - hashedSubpackets, - )?.expirationTime; + DateTime? get expirationTime => getSubpacket()?.expirationTime; @override bool get isCertRevocation => signatureType == SignatureType.certRevocation; @@ -104,11 +100,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { bool get isKeyRevocation => signatureType == SignatureType.keyRevocation; @override - bool get isPrimaryUserID => - _getSubpacket( - hashedSubpackets, - )?.isPrimary ?? - false; + bool get isPrimaryUserID => getSubpacket()?.isPrimary ?? false; @override bool get isSubkeyBinding => signatureType == SignatureType.subkeyBinding; @@ -118,23 +110,13 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { @override Uint8List get issuerFingerprint { - final subpacket = _getSubpacket( - hashedSubpackets, - ) ?? - _getSubpacket( - unhashedSubpackets, - ); + final subpacket = getSubpacket() ?? getSubpacket(); return subpacket?.fingerprint ?? Uint8List(version == 6 ? 32 : 20); } @override Uint8List get issuerKeyID { - final subpacket = _getSubpacket( - hashedSubpackets, - ) ?? - _getSubpacket( - unhashedSubpackets, - ); + final subpacket = getSubpacket() ?? getSubpacket(); if (subpacket != null) { return subpacket.keyID; } else { @@ -144,6 +126,11 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { } } + @override + T? getSubpacket() { + return hashedSubpackets.whereType().elementAtOrNull(0) ?? unhashedSubpackets.whereType().elementAtOrNull(0); + } + @override bool isExpired([DateTime? time]) { // TODO: implement isExpired @@ -156,13 +143,6 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { throw UnimplementedError(); } - static T? _getSubpacket( - final Iterable subpackets, - ) { - final typedSubpackets = subpackets.whereType(); - return typedSubpackets.isNotEmpty ? typedSubpackets.first : null; - } - /// Encode subpacket to bytes static Uint8List _encodeSubpackets( final Iterable subpackets, diff --git a/lib/src/type/signature_packet.dart b/lib/src/type/signature_packet.dart index 3f46068e..f62f32e1 100644 --- a/lib/src/type/signature_packet.dart +++ b/lib/src/type/signature_packet.dart @@ -58,6 +58,8 @@ abstract class SignaturePacketInterface extends PacketInterface { bool get isSubkeyRevocation; + T? getSubpacket(); + bool isExpired([DateTime? time]); bool verify( From ea92804e83d6370d1d86d7342502671da13e02ae Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 19 Nov 2024 16:39:11 +0700 Subject: [PATCH 011/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/signature.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index e6578ad0..44dfb429 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -109,14 +109,12 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { bool get isSubkeyRevocation => signatureType == SignatureType.subkeyRevocation; @override - Uint8List get issuerFingerprint { - final subpacket = getSubpacket() ?? getSubpacket(); - return subpacket?.fingerprint ?? Uint8List(version == 6 ? 32 : 20); - } + Uint8List get issuerFingerprint => + getSubpacket()?.fingerprint ?? Uint8List(version == 6 ? 32 : 20); @override Uint8List get issuerKeyID { - final subpacket = getSubpacket() ?? getSubpacket(); + final subpacket = getSubpacket(); if (subpacket != null) { return subpacket.keyID; } else { From 4062d69d28d9c4dee4fb788b8c5d05d2a46928bf Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 19 Nov 2024 17:01:32 +0700 Subject: [PATCH 012/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/dsa_public_material.dart | 4 ++-- lib/src/packet/key/ecdsa_public_material.dart | 4 ++-- .../packet/key/eddsa_legacy_public_material.dart | 4 ++-- lib/src/packet/key/eddsa_public_material.dart | 4 ++-- lib/src/packet/key/rsa_public_material.dart | 4 ++-- lib/src/packet/signature.dart | 14 ++++++++++---- lib/src/type/signature_packet.dart | 8 ++++---- ...aterial.dart => verification_key_material.dart} | 5 +++-- 8 files changed, 27 insertions(+), 20 deletions(-) rename lib/src/type/{public_key_material.dart => verification_key_material.dart} (82%) diff --git a/lib/src/packet/key/dsa_public_material.dart b/lib/src/packet/key/dsa_public_material.dart index 82b51d9a..102c377d 100644 --- a/lib/src/packet/key/dsa_public_material.dart +++ b/lib/src/packet/key/dsa_public_material.dart @@ -11,11 +11,11 @@ import 'package:pointycastle/export.dart'; import '../../common/helpers.dart'; import '../../cryptor/asymmetric/dsa.dart'; import '../../enum/hash_algorithm.dart'; -import '../../type/public_key_material.dart'; +import '../../type/verification_key_material.dart'; /// DSA public key material /// Author Nguyen Van Nguyen -class DSAPublicMaterial implements PublicKeyMaterialInterface { +class DSAPublicMaterial implements VerificationKeyMaterial { /// DSA prime p final BigInt prime; diff --git a/lib/src/packet/key/ecdsa_public_material.dart b/lib/src/packet/key/ecdsa_public_material.dart index 8811701d..eeb50719 100644 --- a/lib/src/packet/key/ecdsa_public_material.dart +++ b/lib/src/packet/key/ecdsa_public_material.dart @@ -10,11 +10,11 @@ import 'package:pointycastle/pointycastle.dart'; import '../../common/helpers.dart'; import '../../enum/hash_algorithm.dart'; import '../../packet/key/ec_public_material.dart'; -import '../../type/public_key_material.dart'; +import '../../type/verification_key_material.dart'; /// ECDSA public key material /// Author Nguyen Van Nguyen -class ECDSAPublicMaterial extends ECPublicMaterial implements PublicKeyMaterialInterface { +class ECDSAPublicMaterial extends ECPublicMaterial implements VerificationKeyMaterial { ECDSAPublicMaterial(super.oid, super.q); factory ECDSAPublicMaterial.fromBytes(final Uint8List bytes) { diff --git a/lib/src/packet/key/eddsa_legacy_public_material.dart b/lib/src/packet/key/eddsa_legacy_public_material.dart index fe918a9a..bb986983 100644 --- a/lib/src/packet/key/eddsa_legacy_public_material.dart +++ b/lib/src/packet/key/eddsa_legacy_public_material.dart @@ -9,12 +9,12 @@ import 'package:pointycastle/asn1.dart'; import '../../common/helpers.dart'; import '../../enum/hash_algorithm.dart'; -import '../../type/public_key_material.dart'; +import '../../type/verification_key_material.dart'; import 'ec_public_material.dart'; /// EdDSA legacy public key material /// Author Nguyen Van Nguyen -class EdDSALegacyPublicMaterial extends ECPublicMaterial implements PublicKeyMaterialInterface { +class EdDSALegacyPublicMaterial extends ECPublicMaterial implements VerificationKeyMaterial { EdDSALegacyPublicMaterial(super.oid, super.q); factory EdDSALegacyPublicMaterial.fromBytes(final Uint8List bytes) { diff --git a/lib/src/packet/key/eddsa_public_material.dart b/lib/src/packet/key/eddsa_public_material.dart index 5dbba7f4..622cf31b 100644 --- a/lib/src/packet/key/eddsa_public_material.dart +++ b/lib/src/packet/key/eddsa_public_material.dart @@ -12,11 +12,11 @@ import 'package:sign_dart/sign_dart.dart'; import '../../common/helpers.dart'; import '../../enum/eddsa_curve.dart'; import '../../enum/hash_algorithm.dart'; -import '../../type/public_key_material.dart'; +import '../../type/verification_key_material.dart'; /// EdDSA public key material /// Author Nguyen Van Nguyen -class EdDSAPublicMaterial implements PublicKeyMaterialInterface { +class EdDSAPublicMaterial implements VerificationKeyMaterial { final Uint8List publicKey; final EdDSACurve curve; diff --git a/lib/src/packet/key/rsa_public_material.dart b/lib/src/packet/key/rsa_public_material.dart index ab807bb0..82841805 100644 --- a/lib/src/packet/key/rsa_public_material.dart +++ b/lib/src/packet/key/rsa_public_material.dart @@ -9,11 +9,11 @@ import 'package:pointycastle/pointycastle.dart'; import '../../common/helpers.dart'; import '../../enum/hash_algorithm.dart'; -import '../../type/public_key_material.dart'; +import '../../type/verification_key_material.dart'; /// RSA public key material /// Author Nguyen Van Nguyen -class RSAPublicMaterial implements PublicKeyMaterialInterface { +class RSAPublicMaterial implements VerificationKeyMaterial { /// RSA modulus n final BigInt modulus; diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 44dfb429..77d01f2a 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -130,13 +130,19 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { } @override - bool isExpired([DateTime? time]) { - // TODO: implement isExpired - throw UnimplementedError(); + bool isExpired([final DateTime? time]) { + final timestamp = time?.millisecondsSinceEpoch ?? DateTime.now().millisecondsSinceEpoch; + final creation = creationTime?.millisecondsSinceEpoch ?? 0; + final expiration = expirationTime?.millisecondsSinceEpoch ?? 0; + return !(creation < timestamp && timestamp < expiration); } @override - bool verify(KeyPacketInterface verifyKey, Uint8List dataToVerify, [DateTime? time]) { + bool verify( + final KeyPacketInterface verifyKey, + final Uint8List dataToVerify, [ + final DateTime? time, + ]) { // TODO: implement verify throw UnimplementedError(); } diff --git a/lib/src/type/signature_packet.dart b/lib/src/type/signature_packet.dart index f62f32e1..303df25c 100644 --- a/lib/src/type/signature_packet.dart +++ b/lib/src/type/signature_packet.dart @@ -60,11 +60,11 @@ abstract class SignaturePacketInterface extends PacketInterface { T? getSubpacket(); - bool isExpired([DateTime? time]); + bool isExpired([final DateTime? time]); bool verify( - KeyPacketInterface verifyKey, - Uint8List dataToVerify, [ - DateTime? time, + final KeyPacketInterface verifyKey, + final Uint8List dataToVerify, [ + final DateTime? time, ]); } diff --git a/lib/src/type/public_key_material.dart b/lib/src/type/verification_key_material.dart similarity index 82% rename from lib/src/type/public_key_material.dart rename to lib/src/type/verification_key_material.dart index 05d5077c..62e36b4c 100644 --- a/lib/src/type/public_key_material.dart +++ b/lib/src/type/verification_key_material.dart @@ -5,12 +5,13 @@ library; import 'dart:typed_data'; + import '../enum/hash_algorithm.dart'; import 'key_material.dart'; -/// Public key material interface +/// Verification key material interface /// Author Nguyen Van Nguyen -abstract class PublicKeyMaterialInterface extends KeyMaterialInterface { +abstract class VerificationKeyMaterial extends KeyMaterialInterface { /// Verify a signature with message bool verify( final Uint8List message, From 96be992197ef40b3f0c8a3e896e5973078fb0d19 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 19 Nov 2024 17:16:32 +0700 Subject: [PATCH 013/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/signature.dart | 47 +++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 77d01f2a..774dcd02 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -15,6 +15,7 @@ import '../enum/packet_type.dart'; import '../enum/signature_type.dart'; import '../type/signature_packet.dart'; import '../type/subpacket.dart'; +import '../type/verification_key_material.dart'; import 'base.dart'; import 'public_key.dart'; import 'signature_subpacket.dart'; @@ -143,8 +144,50 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { final Uint8List dataToVerify, [ final DateTime? time, ]) { - // TODO: implement verify - throw UnimplementedError(); + if (issuerKeyID != verifyKey.keyID) { + throw ArgumentError('Signature was not issued by the given public key.'); + } + if (keyAlgorithm != verifyKey.keyAlgorithm) { + throw ArgumentError( + 'Public key algorithm used to sign signature does not match issuer key algorithm.', + ); + } + if (isExpired(time)) { + throw StateError('Signature is expired.'); + } + + final message = Uint8List.fromList([ + ...dataToVerify, + ...signatureData, + ..._calculateTrailer( + version, + signatureData.length, + ) + ]); + final hash = Helper.hashDigest(message, hashAlgorithm); + if (signedHashValue[0] != hash[0] || signedHashValue[1] != hash[1]) { + throw StateError('Signed digest did not match!'); + } + + final keyMaterial = verifyKey.keyMaterial; + if (keyMaterial is VerificationKeyMaterial) { + return keyMaterial.verify(message, hashAlgorithm, signature); + } else { + throw UnsupportedError( + 'Unsupported public key algorithm for verification.', + ); + } + } + + static Uint8List _calculateTrailer( + final int version, + final int dataLength, + ) { + return Uint8List.fromList([ + version, + 0xff, + ...dataLength.pack32(), + ]); } /// Encode subpacket to bytes From 70fefeff7e6eeef9be67d679056640d92880fe77 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 19 Nov 2024 17:32:16 +0700 Subject: [PATCH 014/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/dsa_secret_material.dart | 2 +- lib/src/packet/key/ecdh_secret_material.dart | 2 +- lib/src/packet/key/ecdh_session_key_cryptor.dart | 2 +- lib/src/packet/key/ecdsa_secret_material.dart | 2 +- lib/src/packet/key/rsa_secret_material.dart | 8 ++++---- lib/src/packet/key/session_key.dart | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/src/packet/key/dsa_secret_material.dart b/lib/src/packet/key/dsa_secret_material.dart index 16239697..9c4c6c7d 100644 --- a/lib/src/packet/key/dsa_secret_material.dart +++ b/lib/src/packet/key/dsa_secret_material.dart @@ -43,7 +43,7 @@ class DSASecretMaterial implements SigningKeyMaterialInterface { int get keyLength => publicMaterial.keyLength; @override - Uint8List sign(Uint8List message, HashAlgorithm hash) { + Uint8List sign(final Uint8List message, final HashAlgorithm hash) { final signer = DSASigner(Digest(hash.digestName)) ..init( true, diff --git a/lib/src/packet/key/ecdh_secret_material.dart b/lib/src/packet/key/ecdh_secret_material.dart index b175d29b..9141b4dc 100644 --- a/lib/src/packet/key/ecdh_secret_material.dart +++ b/lib/src/packet/key/ecdh_secret_material.dart @@ -32,7 +32,7 @@ class ECDHSecretMaterial extends ECSecretMaterial implements SecretKeyMaterialIn publicMaterial, ); - factory ECDHSecretMaterial.generate(Ecc curve) { + factory ECDHSecretMaterial.generate(final Ecc curve) { switch (curve) { case Ecc.curve25519: final secret = Helper.secureRandom().nextBytes( diff --git a/lib/src/packet/key/ecdh_session_key_cryptor.dart b/lib/src/packet/key/ecdh_session_key_cryptor.dart index 78798f7e..eb7ce19c 100644 --- a/lib/src/packet/key/ecdh_session_key_cryptor.dart +++ b/lib/src/packet/key/ecdh_session_key_cryptor.dart @@ -124,7 +124,7 @@ class ECDHSessionKeyCryptor extends SessionKeyCryptor { } @override - Uint8List decrypt(SecretKeyMaterialInterface key) { + Uint8List decrypt(final SecretKeyMaterialInterface key) { if (key is ECDHSecretMaterial) { final Uint8List sharedKey; switch (key.publicMaterial.curve) { diff --git a/lib/src/packet/key/ecdsa_secret_material.dart b/lib/src/packet/key/ecdsa_secret_material.dart index 35d973fe..73b6fd1d 100644 --- a/lib/src/packet/key/ecdsa_secret_material.dart +++ b/lib/src/packet/key/ecdsa_secret_material.dart @@ -31,7 +31,7 @@ class ECDSASecretMaterial extends ECSecretMaterial implements SigningKeyMaterial publicMaterial, ); - factory ECDSASecretMaterial.generate(Ecc curve) { + factory ECDSASecretMaterial.generate(final Ecc curve) { switch (curve) { case Ecc.curve25519: case Ecc.ed25519: diff --git a/lib/src/packet/key/rsa_secret_material.dart b/lib/src/packet/key/rsa_secret_material.dart index ba4bb8d7..74425b97 100644 --- a/lib/src/packet/key/rsa_secret_material.dart +++ b/lib/src/packet/key/rsa_secret_material.dart @@ -42,9 +42,9 @@ class RSASecretMaterial implements SigningKeyMaterialInterface { this.exponent, this.primeP, this.primeQ, - this.publicMaterial, { - BigInt? coefficient, - }) : coefficient = coefficient ?? primeP.modInverse(primeQ), + this.publicMaterial, [ + final BigInt? coefficient, + ]) : coefficient = coefficient ?? primeP.modInverse(primeQ), privateKey = RSAPrivateKey( primeP * primeQ, exponent, @@ -73,7 +73,7 @@ class RSASecretMaterial implements SigningKeyMaterialInterface { primeP, primeQ, publicMaterial, - coefficient: coefficient, + coefficient, ); } diff --git a/lib/src/packet/key/session_key.dart b/lib/src/packet/key/session_key.dart index 2c7a1344..808e458d 100644 --- a/lib/src/packet/key/session_key.dart +++ b/lib/src/packet/key/session_key.dart @@ -56,7 +56,7 @@ class SessionKey implements SessionKeyInterface { } @override - void checksum(Uint8List checksum) { + void checksum(final Uint8List checksum) { final computedChecksum = computeChecksum(); if (!((computedChecksum[0] == checksum[0]) && (computedChecksum[1] == checksum[1]))) { throw StateError('Session key checksum mismatch!'); From d17ae341256a37527ff19bc630ca46c58df50046 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 20 Nov 2024 12:04:51 +0700 Subject: [PATCH 015/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/signature.dart | 285 +++++++++++++++++- .../intended_recipient_fingerprint.dart | 40 +++ .../packet/signature/issuer_fingerprint.dart | 1 - lib/src/packet/signature/issuer_key_id.dart | 1 - .../packet/signature/key_expiration_time.dart | 1 - lib/src/packet/signature/key_flags.dart | 1 - .../signature/key_server_preferences.dart | 1 - .../signature/preferred_aead_ciphers.dart | 24 ++ lib/src/packet/signature_subpacket.dart | 2 + 9 files changed, 350 insertions(+), 6 deletions(-) create mode 100644 lib/src/packet/signature/intended_recipient_fingerprint.dart create mode 100644 lib/src/packet/signature/preferred_aead_ciphers.dart diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 774dcd02..dd70b738 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -12,6 +12,7 @@ import '../common/helpers.dart'; import '../enum/hash_algorithm.dart'; import '../enum/key_algorithm.dart'; import '../enum/packet_type.dart'; +import '../enum/signature_subpacket_type.dart'; import '../enum/signature_type.dart'; import '../type/signature_packet.dart'; import '../type/subpacket.dart'; @@ -19,6 +20,7 @@ import '../type/verification_key_material.dart'; import 'base.dart'; import 'public_key.dart'; import 'signature_subpacket.dart'; +import 'subpacket_reader.dart'; /// Implementation of the Signature Packet (Tag 2) /// Author Nguyen Van Nguyen @@ -70,7 +72,77 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { hashAlgorithm.value, ..._encodeSubpackets(hashedSubpackets), ]), - super(PacketType.signature); + super(PacketType.signature) { + if (version != 4 || version != 6) { + throw UnsupportedError( + 'Version $version of the signature packet is unsupported.', + ); + } + } + + factory SignaturePacket.fromByteData(final Uint8List bytes) { + var pos = 0; + + /// A one-octet version number (4 or 6). + final version = bytes[pos++]; + final isV6 = version == 6; + + /// One-octet signature type. + final signatureType = SignatureType.values.firstWhere( + (type) => type.value == bytes[pos], + ); + pos++; + + /// One-octet public-key algorithm. + final keyAlgorithm = KeyAlgorithm.values.firstWhere( + (alg) => alg.value == bytes[pos], + ); + pos++; + + /// One-octet hash algorithm. + final hashAlgorithm = HashAlgorithm.values.firstWhere( + (alg) => alg.value == bytes[pos], + ); + pos++; + + /// read hashed subpackets + final hashedLength = isV6 ? bytes.sublist(pos, pos + 4).unpack32() : bytes.sublist(pos, pos + 2).unpack16(); + pos += isV6 ? 4 : 2; + final hashedSubpackets = _readSubpackets( + bytes.sublist(pos, pos + hashedLength), + ); + pos += hashedLength; + + /// read unhashed subpackets + final unhashedLength = isV6 ? bytes.sublist(pos, pos + 4).unpack32() : bytes.sublist(pos, pos + 2).unpack16(); + pos += isV6 ? 4 : 2; + final unhashedSubpackets = _readSubpackets( + bytes.sublist(pos, pos + unhashedLength), + ); + pos += unhashedLength; + + /// Two-octet field holding left 16 bits of signed hash value. + final signedHashValue = bytes.sublist(pos, pos + 2); + pos += 2; + + final saltLength = isV6 ? bytes[pos++] : 0; + final salt = bytes.sublist(pos, pos + saltLength); + pos += saltLength; + + final signature = bytes.sublist(pos); + + return SignaturePacket( + version, + signatureType, + keyAlgorithm, + hashAlgorithm, + signedHashValue, + salt, + signature, + hashedSubpackets: hashedSubpackets, + unhashedSubpackets: unhashedSubpackets, + ); + } @override Uint8List get data => Uint8List.fromList([]); @@ -179,6 +251,217 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { } } + static Iterable _readSubpackets(final Uint8List bytes) { + final subpackets = []; + var offset = 0; + while (offset < bytes.length) { + final reader = SubpacketReader.read(bytes, offset); + offset = reader.offset; + final data = reader.data; + if (data.isNotEmpty) { + final critical = ((reader.type & 0x80) != 0); + final type = SignatureSubpacketType.values.firstWhere( + (type) => type.value == (reader.type & 0x7f), + ); + switch (type) { + case SignatureSubpacketType.signatureCreationTime: + subpackets.add(SignatureCreationTime( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.signatureExpirationTime: + subpackets.add(SignatureExpirationTime( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.exportableCertification: + subpackets.add(ExportableCertification( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.trustSignature: + subpackets.add(TrustSignature( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.regularExpression: + subpackets.add(RegularExpression( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.revocable: + subpackets.add(Revocable( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.keyExpirationTime: + subpackets.add(KeyExpirationTime( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.preferredSymmetricAlgorithms: + subpackets.add(PreferredSymmetricAlgorithms( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.revocationKey: + subpackets.add(RevocationKey( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.issuerKeyID: + subpackets.add(IssuerKeyID( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.notationData: + subpackets.add(NotationData( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.preferredHashAlgorithms: + subpackets.add(PreferredHashAlgorithms( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.preferredCompressionAlgorithms: + subpackets.add(PreferredCompressionAlgorithms( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.keyServerPreferences: + subpackets.add(KeyServerPreferences( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.preferredKeyServer: + subpackets.add(PreferredKeyServer( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.primaryUserID: + subpackets.add(PrimaryUserID(data, critical: critical)); + break; + case SignatureSubpacketType.policyURI: + subpackets.add(PolicyURI( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.keyFlags: + subpackets.add(KeyFlags( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.signerUserID: + subpackets.add(SignerUserID( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.revocationReason: + subpackets.add(RevocationReason( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.features: + subpackets.add(Features( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.signatureTarget: + subpackets.add(SignatureTarget( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.embeddedSignature: + subpackets.add(EmbeddedSignature( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.issuerFingerprint: + subpackets.add(IssuerFingerprint( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.preferredAeadAlgorithms: + subpackets.add(PreferredAeadAlgorithms( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.intendedRecipientFingerprint: + subpackets.add(IntendedRecipientFingerprint( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + case SignatureSubpacketType.preferredAeadCiphers: + subpackets.add(PreferredAeadCiphers( + data, + critical: critical, + isLong: reader.isLong, + )); + break; + default: + subpackets.add(SignatureSubpacket( + type, + data, + critical: critical, + isLong: reader.isLong, + )); + } + } + } + return subpackets; + } + static Uint8List _calculateTrailer( final int version, final int dataLength, diff --git a/lib/src/packet/signature/intended_recipient_fingerprint.dart b/lib/src/packet/signature/intended_recipient_fingerprint.dart new file mode 100644 index 00000000..6be44a28 --- /dev/null +++ b/lib/src/packet/signature/intended_recipient_fingerprint.dart @@ -0,0 +1,40 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'package:dart_pg/src/type/key_packet.dart'; + +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// The IntendedRecipientFingerprint sub-packet class +/// Giving the intended recipient fingerprint. +/// Author Nguyen Van Nguyen +class IntendedRecipientFingerprint extends SignatureSubpacket { + IntendedRecipientFingerprint( + final Uint8List data, { + super.critical, + super.isLong, + }) : super( + SignatureSubpacketType.intendedRecipientFingerprint, + data, + ); + + factory IntendedRecipientFingerprint.fromKey( + final KeyPacketInterface key, + ) => + IntendedRecipientFingerprint( + Uint8List.fromList([ + key.keyVersion, + ...key.fingerprint, + ]), + ); + + int get keyVersion => data[0]; + + Uint8List get fingerprint => data.sublist(1); +} diff --git a/lib/src/packet/signature/issuer_fingerprint.dart b/lib/src/packet/signature/issuer_fingerprint.dart index f315bd6c..83173962 100644 --- a/lib/src/packet/signature/issuer_fingerprint.dart +++ b/lib/src/packet/signature/issuer_fingerprint.dart @@ -12,7 +12,6 @@ import '../../enum/signature_subpacket_type.dart'; import '../signature_subpacket.dart'; /// The OpenPGP Key fingerprint of the key issuing the signature. -/// See https://datatracker.ietf.org/doc/html/draft-ietf-openpgp-rfc4880bis#section-5.2.3.28 /// Author Nguyen Van Nguyen class IssuerFingerprint extends SignatureSubpacket { IssuerFingerprint( diff --git a/lib/src/packet/signature/issuer_key_id.dart b/lib/src/packet/signature/issuer_key_id.dart index 427e3b89..9a055295 100644 --- a/lib/src/packet/signature/issuer_key_id.dart +++ b/lib/src/packet/signature/issuer_key_id.dart @@ -11,7 +11,6 @@ import '../../enum/signature_subpacket_type.dart'; import '../signature_subpacket.dart'; /// The OpenPGP Key ID of the key issuing the signature. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.5 /// Author Nguyen Van Nguyen class IssuerKeyID extends SignatureSubpacket { IssuerKeyID( diff --git a/lib/src/packet/signature/key_expiration_time.dart b/lib/src/packet/signature/key_expiration_time.dart index ff2009c3..4429b66b 100644 --- a/lib/src/packet/signature/key_expiration_time.dart +++ b/lib/src/packet/signature/key_expiration_time.dart @@ -15,7 +15,6 @@ import '../signature_subpacket.dart'; /// the key creation time that the key expires. If this is not present /// or has a value of zero, the key never expires. This is found only on /// a self-signature. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.6 /// Author Nguyen Van Nguyen class KeyExpirationTime extends SignatureSubpacket { KeyExpirationTime( diff --git a/lib/src/packet/signature/key_flags.dart b/lib/src/packet/signature/key_flags.dart index 8889a88b..62dd2d7c 100644 --- a/lib/src/packet/signature/key_flags.dart +++ b/lib/src/packet/signature/key_flags.dart @@ -11,7 +11,6 @@ import '../../enum/signature_subpacket_type.dart'; import '../signature_subpacket.dart'; /// This subpacket contains a list of binary flags that hold information about a key. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.21 /// Author Nguyen Van Nguyen class KeyFlags extends SignatureSubpacket { KeyFlags( diff --git a/lib/src/packet/signature/key_server_preferences.dart b/lib/src/packet/signature/key_server_preferences.dart index ee3c3187..fc96fd09 100644 --- a/lib/src/packet/signature/key_server_preferences.dart +++ b/lib/src/packet/signature/key_server_preferences.dart @@ -14,7 +14,6 @@ import '../signature_subpacket.dart'; /// This is a list of one-bit flags that indicate preferences that the /// key holder has about how the key is handled on a key server. /// All undefined flags MUST be zero. -/// See https://www.rfc-editor.org/rfc/rfc4880#section-5.2.3.17 /// Author Nguyen Van Nguyen class KeyServerPreferences extends SignatureSubpacket { KeyServerPreferences( diff --git a/lib/src/packet/signature/preferred_aead_ciphers.dart b/lib/src/packet/signature/preferred_aead_ciphers.dart new file mode 100644 index 00000000..19501d9d --- /dev/null +++ b/lib/src/packet/signature/preferred_aead_ciphers.dart @@ -0,0 +1,24 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../../enum/signature_subpacket_type.dart'; +import '../signature_subpacket.dart'; + +/// The IntendedRecipientFingerprint sub-packet class +/// Giving the intended recipient fingerprint. +/// Author Nguyen Van Nguyen +class PreferredAeadCiphers extends SignatureSubpacket { + PreferredAeadCiphers( + final Uint8List data, { + super.critical, + super.isLong, + }) : super( + SignatureSubpacketType.preferredAeadCiphers, + data, + ); +} diff --git a/lib/src/packet/signature_subpacket.dart b/lib/src/packet/signature_subpacket.dart index 62ad7599..6768acbb 100644 --- a/lib/src/packet/signature_subpacket.dart +++ b/lib/src/packet/signature_subpacket.dart @@ -13,6 +13,7 @@ import '../type/subpacket.dart'; export 'signature/embedded_signature.dart'; export 'signature/exportable_certification.dart'; export 'signature/features.dart'; +export 'signature/intended_recipient_fingerprint.dart'; export 'signature/issuer_fingerprint.dart'; export 'signature/issuer_key_id.dart'; export 'signature/key_expiration_time.dart'; @@ -21,6 +22,7 @@ export 'signature/key_server_preferences.dart'; export 'signature/notation_data.dart'; export 'signature/policy_uri.dart'; export 'signature/preferred_aead_algorithms.dart'; +export 'signature/preferred_aead_ciphers.dart'; export 'signature/preferred_compression_algorithms.dart'; export 'signature/preferred_hash_algorithms.dart'; export 'signature/preferred_key_server.dart'; From 43d40aab22db526438b08f050b34a9144478fd0f Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 20 Nov 2024 13:10:22 +0700 Subject: [PATCH 016/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/public_material.dart | 1 + lib/src/packet/secret_key.dart | 15 ++++ lib/src/packet/signature.dart | 96 +++++++++++++++++++++++-- lib/src/type/secret_key_packet.dart | 4 ++ 4 files changed, 110 insertions(+), 6 deletions(-) diff --git a/lib/src/packet/key/public_material.dart b/lib/src/packet/key/public_material.dart index b5eba66e..5e3fe58b 100644 --- a/lib/src/packet/key/public_material.dart +++ b/lib/src/packet/key/public_material.dart @@ -5,6 +5,7 @@ library; export 'dsa_public_material.dart'; +export 'ec_public_material.dart'; export 'ecdh_public_material.dart'; export 'ecdsa_public_material.dart'; export 'eddsa_legacy_public_material.dart'; diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 89a200b6..0b187018 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -388,6 +388,21 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { @override int get keyVersion => publicKey.keyVersion; + @override + HashAlgorithm get preferredHash { + if ((keyMaterial is ECPublicMaterial)) { + final curve = Ecc.values.firstWhere( + (info) => info.asn1Oid == (keyMaterial as ECPublicMaterial).oid, + orElse: () => Ecc.secp521r1, + ); + return curve.hashAlgorithm; + } else if (keyMaterial is EdDSAPublicMaterial) { + return (keyMaterial as EdDSAPublicMaterial).curve.hashAlgorithm; + } else { + return HashAlgorithm.sha256; + } + } + static Uint8List _produceEncryptionKey( final String passphrase, final SymmetricAlgorithm symmetric, diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index dd70b738..440de86f 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -6,15 +6,16 @@ library; import 'dart:typed_data'; -import 'package:dart_pg/src/type/key_packet.dart'; - import '../common/helpers.dart'; import '../enum/hash_algorithm.dart'; import '../enum/key_algorithm.dart'; import '../enum/packet_type.dart'; import '../enum/signature_subpacket_type.dart'; import '../enum/signature_type.dart'; +import '../type/key_packet.dart'; +import '../type/secret_key_packet.dart'; import '../type/signature_packet.dart'; +import '../type/signing_key_material.dart'; import '../type/subpacket.dart'; import '../type/verification_key_material.dart'; import 'base.dart'; @@ -70,7 +71,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { signatureType.value, keyAlgorithm.value, hashAlgorithm.value, - ..._encodeSubpackets(hashedSubpackets), + ..._encodeSubpackets(hashedSubpackets, version == 6), ]), super(PacketType.signature) { if (version != 4 || version != 6) { @@ -80,7 +81,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { } } - factory SignaturePacket.fromByteData(final Uint8List bytes) { + factory SignaturePacket.fromBytes(final Uint8List bytes) { var pos = 0; /// A one-octet version number (4 or 6). @@ -144,8 +145,72 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { ); } + factory SignaturePacket.createSignature( + SecretKeyPacketInterface signKey, + SignatureType signatureType, + Uint8List dataToSign, { + final HashAlgorithm? preferredHash, + final Iterable subpackets = const [], + final int keyExpirationTime = 0, + final DateTime? date, + }) { + final version = signKey.keyVersion; + final keyAlgorithm = signKey.keyAlgorithm; + final hashAlgorithm = preferredHash ?? signKey.preferredHash; + Helper.assertHash(hashAlgorithm); + + final hashedSubpackets = [ + SignatureCreationTime.fromTime(date ?? DateTime.now()), + IssuerFingerprint.fromKey(signKey), + IssuerKeyID(signKey.keyID), + ...subpackets, + ]; + if (version == 4) { + hashedSubpackets.add(NotationData.saltNotation(hashAlgorithm.saltSize)); + } + if (keyExpirationTime > 0) { + hashedSubpackets.add(KeyExpirationTime.fromTime(keyExpirationTime)); + } + final salt = Helper.secureRandom().nextBytes(hashAlgorithm.saltSize); + + final signatureData = Uint8List.fromList([ + version, + signatureType.value, + keyAlgorithm.value, + hashAlgorithm.value, + ..._encodeSubpackets(hashedSubpackets, version == 6), + ]); + + final message = Uint8List.fromList([ + ...dataToSign, + ...signatureData, + ..._calculateTrailer( + version, + signatureData.lengthInBytes, + ) + ]); + + return SignaturePacket( + version, + signatureType, + keyAlgorithm, + hashAlgorithm, + Helper.hashDigest(message, hashAlgorithm).sublist(0, 2), + salt, + _signMessage(signKey, hashAlgorithm, message), + hashedSubpackets: hashedSubpackets, + ); + } + @override - Uint8List get data => Uint8List.fromList([]); + Uint8List get data => Uint8List.fromList([ + ...signatureData, + ..._encodeSubpackets(unhashedSubpackets, version == 6), + ...signedHashValue, + ...version == 6 ? [salt.length] : [], + ...version == 6 ? salt : [], + ...signature, + ]); @override DateTime? get creationTime => getSubpacket()?.creationTime; @@ -251,6 +316,21 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { } } + static Uint8List _signMessage( + final SecretKeyPacketInterface key, + final HashAlgorithm hash, + final Uint8List message, + ) { + final keyMaterial = key.secretKeyMaterial; + if (keyMaterial is SigningKeyMaterialInterface) { + return keyMaterial.sign(message, hash); + } else { + throw UnsupportedError( + 'Unsupported public key algorithm for signing.', + ); + } + } + static Iterable _readSubpackets(final Uint8List bytes) { final subpackets = []; var offset = 0; @@ -476,12 +556,16 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { /// Encode subpacket to bytes static Uint8List _encodeSubpackets( final Iterable subpackets, + bool isV6, ) { final bytes = subpackets .map( (subpacket) => subpacket.encode(), ) .expand((byte) => byte); - return Uint8List.fromList([...bytes.length.pack16(), ...bytes]); + return Uint8List.fromList([ + ...isV6 ? bytes.length.pack32() : bytes.length.pack16(), + ...bytes, + ]); } } diff --git a/lib/src/type/secret_key_packet.dart b/lib/src/type/secret_key_packet.dart index 43bbd4d4..d75c8bb3 100644 --- a/lib/src/type/secret_key_packet.dart +++ b/lib/src/type/secret_key_packet.dart @@ -4,6 +4,7 @@ library; +import '../enum/hash_algorithm.dart'; import '../enum/symmetric_algorithm.dart'; import '../enum/aead_algorithm.dart'; import 'key_packet.dart'; @@ -27,6 +28,9 @@ abstract class SecretKeyPacketInterface extends KeyPacketInterface { /// Secret key packed is decrypted bool get isDecrypted; + /// Get preferred hash algorithm + HashAlgorithm get preferredHash; + SecretKeyPacketInterface encrypt( String passphrase, SymmetricAlgorithm symmetric, From e677d529e4369e4ea50a99349f8800f8c54bc2e2 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 20 Nov 2024 13:21:06 +0700 Subject: [PATCH 017/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/secret_key.dart | 4 ++-- lib/src/packet/signature.dart | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 0b187018..77089fc4 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -5,13 +5,13 @@ library; import 'dart:typed_data'; - import 'package:pointycastle/api.dart'; import 'key/public_material.dart'; import 'key/secret_material.dart'; import '../common/argon2_s2k.dart'; +import '../common/config.dart'; import '../common/generic_s2k.dart'; import '../common/helpers.dart'; import '../cryptor/symmetric/buffered_cipher.dart'; @@ -399,7 +399,7 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { } else if (keyMaterial is EdDSAPublicMaterial) { return (keyMaterial as EdDSAPublicMaterial).curve.hashAlgorithm; } else { - return HashAlgorithm.sha256; + return Config.preferredHash; } } diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 440de86f..80516b07 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -171,7 +171,11 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { if (keyExpirationTime > 0) { hashedSubpackets.add(KeyExpirationTime.fromTime(keyExpirationTime)); } - final salt = Helper.secureRandom().nextBytes(hashAlgorithm.saltSize); + final salt = version == 6 + ? Helper.secureRandom().nextBytes( + hashAlgorithm.saltSize, + ) + : Uint8List(0); final signatureData = Uint8List.fromList([ version, @@ -186,7 +190,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { ...signatureData, ..._calculateTrailer( version, - signatureData.lengthInBytes, + signatureData.length, ) ]); From 0a41ec1edd03b5fb2a8b891aa3251396fe0e8509 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 20 Nov 2024 13:39:14 +0700 Subject: [PATCH 018/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/signature.dart | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 80516b07..798d14a4 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -107,7 +107,19 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { pos++; /// read hashed subpackets - final hashedLength = isV6 ? bytes.sublist(pos, pos + 4).unpack32() : bytes.sublist(pos, pos + 2).unpack16(); + final hashedLength = isV6 + ? bytes + .sublist( + pos, + pos + 4, + ) + .unpack32() + : bytes + .sublist( + pos, + pos + 2, + ) + .unpack16(); pos += isV6 ? 4 : 2; final hashedSubpackets = _readSubpackets( bytes.sublist(pos, pos + hashedLength), @@ -115,7 +127,19 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { pos += hashedLength; /// read unhashed subpackets - final unhashedLength = isV6 ? bytes.sublist(pos, pos + 4).unpack32() : bytes.sublist(pos, pos + 2).unpack16(); + final unhashedLength = isV6 + ? bytes + .sublist( + pos, + pos + 4, + ) + .unpack32() + : bytes + .sublist( + pos, + pos + 2, + ) + .unpack16(); pos += isV6 ? 4 : 2; final unhashedSubpackets = _readSubpackets( bytes.sublist(pos, pos + unhashedLength), From 9d79de51332611987b9091b051a03d7a6e71f44e Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 21 Nov 2024 10:22:17 +0700 Subject: [PATCH 019/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/one_pass_signature.dart | 134 +++++++++++++++++++++++++ lib/src/packet/public_key.dart | 2 +- lib/src/packet/signature.dart | 2 +- 3 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 lib/src/packet/one_pass_signature.dart diff --git a/lib/src/packet/one_pass_signature.dart b/lib/src/packet/one_pass_signature.dart new file mode 100644 index 00000000..15a7ccd8 --- /dev/null +++ b/lib/src/packet/one_pass_signature.dart @@ -0,0 +1,134 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../enum/hash_algorithm.dart'; +import '../enum/key_algorithm.dart'; +import '../enum/packet_type.dart'; +import '../enum/signature_type.dart'; +import '../type/signature_packet.dart'; +import 'base.dart'; + +/// Implementation an OpenPGP One-Pass Signature Packet (Tag 4). +/// Author Nguyen Van Nguyen +class OnePassSignaturePacket extends BasePacket { + final int version; + + final SignatureType signatureType; + + final HashAlgorithm hashAlgorithm; + + final KeyAlgorithm keyAlgorithm; + + final Uint8List salt; + + final Uint8List issuerFingerprint; + + final Uint8List issuerKeyID; + + final int nested; + + OnePassSignaturePacket( + this.version, + this.signatureType, + this.hashAlgorithm, + this.keyAlgorithm, + this.salt, + this.issuerFingerprint, + this.issuerKeyID, [ + this.nested = 0, + ]) : super(PacketType.onePassSignature) { + if (version != 3 && version != 6) { + throw UnsupportedError( + 'Version $version of the one-pass signature packet is unsupported.', + ); + } + } + + factory OnePassSignaturePacket.fromBytes(final Uint8List bytes) { + var pos = 0; + + /// A one-octet version number (4 or 6). + final version = bytes[pos++]; + + /// One-octet signature type. + final signatureType = SignatureType.values.firstWhere( + (type) => type.value == bytes[pos], + ); + pos++; + + /// One-octet hash algorithm. + final hashAlgorithm = HashAlgorithm.values.firstWhere( + (alg) => alg.value == bytes[pos], + ); + pos++; + + /// One-octet public-key algorithm. + final keyAlgorithm = KeyAlgorithm.values.firstWhere( + (alg) => alg.value == bytes[pos], + ); + pos++; + + final Uint8List salt; + final Uint8List issuerFingerprint; + final Uint8List issuerKeyID; + if (version == 6) { + final saltLength = bytes[pos++]; + salt = bytes.sublist(pos, pos + saltLength); + pos += saltLength; + + issuerFingerprint = bytes.sublist(pos, pos + 32); + pos += 32; + + issuerKeyID = issuerFingerprint.sublist(0, 8); + } else { + salt = issuerFingerprint = Uint8List(0); + issuerKeyID = bytes.sublist(pos, pos + 8); + pos += 8; + } + + return OnePassSignaturePacket( + version, + signatureType, + hashAlgorithm, + keyAlgorithm, + salt, + issuerFingerprint, + issuerKeyID, + bytes[pos], + ); + } + + factory OnePassSignaturePacket.fromSignature( + final SignaturePacketInterface signature, [ + int nested = 0, + ]) { + return OnePassSignaturePacket( + signature.version == 6 ? 6 : 3, + signature.signatureType, + signature.hashAlgorithm, + signature.keyAlgorithm, + signature.salt, + signature.issuerFingerprint, + signature.issuerKeyID, + nested, + ); + } + + @override + Uint8List get data => Uint8List.fromList([ + version, + signatureType.value, + hashAlgorithm.value, + keyAlgorithm.value, + ...version == 6 ? [salt.length] : [], + ...version == 6 ? salt : [], + ...version == 6 ? issuerFingerprint : [], + ...version == 3 ? issuerKeyID : [], + nested, + ]); +} diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index 70885620..694d72d1 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -46,7 +46,7 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { this.keyMaterial, { this.keyAlgorithm = KeyAlgorithm.rsaEncryptSign, }) : super(PacketType.publicKey) { - if (keyVersion != KeyVersion.v4.value || keyVersion != KeyVersion.v6.value) { + if (keyVersion != KeyVersion.v4.value && keyVersion != KeyVersion.v6.value) { throw UnsupportedError( 'Version $keyVersion of the key packet is unsupported.', ); diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 798d14a4..6d532e70 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -74,7 +74,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { ..._encodeSubpackets(hashedSubpackets, version == 6), ]), super(PacketType.signature) { - if (version != 4 || version != 6) { + if (version != 4 && version != 6) { throw UnsupportedError( 'Version $version of the signature packet is unsupported.', ); From 4288ba803e3a443785f0ff96e76bdac23ed04790 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 21 Nov 2024 10:40:07 +0700 Subject: [PATCH 020/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/base.dart | 20 +++++++++++++ lib/src/packet/packet_list.dart | 52 +++++++++++++++++++++++++++++++-- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/lib/src/packet/base.dart b/lib/src/packet/base.dart index 9bb06e36..e67bd3dd 100644 --- a/lib/src/packet/base.dart +++ b/lib/src/packet/base.dart @@ -11,6 +11,26 @@ import '../common/extensions.dart'; import '../enum/packet_type.dart'; import '../type/packet.dart'; +export 'aead_encrypted_data.dart'; +export 'compressed_data.dart'; +export 'literal_data.dart'; +export 'marker.dart'; +export 'one_pass_signature.dart'; +export 'packet_reader.dart'; +export 'padding.dart'; +export 'public_key.dart'; +export 'public_key_encrypted_session_key.dart'; +export 'public_subkey.dart'; +export 'secret_key.dart'; +export 'secret_subkey.dart'; +export 'signature.dart'; +export 'sym_encrypted_data.dart'; +export 'sym_encrypted_integrity_protected_data.dart'; +export 'sym_encrypted_session_key.dart'; +export 'trust.dart'; +export 'user_attribute.dart'; +export 'user_id.dart'; + /// Base packet abstract class /// Author Nguyen Van Nguyen abstract class BasePacket implements PacketInterface { diff --git a/lib/src/packet/packet_list.dart b/lib/src/packet/packet_list.dart index b7290c6b..88937a2b 100644 --- a/lib/src/packet/packet_list.dart +++ b/lib/src/packet/packet_list.dart @@ -7,10 +7,10 @@ library; import 'dart:collection'; import 'dart:typed_data'; -import 'package:dart_pg/src/enum/packet_type.dart'; - +import '../enum/packet_type.dart'; import '../type/packet.dart'; import '../type/packet_list.dart'; +import 'base.dart'; /// This class represents a list of OpenPGP packets. /// Author Nguyen Van Nguyen @@ -26,12 +26,58 @@ class PacketList extends ListBase implements PacketListInterfac /// Decode packets from bytes factory PacketList.decode(Uint8List bytes) { final packets = []; + var offset = 0; + while (offset < bytes.length) { + final reader = PacketReader.read(bytes, offset); + offset = reader.offset; + final packet = switch (reader.type) { + PacketType.publicKeyEncryptedSessionKey => PublicKeyEncryptedSessionKeyPacket.fromBytes( + reader.data, + ), + PacketType.signature => SignaturePacket.fromBytes(reader.data), + PacketType.symEncryptedSessionKey => SymEncryptedSessionKeyPacket.fromBytes( + reader.data, + ), + PacketType.onePassSignature => OnePassSignaturePacket.fromBytes( + reader.data, + ), + PacketType.secretKey => SecretKeyPacket.fromBytes(reader.data), + PacketType.publicKey => PublicKeyPacket.fromBytes(reader.data), + PacketType.secretSubkey => SecretSubkeyPacket.fromBytes(reader.data), + PacketType.compressedData => CompressedDataPacket.fromBytes( + reader.data, + ), + PacketType.symEncryptedData => SymEncryptedDataPacket.fromBytes( + reader.data, + ), + PacketType.marker => MarkerPacket(), + PacketType.literalData => LiteralDataPacket.fromBytes(reader.data), + PacketType.trust => TrustPacket.fromBytes(reader.data), + PacketType.userID => UserIDPacket.fromBytes(reader.data), + PacketType.publicSubkey => PublicSubkeyPacket.fromBytes(reader.data), + PacketType.userAttribute => UserAttributePacket.fromBytes( + reader.data, + ), + PacketType.symEncryptedIntegrityProtectedData => + SymEncryptedIntegrityProtectedDataPacket.fromBytes(reader.data), + PacketType.aeadEncryptedData => AeadEncryptedDataPacket.fromBytes( + reader.data, + ), + PacketType.padding => PaddingPacket(reader.data), + }; + packets.add(packet); + } return PacketList(packets); } @override Uint8List encode() => Uint8List.fromList( - packets.map((packet) => packet.encode()).expand((byte) => byte).toList(growable: false), + packets + .map( + (packet) => packet.encode(), + ) + .expand((byte) => byte) + .toList(growable: false), ); PacketList filterByTypes([final List tags = const []]) { From 4294f0306ec65df8cb7ab35c54ba7129668e6d44 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 21 Nov 2024 10:44:27 +0700 Subject: [PATCH 021/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/aead_encrypted_data.dart | 1 - lib/src/packet/base.dart | 1 + lib/src/packet/compressed_data.dart | 1 - lib/src/packet/literal_data.dart | 1 - lib/src/packet/marker.dart | 1 - lib/src/packet/one_pass_signature.dart | 1 - lib/src/packet/packet_list.dart | 1 - lib/src/packet/padding.dart | 1 - lib/src/packet/public_key.dart | 1 - lib/src/packet/public_key_encrypted_session_key.dart | 2 -- lib/src/packet/secret_key.dart | 2 -- lib/src/packet/signature.dart | 2 -- lib/src/packet/sym_encrypted_data.dart | 1 - lib/src/packet/sym_encrypted_integrity_protected_data.dart | 1 - lib/src/packet/sym_encrypted_session_key.dart | 1 - lib/src/packet/trust.dart | 2 -- lib/src/packet/user_attribute.dart | 1 - lib/src/packet/user_id.dart | 1 - 18 files changed, 1 insertion(+), 21 deletions(-) diff --git a/lib/src/packet/aead_encrypted_data.dart b/lib/src/packet/aead_encrypted_data.dart index 57725de5..72718d8a 100644 --- a/lib/src/packet/aead_encrypted_data.dart +++ b/lib/src/packet/aead_encrypted_data.dart @@ -8,7 +8,6 @@ import 'dart:typed_data'; import '../common/helpers.dart'; import '../enum/aead_algorithm.dart'; -import '../enum/packet_type.dart'; import '../enum/symmetric_algorithm.dart'; import '../type/packet_list.dart'; import 'base.dart'; diff --git a/lib/src/packet/base.dart b/lib/src/packet/base.dart index e67bd3dd..da380b13 100644 --- a/lib/src/packet/base.dart +++ b/lib/src/packet/base.dart @@ -11,6 +11,7 @@ import '../common/extensions.dart'; import '../enum/packet_type.dart'; import '../type/packet.dart'; +export '../enum/packet_type.dart'; export 'aead_encrypted_data.dart'; export 'compressed_data.dart'; export 'literal_data.dart'; diff --git a/lib/src/packet/compressed_data.dart b/lib/src/packet/compressed_data.dart index d8f48bee..8e36836f 100644 --- a/lib/src/packet/compressed_data.dart +++ b/lib/src/packet/compressed_data.dart @@ -8,7 +8,6 @@ import 'dart:io'; import 'dart:typed_data'; import '../enum/compression_algorithm.dart'; -import '../enum/packet_type.dart'; import 'base.dart'; import 'packet_list.dart'; diff --git a/lib/src/packet/literal_data.dart b/lib/src/packet/literal_data.dart index b464a4ab..d01f8fe8 100644 --- a/lib/src/packet/literal_data.dart +++ b/lib/src/packet/literal_data.dart @@ -8,7 +8,6 @@ import 'dart:convert'; import 'dart:typed_data'; import '../common/helpers.dart'; -import '../enum/packet_type.dart'; import '../enum/literal_format.dart'; import '../type/literal_data.dart'; import 'base.dart'; diff --git a/lib/src/packet/marker.dart b/lib/src/packet/marker.dart index 56d55c89..61ff88de 100644 --- a/lib/src/packet/marker.dart +++ b/lib/src/packet/marker.dart @@ -7,7 +7,6 @@ library; import 'dart:convert'; import 'dart:typed_data'; -import '../enum/packet_type.dart'; import 'base.dart'; /// Implementation of the strange "Marker packet" (Tag 10) diff --git a/lib/src/packet/one_pass_signature.dart b/lib/src/packet/one_pass_signature.dart index 15a7ccd8..bf02e8e9 100644 --- a/lib/src/packet/one_pass_signature.dart +++ b/lib/src/packet/one_pass_signature.dart @@ -8,7 +8,6 @@ import 'dart:typed_data'; import '../enum/hash_algorithm.dart'; import '../enum/key_algorithm.dart'; -import '../enum/packet_type.dart'; import '../enum/signature_type.dart'; import '../type/signature_packet.dart'; import 'base.dart'; diff --git a/lib/src/packet/packet_list.dart b/lib/src/packet/packet_list.dart index 88937a2b..e5d716e7 100644 --- a/lib/src/packet/packet_list.dart +++ b/lib/src/packet/packet_list.dart @@ -7,7 +7,6 @@ library; import 'dart:collection'; import 'dart:typed_data'; -import '../enum/packet_type.dart'; import '../type/packet.dart'; import '../type/packet_list.dart'; import 'base.dart'; diff --git a/lib/src/packet/padding.dart b/lib/src/packet/padding.dart index 703ab5b7..d11bfd53 100644 --- a/lib/src/packet/padding.dart +++ b/lib/src/packet/padding.dart @@ -7,7 +7,6 @@ library; import 'dart:typed_data'; import '../common/helpers.dart'; -import '../enum/packet_type.dart'; import 'base.dart'; /// Implementation of the Padding Packet (Type 21) diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index 694d72d1..eb7e9e1a 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -12,7 +12,6 @@ import '../enum/hash_algorithm.dart'; import '../enum/key_version.dart'; import '../enum/montgomery_curve.dart'; import '../enum/key_algorithm.dart'; -import '../enum/packet_type.dart'; import '../type/key_material.dart'; import '../type/key_packet.dart'; import '../type/subkey_packet.dart'; diff --git a/lib/src/packet/public_key_encrypted_session_key.dart b/lib/src/packet/public_key_encrypted_session_key.dart index 6ace64ea..53201e8e 100644 --- a/lib/src/packet/public_key_encrypted_session_key.dart +++ b/lib/src/packet/public_key_encrypted_session_key.dart @@ -9,14 +9,12 @@ import 'dart:typed_data'; import '../type/key_packet.dart'; import '../enum/key_algorithm.dart'; import '../enum/montgomery_curve.dart'; -import '../enum/packet_type.dart'; import '../type/secret_key_packet.dart'; import '../type/session_key.dart'; import '../type/session_key_cryptor.dart'; import 'base.dart'; import 'key/public_material.dart'; import 'key/session_key_cryptor.dart'; -import 'public_key.dart'; /// Implementation of the Public-Key Encrypted Session Key (PKESK) Packet (Type 1) /// Author Nguyen Van Nguyen diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 77089fc4..402546be 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -23,7 +23,6 @@ import '../enum/montgomery_curve.dart'; import '../enum/hash_algorithm.dart'; import '../enum/key_version.dart'; import '../enum/key_algorithm.dart'; -import '../enum/packet_type.dart'; import '../enum/s2k_type.dart'; import '../enum/s2k_usage.dart'; import '../enum/symmetric_algorithm.dart'; @@ -33,7 +32,6 @@ import '../type/secret_key_material.dart'; import '../type/secret_key_packet.dart'; import '../type/subkey_packet.dart'; import 'base.dart'; -import 'public_key.dart'; /// Implementation of the Secret Key Packet (Type 5) /// Author Nguyen Van Nguyen diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 6d532e70..5edc2174 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -9,7 +9,6 @@ import 'dart:typed_data'; import '../common/helpers.dart'; import '../enum/hash_algorithm.dart'; import '../enum/key_algorithm.dart'; -import '../enum/packet_type.dart'; import '../enum/signature_subpacket_type.dart'; import '../enum/signature_type.dart'; import '../type/key_packet.dart'; @@ -19,7 +18,6 @@ import '../type/signing_key_material.dart'; import '../type/subpacket.dart'; import '../type/verification_key_material.dart'; import 'base.dart'; -import 'public_key.dart'; import 'signature_subpacket.dart'; import 'subpacket_reader.dart'; diff --git a/lib/src/packet/sym_encrypted_data.dart b/lib/src/packet/sym_encrypted_data.dart index 7ef44088..6ce5c1b7 100644 --- a/lib/src/packet/sym_encrypted_data.dart +++ b/lib/src/packet/sym_encrypted_data.dart @@ -12,7 +12,6 @@ import '../type/packet_list.dart'; import '../common/config.dart'; import '../common/helpers.dart'; import '../cryptor/symmetric/buffered_cipher.dart'; -import '../enum/packet_type.dart'; import '../enum/symmetric_algorithm.dart'; import 'base.dart'; import 'packet_list.dart'; diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index 8ef547d6..fe2c2be7 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -13,7 +13,6 @@ import '../common/helpers.dart'; import '../cryptor/symmetric/buffered_cipher.dart'; import '../enum/aead_algorithm.dart'; import '../enum/hash_algorithm.dart'; -import '../enum/packet_type.dart'; import '../enum/symmetric_algorithm.dart'; import '../type/packet_list.dart'; import 'base.dart'; diff --git a/lib/src/packet/sym_encrypted_session_key.dart b/lib/src/packet/sym_encrypted_session_key.dart index 402e3411..11f3c051 100644 --- a/lib/src/packet/sym_encrypted_session_key.dart +++ b/lib/src/packet/sym_encrypted_session_key.dart @@ -13,7 +13,6 @@ import '../common/generic_s2k.dart'; import '../common/helpers.dart'; import '../cryptor/symmetric/buffered_cipher.dart'; import '../enum/aead_algorithm.dart'; -import '../enum/packet_type.dart'; import '../enum/s2k_type.dart'; import '../enum/symmetric_algorithm.dart'; import '../type/s2k.dart'; diff --git a/lib/src/packet/trust.dart b/lib/src/packet/trust.dart index e5da9fb3..238518ad 100644 --- a/lib/src/packet/trust.dart +++ b/lib/src/packet/trust.dart @@ -6,8 +6,6 @@ library; import 'dart:typed_data'; -import 'package:dart_pg/src/enum/packet_type.dart'; - import 'base.dart'; /// Implementation of the Trust Packet (Tag 12) diff --git a/lib/src/packet/user_attribute.dart b/lib/src/packet/user_attribute.dart index 4a7650a3..0398c9de 100644 --- a/lib/src/packet/user_attribute.dart +++ b/lib/src/packet/user_attribute.dart @@ -8,7 +8,6 @@ import 'dart:typed_data'; import '../type/user_id_packet.dart'; import '../common/extensions.dart'; -import '../enum/packet_type.dart'; import 'base.dart'; import 'image_user_attribute.dart'; import 'subpacket_reader.dart'; diff --git a/lib/src/packet/user_id.dart b/lib/src/packet/user_id.dart index be5900d4..6b6cc3f6 100644 --- a/lib/src/packet/user_id.dart +++ b/lib/src/packet/user_id.dart @@ -8,7 +8,6 @@ import 'dart:convert'; import 'dart:typed_data'; import '../common/helpers.dart'; -import '../enum/packet_type.dart'; import '../type/user_id_packet.dart'; import 'base.dart'; From 832a82d2905c425c3aa780c4811fcfcde17b37d9 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 21 Nov 2024 11:38:20 +0700 Subject: [PATCH 022/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/common/s2k_test.dart | 272 +++++++++++++++++++++++++++++++++++++- 1 file changed, 271 insertions(+), 1 deletion(-) diff --git a/test/common/s2k_test.dart b/test/common/s2k_test.dart index a3ab527a..436db107 100644 --- a/test/common/s2k_test.dart +++ b/test/common/s2k_test.dart @@ -1,7 +1,277 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:dart_pg/src/common/extensions.dart'; +import 'package:dart_pg/src/common/generic_s2k.dart'; +import 'package:dart_pg/src/enum/hash_algorithm.dart'; +import 'package:dart_pg/src/enum/s2k_type.dart'; +import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; +import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/packet_list.dart'; import 'package:test/test.dart'; void main() { - group('Generic S2k', () {}); + group('Generic S2k', () { + const mode0Password1234 = 'jAQECQAC'; + const mode1Password123456 = 'jAwECQECqEKnqVn6Qio='; + const mode1PasswordFoobar = 'jAwECQECvJVYRYE8fDc='; + const mode3Aes128Password13Times0123456789 = 'jA0EBwMCBuRhXKRI+d3u'; + const mode3Aes192Password123 = 'jA0ECAMCj4F0xdlhx3nu'; + // const mode3EncryptedKeyPasswordBgtyhn = 'jC4EBwMCglmgbpjalBzuCM5Wxz59Z2/MekL482qgQu4ZSP0MmZ9OiPAOHkSr7OjG'; + const mode3Password9876 = 'jA0ECQMCuWfqllPbasgr'; + const mode3PasswordQwerty = 'jA0ECQMCeEXwW1X3tJ7x'; + const mode3TwofishPassword13Times0123456789 = 'jA0ECgMCUe38FUVAZazu'; + test('mode-0-password-1234 test', () async { + const passphrase = '1234'; + final salt = Uint8List.fromList([]); + + final packets = PacketList.decode(base64.decode(mode0Password1234)); + final skesk = packets[0] as SymEncryptedSessionKeyPacket; + final skeskS2k = skesk.s2k as GenericS2k; + expect(skeskS2k.type, S2kType.simple); + expect(skeskS2k.hash, HashAlgorithm.sha1); + expect(skeskS2k.salt, equals(salt)); + expect(skesk.symmetric, SymmetricAlgorithm.aes256); + + final s2k = GenericS2k( + salt, + type: skesk.s2k.type, + hash: skeskS2k.hash, + ); + final key = s2k.produceKey( + passphrase, + (skesk.symmetric.keySize + 7) >> 3, + ); + expect( + key.toHexadecimal(), + '7110eda4d09e062aa5e4a390b0a572ac0d2c0220f352b0d292b65164c2a67301', + ); + }); + + test('mode-1-password-123456 test', () async { + const passphrase = '123456'; + final salt = Uint8List.fromList( + [0xa8, 0x42, 0xa7, 0xa9, 0x59, 0xfa, 0x42, 0x2a], + ); + + final packets = PacketList.decode(base64.decode(mode1Password123456)); + final skesk = packets[0] as SymEncryptedSessionKeyPacket; + final skeskS2k = skesk.s2k as GenericS2k; + expect(skeskS2k.type, S2kType.salted); + expect(skeskS2k.hash, HashAlgorithm.sha1); + expect(skeskS2k.salt, equals(salt)); + expect(skesk.symmetric, SymmetricAlgorithm.aes256); + + final s2k = GenericS2k( + salt, + type: skesk.s2k.type, + hash: skeskS2k.hash, + ); + final key = s2k.produceKey( + passphrase, + (skesk.symmetric.keySize + 7) >> 3, + ); + expect( + key.toHexadecimal(), + '8b79077ca448f6fb3d3ad2a264d3b938d357c9fb3e41219fd962df960a9afa08', + ); + }); + + test('mode-1-password-foobar test', () async { + const passphrase = 'foobar'; + final salt = Uint8List.fromList( + [0xbc, 0x95, 0x58, 0x45, 0x81, 0x3c, 0x7c, 0x37], + ); + + final packets = PacketList.decode(base64.decode(mode1PasswordFoobar)); + final skesk = packets[0] as SymEncryptedSessionKeyPacket; + final skeskS2k = skesk.s2k as GenericS2k; + expect(skeskS2k.type, S2kType.salted); + expect(skeskS2k.hash, HashAlgorithm.sha1); + expect(skeskS2k.salt, equals(salt)); + expect(skesk.symmetric, SymmetricAlgorithm.aes256); + + final s2k = GenericS2k( + salt, + type: skesk.s2k.type, + hash: skeskS2k.hash, + ); + final key = s2k.produceKey( + passphrase, + (skesk.symmetric.keySize + 7) >> 3, + ); + expect( + key.toHexadecimal(), + 'b7d48aae9b943b22a4d390083e8460b5edfa118fe1688bf0c473b8094d1a8d10', + ); + }); + + test('mode-3-password-qwerty test', () async { + const itCount = 241; + const passphrase = 'qwerty'; + final salt = Uint8List.fromList( + [0x78, 0x45, 0xf0, 0x5b, 0x55, 0xf7, 0xb4, 0x9e], + ); + + final packets = PacketList.decode(base64.decode(mode3PasswordQwerty)); + final skesk = packets[0] as SymEncryptedSessionKeyPacket; + final skeskS2k = skesk.s2k as GenericS2k; + expect(skeskS2k.type, S2kType.iterated); + expect(skeskS2k.hash, HashAlgorithm.sha1); + expect(skeskS2k.itCount, itCount); + expect(skeskS2k.salt, equals(salt)); + expect(skesk.symmetric, SymmetricAlgorithm.aes256); + + final s2k = GenericS2k( + salt, + type: skesk.s2k.type, + hash: skeskS2k.hash, + itCount: skeskS2k.itCount, + ); + final key = s2k.produceKey( + passphrase, + (skesk.symmetric.keySize + 7) >> 3, + ); + expect( + key.toHexadecimal(), + '575ad156187a3f8cec11108309236eb499f1e682f0d1afadfac4ecf97613108a', + ); + }); + + test('mode-3-password-9876 test', () async { + const itCount = 43; + const passphrase = '9876'; + final salt = Uint8List.fromList( + [0xb9, 0x67, 0xea, 0x96, 0x53, 0xdb, 0x6a, 0xc8], + ); + + final packets = PacketList.decode(base64.decode(mode3Password9876)); + final skesk = packets[0] as SymEncryptedSessionKeyPacket; + final skeskS2k = skesk.s2k as GenericS2k; + expect(skeskS2k.type, S2kType.iterated); + expect(skeskS2k.hash, HashAlgorithm.sha1); + expect(skeskS2k.itCount, itCount); + expect(skeskS2k.salt, equals(salt)); + expect(skesk.symmetric, SymmetricAlgorithm.aes256); + + final s2k = GenericS2k( + salt, + type: skesk.s2k.type, + hash: skeskS2k.hash, + itCount: skeskS2k.itCount, + ); + final key = s2k.produceKey( + passphrase, + (skesk.symmetric.keySize + 7) >> 3, + ); + expect( + key.toHexadecimal(), + '736c226b8c64e4e6d0325c6c552ef7c0738f98f48fed65fd8c93265103efa23a', + ); + }); + + test('mode-3-aes192-password-123 test', () async { + const itCount = 238; + const passphrase = '123'; + final salt = Uint8List.fromList( + [0x8f, 0x81, 0x74, 0xc5, 0xd9, 0x61, 0xc7, 0x79], + ); + + final packets = PacketList.decode(base64.decode(mode3Aes192Password123)); + final skesk = packets[0] as SymEncryptedSessionKeyPacket; + final skeskS2k = skesk.s2k as GenericS2k; + expect(skeskS2k.type, S2kType.iterated); + expect(skeskS2k.hash, HashAlgorithm.sha1); + expect(skeskS2k.itCount, itCount); + expect(skeskS2k.salt, equals(salt)); + expect(skesk.symmetric, SymmetricAlgorithm.aes192); + + final s2k = GenericS2k( + salt, + type: skesk.s2k.type, + hash: skeskS2k.hash, + itCount: skeskS2k.itCount, + ); + final key = s2k.produceKey( + passphrase, + (skesk.symmetric.keySize + 7) >> 3, + ); + expect( + key.toHexadecimal(), + '915e96fc694e7f90a6850b740125ea005199c725f3bd27e3', + ); + }); + + test('mode-3-twofish-password-13-times-0123456789 test', () async { + const itCount = 238; + const passphrase = + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'; + final salt = Uint8List.fromList( + [0x51, 0xed, 0xfc, 0x15, 0x45, 0x40, 0x65, 0xac], + ); + + final packets = PacketList.decode(base64.decode( + mode3TwofishPassword13Times0123456789, + )); + final skesk = packets[0] as SymEncryptedSessionKeyPacket; + final skeskS2k = skesk.s2k as GenericS2k; + expect(skeskS2k.type, S2kType.iterated); + expect(skeskS2k.hash, HashAlgorithm.sha1); + expect(skeskS2k.itCount, itCount); + expect(skeskS2k.salt, equals(salt)); + expect(skesk.symmetric, SymmetricAlgorithm.twofish); + + final s2k = GenericS2k( + salt, + type: skesk.s2k.type, + hash: skeskS2k.hash, + itCount: skeskS2k.itCount, + ); + final key = s2k.produceKey( + passphrase, + (skesk.symmetric.keySize + 7) >> 3, + ); + expect( + key.toHexadecimal(), + 'ea264fada5a859c40d88a159b344ecf1f51ff327fdb3c558b0a7dc299777173e', + ); + }); + + test('mode-3-aes128-password-13-times-0123456789 test', () async { + const itCount = 238; + const passphrase = + '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'; + final salt = Uint8List.fromList( + [0x06, 0xe4, 0x61, 0x5c, 0xa4, 0x48, 0xf9, 0xdd], + ); + + final packets = PacketList.decode( + base64.decode(mode3Aes128Password13Times0123456789), + ); + final skesk = packets[0] as SymEncryptedSessionKeyPacket; + final skeskS2k = skesk.s2k as GenericS2k; + expect(skeskS2k.type, S2kType.iterated); + expect(skeskS2k.hash, HashAlgorithm.sha1); + expect(skeskS2k.itCount, itCount); + expect(skesk.s2k.salt, equals(salt)); + expect(skesk.symmetric, SymmetricAlgorithm.aes128); + + final s2k = GenericS2k( + salt, + type: skesk.s2k.type, + hash: skeskS2k.hash, + itCount: skeskS2k.itCount, + ); + final key = s2k.produceKey( + passphrase, + (skesk.symmetric.keySize + 7) >> 3, + ); + expect( + key.toHexadecimal(), + 'f3d0ce52ed6143637443e3399437fd0f', + ); + }); + }); group('Argon2 S2k', () {}); } From ae710d63b29bcd8fd4c3be01316627978359c170 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 21 Nov 2024 11:56:41 +0700 Subject: [PATCH 023/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/argon2_s2k.dart | 10 ++-- lib/src/common/generic_s2k.dart | 10 ++-- test/common/s2k_test.dart | 96 +++++++++++++++++++++++++-------- 3 files changed, 84 insertions(+), 32 deletions(-) diff --git a/lib/src/common/argon2_s2k.dart b/lib/src/common/argon2_s2k.dart index ed179987..423bb341 100644 --- a/lib/src/common/argon2_s2k.dart +++ b/lib/src/common/argon2_s2k.dart @@ -30,11 +30,11 @@ class Argon2S2k implements S2kInterface { final int memoryExponent; Argon2S2k( - this.salt, { + this.salt, [ this.iteration = Argon2Parameters.DEFAULT_ITERATIONS, this.parallelism = Argon2Parameters.DEFAULT_LANES, this.memoryExponent = Argon2Parameters.DEFAULT_MEMORY_COST, - }); + ]); /// Parsing function for a string-to-key specifier factory Argon2S2k.fromBytes(final Uint8List bytes) { @@ -46,9 +46,9 @@ class Argon2S2k implements S2kInterface { final memoryExponent = bytes[pos++]; return Argon2S2k( salt, - iteration: iteration, - parallelism: parallelism, - memoryExponent: memoryExponent, + iteration, + parallelism, + memoryExponent, ); } diff --git a/lib/src/common/generic_s2k.dart b/lib/src/common/generic_s2k.dart index c9cc46c1..0dc63bcf 100644 --- a/lib/src/common/generic_s2k.dart +++ b/lib/src/common/generic_s2k.dart @@ -40,11 +40,11 @@ class GenericS2k implements S2kInterface { final S2kType type; GenericS2k( - this.salt, { + this.salt, [ this.type = S2kType.iterated, this.hash = HashAlgorithm.sha256, this.itCount = _defaultItCount, - }) : count = (16 + (itCount & 15)) << ((itCount >> 4) + _expbias); + ]) : count = (16 + (itCount & 15)) << ((itCount >> 4) + _expbias); /// Parsing function for a string-to-key specifier factory GenericS2k.fromBytes(final Uint8List bytes) { @@ -74,9 +74,9 @@ class GenericS2k implements S2kInterface { } return GenericS2k( salt, - type: type, - hash: hash, - itCount: itCount, + type, + hash, + itCount, ); } diff --git a/test/common/s2k_test.dart b/test/common/s2k_test.dart index 436db107..4bdd99db 100644 --- a/test/common/s2k_test.dart +++ b/test/common/s2k_test.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:dart_pg/src/common/argon2_s2k.dart'; import 'package:dart_pg/src/common/extensions.dart'; import 'package:dart_pg/src/common/generic_s2k.dart'; import 'package:dart_pg/src/enum/hash_algorithm.dart'; @@ -21,6 +22,7 @@ void main() { const mode3Password9876 = 'jA0ECQMCuWfqllPbasgr'; const mode3PasswordQwerty = 'jA0ECQMCeEXwW1X3tJ7x'; const mode3TwofishPassword13Times0123456789 = 'jA0ECgMCUe38FUVAZazu'; + test('mode-0-password-1234 test', () async { const passphrase = '1234'; final salt = Uint8List.fromList([]); @@ -35,8 +37,8 @@ void main() { final s2k = GenericS2k( salt, - type: skesk.s2k.type, - hash: skeskS2k.hash, + skesk.s2k.type, + skeskS2k.hash, ); final key = s2k.produceKey( passphrase, @@ -64,8 +66,8 @@ void main() { final s2k = GenericS2k( salt, - type: skesk.s2k.type, - hash: skeskS2k.hash, + skesk.s2k.type, + skeskS2k.hash, ); final key = s2k.produceKey( passphrase, @@ -93,8 +95,8 @@ void main() { final s2k = GenericS2k( salt, - type: skesk.s2k.type, - hash: skeskS2k.hash, + skesk.s2k.type, + skeskS2k.hash, ); final key = s2k.produceKey( passphrase, @@ -124,9 +126,9 @@ void main() { final s2k = GenericS2k( salt, - type: skesk.s2k.type, - hash: skeskS2k.hash, - itCount: skeskS2k.itCount, + skesk.s2k.type, + skeskS2k.hash, + skeskS2k.itCount, ); final key = s2k.produceKey( passphrase, @@ -156,9 +158,9 @@ void main() { final s2k = GenericS2k( salt, - type: skesk.s2k.type, - hash: skeskS2k.hash, - itCount: skeskS2k.itCount, + skesk.s2k.type, + skeskS2k.hash, + skeskS2k.itCount, ); final key = s2k.produceKey( passphrase, @@ -188,9 +190,9 @@ void main() { final s2k = GenericS2k( salt, - type: skesk.s2k.type, - hash: skeskS2k.hash, - itCount: skeskS2k.itCount, + skesk.s2k.type, + skeskS2k.hash, + skeskS2k.itCount, ); final key = s2k.produceKey( passphrase, @@ -223,9 +225,9 @@ void main() { final s2k = GenericS2k( salt, - type: skesk.s2k.type, - hash: skeskS2k.hash, - itCount: skeskS2k.itCount, + skesk.s2k.type, + skeskS2k.hash, + skeskS2k.itCount, ); final key = s2k.produceKey( passphrase, @@ -258,9 +260,9 @@ void main() { final s2k = GenericS2k( salt, - type: skesk.s2k.type, - hash: skeskS2k.hash, - itCount: skeskS2k.itCount, + skesk.s2k.type, + skeskS2k.hash, + skeskS2k.itCount, ); final key = s2k.produceKey( passphrase, @@ -273,5 +275,55 @@ void main() { }); }); - group('Argon2 S2k', () {}); + group('Argon2 S2k', () { + const passphrase = 'password'; + + test('4 iterations 1mb 16 key length', () { + const salt = "dH3Z8hGL7bBUyp1i"; + const hash = "eaf0095c8412e432cb9ff172957fef91"; + + final s2k = Argon2S2k(salt.toBytes(), 4, 1, 10); + final key = s2k.produceKey(passphrase, 16); + expect( + key.toHexadecimal(), + hash, + ); + }); + + test('4 iterations 64mb 16 key length', () { + const salt = "IeCBTBvkzbmxT87I"; + const hash = "050ebb7bcb8c1165502af049a664f2db"; + + final s2k = Argon2S2k(salt.toBytes(), 4, 1, 16); + final key = s2k.produceKey(passphrase, 16); + expect( + key.toHexadecimal(), + hash, + ); + }); + + test('4 iterations 10mb 32 keylength', () { + const salt = "KtPeAgudgN7xrgUK"; + const hash = "66b3d1c15f544eae5810c29381ad477167d5a1d5360c9b97340bd5b8b06c589b"; + + final s2k = Argon2S2k(salt.toBytes(), 4, 1, 10); + final key = s2k.produceKey(passphrase, 32); + expect( + key.toHexadecimal(), + hash, + ); + }); + + test('4 iterations 64 mb 32 key length', () { + const salt = "D85Euo8RwvlkUxb5"; + const hash = "cb1f8f04ec5ecb681e4ffb2665af6e4ad6aed540b5e62f625f48c834e8b88fa6"; + + final s2k = Argon2S2k(salt.toBytes(), 4, 1, 16); + final key = s2k.produceKey(passphrase, 32); + expect( + key.toHexadecimal(), + hash, + ); + }); + }); } From f75887301a4160f8bc00e1f8f93ce3a3c7094d88 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 21 Nov 2024 11:58:52 +0700 Subject: [PATCH 024/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/argon2_s2k.dart | 2 +- lib/src/common/armor.dart | 2 +- lib/src/common/generic_s2k.dart | 2 +- lib/src/common/helpers.dart | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/common/argon2_s2k.dart b/lib/src/common/argon2_s2k.dart index 423bb341..3c6c8a9d 100644 --- a/lib/src/common/argon2_s2k.dart +++ b/lib/src/common/argon2_s2k.dart @@ -7,9 +7,9 @@ library; import 'dart:typed_data'; import 'package:pointycastle/export.dart'; -import 'helpers.dart'; import '../enum/s2k_type.dart'; import '../type/s2k.dart'; +import 'helpers.dart'; /// Implementation of the Argon2 string-to-key specifier /// See https://www.rfc-editor.org/rfc/rfc9580#section-3.7 diff --git a/lib/src/common/armor.dart b/lib/src/common/armor.dart index 95143ed1..d653a359 100644 --- a/lib/src/common/armor.dart +++ b/lib/src/common/armor.dart @@ -7,8 +7,8 @@ library; import 'dart:convert'; import 'dart:typed_data'; -import 'helpers.dart'; import '../enum/armor_type.dart'; +import 'helpers.dart'; /// ASCII Armor class /// OpenPGP's Radix-64 encoding. diff --git a/lib/src/common/generic_s2k.dart b/lib/src/common/generic_s2k.dart index 0dc63bcf..4099ac19 100644 --- a/lib/src/common/generic_s2k.dart +++ b/lib/src/common/generic_s2k.dart @@ -7,10 +7,10 @@ library; import 'dart:convert'; import 'dart:typed_data'; -import 'helpers.dart'; import '../enum/hash_algorithm.dart'; import '../enum/s2k_type.dart'; import '../type/s2k.dart'; +import 'helpers.dart'; /// Implementation of the string-to-key specifier /// See https://www.rfc-editor.org/rfc/rfc9580#section-3.7 diff --git a/lib/src/common/helpers.dart b/lib/src/common/helpers.dart index b31359b1..aa6d7491 100644 --- a/lib/src/common/helpers.dart +++ b/lib/src/common/helpers.dart @@ -8,13 +8,13 @@ import 'dart:math'; import 'dart:typed_data'; import 'package:pointycastle/export.dart'; -import 'argon2_s2k.dart'; -import 'extensions.dart'; -import 'generic_s2k.dart'; import '../type/s2k.dart'; import '../enum/s2k_type.dart'; import '../enum/hash_algorithm.dart'; import '../enum/symmetric_algorithm.dart'; +import 'argon2_s2k.dart'; +import 'extensions.dart'; +import 'generic_s2k.dart'; export 'extensions.dart'; From 27baae47447145e65999c8c1e9c32a2b9c5ff665 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 21 Nov 2024 13:07:38 +0700 Subject: [PATCH 025/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/armor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/common/armor.dart b/lib/src/common/armor.dart index d653a359..206cf186 100644 --- a/lib/src/common/armor.dart +++ b/lib/src/common/armor.dart @@ -62,7 +62,7 @@ class Armor { /// Verify the checksum and return the encoded bytes factory Armor.decode( final String armored, [ - final bool checksumRequired = true, + final bool checksumRequired = false, ]) { var textDone = false; var checksum = ''; From e413bf58c74d3aa43c5b6541410d1f8ec1008bec Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 21 Nov 2024 14:21:17 +0700 Subject: [PATCH 026/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/armor.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/src/common/armor.dart b/lib/src/common/armor.dart index 206cf186..35bc5337 100644 --- a/lib/src/common/armor.dart +++ b/lib/src/common/armor.dart @@ -8,6 +8,7 @@ import 'dart:convert'; import 'dart:typed_data'; import '../enum/armor_type.dart'; +import 'config.dart'; import 'helpers.dart'; /// ASCII Armor class @@ -60,10 +61,7 @@ class Armor { /// Dearmor an OpenPGP armored message; /// Verify the checksum and return the encoded bytes - factory Armor.decode( - final String armored, [ - final bool checksumRequired = false, - ]) { + factory Armor.decode(final String armored) { var textDone = false; var checksum = ''; ArmorType? type; @@ -101,7 +99,7 @@ class Armor { final text = textLines.join('\r\n').trim(); final data = base64.decode(dataLines.join().trim()); - if ((checksum != _crc24Checksum(data)) && (checksum.isNotEmpty || checksumRequired)) { + if ((checksum != _crc24Checksum(data)) && (checksum.isNotEmpty || Config.checksumRequired)) { throw StateError('Ascii armor integrity check failed'); } From e0f9b26088100c86fd2bf7eb458e39305e7453f2 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 21 Nov 2024 14:32:14 +0700 Subject: [PATCH 027/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/extensions.dart | 2 +- lib/src/cryptor/symmetric/blowfish.dart | 4 ++-- lib/src/cryptor/symmetric/cast5.dart | 4 ++-- lib/src/cryptor/symmetric/idea.dart | 4 ++-- lib/src/cryptor/symmetric/twofish.dart | 4 ++-- lib/src/packet/key/ecdh_session_key_cryptor.dart | 2 +- lib/src/packet/key/key_wrapper.dart | 16 ++++++++-------- lib/src/packet/key/rsa_secret_material.dart | 2 +- lib/src/packet/key/session_key_cryptor.dart | 7 ++++--- lib/src/packet/user_id.dart | 2 +- 10 files changed, 24 insertions(+), 23 deletions(-) diff --git a/lib/src/common/extensions.dart b/lib/src/common/extensions.dart index f6599a60..f023f51d 100644 --- a/lib/src/common/extensions.dart +++ b/lib/src/common/extensions.dart @@ -223,7 +223,7 @@ extension Uint8ListExt on Uint8List { String toHexadecimal() { final result = StringBuffer(); - for (var i = 0; i < lengthInBytes; i++) { + for (var i = 0; i < length; i++) { final part = this[i]; result.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}'); } diff --git a/lib/src/cryptor/symmetric/blowfish.dart b/lib/src/cryptor/symmetric/blowfish.dart index bceabe1e..61b7d18c 100644 --- a/lib/src/cryptor/symmetric/blowfish.dart +++ b/lib/src/cryptor/symmetric/blowfish.dart @@ -339,10 +339,10 @@ class BlowfishEngine extends BaseEngine { if (_workingKey.isEmpty) { throw StateError('$algorithmName not initialised'); } - if ((inOff + _blockSize) > input.lengthInBytes) { + if ((inOff + _blockSize) > input.length) { throw ArgumentError('input buffer too short for $algorithmName engine'); } - if ((outOff + _blockSize) > output.lengthInBytes) { + if ((outOff + _blockSize) > output.length) { throw ArgumentError('output buffer too short for $algorithmName engine'); } diff --git a/lib/src/cryptor/symmetric/cast5.dart b/lib/src/cryptor/symmetric/cast5.dart index a1a7d56d..72238edc 100644 --- a/lib/src/cryptor/symmetric/cast5.dart +++ b/lib/src/cryptor/symmetric/cast5.dart @@ -602,10 +602,10 @@ class CAST5Engine extends BaseEngine { if (_workingKey.isEmpty) { throw StateError('$algorithmName not initialised'); } - if ((inOff + _blockSize) > input.lengthInBytes) { + if ((inOff + _blockSize) > input.length) { throw ArgumentError('input buffer too short for $algorithmName engine'); } - if ((outOff + _blockSize) > output.lengthInBytes) { + if ((outOff + _blockSize) > output.length) { throw ArgumentError('output buffer too short for $algorithmName engine'); } diff --git a/lib/src/cryptor/symmetric/idea.dart b/lib/src/cryptor/symmetric/idea.dart index 2b06ad7b..e460832e 100644 --- a/lib/src/cryptor/symmetric/idea.dart +++ b/lib/src/cryptor/symmetric/idea.dart @@ -47,10 +47,10 @@ class IDEAEngine extends BaseEngine { if (_workingKey.isEmpty) { throw StateError('$algorithmName not initialised'); } - if ((inOff + _blockSize) > input.lengthInBytes) { + if ((inOff + _blockSize) > input.length) { throw ArgumentError('input buffer too short for $algorithmName engine'); } - if ((outOff + _blockSize) > output.lengthInBytes) { + if ((outOff + _blockSize) > output.length) { throw ArgumentError('output buffer too short for $algorithmName engine'); } diff --git a/lib/src/cryptor/symmetric/twofish.dart b/lib/src/cryptor/symmetric/twofish.dart index da5abdc0..75f6947a 100644 --- a/lib/src/cryptor/symmetric/twofish.dart +++ b/lib/src/cryptor/symmetric/twofish.dart @@ -411,10 +411,10 @@ class TwofishEngine extends BaseEngine { if (_workingKey.isEmpty) { throw StateError('algorithmName not initialised'); } - if ((inOff + _blockSize) > input.lengthInBytes) { + if ((inOff + _blockSize) > input.length) { throw ArgumentError('input buffer too short for algorithmName engine'); } - if ((outOff + _blockSize) > output.lengthInBytes) { + if ((outOff + _blockSize) > output.length) { throw ArgumentError('output buffer too short for algorithmName engine'); } diff --git a/lib/src/packet/key/ecdh_session_key_cryptor.dart b/lib/src/packet/key/ecdh_session_key_cryptor.dart index eb7ce19c..1e4509ef 100644 --- a/lib/src/packet/key/ecdh_session_key_cryptor.dart +++ b/lib/src/packet/key/ecdh_session_key_cryptor.dart @@ -218,7 +218,7 @@ class ECDHSessionKeyCryptor extends SessionKeyCryptor { /// Add pkcs5 padding to a message static Uint8List _pkcs5Encode(final Uint8List message) { - final c = 8 - (message.lengthInBytes % 8); + final c = 8 - (message.length % 8); return Uint8List.fromList( List.filled(message.length + c, c), )..setAll(0, message); diff --git a/lib/src/packet/key/key_wrapper.dart b/lib/src/packet/key/key_wrapper.dart index f232992d..4d5fec21 100644 --- a/lib/src/packet/key/key_wrapper.dart +++ b/lib/src/packet/key/key_wrapper.dart @@ -30,20 +30,20 @@ abstract class KeyWrapper { final Uint8List kek, final Uint8List key, ) { - if (kek.lengthInBytes != keySize) { + if (kek.length != keySize) { throw ArgumentError('Key encryption key size must be $keySize bytes.'); } - if (key.lengthInBytes < 16) { + if (key.length < 16) { throw ArgumentError('Key length must be at least 16 octets.'); } - if (key.lengthInBytes % 8 != 0) { + if (key.length % 8 != 0) { throw ArgumentError('Key length must be a multiple of 64 bits.'); } cipher.init(true, KeyParameter(kek)); final a = Uint8List.fromList(_iv); final r = Uint8List.fromList(key); - final n = key.lengthInBytes ~/ 8; + final n = key.length ~/ 8; for (var j = 0; j <= 5; j++) { for (var i = 1; i <= n; i++) { final buffer = Uint8List.fromList([ @@ -64,20 +64,20 @@ abstract class KeyWrapper { final Uint8List kek, final Uint8List wrappedKey, ) { - if (kek.lengthInBytes != keySize) { + if (kek.length != keySize) { throw ArgumentError('Key encryption key size must be $keySize bytes.'); } - if (wrappedKey.lengthInBytes < 16) { + if (wrappedKey.length < 16) { throw ArgumentError('Wrapped key length must be at least 16 octets.'); } - if (wrappedKey.lengthInBytes % 8 != 0) { + if (wrappedKey.length % 8 != 0) { throw ArgumentError('Wrapped key length must be a multiple of 64 bits.'); } cipher.init(false, KeyParameter(kek)); final a = wrappedKey.sublist(0, 8); final r = wrappedKey.sublist(8); - final n = (wrappedKey.lengthInBytes ~/ 8) - 1; + final n = (wrappedKey.length ~/ 8) - 1; for (var j = 5; j >= 0; j--) { for (var i = n; i >= 1; i--) { a[7] ^= (n * j + i) & 0xff; diff --git a/lib/src/packet/key/rsa_secret_material.dart b/lib/src/packet/key/rsa_secret_material.dart index 74425b97..86d1879d 100644 --- a/lib/src/packet/key/rsa_secret_material.dart +++ b/lib/src/packet/key/rsa_secret_material.dart @@ -135,7 +135,7 @@ class RSASecretMaterial implements SigningKeyMaterialInterface { ); final signature = signer.generateSignature(message) as RSASignature; return Uint8List.fromList([ - ...(signature.bytes.lengthInBytes * 8).pack16(), + ...(signature.bytes.length * 8).pack16(), ...signature.bytes, ]); } diff --git a/lib/src/packet/key/session_key_cryptor.dart b/lib/src/packet/key/session_key_cryptor.dart index 7e6c3049..4f927fba 100644 --- a/lib/src/packet/key/session_key_cryptor.dart +++ b/lib/src/packet/key/session_key_cryptor.dart @@ -29,16 +29,17 @@ abstract class SessionKeyCryptor implements SessionKeyCryptorInterface { final Uint8List input, ) { final numBlocks = - input.length ~/ engine.inputBlockSize + ((input.lengthInBytes % engine.inputBlockSize != 0) ? 1 : 0); + input.length ~/ engine.inputBlockSize + ((input.length % engine.inputBlockSize != 0) ? 1 : 0); final output = Uint8List(numBlocks * engine.outputBlockSize); var inpOff = 0; var outOff = 0; while (inpOff < input.length) { - final chunkSize = (inpOff + engine.inputBlockSize <= input.lengthInBytes) + final chunkSize = + (inpOff + engine.inputBlockSize <= input.length) ? engine.inputBlockSize - : input.lengthInBytes - inpOff; + : input.length - inpOff; outOff += engine.processBlock( input, diff --git a/lib/src/packet/user_id.dart b/lib/src/packet/user_id.dart index 6b6cc3f6..75d0aeb7 100644 --- a/lib/src/packet/user_id.dart +++ b/lib/src/packet/user_id.dart @@ -39,7 +39,7 @@ class UserIDPacket extends BasePacket implements UserIDPacketInterface { @override Uint8List get signBytes => Uint8List.fromList([ 0xb4, - ...data.lengthInBytes.pack32(), + ...data.length.pack32(), ...data, ]); From 3ba76d23ec2d7b2c175757c6a73b61d4902a5f84 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 21 Nov 2024 14:39:06 +0700 Subject: [PATCH 028/202] WIP Signed-off-by: Nguyen Van Nguyen --- pubspec.yaml | 1 + test/common/armor_test.dart | 158 ++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 test/common/armor_test.dart diff --git a/pubspec.yaml b/pubspec.yaml index da5d5bcf..4f9d950a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,4 +13,5 @@ dependencies: dev_dependencies: lints: ^3.0.0 + faker: ^2.2.0 test: ^1.24.0 diff --git a/test/common/armor_test.dart b/test/common/armor_test.dart new file mode 100644 index 00000000..c16cbab7 --- /dev/null +++ b/test/common/armor_test.dart @@ -0,0 +1,158 @@ +import 'dart:convert'; + +import 'package:dart_pg/src/common/armor.dart'; +import 'package:dart_pg/src/enum/armor_type.dart'; +import 'package:dart_pg/src/enum/hash_algorithm.dart'; +import 'package:faker/faker.dart'; +import 'package:test/test.dart'; + +void main() { + group('armor tests', () { + final faker = Faker(); + + test('multipart section test', (() { + final bytes = utf8.encoder.convert(faker.lorem.words(100).join(' ')); + final partIndex = faker.randomGenerator.integer(100); + final partTotal = faker.randomGenerator.integer(100); + + final armored = Armor.encode( + ArmorType.multipartSection, + bytes, + partIndex: partIndex, + partTotal: partTotal, + ); + final beginReg = RegExp(r'BEGIN PGP MESSAGE, PART \d+\/\d+'); + expect(beginReg.hasMatch(armored), true); + + final endReg = RegExp(r'END PGP MESSAGE, PART \d+\/\d+'); + expect(endReg.hasMatch(armored), true); + + final armor = Armor.decode(armored); + expect(armor.type, ArmorType.multipartSection); + expect(armor.data, bytes); + })); + + test('multipart last test', (() { + final bytes = utf8.encoder.convert(faker.lorem.words(100).join(' ')); + final partIndex = faker.randomGenerator.integer(100); + + final armored = Armor.encode( + ArmorType.multipartLast, + bytes, + partIndex: partIndex, + ); + + final beginReg = RegExp(r'BEGIN PGP MESSAGE, PART \d+'); + expect(beginReg.hasMatch(armored), true); + + final endReg = RegExp(r'END PGP MESSAGE, PART \d+'); + expect(endReg.hasMatch(armored), true); + + final armor = Armor.decode(armored); + expect(armor.type, ArmorType.multipartLast); + expect(armor.data, bytes); + })); + + test('signed message test', (() { + final text = faker.lorem.words(100).join(' '); + final bytes = utf8.encoder.convert(text); + + final armored = Armor.encode( + ArmorType.signedMessage, + bytes, + text: text, + hashAlgo: HashAlgorithm.sha256.digestName, + ); + + final beginReg = RegExp(r'BEGIN PGP SIGNED MESSAGE'); + expect(beginReg.hasMatch(armored), true); + + final beginSignReg = RegExp(r'BEGIN PGP SIGNATURE'); + expect(beginSignReg.hasMatch(armored), true); + + final endSignReg = RegExp(r'END PGP SIGNATURE'); + expect(endSignReg.hasMatch(armored), true); + + final armor = Armor.decode(armored); + expect(armor.type, ArmorType.signedMessage); + expect(armor.data, bytes); + expect(armor.text, text); + })); + + test('message test', (() { + final message = utf8.encoder.convert(faker.lorem.words(100).join(' ')); + + final armored = Armor.encode( + ArmorType.message, + message, + ); + + final beginReg = RegExp(r'BEGIN PGP MESSAGE'); + expect(beginReg.hasMatch(armored), true); + + final endReg = RegExp(r'END PGP MESSAGE'); + expect(endReg.hasMatch(armored), true); + + final armor = Armor.decode(armored); + expect(armor.type, ArmorType.message); + expect(armor.data, message); + })); + + test('public key test', (() { + final publicKey = utf8.encoder.convert(faker.lorem.words(100).join(' ')); + + final armored = Armor.encode( + ArmorType.publicKey, + publicKey, + ); + + final beginReg = RegExp(r'BEGIN PGP PUBLIC KEY BLOCK'); + expect(beginReg.hasMatch(armored), true); + + final endReg = RegExp(r'END PGP PUBLIC KEY BLOCK'); + expect(endReg.hasMatch(armored), true); + + final armor = Armor.decode(armored); + expect(armor.type, ArmorType.publicKey); + expect(armor.data, publicKey); + })); + + test('private key test', (() { + final privateKey = utf8.encoder.convert(faker.lorem.words(100).join(' ')); + + final armored = Armor.encode( + ArmorType.privateKey, + privateKey, + ); + + final beginReg = RegExp(r'BEGIN PGP PRIVATE KEY BLOCK'); + expect(beginReg.hasMatch(armored), true); + + final endReg = RegExp(r'END PGP PRIVATE KEY BLOCK'); + expect(endReg.hasMatch(armored), true); + + final armor = Armor.decode(armored); + expect(armor.type, ArmorType.privateKey); + expect(armor.data, privateKey); + })); + + test('signature test', (() { + final signature = utf8.encoder.convert(faker.lorem.words(100).join(' ')); + + final armored = Armor.encode( + ArmorType.signature, + signature, + ); + + final beginReg = RegExp(r'BEGIN PGP SIGNATURE'); + expect(beginReg.hasMatch(armored), true); + + final endReg = RegExp(r'END PGP SIGNATURE'); + expect(endReg.hasMatch(armored), true); + + final armor = Armor.decode(armored); + expect(armor.type, ArmorType.signature); + expect(armor.data, signature); + })); + }); +} From a9849586c87c1d6c127e5c5dc00483a840fee5f5 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 21 Nov 2024 15:33:38 +0700 Subject: [PATCH 029/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/base.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/src/packet/base.dart b/lib/src/packet/base.dart index da380b13..9cd63cf9 100644 --- a/lib/src/packet/base.dart +++ b/lib/src/packet/base.dart @@ -63,7 +63,7 @@ abstract class BasePacket implements PacketInterface { /// Encode package to the openpgp partial body specifier Uint8List _partialEncode() { - final List partialData = []; + final partialData = []; var bodyData = data; var dataLengh = bodyData.length; while (dataLengh >= partialMinSize) { @@ -86,13 +86,15 @@ abstract class BasePacket implements PacketInterface { return Uint8List.fromList([0xc0 | type.value, ...partialData]); } - List _simpleLength(int length) { + Uint8List _simpleLength(int length) { if (length < 192) { - return [length]; + return Uint8List.fromList([length]); } else if (length < 8384) { - return [(((length - 192) >> 8) & 0xff) + 192, length - 192]; + return Uint8List.fromList( + [(((length - 192) >> 8) & 0xff) + 192, length - 192], + ); } else { - return [0xff, ...length.pack32()]; + return Uint8List.fromList([0xff, ...length.pack32()]); } } } From 65a7afca4f9b6d6b322a2a9d9dfbcba95c90257b Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 21 Nov 2024 16:05:09 +0700 Subject: [PATCH 030/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/base.dart | 2 +- test/packet/compression_test.dart | 70 +++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 test/packet/compression_test.dart diff --git a/lib/src/packet/base.dart b/lib/src/packet/base.dart index 9cd63cf9..771ec0eb 100644 --- a/lib/src/packet/base.dart +++ b/lib/src/packet/base.dart @@ -83,7 +83,7 @@ abstract class BasePacket implements PacketInterface { ..._simpleLength(dataLengh), ...bodyData, ]); - return Uint8List.fromList([0xc0 | type.value, ...partialData]); + return Uint8List.fromList([type.value | 0xc0, ...partialData]); } Uint8List _simpleLength(int length) { diff --git a/test/packet/compression_test.dart b/test/packet/compression_test.dart new file mode 100644 index 00000000..3c2f8096 --- /dev/null +++ b/test/packet/compression_test.dart @@ -0,0 +1,70 @@ +import 'package:dart_pg/src/enum/compression_algorithm.dart'; +import 'package:dart_pg/src/packet/compressed_data.dart'; +import 'package:dart_pg/src/packet/literal_data.dart'; +import 'package:dart_pg/src/packet/packet_list.dart'; +import 'package:faker/faker.dart'; +import 'package:test/test.dart'; + +void main() { + group('Compression', () { + final literalData = LiteralDataPacket.fromText(faker.randomGenerator.string(10000)); + + test('zip test', () { + final compressedPacket = CompressedDataPacket.fromPacketList( + PacketList([literalData]), + algorithm: CompressionAlgorithm.zip, + ); + + final decompressedPacket = CompressedDataPacket.fromBytes( + compressedPacket.data, + ); + + expect( + compressedPacket.algorithm, + equals(decompressedPacket.algorithm), + ); + expect( + compressedPacket.compressed, + equals(decompressedPacket.compressed), + ); + expect( + compressedPacket.packets[0].encode(), + equals(literalData.encode()), + ); + }); + + test('zlib test', () { + final compressedPacket = CompressedDataPacket.fromPacketList( + PacketList([literalData]), + algorithm: CompressionAlgorithm.zlib, + ); + + final decompressedPacket = CompressedDataPacket.fromBytes( + compressedPacket.data, + ); + + expect( + compressedPacket.algorithm, + equals(decompressedPacket.algorithm), + ); + expect( + compressedPacket.compressed, + equals(decompressedPacket.compressed), + ); + expect( + compressedPacket.packets[0].encode(), + equals(literalData.encode()), + ); + }); + + test('bzip2 test', () { + expect( + () => CompressedDataPacket.fromPacketList( + PacketList([literalData]), + algorithm: CompressionAlgorithm.bzip2, + ), + throwsUnsupportedError, + ); + }); + }); +} From b473b1901fa1b43bdb7cd388a93e32ef08661201 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 21 Nov 2024 17:35:55 +0700 Subject: [PATCH 031/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/dart_pg.dart | 9 ++-- lib/src/openpgp.dart | 103 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 7 deletions(-) diff --git a/lib/dart_pg.dart b/lib/dart_pg.dart index 2d38aef3..687a4217 100644 --- a/lib/dart_pg.dart +++ b/lib/dart_pg.dart @@ -1,8 +1,7 @@ -/// Support for doing something awesome. -/// -/// More dartdocs go here. +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + library; export 'src/openpgp.dart'; - -// TODO: Export any libraries intended for clients of this package. diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index b544d630..6426164f 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -1,4 +1,103 @@ -/// Checks if you are awesome. Spoiler: you are. -class OpenPGP { +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +/// Export high level API for developers. +/// Author Nguyen Van Nguyen +final class OpenPGP { bool get isAwesome => true; + + /// Generate a new OpenPGP key pair. Support RSA, ECC, Curve25519 and Curve448 key types. + /// The generated primary key will have signing capabilities. + /// One subkey with encryption capabilities is also generated if `signOnly` is false. + generateKey() {} + + /// Read OpenPGP public key from armored/binary string. + /// Return a public key object. + readPublicKey() {} + + /// Read OpenPGP public key list from armored/binary string. + /// Return array of public key objects. + readPublicKeys() {} + + /// Armor multiple public key. + armorPublicKeys() {} + + /// Read OpenPGP private key from armored/binary string. + /// Return a private key object. + readPrivateKey() {} + + /// Lock a private key with the given passphrase. + /// The private key must be decrypted. + encryptPrivateKey() {} + + /// Read & unlock OpenPGP private key with the given passphrase. + decryptPrivateKey() {} + + /// Certify an OpenPGP key by a private key. + /// Return clone of the key object with the new certification added. + certifyKey() {} + + /// Revoke an OpenPGP key by a private key. + /// Return clone of the key object with the new revocation signature added. + revokeKey() {} + + /// Read OpenPGP signature from armored/binary string. + /// Return a signature object. + readSignature() {} + + /// Read OpenPGP signed message from armored string. + /// Return a signed message object. + readSignedMessage() {} + + /// Read OpenPGP encrypted message from armored/binary string. + /// Return an encrypted message object. + readEncryptedMessage() {} + + /// Read OpenPGP literal message from armored/binary string. + /// Return a literal message object. + readLiteralMessage() {} + + /// Create new cleartext message object from text. + createCleartextMessage() {} + + /// Create new literal message object from literal data. + createLiteralMessage() {} + + /// Sign a cleartext message. + /// Return a signed message object. + signCleartext() {} + + /// Sign a cleartext message & return detached signature. + signDetachedCleartext() {} + + /// Sign a message & return signed literal message. + sign() {} + + /// Sign a message & return detached signature. + signDetached() {} + + /// Verify signatures of cleartext signed message. + /// Return verification array. + verify() {} + + /// Verify detached signatures of cleartext message. + /// Return verification array. + verifyDetached() {} + + /// Encrypt a message using public keys, passwords or both at once. + /// At least one of `encryptionKeys`, `passwords`must be specified. + /// If signing keys are specified, those will be used to sign the message. + encrypt() {} + + /// Decrypt a message with the user's private keys, or passwords. + /// One of `decryptionKeys` or `passwords` must be specified + decrypt() {} + + /// Decrypt a armored/binary encrypted string with + /// the user's private keys, or passwords. + /// One of `decryptionKeys` or `passwords` must be specified + decryptMessage() {} } From 9208395a2e17a36e5b618501eee486691529c1ea Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 22 Nov 2024 08:17:30 +0700 Subject: [PATCH 032/202] WIP Signed-off-by: Nguyen Van Nguyen --- .../for_signing.dart => enum/key_type.dart} | 12 ++-- lib/src/openpgp.dart | 56 ++++++++++++------- 2 files changed, 42 insertions(+), 26 deletions(-) rename lib/src/{type/for_signing.dart => enum/key_type.dart} (65%) diff --git a/lib/src/type/for_signing.dart b/lib/src/enum/key_type.dart similarity index 65% rename from lib/src/type/for_signing.dart rename to lib/src/enum/key_type.dart index 35883cd4..bd8c93bf 100644 --- a/lib/src/type/for_signing.dart +++ b/lib/src/enum/key_type.dart @@ -4,11 +4,11 @@ library; -import 'dart:typed_data'; - -/// For signing interface +/// Key types enum /// Author Nguyen Van Nguyen -abstract class ForSigningInterface { - /// Get bytes for sign - Uint8List get signBytes; +enum KeyType { + rsa, + ecc, + curve25519, + curve448; } diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index 6426164f..de8990d0 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -4,6 +4,10 @@ library; +import 'enum/ecc.dart'; +import 'enum/key_type.dart'; +import 'enum/rsa_key_size.dart'; + /// Export high level API for developers. /// Author Nguyen Van Nguyen final class OpenPGP { @@ -12,22 +16,30 @@ final class OpenPGP { /// Generate a new OpenPGP key pair. Support RSA, ECC, Curve25519 and Curve448 key types. /// The generated primary key will have signing capabilities. /// One subkey with encryption capabilities is also generated if `signOnly` is false. - generateKey() {} - - /// Read OpenPGP public key from armored/binary string. + generateKey( + Iterator userIDs, + String passphrase, { + KeyType type = KeyType.rsa, + RSAKeySize rsaKeySize = RSAKeySize.normal, + Ecc curve = Ecc.secp521r1, + int keyExpiry = 0, + DateTime? time, + }) {} + + /// Read OpenPGP public key from armored string. /// Return a public key object. - readPublicKey() {} + readPublicKey(String keyData) {} - /// Read OpenPGP public key list from armored/binary string. - /// Return array of public key objects. - readPublicKeys() {} + /// Read OpenPGP public key list from armored string. + /// Return iterator of public key objects. + readPublicKeys(String keyData) {} /// Armor multiple public key. armorPublicKeys() {} - /// Read OpenPGP private key from armored/binary string. + /// Read OpenPGP private key from armored string. /// Return a private key object. - readPrivateKey() {} + readPrivateKey(String keyData) {} /// Lock a private key with the given passphrase. /// The private key must be decrypted. @@ -44,27 +56,31 @@ final class OpenPGP { /// Return clone of the key object with the new revocation signature added. revokeKey() {} - /// Read OpenPGP signature from armored/binary string. + /// Read OpenPGP signature from armored string. /// Return a signature object. - readSignature() {} + readSignature(String signatureData) {} /// Read OpenPGP signed message from armored string. /// Return a signed message object. - readSignedMessage() {} + readSignedMessage(String messageData) {} - /// Read OpenPGP encrypted message from armored/binary string. + /// Read OpenPGP encrypted message from armored string. /// Return an encrypted message object. - readEncryptedMessage() {} + readEncryptedMessage(String messageData) {} - /// Read OpenPGP literal message from armored/binary string. + /// Read OpenPGP literal message from armored string. /// Return a literal message object. - readLiteralMessage() {} + readLiteralMessage(String messageData) {} /// Create new cleartext message object from text. - createCleartextMessage() {} + createCleartextMessage(String text) {} /// Create new literal message object from literal data. - createLiteralMessage() {} + createLiteralMessage( + String literalData, { + String filename = '', + DateTime? time, + }) {} /// Sign a cleartext message. /// Return a signed message object. @@ -93,11 +109,11 @@ final class OpenPGP { encrypt() {} /// Decrypt a message with the user's private keys, or passwords. - /// One of `decryptionKeys` or `passwords` must be specified + /// One of `decryptionKeys` or `passwords` must be specified. decrypt() {} /// Decrypt a armored/binary encrypted string with /// the user's private keys, or passwords. - /// One of `decryptionKeys` or `passwords` must be specified + /// One of `decryptionKeys` or `passwords` must be specified. decryptMessage() {} } From 93598df6b5dc1732219837e984e1915c4e302b5a Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 22 Nov 2024 08:21:32 +0700 Subject: [PATCH 033/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/enum/aead_algorithm.dart | 3 +-- lib/src/enum/armor_type.dart | 2 +- lib/src/enum/compression_algorithm.dart | 1 - lib/src/enum/ecc.dart | 1 - lib/src/enum/hash_algorithm.dart | 3 +-- lib/src/enum/kek_size.dart | 2 +- lib/src/enum/key_algorithm.dart | 3 +-- lib/src/enum/key_flag.dart | 2 +- lib/src/enum/packet_type.dart | 1 - lib/src/enum/revocation_key_tag.dart | 2 +- lib/src/enum/revocation_reason_tag.dart | 2 +- lib/src/enum/rsa_key_size.dart | 2 +- lib/src/enum/s2k_type.dart | 3 +-- lib/src/enum/s2k_usage.dart | 3 +-- lib/src/enum/signature_subpacket_type.dart | 2 +- lib/src/enum/signature_type.dart | 2 +- lib/src/enum/support_feature.dart | 2 +- lib/src/enum/symmetric_algorithm.dart | 3 +-- 18 files changed, 15 insertions(+), 24 deletions(-) diff --git a/lib/src/enum/aead_algorithm.dart b/lib/src/enum/aead_algorithm.dart index 3e88d148..8421e2c7 100644 --- a/lib/src/enum/aead_algorithm.dart +++ b/lib/src/enum/aead_algorithm.dart @@ -12,8 +12,7 @@ import '../cryptor/aead/ocb.dart'; import '../type/aead.dart'; import 'symmetric_algorithm.dart'; -/// Aead Algorithms -/// See https://www.rfc-editor.org/rfc/rfc9580#section-9.6 +/// Aead algorithms enum /// Author Nguyen Van Nguyen enum AeadAlgorithm { eax(1), diff --git a/lib/src/enum/armor_type.dart b/lib/src/enum/armor_type.dart index 6ac87cb1..74e41afe 100644 --- a/lib/src/enum/armor_type.dart +++ b/lib/src/enum/armor_type.dart @@ -4,7 +4,7 @@ library; -/// Armor type +/// Armor types enum /// Author Nguyen Van Nguyen enum ArmorType { multipartSection, diff --git a/lib/src/enum/compression_algorithm.dart b/lib/src/enum/compression_algorithm.dart index abeb66dc..c2229915 100644 --- a/lib/src/enum/compression_algorithm.dart +++ b/lib/src/enum/compression_algorithm.dart @@ -5,7 +5,6 @@ library; /// Compression algorithms enum -/// See https://www.rfc-editor.org/rfc/rfc9580#section-9.4 /// Author Nguyen Van Nguyen enum CompressionAlgorithm { uncompressed(0), diff --git a/lib/src/enum/ecc.dart b/lib/src/enum/ecc.dart index 96dde56e..6abaa980 100644 --- a/lib/src/enum/ecc.dart +++ b/lib/src/enum/ecc.dart @@ -10,7 +10,6 @@ import 'hash_algorithm.dart'; import 'symmetric_algorithm.dart'; /// Elliptic curve cryptography enum -/// See https://www.rfc-editor.org/rfc/rfc9580#section-9.2 /// Author Nguyen Van Nguyen enum Ecc { secp256r1([1, 2, 840, 10045, 3, 1, 7], '1.2.840.10045.3.1.7'), diff --git a/lib/src/enum/hash_algorithm.dart b/lib/src/enum/hash_algorithm.dart index 2574e363..cde8fde5 100644 --- a/lib/src/enum/hash_algorithm.dart +++ b/lib/src/enum/hash_algorithm.dart @@ -4,8 +4,7 @@ library; -/// Hash Algorithms enum -/// See https://www.rfc-editor.org/rfc/rfc9580#section-9.5 +/// Hash algorithms enum /// Author Nguyen Van Nguyen enum HashAlgorithm { md5(1), diff --git a/lib/src/enum/kek_size.dart b/lib/src/enum/kek_size.dart index 81369d79..8b38a2f9 100644 --- a/lib/src/enum/kek_size.dart +++ b/lib/src/enum/kek_size.dart @@ -4,7 +4,7 @@ library; -/// Key encryption key size enum +/// Key encryption key sizes enum /// Author Nguyen Van Nguyen enum KekSize { normal(16), diff --git a/lib/src/enum/key_algorithm.dart b/lib/src/enum/key_algorithm.dart index 415608c5..6bb6d3cc 100644 --- a/lib/src/enum/key_algorithm.dart +++ b/lib/src/enum/key_algorithm.dart @@ -7,8 +7,7 @@ library; import '../common/config.dart'; import 'key_version.dart'; -/// Public-Key Algorithms -/// See https://www.rfc-editor.org/rfc/rfc9580#section-9.1 +/// Public key algorithms enum /// Author Nguyen Van Nguyen enum KeyAlgorithm { /// RSA (Encrypt or Sign) [HAC] diff --git a/lib/src/enum/key_flag.dart b/lib/src/enum/key_flag.dart index cc511095..a2f532dd 100644 --- a/lib/src/enum/key_flag.dart +++ b/lib/src/enum/key_flag.dart @@ -4,7 +4,7 @@ library; -/// Key flag enum +/// Key flags enum /// Author Nguyen Van Nguyen enum KeyFlag { /// 0x01 - This key may be used to certify other keys. diff --git a/lib/src/enum/packet_type.dart b/lib/src/enum/packet_type.dart index 486bc988..61b5099a 100644 --- a/lib/src/enum/packet_type.dart +++ b/lib/src/enum/packet_type.dart @@ -5,7 +5,6 @@ library; /// A list of packet types and numeric tags associated with them. -/// See https://www.rfc-editor.org/rfc/rfc9580#section-5 /// Author Nguyen Van Nguyen enum PacketType { publicKeyEncryptedSessionKey(1), diff --git a/lib/src/enum/revocation_key_tag.dart b/lib/src/enum/revocation_key_tag.dart index 41fbc3d4..53b51e64 100644 --- a/lib/src/enum/revocation_key_tag.dart +++ b/lib/src/enum/revocation_key_tag.dart @@ -4,7 +4,7 @@ library; -/// Revocation key tag enum +/// Revocation key tags enum /// Author Nguyen Van Nguyen enum RevocationKeyTag { classDefault(128), diff --git a/lib/src/enum/revocation_reason_tag.dart b/lib/src/enum/revocation_reason_tag.dart index 1186a097..d63bb328 100644 --- a/lib/src/enum/revocation_reason_tag.dart +++ b/lib/src/enum/revocation_reason_tag.dart @@ -4,7 +4,7 @@ library; -/// Reason for Revocation +/// Reason for revocation enum /// Author Nguyen Van Nguyen enum RevocationReasonTag { /// No reason specified (key revocations or cert revocations) diff --git a/lib/src/enum/rsa_key_size.dart b/lib/src/enum/rsa_key_size.dart index 2867296b..998b2cf2 100644 --- a/lib/src/enum/rsa_key_size.dart +++ b/lib/src/enum/rsa_key_size.dart @@ -4,7 +4,7 @@ library; -/// RSA key size +/// RSA key sizes enum /// Author Nguyen Van Nguyen enum RSAKeySize { normal(2048), diff --git a/lib/src/enum/s2k_type.dart b/lib/src/enum/s2k_type.dart index 3b4576b7..d9ef53f2 100644 --- a/lib/src/enum/s2k_type.dart +++ b/lib/src/enum/s2k_type.dart @@ -4,8 +4,7 @@ library; -/// String to key specifier type -/// See https://www.rfc-editor.org/rfc/rfc9580#section-3.7.1 +/// String to key specifier types enum /// Author Nguyen Van Nguyen enum S2kType { simple(0), diff --git a/lib/src/enum/s2k_usage.dart b/lib/src/enum/s2k_usage.dart index d99347d1..95f5d74b 100644 --- a/lib/src/enum/s2k_usage.dart +++ b/lib/src/enum/s2k_usage.dart @@ -4,9 +4,8 @@ library; -/// S2k usage +/// S2k usages enum /// Indicating whether and how the secret key material is protected by a passphrase -/// See https://www.rfc-editor.org/rfc/rfc9580#section-3.7.2 /// Author Nguyen Van Nguyen enum S2kUsage { none(0), diff --git a/lib/src/enum/signature_subpacket_type.dart b/lib/src/enum/signature_subpacket_type.dart index fb4570e4..9a7035eb 100644 --- a/lib/src/enum/signature_subpacket_type.dart +++ b/lib/src/enum/signature_subpacket_type.dart @@ -4,7 +4,7 @@ library; -/// Signature subpacket type enum +/// Signature subpacket types enum /// Author Nguyen Van Nguyen enum SignatureSubpacketType { signatureCreationTime(2), diff --git a/lib/src/enum/signature_type.dart b/lib/src/enum/signature_type.dart index b70eda54..3b6e1326 100644 --- a/lib/src/enum/signature_type.dart +++ b/lib/src/enum/signature_type.dart @@ -4,7 +4,7 @@ library; -///Signature type enum +///Signature types enum /// Author Nguyen Van Nguyen enum SignatureType { /// Signature of a binary document. diff --git a/lib/src/enum/support_feature.dart b/lib/src/enum/support_feature.dart index 54551984..76a15df7 100644 --- a/lib/src/enum/support_feature.dart +++ b/lib/src/enum/support_feature.dart @@ -4,7 +4,7 @@ library; -/// Support Feature +/// Support features enum /// Author Nguyen Van Nguyen enum SupportFeature { /// 0x01 - Modification Detection (packets 18 and 19) diff --git a/lib/src/enum/symmetric_algorithm.dart b/lib/src/enum/symmetric_algorithm.dart index 1941865a..15d0c717 100644 --- a/lib/src/enum/symmetric_algorithm.dart +++ b/lib/src/enum/symmetric_algorithm.dart @@ -10,8 +10,7 @@ import '../cryptor/symmetric/cast5.dart'; import '../cryptor/symmetric/idea.dart'; import '../cryptor/symmetric/twofish.dart'; -/// Symmetric Key Algorithms enum -/// See https://www.rfc-editor.org/rfc/rfc9580#section-9.3 +/// Symmetric key algorithms enum /// Author Nguyen Van Nguyen enum SymmetricAlgorithm { plaintext(0), From f9fa2a136b213996f5dd43bef7cc7690f5834312 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 22 Nov 2024 08:28:01 +0700 Subject: [PATCH 034/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/key_wrap_test.dart | 381 ++++++++++++++++++++++++++++++ test/packet/user_packet_test.dart | 49 ++++ 2 files changed, 430 insertions(+) create mode 100644 test/packet/key_wrap_test.dart create mode 100644 test/packet/user_packet_test.dart diff --git a/test/packet/key_wrap_test.dart b/test/packet/key_wrap_test.dart new file mode 100644 index 00000000..5cf88016 --- /dev/null +++ b/test/packet/key_wrap_test.dart @@ -0,0 +1,381 @@ +import 'dart:typed_data'; + +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/packet/key/key_wrapper.dart'; +import 'package:test/test.dart'; + +void main() { + group('Key wrap & unwrap', () { + final key128 = Uint8List.fromList(List.generate(16, (index) => index)); + final key192 = Uint8List.fromList(List.generate(24, (index) => index)); + final key256 = Uint8List.fromList(List.generate(32, (index) => index)); + + final keyData128 = Uint8List.fromList([ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, // 0 - 7 + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, + ]); + final keyData192 = Uint8List.fromList([ + 0x00, + 0x11, + 0x22, + 0x33, + 0x44, + 0x55, + 0x66, + 0x77, + 0x88, + 0x99, + 0xaa, + 0xbb, + 0xcc, + 0xdd, + 0xee, + 0xff, + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + ]); + final keyData256 = Uint8List.fromList([ + 0x00, + 0x11, + 0x22, + 0x33, + 0x44, + 0x55, + 0x66, + 0x77, + 0x88, + 0x99, + 0xaa, + 0xbb, + 0xcc, + 0xdd, + 0xee, + 0xff, + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0a, + 0x0b, + 0x0c, + 0x0d, + 0x0e, + 0x0f, + ]); + + test('aes 128-bit test', () async { + final aes = AesKeyWrapper(16); + + final wrappedKey128128 = Uint8List.fromList([ + 0x1f, + 0xa6, + 0x8b, + 0x0a, + 0x81, + 0x12, + 0xb4, + 0x47, + 0xae, + 0xf3, + 0x4b, + 0xd8, + 0xfb, + 0x5a, + 0x7b, + 0x82, + 0x9d, + 0x3e, + 0x86, + 0x23, + 0x71, + 0xd2, + 0xcf, + 0xe5, + ]); + final wrappedKey128 = aes.wrap(key128, keyData128); + final unwrappedKey128 = aes.unwrap(key128, wrappedKey128); + + expect(wrappedKey128, equals(wrappedKey128128)); + expect(unwrappedKey128, equals(keyData128)); + + final key = Helper.secureRandom().nextBytes(16); + final keyData = Helper.secureRandom().nextBytes(32); + final wrappedKey = aes.wrap(key, keyData); + final unwrappedKey = aes.unwrap(key, wrappedKey); + expect(unwrappedKey, equals(keyData)); + }); + + test('aes 192-bit test', () async { + final aes = AesKeyWrapper(24); + final wrappedKey128192 = Uint8List.fromList([ + 0x96, + 0x77, + 0x8b, + 0x25, + 0xae, + 0x6c, + 0xa4, + 0x35, + 0xf9, + 0x2b, + 0x5b, + 0x97, + 0xc0, + 0x50, + 0xae, + 0xd2, + 0x46, + 0x8a, + 0xb8, + 0xa1, + 0x7a, + 0xd8, + 0x4e, + 0x5d, + ]); + final wrappedKey192192 = Uint8List.fromList([ + 0x03, + 0x1d, + 0x33, + 0x26, + 0x4e, + 0x15, + 0xd3, + 0x32, + 0x68, + 0xf2, + 0x4e, + 0xc2, + 0x60, + 0x74, + 0x3e, + 0xdc, + 0xe1, + 0xc6, + 0xc7, + 0xdd, + 0xee, + 0x72, + 0x5a, + 0x93, + 0x6b, + 0xa8, + 0x14, + 0x91, + 0x5c, + 0x67, + 0x62, + 0xd2 + ]); + + final wrappedKey128 = aes.wrap(key192, keyData128); + final unwrappedKey128 = aes.unwrap(key192, wrappedKey128); + expect(wrappedKey128, equals(wrappedKey128192)); + expect(unwrappedKey128, equals(keyData128)); + + final wrappedKey192 = aes.wrap(key192, keyData192); + final unwrappedKey192 = aes.unwrap(key192, wrappedKey192); + expect(wrappedKey192, equals(wrappedKey192192)); + expect(unwrappedKey192, equals(keyData192)); + + final key = Helper.secureRandom().nextBytes(24); + final keyData = Helper.secureRandom().nextBytes(32); + final wrappedKey = aes.wrap(key, keyData); + final unwrappedKey = aes.unwrap(key, wrappedKey); + expect(unwrappedKey, equals(keyData)); + }); + + test('aes 256-bit test', () async { + final aes = AesKeyWrapper(32); + final wrappedKey128256 = Uint8List.fromList([ + 0x64, + 0xe8, + 0xc3, + 0xf9, + 0xce, + 0x0f, + 0x5b, + 0xa2, + 0x63, + 0xe9, + 0x77, + 0x79, + 0x05, + 0x81, + 0x8a, + 0x2a, + 0x93, + 0xc8, + 0x19, + 0x1e, + 0x7d, + 0x6e, + 0x8a, + 0xe7, + ]); + final wrappedKey192256 = Uint8List.fromList([ + 0xa8, + 0xf9, + 0xbc, + 0x16, + 0x12, + 0xc6, + 0x8b, + 0x3f, + 0xf6, + 0xe6, + 0xf4, + 0xfb, + 0xe3, + 0x0e, + 0x71, + 0xe4, + 0x76, + 0x9c, + 0x8b, + 0x80, + 0xa3, + 0x2c, + 0xb8, + 0x95, + 0x8c, + 0xd5, + 0xd1, + 0x7d, + 0x6b, + 0x25, + 0x4d, + 0xa1, + ]); + final wrappedKey256256 = Uint8List.fromList([ + 0x28, + 0xc9, + 0xf4, + 0x04, + 0xc4, + 0xb8, + 0x10, + 0xf4, + 0xcb, + 0xcc, + 0xb3, + 0x5c, + 0xfb, + 0x87, + 0xf8, + 0x26, + 0x3f, + 0x57, + 0x86, + 0xe2, + 0xd8, + 0x0e, + 0xd3, + 0x26, + 0xcb, + 0xc7, + 0xf0, + 0xe7, + 0x1a, + 0x99, + 0xf4, + 0x3b, + 0xfb, + 0x98, + 0x8b, + 0x9b, + 0x7a, + 0x02, + 0xdd, + 0x21, + ]); + + final wrappedKey128 = aes.wrap(key256, keyData128); + final unwrappedKey128 = aes.unwrap(key256, wrappedKey128); + expect(wrappedKey128, equals(wrappedKey128256)); + expect(unwrappedKey128, equals(keyData128)); + + final wrappedKey192 = aes.wrap(key256, keyData192); + final unwrappedKey192 = aes.unwrap(key256, wrappedKey192); + expect(wrappedKey192, equals(wrappedKey192256)); + expect(unwrappedKey192, equals(keyData192)); + + final wrappedKey256 = aes.wrap(key256, keyData256); + final unwrappedKey256 = aes.unwrap(key256, wrappedKey256); + expect(wrappedKey256, equals(wrappedKey256256)); + expect(unwrappedKey256, equals(keyData256)); + + final key = Helper.secureRandom().nextBytes(32); + final keyData = Helper.secureRandom().nextBytes(32); + final wrappedKey = aes.wrap(key, keyData); + final unwrappedKey = aes.unwrap(key, wrappedKey); + expect(unwrappedKey, equals(keyData)); + }); + + test('camellia 128-bit test', () async { + final camellia = CamelliaKeyWrapper(16); + + final wrappedKey128 = camellia.wrap(key128, keyData128); + final unwrappedKey128 = camellia.unwrap(key128, wrappedKey128); + expect(unwrappedKey128, equals(keyData128)); + + final key = Helper.secureRandom().nextBytes(16); + final keyData = Helper.secureRandom().nextBytes(32); + final wrappedKey = camellia.wrap(key, keyData); + final unwrappedKey = camellia.unwrap(key, wrappedKey); + expect(unwrappedKey, equals(keyData)); + }); + + test('camellia 192-bit test', () async { + final camellia = CamelliaKeyWrapper(24); + + final wrappedKey128 = camellia.wrap(key192, keyData128); + final unwrappedKey128 = camellia.unwrap(key192, wrappedKey128); + expect(unwrappedKey128, equals(keyData128)); + + final wrappedKey192 = camellia.wrap(key192, keyData192); + final unwrappedKey192 = camellia.unwrap(key192, wrappedKey192); + expect(unwrappedKey192, equals(keyData192)); + + final key = Helper.secureRandom().nextBytes(24); + final keyData = Helper.secureRandom().nextBytes(32); + final wrappedKey = camellia.wrap(key, keyData); + final unwrappedKey = camellia.unwrap(key, wrappedKey); + expect(unwrappedKey, equals(keyData)); + }); + + test('camellia 256-bit test', () async { + final camellia = CamelliaKeyWrapper(32); + + final wrappedKey128 = camellia.wrap(key256, keyData128); + final unwrappedKey128 = camellia.unwrap(key256, wrappedKey128); + expect(unwrappedKey128, equals(keyData128)); + + final wrappedKey192 = camellia.wrap(key256, keyData192); + final unwrappedKey192 = camellia.unwrap(key256, wrappedKey192); + expect(unwrappedKey192, equals(keyData192)); + + final wrappedKey256 = camellia.wrap(key256, keyData256); + final unwrappedKey256 = camellia.unwrap(key256, wrappedKey256); + expect(unwrappedKey256, equals(keyData256)); + + final key = Helper.secureRandom().nextBytes(32); + final keyData = Helper.secureRandom().nextBytes(32); + final wrappedKey = camellia.wrap(key, keyData); + final unwrappedKey = camellia.unwrap(key, wrappedKey); + expect(unwrappedKey, equals(keyData)); + }); + }); +} diff --git a/test/packet/user_packet_test.dart b/test/packet/user_packet_test.dart new file mode 100644 index 00000000..e5c55161 --- /dev/null +++ b/test/packet/user_packet_test.dart @@ -0,0 +1,49 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:dart_pg/src/packet/image_user_attribute.dart'; +import 'package:dart_pg/src/packet/user_attribute.dart'; +import 'package:dart_pg/src/packet/user_attribute_subpacket.dart'; +import 'package:dart_pg/src/packet/user_id.dart'; +import 'package:faker/faker.dart'; +import 'package:test/test.dart'; + +void main() { + group('User packet', (() { + test('user id test', (() { + final name = faker.person.name(); + final email = faker.internet.email().replaceAll("'", ''); + final comment = faker.lorem.words(3).join(' '); + + final userID = UserIDPacket([name, '($comment)', '<$email>'].join(' ')); + expect(userID.name, name); + expect(userID.email, email); + expect(userID.comment, comment); + + final cloneUserId = UserIDPacket.fromBytes(userID.data); + expect(userID.name, cloneUserId.name); + expect(userID.email, cloneUserId.email); + expect(userID.comment, cloneUserId.comment); + })); + + test('user attribute test', (() { + final imageData = Uint8List.fromList(faker.randomGenerator.numbers(255, 100)); + final subpacketType = faker.randomGenerator.integer(100); + final subpacketData = utf8.encoder.convert(faker.lorem.words(100).join(' ')); + + final userAttr = UserAttributePacket.fromBytes(UserAttributePacket([ + ImageUserAttribute.fromBytes(imageData), + UserAttributeSubpacket(subpacketType, subpacketData), + ]).data); + final imageAttr = userAttr.attributes[0] as ImageUserAttribute; + final subpacket = userAttr.attributes[1]; + + expect(imageAttr.version, 0x01); + expect(imageAttr.encoding, ImageUserAttribute.jpeg); + expect(imageAttr.imageData, imageData); + + expect(subpacket.type, subpacketType); + expect(subpacket.data, subpacketData); + })); + })); +} From 2e5b64a20f6ea3ff4e134727fe751b9c7e85d82f Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 22 Nov 2024 09:13:58 +0700 Subject: [PATCH 035/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/enum/key_algorithm.dart | 2 +- lib/src/packet/aead_encrypted_data.dart | 15 ++++++++------- lib/src/packet/sym_encrypted_data.dart | 12 ++++++------ .../sym_encrypted_integrity_protected_data.dart | 16 +++++++++------- lib/src/type/encrypted_data_packet.dart | 2 +- 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/lib/src/enum/key_algorithm.dart b/lib/src/enum/key_algorithm.dart index 6bb6d3cc..e30bb5b9 100644 --- a/lib/src/enum/key_algorithm.dart +++ b/lib/src/enum/key_algorithm.dart @@ -19,7 +19,7 @@ enum KeyAlgorithm { /// RSA (Sign only) [HAC] rsaSign(3), - /// Elgamal (Encrypt only) [ELGAMAL] [HAC] + /// ElGamal (Encrypt only) [ELGAMAL] [HAC] elgamal(16), /// DSA (Sign only) [FIPS186] [HAC] diff --git a/lib/src/packet/aead_encrypted_data.dart b/lib/src/packet/aead_encrypted_data.dart index 72718d8a..2a9f1d39 100644 --- a/lib/src/packet/aead_encrypted_data.dart +++ b/lib/src/packet/aead_encrypted_data.dart @@ -9,6 +9,7 @@ import 'dart:typed_data'; import '../common/helpers.dart'; import '../enum/aead_algorithm.dart'; import '../enum/symmetric_algorithm.dart'; +import '../type/encrypted_data_packet.dart'; import '../type/packet_list.dart'; import 'base.dart'; import 'packet_list.dart'; @@ -16,7 +17,7 @@ import 'packet_list.dart'; /// Implementation of the Symmetrically Encrypted Authenticated Encryption with /// Additional Data (AEAD) Protected Data Packet(Tag 20) /// Author Nguyen Van Nguyen -class AeadEncryptedDataPacket extends BasePacket { +class AeadEncryptedDataPacket extends BasePacket implements EncryptedDataPacketInterface { static const version = 1; final SymmetricAlgorithm symmetric; @@ -24,10 +25,10 @@ class AeadEncryptedDataPacket extends BasePacket { final int chunkSize; final Uint8List iv; - /// Encrypted data + @override final Uint8List encrypted; - /// Decrypted packets contained within. + @override final PacketListInterface? packets; AeadEncryptedDataPacket( @@ -113,24 +114,24 @@ class AeadEncryptedDataPacket extends BasePacket { ...encrypted, ]); - /// Encrypt the payload in the packet. + @override AeadEncryptedDataPacket encrypt( final Uint8List key, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - final AeadAlgorithm aead = AeadAlgorithm.ocb, - final int chunkSize = 12, }) { if (packets != null && packets!.isNotEmpty) { return AeadEncryptedDataPacket.encryptPackets( key, packets!, symmetric: symmetric, + aead: aead, + chunkSize: chunkSize, ); } return this; } - /// Decrypts the encrypted data contained in the packet. + @override AeadEncryptedDataPacket decrypt( final Uint8List key, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, diff --git a/lib/src/packet/sym_encrypted_data.dart b/lib/src/packet/sym_encrypted_data.dart index 6ce5c1b7..f9ccf02f 100644 --- a/lib/src/packet/sym_encrypted_data.dart +++ b/lib/src/packet/sym_encrypted_data.dart @@ -24,9 +24,9 @@ class SymEncryptedDataPacket extends BasePacket implements EncryptedDataPacketIn final Uint8List encrypted; @override - final PacketListInterface? packetList; + final PacketListInterface? packets; - SymEncryptedDataPacket(this.encrypted, {this.packetList}) : super(PacketType.symEncryptedData); + SymEncryptedDataPacket(this.encrypted, {this.packets}) : super(PacketType.symEncryptedData); factory SymEncryptedDataPacket.fromBytes(final Uint8List bytes) => SymEncryptedDataPacket(bytes); @@ -54,7 +54,7 @@ class SymEncryptedDataPacket extends BasePacket implements EncryptedDataPacketIn ...prefix, ...cipher.process(packets.encode()), ]), - packetList: packets, + packets: packets, ); } @@ -66,10 +66,10 @@ class SymEncryptedDataPacket extends BasePacket implements EncryptedDataPacketIn final Uint8List key, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, }) { - if (packetList != null && packetList!.isNotEmpty) { + if (packets != null && packets!.isNotEmpty) { return SymEncryptedDataPacket.encryptPackets( key, - packetList!, + packets!, symmetric: symmetric, ); } @@ -95,7 +95,7 @@ class SymEncryptedDataPacket extends BasePacket implements EncryptedDataPacketIn ); return SymEncryptedDataPacket( encrypted, - packetList: PacketList.decode( + packets: PacketList.decode( cipher.process(encrypted.sublist(blockSize + 2)), ), ); diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index fe2c2be7..79c29e12 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -14,20 +14,23 @@ import '../cryptor/symmetric/buffered_cipher.dart'; import '../enum/aead_algorithm.dart'; import '../enum/hash_algorithm.dart'; import '../enum/symmetric_algorithm.dart'; +import '../type/encrypted_data_packet.dart'; import '../type/packet_list.dart'; import 'base.dart'; import 'packet_list.dart'; /// Implementation of the Sym. Encrypted Integrity Protected Data Packet (Tag 18) /// Author Nguyen Van Nguyen -class SymEncryptedIntegrityProtectedDataPacket extends BasePacket { +class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements EncryptedDataPacketInterface { static const saltSize = 32; static const mdcSuffix = [0xd3, 0x14]; final int version; + @override final Uint8List encrypted; + @override final PacketListInterface? packets; final SymmetricAlgorithm? symmetric; @@ -128,7 +131,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket { final Uint8List key, final PacketListInterface packets, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - final AeadAlgorithm aead = AeadAlgorithm.gcm, + final AeadAlgorithm? aead, final bool aeadProtect = false, }) { Helper.assertSymmetric(symmetric); @@ -144,7 +147,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket { key, packets.encode(), symmetric: symmetric, - aead: aead, + aead: aead ?? Config.preferredAead, chunkSizeByte: chunkSize, salt: salt, ); @@ -189,12 +192,10 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket { ...encrypted, ]); - /// Encrypt the payload in the packet. + @override SymEncryptedIntegrityProtectedDataPacket encrypt( final Uint8List key, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - AeadAlgorithm aead = AeadAlgorithm.gcm, - bool aeadProtect = false, }) { if (packets != null && packets!.isNotEmpty) { return SymEncryptedIntegrityProtectedDataPacket.encryptPackets( @@ -202,12 +203,13 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket { packets!, symmetric: symmetric, aead: aead, - aeadProtect: aeadProtect, + aeadProtect: aead != null, ); } return this; } + @override SymEncryptedIntegrityProtectedDataPacket decrypt( final Uint8List key, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, diff --git a/lib/src/type/encrypted_data_packet.dart b/lib/src/type/encrypted_data_packet.dart index 69cf3bb7..3d3bfd8a 100644 --- a/lib/src/type/encrypted_data_packet.dart +++ b/lib/src/type/encrypted_data_packet.dart @@ -16,7 +16,7 @@ abstract class EncryptedDataPacketInterface { Uint8List get encrypted; /// Decrypted packets contained within. - PacketListInterface? get packetList; + PacketListInterface? get packets; /// Encrypt the payload in the packet. EncryptedDataPacketInterface encrypt( From a8608e64c11b3922498fd87b187c040ef6992d2e Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 22 Nov 2024 11:11:42 +0700 Subject: [PATCH 036/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/ec_public_material.dart | 9 ++++++++- lib/src/packet/key/ecdsa_public_material.dart | 6 +++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/src/packet/key/ec_public_material.dart b/lib/src/packet/key/ec_public_material.dart index 91941ecd..4d6e7f9b 100644 --- a/lib/src/packet/key/ec_public_material.dart +++ b/lib/src/packet/key/ec_public_material.dart @@ -29,7 +29,14 @@ abstract class ECPublicMaterial implements KeyMaterialInterface { ); @override - int get keyLength => q.bitLength; + int get keyLength { + final params = ECDomainParameters(curve.name.toLowerCase()); + final key = ECPublicKey( + params.curve.decodePoint(q.toUnsignedBytes()), + params, + ); + return key.Q!.curve.fieldSize; + } @override Uint8List get toBytes { diff --git a/lib/src/packet/key/ecdsa_public_material.dart b/lib/src/packet/key/ecdsa_public_material.dart index eeb50719..63e9d281 100644 --- a/lib/src/packet/key/ecdsa_public_material.dart +++ b/lib/src/packet/key/ecdsa_public_material.dart @@ -35,14 +35,14 @@ class ECDSAPublicMaterial extends ECPublicMaterial implements VerificationKeyMat final HashAlgorithm hash, final Uint8List signature, ) { - final parameters = ECDomainParameters(curve.name.toLowerCase()); + final params = ECDomainParameters(curve.name.toLowerCase()); final signer = Signer('${hash.digestName}/DET-ECDSA') ..init( false, PublicKeyParameter( ECPublicKey( - parameters.curve.decodePoint(q.toUnsignedBytes()), - parameters, + params.curve.decodePoint(q.toUnsignedBytes()), + params, ), ), ); From 0f5651879f2ffca51ddd9c84456b7e7da428f802 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 22 Nov 2024 11:38:42 +0700 Subject: [PATCH 037/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/enum/ecc.dart | 2 +- lib/src/packet/key/ec_public_material.dart | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/src/enum/ecc.dart b/lib/src/enum/ecc.dart index 6abaa980..a8a5de92 100644 --- a/lib/src/enum/ecc.dart +++ b/lib/src/enum/ecc.dart @@ -18,7 +18,7 @@ enum Ecc { brainpoolP256r1([1, 3, 36, 3, 3, 2, 8, 1, 1, 7], '1.3.36.3.3.2.8.1.1.7'), brainpoolP384r1([1, 3, 36, 3, 3, 2, 8, 1, 1, 11], '1.3.36.3.3.2.8.1.1.11'), brainpoolP512r1([1, 3, 36, 3, 3, 2, 8, 1, 1, 13], '1.3.36.3.3.2.8.1.1.13'), - ed25519([1, 3, 6, 1, 4, 1, 11591, 15, 1], '11.3.6.1.4.1.3029.1.5.1'), + ed25519([1, 3, 6, 1, 4, 1, 11591, 15, 1], '1.3.6.1.4.1.11591.15.1'), curve25519([1, 3, 6, 1, 4, 1, 3029, 1, 5, 1], '1.3.6.1.4.1.3029.1.5.1'); final List identifier; diff --git a/lib/src/packet/key/ec_public_material.dart b/lib/src/packet/key/ec_public_material.dart index 4d6e7f9b..7bb1d2f1 100644 --- a/lib/src/packet/key/ec_public_material.dart +++ b/lib/src/packet/key/ec_public_material.dart @@ -30,12 +30,16 @@ abstract class ECPublicMaterial implements KeyMaterialInterface { @override int get keyLength { - final params = ECDomainParameters(curve.name.toLowerCase()); - final key = ECPublicKey( - params.curve.decodePoint(q.toUnsignedBytes()), - params, - ); - return key.Q!.curve.fieldSize; + if (curve == Ecc.ed25519 || curve == Ecc.curve25519) { + return 255; + } else { + final params = ECDomainParameters(curve.name.toLowerCase()); + final key = ECPublicKey( + params.curve.decodePoint(q.toUnsignedBytes()), + params, + ); + return key.Q!.curve.fieldSize; + } } @override From ffa3c564d4765380544329b34ab1ea783587cf36 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 22 Nov 2024 12:05:53 +0700 Subject: [PATCH 038/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/rsa_secret_material.dart | 6 ++++-- lib/src/packet/secret_key.dart | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/src/packet/key/rsa_secret_material.dart b/lib/src/packet/key/rsa_secret_material.dart index 86d1879d..ed460c79 100644 --- a/lib/src/packet/key/rsa_secret_material.dart +++ b/lib/src/packet/key/rsa_secret_material.dart @@ -109,15 +109,17 @@ class RSASecretMaterial implements SigningKeyMaterialInterface { @override bool get isValid { // expect pq = n - if ((primeP * primeQ).compareTo(publicMaterial.exponent) != 0) { + if ((primeP * primeQ).compareTo(publicMaterial.modulus) != 0) { + print('expect pq = n'); return false; } // expect p*u = 1 mod q if (((primeP * coefficient) % primeQ).compareTo(BigInt.one) != 0) { + print('expect p*u = 1 mod q'); return false; } - final sizeOver3 = (privateKey.modulus!.bitLength / 3).floor(); + final sizeOver3 = (publicMaterial.modulus.bitLength / 3).floor(); final r = Helper.randomBigInt(BigInt.two, BigInt.two << sizeOver3); final rde = r * exponent * publicMaterial.exponent; return (rde % (primeP - BigInt.one)).compareTo(r) == 0 && (rde % (primeQ - BigInt.one)).compareTo(r) == 0; diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 402546be..a15137ea 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -74,6 +74,7 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { final s2kUsage = S2kUsage.values.firstWhere( (usage) => usage.value == bytes[pos], ); + pos++; // Only for a version 6 packet where the secret key material encrypted if (isV6 && s2kUsage != S2kUsage.none) { From e97908a0b6f8c44ec79cbd0d160d8837e12919b6 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 22 Nov 2024 12:22:47 +0700 Subject: [PATCH 039/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/key_packet_test.dart | 561 +++++++++++++++++++++++++++++++ 1 file changed, 561 insertions(+) create mode 100644 test/packet/key_packet_test.dart diff --git a/test/packet/key_packet_test.dart b/test/packet/key_packet_test.dart new file mode 100644 index 00000000..82db2d3f --- /dev/null +++ b/test/packet/key_packet_test.dart @@ -0,0 +1,561 @@ +import 'dart:convert'; + +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/ecc.dart'; +import 'package:dart_pg/src/enum/key_algorithm.dart'; +import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/key/public_material.dart'; +import 'package:test/test.dart'; + +void main() { + group('Public key', () { + test('RSA keys', () { + const publicKeyPacket = ''' +BGc/4FEBCACZXe5kmfKWChtAGdoYRxRkEcfm0Ne431ovB4QS3zIOoclfyA7jgyq1UAhKbsYhZmTK +xx0FO5/HKOO2ojJU3AeKbLP+xhH8yVdEK5JiySvX/16Tz27d+r5WGLkjZMYZAxWW9hdfurajYp8v +cdACvErKMXShXUR9npCKH5O0QBagxMg6vczfJfNG1qWJ9aeFO7yffUt9n+39ru3Cc67EAp7m29Wo +wto15xmeXCPkYEgE+BM+86AHGqihOERjPCzWeFVLRuCwj2YjmZvtMh9A2gx5K6dMQ5viMDsTSoB0 +DEZzvY+Q7gr7/qiWCpSfmy3gvmReOvMzk8uI7+Nt6vdJia4vABEBAAE= +'''; + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + publicKeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect( + publicKey.fingerprint.toHexadecimal(), + '5ccceda54f917089f8c488000532372a028f2ff5', + ); + expect(publicKey.keyAlgorithm, KeyAlgorithm.rsaEncryptSign); + expect(publicKey.keyVersion, 4); + expect(publicKey.keyStrength, 2048); + + const publicSubkeyPacket = ''' +BGc/4FEBCACUrspJcjpxuAKKF/2gPWJogkTuk+Ya+BUDjgPI3Cph8APkFfVct+NNwSW1xD6Bg/lG +SH0hedXUTDVyCIXd1PI8tWEjB41aRu2hVJ908UAo7KVmST3zNSRClNJPN0ff7KV67K6K3PcWi2Cz +V58NZSBK2kZqQpzQRCFHq8NYo6NQnJ7yhYxH3dfW7F0hVwMzcgw8SitNRW0vNlJDJQdAbveHh8w3 +uZQ+tEAs7QsZVdVj2J7RFOOAD1J3NlnExE3mcawwPi2KLKv8lGzO0jLNPiEnKD64LbNE8QDr+r2b +R6Cx4/cEJ5qXuHTUESblPwkkPlW3wv2tDdRqQZKyGLOIKe0LABEBAAE= +'''; + final publicSubkey = PublicSubkeyPacket.fromBytes( + base64.decode( + publicSubkeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect( + publicSubkey.fingerprint.toHexadecimal(), + '79fb82ac1204bdc854e87364316ef1539787254f', + ); + expect(publicSubkey.keyAlgorithm, KeyAlgorithm.rsaEncryptSign); + expect(publicSubkey.keyVersion, 4); + expect(publicSubkey.keyStrength, 2048); + expect(publicSubkey.isSubkey, isTrue); + }); + + test('DSA & ElGamal keys', () { + const publicKeyPacket = ''' +BGc/4H8RCAD3gDG7FSfKWRy4/gNe0sgYYgZ4zAFZTLWneObcLYpY+kmwufUIrAG0iWt8kzLbiLon +A1B7ss041yCqJgTHFcttH5nFG/q25MT+xq+V55TRZGzaD6Gy65E++CuS0m3gJmLH14bX6++5Evtc +/bkOTGgIfRGfEH8pL1ePc9ev1EFtmOYgZkX0a7oM8TiNKVNnUKwhuBi7pbC6OEOuDL1HmbsAHIE4 +n6mMs3HzJCNEAkvHNcQGZNdn8SD3d3ukljOq03ko+tC2Orys1AwiZokL10EOiZYQZ55KAOFXM/Hu +ayVQ8xR9Zgg8i+8HdjvAzaifH6ThOMDE2I80Yp/jllG/PBBvAQD3msVRxzpaT3WiMeopZAOM+jeF +xwy8bG0W+BOJwn9BjQgAzFLIFwHauCi4ooI0JsndyKE6TCexzA46r0nA/1BcNpRtUlhLsEwJ0app +PkCufixLL0RYKTnxgfrAbxR+mrIPla1djnNysoYj8NfDms6aIoxKzT7gN9WWwgwaTSo7lesJ8gZ6 +aZLt1kBEHj+72MUb5Jgynzjd6HA8FwQvXIUaQl0HDmYByzr/hBtVzrog5fSR/jTXX/ZbTivqgHV1 +JzT0NlTBrFEGuIFRj54JvEaiYtE/Z/lWkIRmcrAwaSCeutF4w4Xv9BhhGdRtLo2AJCQeR26sOk1k +xihdes4zBrhI+e0wrAG7G4cNOtS090O8tvIWlxlrJXyYmpZR6zHxBYbBIQf+Lk0Jou8CnR1S8oeN +EdBfeuboNWTDVz93iSEBHkSIFfwt07IvHra0wKKkYwX/p8zWyw/Me2e1xtRommFSpIqW9pWimT4b +kTtPqXAwRD4WhfVOnGxzXsXaYSWIf4sr9GusPJTsAi2cdtKTDzXFWDpJDvJ24bPa/P4oEiECno2s +2KkfQqWZbSRbOsDX8/Mdz58XzOWOv3j0A1oLLZFK+a0LiJso31S0jG71IiMFlaXWNQD025UV6Oup +lqe/P6qdArk72Qaum4QBsFfTLyKw12JQc9HPfV4Fst0sSbky+0QqqVfegHnvcnJBrjZ2pahL+EWU +LYR9lty0LinZ0v6+5HtwDA== +'''; + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + publicKeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect( + publicKey.fingerprint.toHexadecimal(), + '9a83344a3711864c1f502094ba727d0f6b50c281', + ); + expect(publicKey.keyAlgorithm, KeyAlgorithm.dsa); + expect(publicKey.keyVersion, 4); + expect(publicKey.keyStrength, 2048); + + const publicSubkeyPacket = ''' +BGc/4H8QCACv2nUuptC3DfhXtkI3H825wX+OwZzwOP8MG/JNUut/RoxFDXnld1S/GVz/dDBXs+cM +QY4M9Fe2KfsGbNVeOId8eBENIFE7DBH6Qigh8WkBUueV6qPeFkWVj7u72TwRPjR3A6uAwLv/ZrMZ +JWqD52tDr2YDv8e5LlS8uzVmTYo9nU8ohn3n6KyKNarEhpuXHqo3SHt4H7601Nr3TqdRU5ZWW1F6 +QEjj0Mj1st7ctPkMjj4R5IzfRZHyDQIrJA5Vp76cZ+oXZFIgrkWwJ//HZ/txc2PGLYYf6OCgTZRF +Rh720ETDf4arK1FheJ2qVad4YX+21PvdzU8DeIn4McO8QyM7AAMFB/9RdNV1kNfAl7LPOUp46sO/ +97esXDT5DfLcBgV+bdUGuQonASsKk53phnwp1mLhfRe1TldkoWpxkOOZAHXDdr+hbSp+UNimK9Bs +X/jCYcQr0Il5Agq8F8Idxrwv2ftlFVAsuDrwyzhGFHFC5mXPeloDG1aF8cyWKXbONM3DzBwISW4p +wKXXYZqqiDgmlQ/anjEExZqmSs6+sqWpl45iAsAB/jYvzuBl06pJFxFJo7TwNAKhgTFHMGlHXjl/ +JCbd1xv8mG2QrL2vLSFOsqaqhdEh22uGrODHC7iH+3aPMyghWLAkARUPkRXjGRXryHtl8w8DRvQj +llGb0KUH6SqD65H0 +'''; + final publicSubkey = PublicSubkeyPacket.fromBytes( + base64.decode( + publicSubkeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect( + publicSubkey.fingerprint.toHexadecimal(), + '8f1cb653b2c3a4808302b63b52c1ed36e9e22006', + ); + expect(publicSubkey.keyAlgorithm, KeyAlgorithm.elgamal); + expect(publicSubkey.keyVersion, 4); + expect(publicSubkey.keyStrength, 2048); + expect(publicSubkey.isSubkey, isTrue); + }); + + test('NIST P-384 keys', () { + const publicKeyPacket = ''' +BGc/+YkTBSuBBAAiAwMEd3FJXIrlQksBiwOLB+ksANWsTQzFNxqpxzjpglaDNKjgF/RXVXj9A6os +ao0WJtqkKFKZvkASvOCgOaBn3GDUZmF8GJwuuLNOci0JC/N+g5nFd/BeXNJNyECMIbllcLt9 +'''; + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + publicKeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final keyMaterial = publicKey.keyMaterial as ECPublicMaterial; + expect( + publicKey.fingerprint.toHexadecimal(), + 'a325107e66bcab3eea407550396ecb8bb86d1922', + ); + expect(publicKey.keyAlgorithm, KeyAlgorithm.ecdsa); + expect(publicKey.keyVersion, 4); + expect(publicKey.keyStrength, 384); + expect(keyMaterial.curve, Ecc.secp384r1); + + const publicSubkeyPacket = ''' +BGc/+YkSBSuBBAAiAwME6+LPwcbdvoEdxPA2L002pKXp8Nt7PxIsjUegQRw2bTQXMlu5zplsUNpr +WJZ1W/iqLhmEH8eLONxcuzVEQMb3dCImxBzmL3Y5HxGGti81EsU7JBIZxHKhl85RY78HdHgCAwEJ +CQ== +'''; + final publicSubkey = PublicSubkeyPacket.fromBytes( + base64.decode( + publicSubkeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final subkeyMaterial = publicSubkey.keyMaterial as ECPublicMaterial; + expect( + publicSubkey.fingerprint.toHexadecimal(), + 'bd7133409a3ad7986fbae32a5a2990ce6bd63b20', + ); + expect(publicSubkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(publicSubkey.keyVersion, 4); + expect(publicSubkey.keyStrength, 384); + expect(publicSubkey.isSubkey, isTrue); + expect(subkeyMaterial.curve, Ecc.secp384r1); + }); + + test('Brainpool P-256 keys', () { + const publicKeyPacket = ''' +BGc/+dATCSskAwMCCAEBBwIDBJ15ari0PCq317awmdNNIwz+yOZ18yUCg8LOAmYEaRAqAh1HmAnS +K5d4i1CX2M2/UKup7f/KD/o5Y6oid+VuTZQ= +'''; + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + publicKeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final keyMaterial = publicKey.keyMaterial as ECPublicMaterial; + expect( + publicKey.fingerprint.toHexadecimal(), + 'cd1b5b14294f80be65cefbef9951219fc9de9578', + ); + expect(publicKey.keyAlgorithm, KeyAlgorithm.ecdsa); + expect(publicKey.keyVersion, 4); + expect(publicKey.keyStrength, 256); + expect(keyMaterial.curve, Ecc.brainpoolP256r1); + + const publicSubkeyPacket = ''' +BGc/+dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGSVO0lI/BV +2JJsuQqI0Pne08Y7og4bhyv9S+D+wcU8sv0DAQgH +'''; + final publicSubkey = PublicSubkeyPacket.fromBytes( + base64.decode( + publicSubkeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final subkeyMaterial = publicSubkey.keyMaterial as ECPublicMaterial; + expect( + publicSubkey.fingerprint.toHexadecimal(), + 'fda27358dac2b11fb5388cca65b53bd1aff05e06', + ); + expect(publicSubkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(publicSubkey.keyVersion, 4); + expect(publicSubkey.keyStrength, 256); + expect(publicSubkey.isSubkey, isTrue); + expect(subkeyMaterial.curve, Ecc.brainpoolP256r1); + }); + + test('Curve 25519 legacy keys', () { + const publicKeyPacket = 'BGc/+gYWCSsGAQQB2kcPAQEHQGUAHTxwcB6TD72goFQpLf3jAqdbm1cZwv1N2mjBffEg'; + final publicKey = PublicKeyPacket.fromBytes( + base64.decode(publicKeyPacket), + ); + final keyMaterial = publicKey.keyMaterial as ECPublicMaterial; + expect( + publicKey.fingerprint.toHexadecimal(), + '1148b2a24f580977c27b26223ed475dd212d221e', + ); + expect(publicKey.keyAlgorithm, KeyAlgorithm.eddsaLegacy); + expect(publicKey.keyVersion, 4); + expect(publicKey.keyStrength, 255); + expect(keyMaterial.curve, Ecc.ed25519); + + const publicSubkeyPacket = 'BGc/+gYSCisGAQQBl1UBBQEBB0AJwaWfEiJqlOIk/O9i2PMaDBmbRUOBHGQdFRPKbifpfwMBCAc='; + final publicSubkey = PublicSubkeyPacket.fromBytes( + base64.decode(publicSubkeyPacket), + ); + final subkeyMaterial = publicSubkey.keyMaterial as ECPublicMaterial; + expect( + publicSubkey.fingerprint.toHexadecimal(), + '12ba12e01b12582680057e8b44d20d3a674af1a7', + ); + expect(publicSubkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(publicSubkey.keyVersion, 4); + expect(publicSubkey.keyStrength, 255); + expect(publicSubkey.isSubkey, isTrue); + expect(subkeyMaterial.curve, Ecc.curve25519); + }); + + test('Curve 25519 keys', () {}); + + test('Curve 448 keys', () {}); + }); + + group('Secret key', () { + const passphrase = 'password'; + + test('RSA keys', () { + final keyPacket = ''' +BGc/4FEBCACZXe5kmfKWChtAGdoYRxRkEcfm0Ne431ovB4QS3zIOoclfyA7jgyq1UAhKbsYhZmTK +xx0FO5/HKOO2ojJU3AeKbLP+xhH8yVdEK5JiySvX/16Tz27d+r5WGLkjZMYZAxWW9hdfurajYp8v +cdACvErKMXShXUR9npCKH5O0QBagxMg6vczfJfNG1qWJ9aeFO7yffUt9n+39ru3Cc67EAp7m29Wo +wto15xmeXCPkYEgE+BM+86AHGqihOERjPCzWeFVLRuCwj2YjmZvtMh9A2gx5K6dMQ5viMDsTSoB0 +DEZzvY+Q7gr7/qiWCpSfmy3gvmReOvMzk8uI7+Nt6vdJia4vABEBAAH+BwMCkDFoVUgR+jX/2wzp +aUM538tEPM92fd+PMCzwMVgu1DObhiVDyBSfLJDWsKPCT4dvE4gBK5exaYcVuLGzuI9AfBx2uDhp +0usI9AWQIQ/QqXEz9dwvLuI2RJzLHC0qarRhZo5H+ae5KskY+gBgdKNQ1o/wO1dTpgVSMhiUzRSn +MqD11MEk7geRh3MEFrf0XWjRykglV6Hs0a/LAYanBsjIAHVFMwQqxJJPtN/u2vkOW2cgplB96Bdz ++wu8jEd9LLDkZSS9AnI6TW1LA+QW1jrkKf86oUfOZ3aIBmw4h7kaVOH3lphe0ozNwWuErx/NjUVA +acTTLas2GERBZOKAefTfrcP7hFwWn6+x07G8EnusA0t6kx2H8m9d1lkJ+YwagxMEEA0txdbrZ6yp +4UzJs1ZrBEDhhE/HSiDJjd4sgtE0FE8BOr8kEA2o/iNo0PcllGm8lUOZzj0z4AUowYTYbYwmin5Z +HaWqfLqaXXzKUq+7UUQ47irgZtxB6WJG5ZjW2n2BYIOrqkcT0DBAaRhg4lDkEtw6fJyTq1luuWQT +0wd6B8y6ldepbequDv1+rU+fAgo9hyd3FplQzwFnxkN3XU7rjA9YF1T2jdy9s6M9QfEHypKFzlLx +p2UWHcmKp50EgN14eMa3QrNN2g/jfJq410QSEb12AVV/bwpLvlMbqvIpyur9qJIkeSoYKKlJUs6t +OOiW8nYJHx6Oh7gPHysqfoqUkavY+W9SFDcAb7ch81WfqzB30WufUI49ZiB9V/Zytrs7He4dyrgF +o430Mw9yVfhDxHUiPoNLBugkKEyNL/VOoPGMdgSW0gAdpqDQecyedjE7LAMnpWXb40R8TcnX7Y5/ +HDDqkxYX2x6gJyijanX8RDqEh4YJZcIx7UdQhLgZ4Ec9aWMazAtViH4zljKpdiABYEdTgsLQ +'''; + final secretKey = SecretKeyPacket.fromBytes( + base64.decode( + keyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ).decrypt(passphrase); + expect( + secretKey.fingerprint.toHexadecimal(), + '5ccceda54f917089f8c488000532372a028f2ff5', + ); + expect(secretKey.keyAlgorithm, KeyAlgorithm.rsaEncryptSign); + expect(secretKey.keyVersion, 4); + expect(secretKey.keyStrength, 2048); + expect(secretKey.isDecrypted, isTrue); + + final subkeyPacket = ''' +BGc/4FEBCACUrspJcjpxuAKKF/2gPWJogkTuk+Ya+BUDjgPI3Cph8APkFfVct+NNwSW1xD6Bg/lG +SH0hedXUTDVyCIXd1PI8tWEjB41aRu2hVJ908UAo7KVmST3zNSRClNJPN0ff7KV67K6K3PcWi2Cz +V58NZSBK2kZqQpzQRCFHq8NYo6NQnJ7yhYxH3dfW7F0hVwMzcgw8SitNRW0vNlJDJQdAbveHh8w3 +uZQ+tEAs7QsZVdVj2J7RFOOAD1J3NlnExE3mcawwPi2KLKv8lGzO0jLNPiEnKD64LbNE8QDr+r2b +R6Cx4/cEJ5qXuHTUESblPwkkPlW3wv2tDdRqQZKyGLOIKe0LABEBAAH+BwMCBDUxfK61uLj/E/nK +zXpSm0iwJUp8wrRjhyY5/2lFqY2gM9sj4366STySJliHR4qe7oS1ZZdRDz1bRN05SIKWdfoJZx+V +ExhLEsVgDIbEMo9gBbDlUV2hPHKjbskdcoE4SBXYFZurtj+aeeeTlQbIlwZxMJJGuXQDzgbYCnPZ +5sdQoHxnTF2LIsHYOGBdQIlL+InuHktk/x6rn6E4fZAo6xHFiTCNfWLVbx4qXpzWfST8QeF9oAvF +CRAxmKOv1gPi5xHryN7qIf8M1sWrLpQgdPBrXj9OQJXpf2z5jQtIzolQRpcCq0bwiMi5p+1cN1TN +PpZRoghf4JWc22s8QxcTsUCSkYzZ3OcHzQU2GSAaZQmyS6g1W2OBgG3sr0kCFSjtBx7RADWAvJzz +UPm7TBybibWviS2kG8bNM49nmw20V3B8CfzAhrtcX39Z0t+c4eU6lsI0ti+pL1TVbgFgYMYUxhjk +CZeW/2XWPeqNkd/RVwbOMlqQTYrgxkS80hgJm9GsAoTRLn6DFolo6l04/RF5DWZDDe4JVMzy/2Cq +aBVWzoU50W9+eLTs39MKkUSy7YLmQ3V8cAVjDhhtTCP0L0XJyDKnXdtQ8MUPMek9Uf8eZfUb6rLX +T7EHxw63dh/dScftNrEiM/4hhexXlpvz5cm07MevAI/lot7/k7WNRfFt3kEnL9snu/J1nwUBok4Q +lm7K/bsmeLzsLaiEXgRyeqCl5u/mqtyXTd0Igf7X39RBjk/EyURL6MhnHbpcUvZ2l8lEadN7B+gD +08qei/YvIFq7IP+rkPkYRVCe2+SBbEII4djEAQ4JJli2RyMUNGLdp8IMacU+HM+hYIVMLdszkPj9 +67DCKbCQHpre89JE183ERftUN+19Mzxx8UQLfAg5XhpQ8xO3ziubO2aID1HiFmRMlQNJm+/d +'''; + final secretSubkey = SecretSubkeyPacket.fromBytes( + base64.decode( + subkeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ).decrypt(passphrase); + expect( + secretSubkey.fingerprint.toHexadecimal(), + '79fb82ac1204bdc854e87364316ef1539787254f', + ); + expect(secretSubkey.keyAlgorithm, KeyAlgorithm.rsaEncryptSign); + expect(secretSubkey.keyVersion, 4); + expect(secretSubkey.keyStrength, 2048); + expect(secretSubkey.isDecrypted, isTrue); + expect(secretSubkey.isSubkey, isTrue); + }); + + test('DSA & ElGamal keys', () { + final keyPacket = ''' +BGc/4H8RCAD3gDG7FSfKWRy4/gNe0sgYYgZ4zAFZTLWneObcLYpY+kmwufUIrAG0iWt8kzLbiLon +A1B7ss041yCqJgTHFcttH5nFG/q25MT+xq+V55TRZGzaD6Gy65E++CuS0m3gJmLH14bX6++5Evtc +/bkOTGgIfRGfEH8pL1ePc9ev1EFtmOYgZkX0a7oM8TiNKVNnUKwhuBi7pbC6OEOuDL1HmbsAHIE4 +n6mMs3HzJCNEAkvHNcQGZNdn8SD3d3ukljOq03ko+tC2Orys1AwiZokL10EOiZYQZ55KAOFXM/Hu +ayVQ8xR9Zgg8i+8HdjvAzaifH6ThOMDE2I80Yp/jllG/PBBvAQD3msVRxzpaT3WiMeopZAOM+jeF +xwy8bG0W+BOJwn9BjQgAzFLIFwHauCi4ooI0JsndyKE6TCexzA46r0nA/1BcNpRtUlhLsEwJ0app +PkCufixLL0RYKTnxgfrAbxR+mrIPla1djnNysoYj8NfDms6aIoxKzT7gN9WWwgwaTSo7lesJ8gZ6 +aZLt1kBEHj+72MUb5Jgynzjd6HA8FwQvXIUaQl0HDmYByzr/hBtVzrog5fSR/jTXX/ZbTivqgHV1 +JzT0NlTBrFEGuIFRj54JvEaiYtE/Z/lWkIRmcrAwaSCeutF4w4Xv9BhhGdRtLo2AJCQeR26sOk1k +xihdes4zBrhI+e0wrAG7G4cNOtS090O8tvIWlxlrJXyYmpZR6zHxBYbBIQf+Lk0Jou8CnR1S8oeN +EdBfeuboNWTDVz93iSEBHkSIFfwt07IvHra0wKKkYwX/p8zWyw/Me2e1xtRommFSpIqW9pWimT4b +kTtPqXAwRD4WhfVOnGxzXsXaYSWIf4sr9GusPJTsAi2cdtKTDzXFWDpJDvJ24bPa/P4oEiECno2s +2KkfQqWZbSRbOsDX8/Mdz58XzOWOv3j0A1oLLZFK+a0LiJso31S0jG71IiMFlaXWNQD025UV6Oup +lqe/P6qdArk72Qaum4QBsFfTLyKw12JQc9HPfV4Fst0sSbky+0QqqVfegHnvcnJBrjZ2pahL+EWU +LYR9lty0LinZ0v6+5HtwDP4HAwJJoSaWNPq4ev84yDB8BqwxkAVvV7PKMkzyYAGDBPNp8lr3o8hg +rG84tNOSruda3xGptnE9aBQ+ZmibNZata3DR3NWfDY1R+WVSw9D3hybj +'''; + final secretKey = SecretKeyPacket.fromBytes( + base64.decode( + keyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ).decrypt(passphrase); + expect( + secretKey.fingerprint.toHexadecimal(), + '9a83344a3711864c1f502094ba727d0f6b50c281', + ); + expect(secretKey.keyAlgorithm, KeyAlgorithm.dsa); + expect(secretKey.keyVersion, 4); + expect(secretKey.keyStrength, 2048); + expect(secretKey.isDecrypted, isTrue); + + final subkeyPacket = ''' +BGc/4H8QCACv2nUuptC3DfhXtkI3H825wX+OwZzwOP8MG/JNUut/RoxFDXnld1S/GVz/dDBXs+cM +QY4M9Fe2KfsGbNVeOId8eBENIFE7DBH6Qigh8WkBUueV6qPeFkWVj7u72TwRPjR3A6uAwLv/ZrMZ +JWqD52tDr2YDv8e5LlS8uzVmTYo9nU8ohn3n6KyKNarEhpuXHqo3SHt4H7601Nr3TqdRU5ZWW1F6 +QEjj0Mj1st7ctPkMjj4R5IzfRZHyDQIrJA5Vp76cZ+oXZFIgrkWwJ//HZ/txc2PGLYYf6OCgTZRF +Rh720ETDf4arK1FheJ2qVad4YX+21PvdzU8DeIn4McO8QyM7AAMFB/9RdNV1kNfAl7LPOUp46sO/ +97esXDT5DfLcBgV+bdUGuQonASsKk53phnwp1mLhfRe1TldkoWpxkOOZAHXDdr+hbSp+UNimK9Bs +X/jCYcQr0Il5Agq8F8Idxrwv2ftlFVAsuDrwyzhGFHFC5mXPeloDG1aF8cyWKXbONM3DzBwISW4p +wKXXYZqqiDgmlQ/anjEExZqmSs6+sqWpl45iAsAB/jYvzuBl06pJFxFJo7TwNAKhgTFHMGlHXjl/ +JCbd1xv8mG2QrL2vLSFOsqaqhdEh22uGrODHC7iH+3aPMyghWLAkARUPkRXjGRXryHtl8w8DRvQj +llGb0KUH6SqD65H0/gcDAi1ba0lMwyKy/9drqpArIcD64YJ6D9a2xugmzyycAWopnNcQOf9ABltG +PFnM3cGVkY28QyU3/x+/fjvvX6AsmGDqZqzYaXjre00dg3c4bBNcJD1e2fEbY9C0Hw== +'''; + final secretSubkey = SecretSubkeyPacket.fromBytes( + base64.decode( + subkeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ).decrypt(passphrase); + expect( + secretSubkey.fingerprint.toHexadecimal(), + '8f1cb653b2c3a4808302b63b52c1ed36e9e22006', + ); + expect(secretSubkey.keyAlgorithm, KeyAlgorithm.elgamal); + expect(secretSubkey.keyVersion, 4); + expect(secretSubkey.keyStrength, 2048); + expect(secretSubkey.isDecrypted, isTrue); + expect(secretSubkey.isSubkey, isTrue); + }); + + test('NIST P-384 keys', () { + final keyPacket = ''' +BGc/+YkTBSuBBAAiAwMEd3FJXIrlQksBiwOLB+ksANWsTQzFNxqpxzjpglaDNKjgF/RXVXj9A6os +ao0WJtqkKFKZvkASvOCgOaBn3GDUZmF8GJwuuLNOci0JC/N+g5nFd/BeXNJNyECMIbllcLt9/gcD +AibS8+RBYAf4/xdsEEtrOuKrD/gnBMI2OCJu1K3bDCZcPc/vgzHCB9Kb1H07yXVqgtKEaFzFBXuH +CD71iViIpDvb++MjKPYY4hHkxRVTvFUeAUldfZ/ryBE7P3L66CRS +'''; + final secretKey = SecretKeyPacket.fromBytes( + base64.decode( + keyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ).decrypt(passphrase); + final keyMaterial = secretKey.keyMaterial as ECPublicMaterial; + expect( + secretKey.fingerprint.toHexadecimal(), + 'a325107e66bcab3eea407550396ecb8bb86d1922', + ); + expect(secretKey.keyAlgorithm, KeyAlgorithm.ecdsa); + expect(secretKey.keyVersion, 4); + expect(secretKey.keyStrength, 384); + expect(secretKey.isDecrypted, isTrue); + expect(keyMaterial.curve, Ecc.secp384r1); + + final subkeyPacket = ''' +BGc/+YkSBSuBBAAiAwME6+LPwcbdvoEdxPA2L002pKXp8Nt7PxIsjUegQRw2bTQXMlu5zplsUNpr +WJZ1W/iqLhmEH8eLONxcuzVEQMb3dCImxBzmL3Y5HxGGti81EsU7JBIZxHKhl85RY78HdHgCAwEJ +Cf4HAwJi7cDpuChI6f9Q43Dx+3m60mXsaALEN2hfX5+bTxBAbp6yK6Qn95plMof6qGO6jqFD0Bzr +vXylI2X9iKleGoFNlyGtZiThWYmH+9xWNkQ/Lrekb5HxplhniQvxEJ41nQ== +'''; + final secretSubkey = SecretSubkeyPacket.fromBytes( + base64.decode( + subkeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ).decrypt(passphrase); + final subkeyMaterial = secretSubkey.keyMaterial as ECPublicMaterial; + expect( + secretSubkey.fingerprint.toHexadecimal(), + 'bd7133409a3ad7986fbae32a5a2990ce6bd63b20', + ); + expect(secretSubkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(secretSubkey.keyVersion, 4); + expect(secretSubkey.keyStrength, 384); + expect(secretSubkey.isDecrypted, isTrue); + expect(secretSubkey.isSubkey, isTrue); + expect(subkeyMaterial.curve, Ecc.secp384r1); + }); + + test('Brainpool P-256 keys', () { + final keyPacket = ''' +BGc/+dATCSskAwMCCAEBBwIDBJ15ari0PCq317awmdNNIwz+yOZ18yUCg8LOAmYEaRAqAh1HmAnS +K5d4i1CX2M2/UKup7f/KD/o5Y6oid+VuTZT+BwMCatDpkioZEvn/eKkvTKbtGeDlyAyJaMjBhXV4 +HS1pjTIMcS0XPyKav9+v5BqZmiZq09KfI3JUV+Ump2JUP7bKfMZj83NW1VDg2NiNXKcs9A== +'''; + final secretKey = SecretKeyPacket.fromBytes( + base64.decode( + keyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ).decrypt(passphrase); + final keyMaterial = secretKey.keyMaterial as ECPublicMaterial; + expect( + secretKey.fingerprint.toHexadecimal(), + 'cd1b5b14294f80be65cefbef9951219fc9de9578', + ); + expect(secretKey.keyAlgorithm, KeyAlgorithm.ecdsa); + expect(secretKey.keyVersion, 4); + expect(secretKey.keyStrength, 256); + expect(secretKey.isDecrypted, isTrue); + expect(keyMaterial.curve, Ecc.brainpoolP256r1); + + final subkeyPacket = ''' +BGc/+dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGSVO0lI/BV +2JJsuQqI0Pne08Y7og4bhyv9S+D+wcU8sv0DAQgH/gcDAuO38MdHOYf2/8rps/QpKMK0ct6VeR8R +VNeB/QxOOoqo0SfMe61feVs/OVpmYKiKUK06YTzL4L4h4j1UlntEFqbWcqV0M0X9zuGGWHMMbZs= +'''; + final secretSubkey = SecretSubkeyPacket.fromBytes( + base64.decode( + subkeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ).decrypt(passphrase); + final subkeyMaterial = secretSubkey.keyMaterial as ECPublicMaterial; + expect( + secretSubkey.fingerprint.toHexadecimal(), + 'fda27358dac2b11fb5388cca65b53bd1aff05e06', + ); + expect(secretSubkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(secretSubkey.keyVersion, 4); + expect(secretSubkey.keyStrength, 256); + expect(secretSubkey.isDecrypted, isTrue); + expect(secretSubkey.isSubkey, isTrue); + expect(subkeyMaterial.curve, Ecc.brainpoolP256r1); + }); + + test('Curve 25519 legacy keys', () { + final keyPacket = ''' +BGc/+gYWCSsGAQQB2kcPAQEHQGUAHTxwcB6TD72goFQpLf3jAqdbm1cZwv1N2mjBffEg/gcDAjRx +sogPOLUK/7mdbYS93x93u4Hvk7ELyLfD0bpBQCsPjGtcoA+mrp4vg+cm/evYvQd74FE+BIudfbrB +STrxtvZu+g7Sf0mBxv0WAszjy6I= +'''; + final secretKey = SecretKeyPacket.fromBytes( + base64.decode( + keyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ).decrypt(passphrase); + final keyMaterial = secretKey.keyMaterial as ECPublicMaterial; + expect( + secretKey.fingerprint.toHexadecimal(), + '1148b2a24f580977c27b26223ed475dd212d221e', + ); + expect(secretKey.keyAlgorithm, KeyAlgorithm.eddsaLegacy); + expect(secretKey.keyVersion, 4); + expect(secretKey.keyStrength, 255); + expect(secretKey.isDecrypted, isTrue); + expect(keyMaterial.curve, Ecc.ed25519); + + final subkeyPacket = ''' +BGc/+gYSCisGAQQBl1UBBQEBB0AJwaWfEiJqlOIk/O9i2PMaDBmbRUOBHGQdFRPKbifpfwMBCAf+ +BwMCw5mADNvfWY3/dQSVZmOCFBZB0xErKUmpHmTXDX5JmzhanhYOnssKCuiJ+Bt2N3logFkur/VK +gWiiTYzGt55CPH+6z9IdUnM/22Y2nOVfXg== +'''; + final secretSubkey = SecretSubkeyPacket.fromBytes( + base64.decode( + subkeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ).decrypt(passphrase); + final subkeyMaterial = secretSubkey.keyMaterial as ECPublicMaterial; + expect( + secretSubkey.fingerprint.toHexadecimal(), + '12ba12e01b12582680057e8b44d20d3a674af1a7', + ); + expect(secretSubkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(secretSubkey.keyVersion, 4); + expect(secretSubkey.keyStrength, 255); + expect(secretSubkey.isDecrypted, isTrue); + expect(secretSubkey.isSubkey, isTrue); + expect(subkeyMaterial.curve, Ecc.curve25519); + }); + + test('Curve25519 keys', () {}); + + test('Curve448 keys', () {}); + }); +} From 7c8dc9db5c286d766c9de39dbf7b62e2ce01fdf9 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 22 Nov 2024 14:09:05 +0700 Subject: [PATCH 040/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/enum/eddsa_curve.dart | 5 ++++ lib/src/enum/montgomery_curve.dart | 5 ++++ lib/src/packet/key/dsa_public_material.dart | 2 +- lib/src/packet/key/dsa_secret_material.dart | 2 +- lib/src/packet/key/ec_public_material.dart | 2 +- lib/src/packet/key/ecdh_secret_material.dart | 2 +- lib/src/packet/key/ecdsa_secret_material.dart | 2 +- .../key/eddsa_legacy_secret_material.dart | 2 +- lib/src/packet/key/eddsa_public_material.dart | 2 +- lib/src/packet/key/eddsa_secret_material.dart | 2 +- .../packet/key/elgamal_public_material.dart | 2 +- .../packet/key/elgamal_secret_material.dart | 2 +- .../key/montgomery_public_material.dart | 2 +- .../key/montgomery_secret_material.dart | 2 +- lib/src/packet/key/rsa_public_material.dart | 2 +- lib/src/packet/key/rsa_secret_material.dart | 2 +- lib/src/packet/public_key.dart | 26 ++++++++++++------- lib/src/type/key_material.dart | 4 +-- 18 files changed, 42 insertions(+), 26 deletions(-) diff --git a/lib/src/enum/eddsa_curve.dart b/lib/src/enum/eddsa_curve.dart index 08fed881..59edef5d 100644 --- a/lib/src/enum/eddsa_curve.dart +++ b/lib/src/enum/eddsa_curve.dart @@ -17,6 +17,11 @@ enum EdDSACurve { ed448 => 57, }; + int get keyStrength => switch (this) { + ed25519 => 255, + ed448 => 448, + }; + HashAlgorithm get hashAlgorithm => switch (this) { ed25519 => HashAlgorithm.sha256, ed448 => HashAlgorithm.sha512, diff --git a/lib/src/enum/montgomery_curve.dart b/lib/src/enum/montgomery_curve.dart index 28833c2b..ccaf9f2d 100644 --- a/lib/src/enum/montgomery_curve.dart +++ b/lib/src/enum/montgomery_curve.dart @@ -22,6 +22,11 @@ enum MontgomeryCurve { x448 => 56, }; + int get keyStrength => switch (this) { + x25519 => 255, + x448 => 448, + }; + HashAlgorithm get hkdfHash => switch (this) { x25519 => HashAlgorithm.sha256, x448 => HashAlgorithm.sha512, diff --git a/lib/src/packet/key/dsa_public_material.dart b/lib/src/packet/key/dsa_public_material.dart index 102c377d..e2126987 100644 --- a/lib/src/packet/key/dsa_public_material.dart +++ b/lib/src/packet/key/dsa_public_material.dart @@ -63,7 +63,7 @@ class DSAPublicMaterial implements VerificationKeyMaterial { } @override - int get keyLength => prime.bitLength; + int get keyStrength => prime.bitLength; @override Uint8List get toBytes => Uint8List.fromList([ diff --git a/lib/src/packet/key/dsa_secret_material.dart b/lib/src/packet/key/dsa_secret_material.dart index 9c4c6c7d..743381ad 100644 --- a/lib/src/packet/key/dsa_secret_material.dart +++ b/lib/src/packet/key/dsa_secret_material.dart @@ -40,7 +40,7 @@ class DSASecretMaterial implements SigningKeyMaterialInterface { DSASecretMaterial(Helper.readMPI(bytes), publicMaterial); @override - int get keyLength => publicMaterial.keyLength; + int get keyStrength => publicMaterial.keyStrength; @override Uint8List sign(final Uint8List message, final HashAlgorithm hash) { diff --git a/lib/src/packet/key/ec_public_material.dart b/lib/src/packet/key/ec_public_material.dart index 7bb1d2f1..8a4285a9 100644 --- a/lib/src/packet/key/ec_public_material.dart +++ b/lib/src/packet/key/ec_public_material.dart @@ -29,7 +29,7 @@ abstract class ECPublicMaterial implements KeyMaterialInterface { ); @override - int get keyLength { + int get keyStrength { if (curve == Ecc.ed25519 || curve == Ecc.curve25519) { return 255; } else { diff --git a/lib/src/packet/key/ecdh_secret_material.dart b/lib/src/packet/key/ecdh_secret_material.dart index 9141b4dc..2c53b564 100644 --- a/lib/src/packet/key/ecdh_secret_material.dart +++ b/lib/src/packet/key/ecdh_secret_material.dart @@ -86,7 +86,7 @@ class ECDHSecretMaterial extends ECSecretMaterial implements SecretKeyMaterialIn } @override - int get keyLength => publicMaterial.keyLength; + int get keyStrength => publicMaterial.keyStrength; @override bool get isValid { diff --git a/lib/src/packet/key/ecdsa_secret_material.dart b/lib/src/packet/key/ecdsa_secret_material.dart index 73b6fd1d..9435e611 100644 --- a/lib/src/packet/key/ecdsa_secret_material.dart +++ b/lib/src/packet/key/ecdsa_secret_material.dart @@ -64,7 +64,7 @@ class ECDSASecretMaterial extends ECSecretMaterial implements SigningKeyMaterial } @override - int get keyLength => publicMaterial.keyLength; + int get keyStrength => publicMaterial.keyStrength; @override Uint8List sign(final Uint8List message, final HashAlgorithm hash) { diff --git a/lib/src/packet/key/eddsa_legacy_secret_material.dart b/lib/src/packet/key/eddsa_legacy_secret_material.dart index ccfd0a04..ac0a9bd0 100644 --- a/lib/src/packet/key/eddsa_legacy_secret_material.dart +++ b/lib/src/packet/key/eddsa_legacy_secret_material.dart @@ -59,7 +59,7 @@ class EdDSALegacySecretMaterial implements SigningKeyMaterialInterface { } @override - int get keyLength => publicMaterial.keyLength; + int get keyStrength => publicMaterial.keyStrength; @override Uint8List sign(Uint8List message, HashAlgorithm hash) { diff --git a/lib/src/packet/key/eddsa_public_material.dart b/lib/src/packet/key/eddsa_public_material.dart index 622cf31b..c5e309c0 100644 --- a/lib/src/packet/key/eddsa_public_material.dart +++ b/lib/src/packet/key/eddsa_public_material.dart @@ -36,7 +36,7 @@ class EdDSAPublicMaterial implements VerificationKeyMaterial { ); @override - int get keyLength => publicKey.toBigInt().bitLength; + int get keyStrength => curve.keyStrength; @override Uint8List get toBytes => publicKey; diff --git a/lib/src/packet/key/eddsa_secret_material.dart b/lib/src/packet/key/eddsa_secret_material.dart index 43232892..1f9e756c 100644 --- a/lib/src/packet/key/eddsa_secret_material.dart +++ b/lib/src/packet/key/eddsa_secret_material.dart @@ -71,7 +71,7 @@ class EdDSASecretMaterial implements SigningKeyMaterialInterface { } @override - int get keyLength => publicMaterial.keyLength; + int get keyStrength => publicMaterial.keyStrength; @override Uint8List sign( diff --git a/lib/src/packet/key/elgamal_public_material.dart b/lib/src/packet/key/elgamal_public_material.dart index 2f1f3a64..9fc8e6bf 100644 --- a/lib/src/packet/key/elgamal_public_material.dart +++ b/lib/src/packet/key/elgamal_public_material.dart @@ -40,7 +40,7 @@ class ElGamalPublicMaterial implements KeyMaterialInterface { } @override - int get keyLength => prime.bitLength; + int get keyStrength => prime.bitLength; @override Uint8List get toBytes => Uint8List.fromList([ diff --git a/lib/src/packet/key/elgamal_secret_material.dart b/lib/src/packet/key/elgamal_secret_material.dart index 772413d8..9f167245 100644 --- a/lib/src/packet/key/elgamal_secret_material.dart +++ b/lib/src/packet/key/elgamal_secret_material.dart @@ -38,7 +38,7 @@ class ElGamalSecretMaterial implements SecretKeyMaterialInterface { ); @override - int get keyLength => publicMaterial.keyLength; + int get keyStrength => publicMaterial.keyStrength; @override bool get isValid { diff --git a/lib/src/packet/key/montgomery_public_material.dart b/lib/src/packet/key/montgomery_public_material.dart index 97a84ab5..c8c2fa9c 100644 --- a/lib/src/packet/key/montgomery_public_material.dart +++ b/lib/src/packet/key/montgomery_public_material.dart @@ -29,7 +29,7 @@ class MontgomeryPublicMaterial implements KeyMaterialInterface { ); @override - int get keyLength => publicKey.toBigInt().bitLength; + int get keyStrength => publicKey.toBigInt().bitLength; @override Uint8List get toBytes => publicKey; diff --git a/lib/src/packet/key/montgomery_secret_material.dart b/lib/src/packet/key/montgomery_secret_material.dart index 8f1e3786..42fb1d84 100644 --- a/lib/src/packet/key/montgomery_secret_material.dart +++ b/lib/src/packet/key/montgomery_secret_material.dart @@ -51,7 +51,7 @@ class MontgomerySecretMaterial implements SecretKeyMaterialInterface { } @override - int get keyLength => publicMaterial.keyLength; + int get keyStrength => publicMaterial.keyStrength; @override Uint8List get toBytes => secretKey; diff --git a/lib/src/packet/key/rsa_public_material.dart b/lib/src/packet/key/rsa_public_material.dart index 82841805..a7bdb705 100644 --- a/lib/src/packet/key/rsa_public_material.dart +++ b/lib/src/packet/key/rsa_public_material.dart @@ -38,7 +38,7 @@ class RSAPublicMaterial implements VerificationKeyMaterial { } @override - int get keyLength => modulus.bitLength; + int get keyStrength => modulus.bitLength; @override Uint8List get toBytes => Uint8List.fromList([ diff --git a/lib/src/packet/key/rsa_secret_material.dart b/lib/src/packet/key/rsa_secret_material.dart index ed460c79..493828a5 100644 --- a/lib/src/packet/key/rsa_secret_material.dart +++ b/lib/src/packet/key/rsa_secret_material.dart @@ -126,7 +126,7 @@ class RSASecretMaterial implements SigningKeyMaterialInterface { } @override - int get keyLength => publicMaterial.keyLength; + int get keyStrength => publicMaterial.keyStrength; @override Uint8List sign(final Uint8List message, final HashAlgorithm hash) { diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index eb7e9e1a..819f34a6 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -81,12 +81,16 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { } @override - Uint8List get data => Uint8List.fromList([ - keyVersion, - ...creationTime.toBytes(), - keyAlgorithm.value, - ...keyMaterial.toBytes, - ]); + Uint8List get data { + final kmBytes = keyMaterial.toBytes; + return Uint8List.fromList([ + keyVersion, + ...creationTime.toBytes(), + keyAlgorithm.value, + ...isV6Key ? kmBytes.length.pack32() : [], + ...kmBytes, + ]); + } @override Uint8List get fingerprint => _fingerprint; @@ -104,17 +108,19 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { Uint8List get keyID => _keyID; @override - int get keyStrength => keyMaterial.keyLength; + int get keyStrength => keyMaterial.keyStrength; @override Uint8List get signBytes => Uint8List.fromList([ - 0x99, - ...data.length.pack16(), + 0x95 + keyVersion, + ...isV6Key ? data.length.pack32() : data.length.pack16(), ...data, ]); + bool get isV6Key => keyVersion == KeyVersion.v6.value; + _calculateFingerprintAndKeyID() { - if (keyVersion == KeyVersion.v6.value) { + if (isV6Key) { _fingerprint = Uint8List.fromList( Helper.hashDigest(signBytes, HashAlgorithm.sha256), ); diff --git a/lib/src/type/key_material.dart b/lib/src/type/key_material.dart index 304cf9f7..77bcccbe 100644 --- a/lib/src/type/key_material.dart +++ b/lib/src/type/key_material.dart @@ -9,8 +9,8 @@ import 'dart:typed_data'; /// Key material interface /// Author Nguyen Van Nguyen abstract class KeyMaterialInterface { - /// Get key length - int get keyLength; + /// Get key strength + int get keyStrength; /// Serialize key material to bytes Uint8List get toBytes; From e218746bbd21224fe80fb15199a4bc25531e57f7 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 22 Nov 2024 14:20:28 +0700 Subject: [PATCH 041/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/public_key.dart | 5 +++ test/packet/key_packet_test.dart | 71 +++++++++++++++++++++----------- 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index 819f34a6..6e6b93fd 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -69,6 +69,11 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { ); pos++; + if (version == KeyVersion.v6.value) { + /// A four-octet scalar octet count for the following key material. + pos += 4; + } + return PublicKeyPacket( version, creation, diff --git a/test/packet/key_packet_test.dart b/test/packet/key_packet_test.dart index 82db2d3f..e612c38f 100644 --- a/test/packet/key_packet_test.dart +++ b/test/packet/key_packet_test.dart @@ -10,7 +10,7 @@ import 'package:test/test.dart'; void main() { group('Public key', () { test('RSA keys', () { - const publicKeyPacket = ''' + const keyPacket = ''' BGc/4FEBCACZXe5kmfKWChtAGdoYRxRkEcfm0Ne431ovB4QS3zIOoclfyA7jgyq1UAhKbsYhZmTK xx0FO5/HKOO2ojJU3AeKbLP+xhH8yVdEK5JiySvX/16Tz27d+r5WGLkjZMYZAxWW9hdfurajYp8v cdACvErKMXShXUR9npCKH5O0QBagxMg6vczfJfNG1qWJ9aeFO7yffUt9n+39ru3Cc67EAp7m29Wo @@ -19,7 +19,7 @@ DEZzvY+Q7gr7/qiWCpSfmy3gvmReOvMzk8uI7+Nt6vdJia4vABEBAAE= '''; final publicKey = PublicKeyPacket.fromBytes( base64.decode( - publicKeyPacket.replaceAll( + keyPacket.replaceAll( RegExp(r'\r?\n', multiLine: true), '', ), @@ -33,7 +33,7 @@ DEZzvY+Q7gr7/qiWCpSfmy3gvmReOvMzk8uI7+Nt6vdJia4vABEBAAE= expect(publicKey.keyVersion, 4); expect(publicKey.keyStrength, 2048); - const publicSubkeyPacket = ''' + const subkeyPacket = ''' BGc/4FEBCACUrspJcjpxuAKKF/2gPWJogkTuk+Ya+BUDjgPI3Cph8APkFfVct+NNwSW1xD6Bg/lG SH0hedXUTDVyCIXd1PI8tWEjB41aRu2hVJ908UAo7KVmST3zNSRClNJPN0ff7KV67K6K3PcWi2Cz V58NZSBK2kZqQpzQRCFHq8NYo6NQnJ7yhYxH3dfW7F0hVwMzcgw8SitNRW0vNlJDJQdAbveHh8w3 @@ -42,7 +42,7 @@ R6Cx4/cEJ5qXuHTUESblPwkkPlW3wv2tDdRqQZKyGLOIKe0LABEBAAE= '''; final publicSubkey = PublicSubkeyPacket.fromBytes( base64.decode( - publicSubkeyPacket.replaceAll( + subkeyPacket.replaceAll( RegExp(r'\r?\n', multiLine: true), '', ), @@ -59,7 +59,7 @@ R6Cx4/cEJ5qXuHTUESblPwkkPlW3wv2tDdRqQZKyGLOIKe0LABEBAAE= }); test('DSA & ElGamal keys', () { - const publicKeyPacket = ''' + const keyPacket = ''' BGc/4H8RCAD3gDG7FSfKWRy4/gNe0sgYYgZ4zAFZTLWneObcLYpY+kmwufUIrAG0iWt8kzLbiLon A1B7ss041yCqJgTHFcttH5nFG/q25MT+xq+V55TRZGzaD6Gy65E++CuS0m3gJmLH14bX6++5Evtc /bkOTGgIfRGfEH8pL1ePc9ev1EFtmOYgZkX0a7oM8TiNKVNnUKwhuBi7pbC6OEOuDL1HmbsAHIE4 @@ -78,7 +78,7 @@ LYR9lty0LinZ0v6+5HtwDA== '''; final publicKey = PublicKeyPacket.fromBytes( base64.decode( - publicKeyPacket.replaceAll( + keyPacket.replaceAll( RegExp(r'\r?\n', multiLine: true), '', ), @@ -92,7 +92,7 @@ LYR9lty0LinZ0v6+5HtwDA== expect(publicKey.keyVersion, 4); expect(publicKey.keyStrength, 2048); - const publicSubkeyPacket = ''' + const subkeyPacket = ''' BGc/4H8QCACv2nUuptC3DfhXtkI3H825wX+OwZzwOP8MG/JNUut/RoxFDXnld1S/GVz/dDBXs+cM QY4M9Fe2KfsGbNVeOId8eBENIFE7DBH6Qigh8WkBUueV6qPeFkWVj7u72TwRPjR3A6uAwLv/ZrMZ JWqD52tDr2YDv8e5LlS8uzVmTYo9nU8ohn3n6KyKNarEhpuXHqo3SHt4H7601Nr3TqdRU5ZWW1F6 @@ -106,7 +106,7 @@ llGb0KUH6SqD65H0 '''; final publicSubkey = PublicSubkeyPacket.fromBytes( base64.decode( - publicSubkeyPacket.replaceAll( + subkeyPacket.replaceAll( RegExp(r'\r?\n', multiLine: true), '', ), @@ -123,13 +123,13 @@ llGb0KUH6SqD65H0 }); test('NIST P-384 keys', () { - const publicKeyPacket = ''' + const keyPacket = ''' BGc/+YkTBSuBBAAiAwMEd3FJXIrlQksBiwOLB+ksANWsTQzFNxqpxzjpglaDNKjgF/RXVXj9A6os ao0WJtqkKFKZvkASvOCgOaBn3GDUZmF8GJwuuLNOci0JC/N+g5nFd/BeXNJNyECMIbllcLt9 '''; final publicKey = PublicKeyPacket.fromBytes( base64.decode( - publicKeyPacket.replaceAll( + keyPacket.replaceAll( RegExp(r'\r?\n', multiLine: true), '', ), @@ -145,14 +145,14 @@ ao0WJtqkKFKZvkASvOCgOaBn3GDUZmF8GJwuuLNOci0JC/N+g5nFd/BeXNJNyECMIbllcLt9 expect(publicKey.keyStrength, 384); expect(keyMaterial.curve, Ecc.secp384r1); - const publicSubkeyPacket = ''' + const subkeyPacket = ''' BGc/+YkSBSuBBAAiAwME6+LPwcbdvoEdxPA2L002pKXp8Nt7PxIsjUegQRw2bTQXMlu5zplsUNpr WJZ1W/iqLhmEH8eLONxcuzVEQMb3dCImxBzmL3Y5HxGGti81EsU7JBIZxHKhl85RY78HdHgCAwEJ CQ== '''; final publicSubkey = PublicSubkeyPacket.fromBytes( base64.decode( - publicSubkeyPacket.replaceAll( + subkeyPacket.replaceAll( RegExp(r'\r?\n', multiLine: true), '', ), @@ -171,13 +171,13 @@ CQ== }); test('Brainpool P-256 keys', () { - const publicKeyPacket = ''' + const keyPacket = ''' BGc/+dATCSskAwMCCAEBBwIDBJ15ari0PCq317awmdNNIwz+yOZ18yUCg8LOAmYEaRAqAh1HmAnS K5d4i1CX2M2/UKup7f/KD/o5Y6oid+VuTZQ= '''; final publicKey = PublicKeyPacket.fromBytes( base64.decode( - publicKeyPacket.replaceAll( + keyPacket.replaceAll( RegExp(r'\r?\n', multiLine: true), '', ), @@ -193,13 +193,13 @@ K5d4i1CX2M2/UKup7f/KD/o5Y6oid+VuTZQ= expect(publicKey.keyStrength, 256); expect(keyMaterial.curve, Ecc.brainpoolP256r1); - const publicSubkeyPacket = ''' + const subkeyPacket = ''' BGc/+dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGSVO0lI/BV 2JJsuQqI0Pne08Y7og4bhyv9S+D+wcU8sv0DAQgH '''; final publicSubkey = PublicSubkeyPacket.fromBytes( base64.decode( - publicSubkeyPacket.replaceAll( + subkeyPacket.replaceAll( RegExp(r'\r?\n', multiLine: true), '', ), @@ -218,9 +218,9 @@ BGc/+dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGSVO0lI/BV }); test('Curve 25519 legacy keys', () { - const publicKeyPacket = 'BGc/+gYWCSsGAQQB2kcPAQEHQGUAHTxwcB6TD72goFQpLf3jAqdbm1cZwv1N2mjBffEg'; + const keyPacket = 'BGc/+gYWCSsGAQQB2kcPAQEHQGUAHTxwcB6TD72goFQpLf3jAqdbm1cZwv1N2mjBffEg'; final publicKey = PublicKeyPacket.fromBytes( - base64.decode(publicKeyPacket), + base64.decode(keyPacket), ); final keyMaterial = publicKey.keyMaterial as ECPublicMaterial; expect( @@ -232,9 +232,9 @@ BGc/+dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGSVO0lI/BV expect(publicKey.keyStrength, 255); expect(keyMaterial.curve, Ecc.ed25519); - const publicSubkeyPacket = 'BGc/+gYSCisGAQQBl1UBBQEBB0AJwaWfEiJqlOIk/O9i2PMaDBmbRUOBHGQdFRPKbifpfwMBCAc='; + const subkeyPacket = 'BGc/+gYSCisGAQQBl1UBBQEBB0AJwaWfEiJqlOIk/O9i2PMaDBmbRUOBHGQdFRPKbifpfwMBCAc='; final publicSubkey = PublicSubkeyPacket.fromBytes( - base64.decode(publicSubkeyPacket), + base64.decode(subkeyPacket), ); final subkeyMaterial = publicSubkey.keyMaterial as ECPublicMaterial; expect( @@ -248,7 +248,32 @@ BGc/+dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGSVO0lI/BV expect(subkeyMaterial.curve, Ecc.curve25519); }); - test('Curve 25519 keys', () {}); + test('Curve 25519 keys', () { + const keyPacket = 'BmOHf+MbAAAAIPlNp7tI1gph5WdwamWH0DMZmbudiRoIJC6thFQ9+JWj'; + final publicKey = PublicKeyPacket.fromBytes( + base64.decode(keyPacket), + ); + expect( + publicKey.fingerprint.toHexadecimal(), + 'cb186c4f0609a697e4d52dfa6c722b0c1f1e27c18a56708f6525ec27bad9acc9', + ); + expect(publicKey.keyAlgorithm, KeyAlgorithm.ed25519); + expect(publicKey.keyVersion, 6); + expect(publicKey.keyStrength, 255); + + const subkeyPacket = 'BmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1'; + final publicSubkey = PublicSubkeyPacket.fromBytes( + base64.decode(subkeyPacket), + ); + expect( + publicSubkey.fingerprint.toHexadecimal(), + '12c83f1e706f6308fe151a417743a1f033790e93e9978488d1db378da9930885', + ); + expect(publicSubkey.keyAlgorithm, KeyAlgorithm.x25519); + expect(publicSubkey.keyVersion, 6); + expect(publicSubkey.keyStrength, 255); + expect(publicSubkey.isSubkey, isTrue); + }); test('Curve 448 keys', () {}); }); @@ -554,8 +579,8 @@ gWiiTYzGt55CPH+6z9IdUnM/22Y2nOVfXg== expect(subkeyMaterial.curve, Ecc.curve25519); }); - test('Curve25519 keys', () {}); + test('Curve 25519 keys', () {}); - test('Curve448 keys', () {}); + test('Curve 448 keys', () {}); }); } From ff558704fa10ef7a588f7c3ece5cbcf3ecb42cc4 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 22 Nov 2024 15:19:57 +0700 Subject: [PATCH 042/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/eddsa_secret_material.dart | 23 +++++---- lib/src/packet/secret_key.dart | 4 +- test/packet/key_packet_test.dart | 48 ++++++++++++++++++- 3 files changed, 63 insertions(+), 12 deletions(-) diff --git a/lib/src/packet/key/eddsa_secret_material.dart b/lib/src/packet/key/eddsa_secret_material.dart index 1f9e756c..47fe8eda 100644 --- a/lib/src/packet/key/eddsa_secret_material.dart +++ b/lib/src/packet/key/eddsa_secret_material.dart @@ -39,9 +39,9 @@ class EdDSASecretMaterial implements SigningKeyMaterialInterface { factory EdDSASecretMaterial.generate(final EdDSACurve curve) { final secretKey = Helper.secureRandom().nextBytes(curve.payloadSize); final publicKey = switch (curve) { - EdDSACurve.ed25519 => nacl.SigningKey.fromValidBytes( + EdDSACurve.ed25519 => nacl.PrivateKey( secretKey, - ).verifyKey.asTypedList, + ).publicKey.asTypedList, EdDSACurve.ed448 => EdPrivateKey.fromBytes( secretKey, TwistedEdwardCurve.ed448(), @@ -59,9 +59,9 @@ class EdDSASecretMaterial implements SigningKeyMaterialInterface { @override bool get isValid { final publicKey = switch (publicMaterial.curve) { - EdDSACurve.ed25519 => nacl.SigningKey.fromValidBytes( + EdDSACurve.ed25519 => nacl.PrivateKey( secretKey, - ).verifyKey.asTypedList, + ).publicKey.asTypedList, EdDSACurve.ed448 => EdPrivateKey.fromBytes( secretKey, TwistedEdwardCurve.ed448(), @@ -79,11 +79,16 @@ class EdDSASecretMaterial implements SigningKeyMaterialInterface { final HashAlgorithm hash, ) => switch (publicMaterial.curve) { - EdDSACurve.ed25519 => nacl.SigningKey.fromValidBytes(secretKey) - .sign( - Helper.hashDigest(message, hash), - ) - .asTypedList, + EdDSACurve.ed25519 => nacl.SigningKey.fromValidBytes( + Uint8List.fromList([ + ...secretKey, + ...publicMaterial.publicKey, + ]), + ) + .sign( + Helper.hashDigest(message, hash), + ) + .asTypedList, EdDSACurve.ed448 => EdPrivateKey.fromBytes( secretKey, TwistedEdwardCurve.ed448(), diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index a15137ea..bdfad8d8 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -128,8 +128,8 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { Uint8List? iv; if (aead != null) { - iv = bytes.sublist(pos, pos + aead.blockLength); - pos += aead.blockLength; + iv = bytes.sublist(pos, pos + aead.ivLength); + pos += aead.ivLength; } else if (!(s2k != null && s2k.type == S2kType.gnu) && s2kUsage != S2kUsage.none) { iv = bytes.sublist(pos, pos + symmetric.blockSize); pos += symmetric.blockSize; diff --git a/test/packet/key_packet_test.dart b/test/packet/key_packet_test.dart index e612c38f..3cd95e04 100644 --- a/test/packet/key_packet_test.dart +++ b/test/packet/key_packet_test.dart @@ -579,7 +579,53 @@ gWiiTYzGt55CPH+6z9IdUnM/22Y2nOVfXg== expect(subkeyMaterial.curve, Ecc.curve25519); }); - test('Curve 25519 keys', () {}); + test('Curve 25519 keys', () { + final passphrase = 'correct horse battery staple'; + final keyPacket = ''' +BmOHf+MbAAAAIPlNp7tI1gph5WdwamWH0DMZmbudiRoIJC6thFQ9+JWj/SYJAhQEXW/XHJ4JbR62 +kXtubh7srgEEFbSoqSdPq+Yy+HWnBlkgIXglj6SE2Isn8iDj0t4CA8oPH+7La3dTgePi2bFIXCIz +jKVR4JomPyLrSZLpZ3qAWA== +'''; + final secretKey = SecretKeyPacket.fromBytes( + base64.decode( + keyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ).decrypt(passphrase); + expect( + secretKey.fingerprint.toHexadecimal(), + 'cb186c4f0609a697e4d52dfa6c722b0c1f1e27c18a56708f6525ec27bad9acc9', + ); + expect(secretKey.keyAlgorithm, KeyAlgorithm.ed25519); + expect(secretKey.keyVersion, 6); + expect(secretKey.keyStrength, 255); + expect(secretKey.isDecrypted, isTrue); + + final subkeyPacket = ''' +BmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1/SYJAhQEDmGEaCnahpq+ +DqYVRdwUzAEEFS4Typ/05yT7HC6x34YCCUGvktXKv+W6nfHFC8dcVKOMDaFpd+g3rFQZF0MQcjr6 +568qNVG/mgDGC7t4mlpc2A== +'''; + final secretSubkey = SecretSubkeyPacket.fromBytes( + base64.decode( + subkeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ).decrypt(passphrase); + expect( + secretSubkey.fingerprint.toHexadecimal(), + '12c83f1e706f6308fe151a417743a1f033790e93e9978488d1db378da9930885', + ); + expect(secretSubkey.keyAlgorithm, KeyAlgorithm.x25519); + expect(secretSubkey.keyVersion, 6); + expect(secretSubkey.keyStrength, 255); + expect(secretSubkey.isDecrypted, isTrue); + expect(secretSubkey.isSubkey, isTrue); + }); test('Curve 448 keys', () {}); }); From ea313020e3112a8c090aa266bacda92e898fb4d2 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 22 Nov 2024 16:03:15 +0700 Subject: [PATCH 043/202] WIP Signed-off-by: Nguyen Van Nguyen --- .../key/montgomery_public_material.dart | 3 +-- test/packet/key_packet_test.dart | 27 ++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/src/packet/key/montgomery_public_material.dart b/lib/src/packet/key/montgomery_public_material.dart index c8c2fa9c..34941f4a 100644 --- a/lib/src/packet/key/montgomery_public_material.dart +++ b/lib/src/packet/key/montgomery_public_material.dart @@ -7,7 +7,6 @@ library; import 'dart:typed_data'; import '../../type/key_material.dart'; -import '../../common/extensions.dart'; import '../../enum/montgomery_curve.dart'; /// Montgomery public key material @@ -29,7 +28,7 @@ class MontgomeryPublicMaterial implements KeyMaterialInterface { ); @override - int get keyStrength => publicKey.toBigInt().bitLength; + int get keyStrength => curve.keyStrength; @override Uint8List get toBytes => publicKey; diff --git a/test/packet/key_packet_test.dart b/test/packet/key_packet_test.dart index 3cd95e04..f38ab5b9 100644 --- a/test/packet/key_packet_test.dart +++ b/test/packet/key_packet_test.dart @@ -275,7 +275,32 @@ BGc/+dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGSVO0lI/BV expect(publicSubkey.isSubkey, isTrue); }); - test('Curve 448 keys', () {}); + test('Curve 448 keys', () { + const keyPacket = 'BmbzbxMcAAAAOclr6WO01hRcPLq6+/O0G+HA8hfV+fWyej4w7y9j6Im19Y5lOhbn99B0mZ0i6ggJLxcf/wPqwG3hAA=='; + final publicKey = PublicKeyPacket.fromBytes( + base64.decode(keyPacket), + ); + expect( + publicKey.fingerprint.toHexadecimal(), + '3005ff8cc9384ac345005882c5419e988efdfcee6646b0ce4f627fa61b23dcf1', + ); + expect(publicKey.keyAlgorithm, KeyAlgorithm.ed448); + expect(publicKey.keyVersion, 6); + expect(publicKey.keyStrength, 448); + + const subkeyPacket = 'BmbzbxMaAAAAOJa5nnjGcHsLaqmdVTiX+7/V12+ROn+wufLdRd1egnJCCGBhvN7XDPd50Em1ZtAbYgbCsR+C8zgZ'; + final publicSubkey = PublicSubkeyPacket.fromBytes( + base64.decode(subkeyPacket), + ); + expect( + publicSubkey.fingerprint.toHexadecimal(), + '6572e897cc45e44e3c707b88ac5a754b4e3372d47d122f22dbaaee139ac82c89', + ); + expect(publicSubkey.keyAlgorithm, KeyAlgorithm.x448); + expect(publicSubkey.keyVersion, 6); + expect(publicSubkey.keyStrength, 448); + expect(publicSubkey.isSubkey, isTrue); + }); }); group('Secret key', () { From 345a41df26ccc88882c21e17e5449e48490716df Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 22 Nov 2024 17:28:30 +0700 Subject: [PATCH 044/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/eddsa_secret_material.dart | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/lib/src/packet/key/eddsa_secret_material.dart b/lib/src/packet/key/eddsa_secret_material.dart index 47fe8eda..59b6f417 100644 --- a/lib/src/packet/key/eddsa_secret_material.dart +++ b/lib/src/packet/key/eddsa_secret_material.dart @@ -39,9 +39,9 @@ class EdDSASecretMaterial implements SigningKeyMaterialInterface { factory EdDSASecretMaterial.generate(final EdDSACurve curve) { final secretKey = Helper.secureRandom().nextBytes(curve.payloadSize); final publicKey = switch (curve) { - EdDSACurve.ed25519 => nacl.PrivateKey( + EdDSACurve.ed25519 => nacl.SigningKey.fromSeed( secretKey, - ).publicKey.asTypedList, + ).verifyKey.asTypedList, EdDSACurve.ed448 => EdPrivateKey.fromBytes( secretKey, TwistedEdwardCurve.ed448(), @@ -59,9 +59,9 @@ class EdDSASecretMaterial implements SigningKeyMaterialInterface { @override bool get isValid { final publicKey = switch (publicMaterial.curve) { - EdDSACurve.ed25519 => nacl.PrivateKey( + EdDSACurve.ed25519 => nacl.SigningKey.fromSeed( secretKey, - ).publicKey.asTypedList, + ).verifyKey.asTypedList, EdDSACurve.ed448 => EdPrivateKey.fromBytes( secretKey, TwistedEdwardCurve.ed448(), @@ -79,16 +79,11 @@ class EdDSASecretMaterial implements SigningKeyMaterialInterface { final HashAlgorithm hash, ) => switch (publicMaterial.curve) { - EdDSACurve.ed25519 => nacl.SigningKey.fromValidBytes( - Uint8List.fromList([ - ...secretKey, - ...publicMaterial.publicKey, - ]), - ) - .sign( - Helper.hashDigest(message, hash), - ) - .asTypedList, + EdDSACurve.ed25519 => nacl.SigningKey.fromSeed(secretKey) + .sign( + Helper.hashDigest(message, hash), + ) + .asTypedList, EdDSACurve.ed448 => EdPrivateKey.fromBytes( secretKey, TwistedEdwardCurve.ed448(), From 19519e8ccefe38300161dfaf347a0e7ebe45c922 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sat, 23 Nov 2024 08:48:44 +0700 Subject: [PATCH 045/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/public_key.dart | 28 ++++++-- lib/src/packet/public_subkey.dart | 10 +-- lib/src/packet/secret_key.dart | 105 ++++++++++++++++++------------ lib/src/packet/secret_subkey.dart | 17 +++-- 4 files changed, 104 insertions(+), 56 deletions(-) diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index 6e6b93fd..1e113b54 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -54,6 +54,21 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { } factory PublicKeyPacket.fromBytes(final Uint8List bytes) { + final keyRecord = parseBytes(bytes); + return PublicKeyPacket( + keyRecord.keyVersion, + keyRecord.creationTime, + keyRecord.keyMaterial, + keyAlgorithm: keyRecord.keyAlgorithm, + ); + } + + static ({ + int keyVersion, + DateTime creationTime, + KeyAlgorithm keyAlgorithm, + KeyMaterialInterface keyMaterial, + }) parseBytes(final Uint8List bytes) { var pos = 0; /// /// A one-octet version number (4 or 6). @@ -73,15 +88,14 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { /// A four-octet scalar octet count for the following key material. pos += 4; } - - return PublicKeyPacket( - version, - creation, - _readKeyMaterial( + return ( + keyVersion: version, + creationTime: creation, + keyAlgorithm: algorithm, + keyMaterial: _readKeyMaterial( bytes.sublist(pos), algorithm, - ), - keyAlgorithm: algorithm, + ) ); } diff --git a/lib/src/packet/public_subkey.dart b/lib/src/packet/public_subkey.dart index 0772f023..7dcd838d 100644 --- a/lib/src/packet/public_subkey.dart +++ b/lib/src/packet/public_subkey.dart @@ -24,12 +24,12 @@ class PublicSubkeyPacket extends PublicKeyPacket implements SubkeyPacketInterfac }); factory PublicSubkeyPacket.fromBytes(final Uint8List bytes) { - final publicKey = PublicKeyPacket.fromBytes(bytes); + final keyRecord = PublicKeyPacket.parseBytes(bytes); return PublicSubkeyPacket( - publicKey.keyVersion, - publicKey.creationTime, - publicKey.keyMaterial, - keyAlgorithm: publicKey.keyAlgorithm, + keyRecord.keyVersion, + keyRecord.creationTime, + keyRecord.keyMaterial, + keyAlgorithm: keyRecord.keyAlgorithm, ); } } diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index bdfad8d8..845a8519 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -68,6 +68,65 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { factory SecretKeyPacket.fromBytes(final Uint8List bytes) { final publicKey = PublicKeyPacket.fromBytes(bytes); + final keyRecord = parseBytes(bytes, publicKey); + return SecretKeyPacket( + publicKey, + keyRecord.keyData, + s2kUsage: keyRecord.s2kUsage, + symmetric: keyRecord.symmetric, + aead: keyRecord.aead, + s2k: keyRecord.s2k, + iv: keyRecord.iv, + secretKeyMaterial: keyRecord.keyMaterial, + ); + } + + /// Generate secret key packet + factory SecretKeyPacket.generate( + final KeyAlgorithm algorithm, { + final RSAKeySize rsaKeySize = RSAKeySize.normal, + final Ecc curve = Ecc.secp521r1, + final DateTime? date, + }) { + final keyMaterial = switch (algorithm) { + KeyAlgorithm.rsaEncryptSign || + KeyAlgorithm.rsaSign || + KeyAlgorithm.rsaEncrypt => + RSASecretMaterial.generate(rsaKeySize), + KeyAlgorithm.ecdsa => ECDSASecretMaterial.generate(curve), + KeyAlgorithm.ecdh => ECDHSecretMaterial.generate(curve), + KeyAlgorithm.eddsaLegacy => EdDSALegacySecretMaterial.generate(), + KeyAlgorithm.x25519 => MontgomerySecretMaterial.generate(MontgomeryCurve.x25519), + KeyAlgorithm.x448 => MontgomerySecretMaterial.generate(MontgomeryCurve.x448), + KeyAlgorithm.ed25519 => EdDSASecretMaterial.generate(EdDSACurve.ed25519), + KeyAlgorithm.ed448 => EdDSASecretMaterial.generate(EdDSACurve.ed448), + _ => throw UnsupportedError("Key algorithm ${algorithm.name} is unsupported."), + }; + + return SecretKeyPacket( + PublicKeyPacket( + algorithm.keyVersion, + date ?? DateTime.now(), + keyMaterial.publicMaterial, + keyAlgorithm: algorithm, + ), + keyMaterial.toBytes, + secretKeyMaterial: keyMaterial, + ); + } + + static ({ + Uint8List keyData, + S2kUsage s2kUsage, + SymmetricAlgorithm symmetric, + AeadAlgorithm? aead, + S2kInterface? s2k, + Uint8List? iv, + SecretKeyMaterialInterface? keyMaterial, + }) parseBytes( + final Uint8List bytes, + final PublicKeyPacket publicKey, + ) { final isV6 = publicKey.keyVersion == KeyVersion.v6.value; var pos = publicKey.data.length; @@ -135,7 +194,7 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { pos += symmetric.blockSize; } - SecretKeyMaterialInterface? secretKeyMaterial; + SecretKeyMaterialInterface? keyMaterial; var keyData = bytes.sublist(pos); if (s2kUsage == S2kUsage.none) { final checksum = keyData.sublist(keyData.length - 2); @@ -143,54 +202,20 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { if (!checksum.equals(_computeChecksum(keyData))) { throw StateError('Key checksum mismatch!'); } - secretKeyMaterial = _readKeyMaterial( + keyMaterial = _readKeyMaterial( keyData, publicKey, ); } - return SecretKeyPacket( - publicKey, - keyData, + + return ( + keyData: keyData, s2kUsage: s2kUsage, symmetric: symmetric, aead: aead, s2k: s2k, iv: iv, - secretKeyMaterial: secretKeyMaterial, - ); - } - - /// Generate secret key packet - factory SecretKeyPacket.generate( - final KeyAlgorithm algorithm, { - final RSAKeySize rsaKeySize = RSAKeySize.normal, - final Ecc curve = Ecc.secp521r1, - final DateTime? date, - }) { - final keyMaterial = switch (algorithm) { - KeyAlgorithm.rsaEncryptSign || - KeyAlgorithm.rsaSign || - KeyAlgorithm.rsaEncrypt => - RSASecretMaterial.generate(rsaKeySize), - KeyAlgorithm.ecdsa => ECDSASecretMaterial.generate(curve), - KeyAlgorithm.ecdh => ECDHSecretMaterial.generate(curve), - KeyAlgorithm.eddsaLegacy => EdDSALegacySecretMaterial.generate(), - KeyAlgorithm.x25519 => MontgomerySecretMaterial.generate(MontgomeryCurve.x25519), - KeyAlgorithm.x448 => MontgomerySecretMaterial.generate(MontgomeryCurve.x448), - KeyAlgorithm.ed25519 => EdDSASecretMaterial.generate(EdDSACurve.ed25519), - KeyAlgorithm.ed448 => EdDSASecretMaterial.generate(EdDSACurve.ed448), - _ => throw UnsupportedError("Key algorithm ${algorithm.name} is unsupported."), - }; - - return SecretKeyPacket( - PublicKeyPacket( - algorithm.keyVersion, - date ?? DateTime.now(), - keyMaterial.publicMaterial, - keyAlgorithm: algorithm, - ), - keyMaterial.toBytes, - secretKeyMaterial: keyMaterial, + keyMaterial: keyMaterial, ); } diff --git a/lib/src/packet/secret_subkey.dart b/lib/src/packet/secret_subkey.dart index 3a9a1f01..9eec6bd0 100644 --- a/lib/src/packet/secret_subkey.dart +++ b/lib/src/packet/secret_subkey.dart @@ -35,10 +35,19 @@ class SecretSubkeyPacket extends SecretKeyPacket implements SubkeyPacketInterfac factory SecretSubkeyPacket.fromBytes( final Uint8List bytes, - ) => - _fromSecretKey( - SecretKeyPacket.fromBytes(bytes), - ); + ) { + final publicKey = PublicSubkeyPacket.fromBytes(bytes); + final keyRecord = SecretKeyPacket.parseBytes(bytes, publicKey); + return SecretSubkeyPacket( + publicKey, + keyRecord.keyData, + s2kUsage: keyRecord.s2kUsage, + symmetric: keyRecord.symmetric, + s2k: keyRecord.s2k, + iv: keyRecord.iv, + secretKeyMaterial: keyRecord.keyMaterial, + ); + } /// Generate secret subkey packet factory SecretSubkeyPacket.generate( From 19c9f947dada53e1cdb995b981c3be0be8759482 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sat, 23 Nov 2024 15:41:01 +0700 Subject: [PATCH 046/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/secret_subkey.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/packet/secret_subkey.dart b/lib/src/packet/secret_subkey.dart index 9eec6bd0..6751a28a 100644 --- a/lib/src/packet/secret_subkey.dart +++ b/lib/src/packet/secret_subkey.dart @@ -38,11 +38,13 @@ class SecretSubkeyPacket extends SecretKeyPacket implements SubkeyPacketInterfac ) { final publicKey = PublicSubkeyPacket.fromBytes(bytes); final keyRecord = SecretKeyPacket.parseBytes(bytes, publicKey); + return SecretSubkeyPacket( publicKey, keyRecord.keyData, s2kUsage: keyRecord.s2kUsage, symmetric: keyRecord.symmetric, + aead: keyRecord.aead, s2k: keyRecord.s2k, iv: keyRecord.iv, secretKeyMaterial: keyRecord.keyMaterial, From 2ec6c7e89b9cf4caa995345214a2cfc54bbd4fe6 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sat, 23 Nov 2024 15:53:32 +0700 Subject: [PATCH 047/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/key_packet_test.dart | 50 +++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/test/packet/key_packet_test.dart b/test/packet/key_packet_test.dart index f38ab5b9..f66113c4 100644 --- a/test/packet/key_packet_test.dart +++ b/test/packet/key_packet_test.dart @@ -652,6 +652,54 @@ DqYVRdwUzAEEFS4Typ/05yT7HC6x34YCCUGvktXKv+W6nfHFC8dcVKOMDaFpd+g3rFQZF0MQcjr6 expect(secretSubkey.isSubkey, isTrue); }); - test('Curve 448 keys', () {}); + test('Curve 448 keys', () { + final passphrase = 'Ax@2bGh;SxD&"A_;El%mPIvLx_!#3Aik'; + final keyPacket = ''' +BmbzftccAAAAOR3+OzyG7CwqCJopfjLl1hr1L8xJb4yIqt3ya0SH/pNXF63tthhChdNUdqtTGppE +g6KH7Dz3jXlFAP4dBwsDCE8kppqto8604GLcRc8hcyhrUDWxU2V3E15psak+FI3xvkubObRWMjxu ++NUhznzac9dBRg+VM2lUWpsZzvqK9qFCWXMUuqikfPeaz0CJkFaEHcvMWHxGZFRNk5fK119DPGF7 +MopiKg== +'''; + final secretKey = SecretKeyPacket.fromBytes( + base64.decode( + keyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ).decrypt(passphrase); + expect( + secretKey.fingerprint.toHexadecimal(), + 'a36695f7d9053d62296e0df63a0d48ba27730d9c3d4f71b82a58c8a7c90fea20', + ); + expect(secretKey.keyAlgorithm, KeyAlgorithm.ed448); + expect(secretKey.keyVersion, 6); + expect(secretKey.keyStrength, 448); + expect(secretKey.isDecrypted, isTrue); + + final subkeyPacket = ''' +BmbzftcaAAAAOLaHi0hNT/oyrJGS0PUH0zidqtP9+p8OinKC8yZaLrffq7v2mUHJ2wMUxzOqT6Jf +TZlbH1pOB2K4/h0HCwMIG24D5RnoS/rgmiZyvFWJPrkY7xGZAtjFCZvsCDnsBvofCLBksUofLmuv +qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt +8S8= +'''; + final secretSubkey = SecretSubkeyPacket.fromBytes( + base64.decode( + subkeyPacket.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ).decrypt(passphrase); + expect( + secretSubkey.fingerprint.toHexadecimal(), + '0464ebb813cd5587314d29b48b116a09251e2383928aed4165670b861046fcb8', + ); + expect(secretSubkey.keyAlgorithm, KeyAlgorithm.x448); + expect(secretSubkey.keyVersion, 6); + expect(secretSubkey.keyStrength, 448); + expect(secretSubkey.isDecrypted, isTrue); + expect(secretSubkey.isSubkey, isTrue); + }); }); } From 8d47d4a687bce4e03f12e575fda1e7798a5c3896 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sat, 23 Nov 2024 16:16:51 +0700 Subject: [PATCH 048/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/secret_key.dart | 226 +++++++++++++++++------------- lib/src/packet/secret_subkey.dart | 71 +++++----- 2 files changed, 162 insertions(+), 135 deletions(-) diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 845a8519..00ca30d3 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -88,7 +88,32 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { final Ecc curve = Ecc.secp521r1, final DateTime? date, }) { - final keyMaterial = switch (algorithm) { + final keyMaterial = generateKeyMaterial( + algorithm, + rsaKeySize: rsaKeySize, + curve: curve, + date: date, + ); + + return SecretKeyPacket( + PublicKeyPacket( + algorithm.keyVersion, + date ?? DateTime.now(), + keyMaterial.publicMaterial, + keyAlgorithm: algorithm, + ), + keyMaterial.toBytes, + secretKeyMaterial: keyMaterial, + ); + } + + static SecretKeyMaterialInterface generateKeyMaterial( + final KeyAlgorithm algorithm, { + final RSAKeySize rsaKeySize = RSAKeySize.normal, + final Ecc curve = Ecc.secp521r1, + final DateTime? date, + }) { + return switch (algorithm) { KeyAlgorithm.rsaEncryptSign || KeyAlgorithm.rsaSign || KeyAlgorithm.rsaEncrypt => @@ -102,17 +127,6 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { KeyAlgorithm.ed448 => EdDSASecretMaterial.generate(EdDSACurve.ed448), _ => throw UnsupportedError("Key algorithm ${algorithm.name} is unsupported."), }; - - return SecretKeyPacket( - PublicKeyPacket( - algorithm.keyVersion, - date ?? DateTime.now(), - keyMaterial.publicMaterial, - keyAlgorithm: algorithm, - ), - keyMaterial.toBytes, - secretKeyMaterial: keyMaterial, - ); } static ({ @@ -226,51 +240,9 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { final AeadAlgorithm? aead, ) { if (secretKeyMaterial != null) { - if (passphrase.isEmpty) { - throw ArgumentError('passphrase are required for key encryption'); - } - assert(s2kUsage != S2kUsage.none); - Helper.assertSymmetric(symmetric); - final aeadProtect = aead != null; - if (aeadProtect && keyVersion != KeyVersion.v6.value) { - throw ArgumentError('Using AEAD with version $keyVersion of the key packet is not allowed.'); - } - final s2k = aeadProtect ? Helper.stringToKey(S2kType.argon2) : Helper.stringToKey(S2kType.iterated); - final random = Helper.secureRandom(); - final iv = random.nextBytes(symmetric.blockSize); - final kek = _produceEncryptionKey( - passphrase, - symmetric, - type, - s2k: s2k, - aead: aead, - ); - final clearText = secretKeyMaterial!.toBytes; - final Uint8List cipherText; - if (aeadProtect) { - final cipher = aead.cipherEngine(kek, symmetric); - cipherText = cipher.encrypt( - clearText, - iv, - Uint8List.fromList([ - type.value | 0xc0, - ...publicKey.data, - ])); - } else { - final cipher = BufferedCipher(symmetric.cfbCipherEngine) - ..init( - true, - ParametersWithIV(KeyParameter(kek), iv), - ); - - cipherText = cipher.process(Uint8List.fromList([ - ...clearText, - ...Helper.hashDigest(clearText, HashAlgorithm.sha1), - ])); - } return SecretKeyPacket( publicKey, - cipherText, + encryptKeyMaterial(passphrase, symmetric, aead), s2kUsage: s2kUsage, symmetric: symmetric, aead: aead, @@ -286,50 +258,6 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { @override decrypt(final String passphrase) { if (secretKeyMaterial == null) { - final Uint8List clearText; - if (isEncrypted) { - final kek = _produceEncryptionKey( - passphrase, - symmetric, - type, - s2k: s2k, - aead: aead, - ); - if (aead != null) { - final cipher = aead!.cipherEngine(kek, symmetric); - clearText = cipher.decrypt( - keyData, - iv!, - Uint8List.fromList([ - type.value | 0xc0, - ...publicKey.data, - ])); - } else { - final cipher = BufferedCipher(symmetric.cfbCipherEngine) - ..init( - false, - ParametersWithIV( - KeyParameter(kek), - iv ?? Uint8List(symmetric.blockSize), - ), - ); - - final clearTextWithHash = cipher.process(keyData); - clearText = clearTextWithHash.sublist( - 0, - clearTextWithHash.length - HashAlgorithm.sha1.digestSize, - ); - final hashText = clearTextWithHash.sublist( - clearTextWithHash.length - HashAlgorithm.sha1.digestSize, - ); - final hashed = Helper.hashDigest(clearText, HashAlgorithm.sha1); - if (!hashed.equals(hashText)) { - throw ArgumentError('Incorrect key passphrase'); - } - } - } else { - clearText = keyData; - } return SecretKeyPacket( publicKey, keyData, @@ -338,13 +266,111 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { aead: aead, s2k: s2k, iv: iv, - secretKeyMaterial: _readKeyMaterial(clearText, publicKey), + secretKeyMaterial: decryptKeyData(passphrase), ); } else { return this; } } + Uint8List encryptKeyMaterial( + final String passphrase, + final SymmetricAlgorithm symmetric, + final AeadAlgorithm? aead, + ) { + if (passphrase.isEmpty) { + throw ArgumentError('passphrase are required for key encryption'); + } + assert(s2kUsage != S2kUsage.none); + Helper.assertSymmetric(symmetric); + final aeadProtect = aead != null; + if (aeadProtect && keyVersion != KeyVersion.v6.value) { + throw ArgumentError('Using AEAD with version $keyVersion of the key packet is not allowed.'); + } + final s2k = aeadProtect ? Helper.stringToKey(S2kType.argon2) : Helper.stringToKey(S2kType.iterated); + final random = Helper.secureRandom(); + final iv = random.nextBytes(symmetric.blockSize); + final kek = _produceEncryptionKey( + passphrase, + symmetric, + type, + s2k: s2k, + aead: aead, + ); + final clearText = secretKeyMaterial!.toBytes; + final Uint8List cipherText; + if (aeadProtect) { + final cipher = aead.cipherEngine(kek, symmetric); + cipherText = cipher.encrypt( + clearText, + iv, + Uint8List.fromList([ + type.value | 0xc0, + ...publicKey.data, + ])); + } else { + final cipher = BufferedCipher(symmetric.cfbCipherEngine) + ..init( + true, + ParametersWithIV(KeyParameter(kek), iv), + ); + + cipherText = cipher.process(Uint8List.fromList([ + ...clearText, + ...Helper.hashDigest(clearText, HashAlgorithm.sha1), + ])); + } + return cipherText; + } + + SecretKeyMaterialInterface decryptKeyData(final String passphrase) { + final Uint8List clearText; + if (isEncrypted) { + final kek = _produceEncryptionKey( + passphrase, + symmetric, + type, + s2k: s2k, + aead: aead, + ); + if (aead != null) { + final cipher = aead!.cipherEngine(kek, symmetric); + clearText = cipher.decrypt( + keyData, + iv!, + Uint8List.fromList([ + type.value | 0xc0, + ...publicKey.data, + ])); + } else { + final cipher = BufferedCipher(symmetric.cfbCipherEngine) + ..init( + false, + ParametersWithIV( + KeyParameter(kek), + iv ?? Uint8List(symmetric.blockSize), + ), + ); + + final clearTextWithHash = cipher.process(keyData); + clearText = clearTextWithHash.sublist( + 0, + clearTextWithHash.length - HashAlgorithm.sha1.digestSize, + ); + final hashText = clearTextWithHash.sublist( + clearTextWithHash.length - HashAlgorithm.sha1.digestSize, + ); + final hashed = Helper.hashDigest(clearText, HashAlgorithm.sha1); + if (!hashed.equals(hashText)) { + throw ArgumentError('Incorrect key passphrase'); + } + } + } else { + clearText = keyData; + } + return _readKeyMaterial(clearText, publicKey); + } + @override Uint8List get data { final isV6 = publicKey.keyVersion == KeyVersion.v6.value; diff --git a/lib/src/packet/secret_subkey.dart b/lib/src/packet/secret_subkey.dart index 6751a28a..d746b99e 100644 --- a/lib/src/packet/secret_subkey.dart +++ b/lib/src/packet/secret_subkey.dart @@ -57,15 +57,24 @@ class SecretSubkeyPacket extends SecretKeyPacket implements SubkeyPacketInterfac final RSAKeySize rsaKeySize = RSAKeySize.normal, final Ecc curve = Ecc.secp521r1, final DateTime? date, - }) => - _fromSecretKey( - SecretKeyPacket.generate( - algorithm, - rsaKeySize: rsaKeySize, - curve: curve, - date: date, - ), - ); + }) { + final keyMaterial = SecretKeyPacket.generateKeyMaterial( + algorithm, + rsaKeySize: rsaKeySize, + curve: curve, + date: date, + ); + return SecretSubkeyPacket( + PublicSubkeyPacket( + algorithm.keyVersion, + date ?? DateTime.now(), + keyMaterial.publicMaterial, + keyAlgorithm: algorithm, + ), + keyMaterial.toBytes, + secretKeyMaterial: keyMaterial, + ); + } @override encrypt( @@ -74,11 +83,15 @@ class SecretSubkeyPacket extends SecretKeyPacket implements SubkeyPacketInterfac final AeadAlgorithm? aead, ) { if (secretKeyMaterial != null) { - return _fromSecretKey(super.encrypt( - passphrase, - symmetric, - aead, - ) as SecretKeyPacket); + return SecretSubkeyPacket( + publicKey as PublicSubkeyPacket, + encryptKeyMaterial(passphrase, symmetric, aead), + s2kUsage: s2kUsage, + symmetric: symmetric, + s2k: s2k, + iv: iv, + secretKeyMaterial: secretKeyMaterial, + ); } else { return this; } @@ -87,29 +100,17 @@ class SecretSubkeyPacket extends SecretKeyPacket implements SubkeyPacketInterfac @override decrypt(final String passphrase) { if (secretKeyMaterial == null) { - return _fromSecretKey(super.decrypt(passphrase) as SecretKeyPacket); + return SecretSubkeyPacket( + publicKey as PublicSubkeyPacket, + keyData, + s2kUsage: s2kUsage, + symmetric: symmetric, + s2k: s2k, + iv: iv, + secretKeyMaterial: decryptKeyData(passphrase), + ); } else { return this; } } - - static SecretSubkeyPacket _fromSecretKey( - final SecretKeyPacket secretKey, - ) { - final publicKey = secretKey.publicKey; - return SecretSubkeyPacket( - PublicSubkeyPacket( - publicKey.keyVersion, - publicKey.creationTime, - publicKey.keyMaterial, - keyAlgorithm: publicKey.keyAlgorithm, - ), - secretKey.keyData, - s2kUsage: secretKey.s2kUsage, - symmetric: secretKey.symmetric, - s2k: secretKey.s2k, - iv: secretKey.iv, - secretKeyMaterial: secretKey.secretKeyMaterial, - ); - } } From 7b9d13a76c7eef29171d79057ce5da8880d3e876 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sat, 23 Nov 2024 16:41:32 +0700 Subject: [PATCH 049/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/secret_key.dart | 25 +++++++++++++++++-------- lib/src/packet/secret_subkey.dart | 15 +++++++++------ lib/src/type/secret_key_packet.dart | 4 ++-- test/packet/key_packet_test.dart | 6 ++++-- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 00ca30d3..93d158ac 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -236,18 +236,19 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { @override encrypt( final String passphrase, - final SymmetricAlgorithm symmetric, + final SymmetricAlgorithm symmetric, [ final AeadAlgorithm? aead, - ) { + ]) { if (secretKeyMaterial != null) { + final record = encryptKeyMaterial(passphrase, symmetric, aead); return SecretKeyPacket( publicKey, - encryptKeyMaterial(passphrase, symmetric, aead), - s2kUsage: s2kUsage, + record.cipherText, + s2kUsage: aead != null ? S2kUsage.aeadProtect : S2kUsage.cfb, symmetric: symmetric, aead: aead, - s2k: s2k, - iv: iv, + s2k: record.s2k, + iv: record.iv, secretKeyMaterial: secretKeyMaterial, ); } else { @@ -273,7 +274,11 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { } } - Uint8List encryptKeyMaterial( + ({ + Uint8List cipherText, + Uint8List iv, + S2kInterface s2k, + }) encryptKeyMaterial( final String passphrase, final SymmetricAlgorithm symmetric, final AeadAlgorithm? aead, @@ -320,7 +325,11 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { ...Helper.hashDigest(clearText, HashAlgorithm.sha1), ])); } - return cipherText; + return ( + cipherText: cipherText, + iv: iv, + s2k: s2k, + ); } SecretKeyMaterialInterface decryptKeyData(final String passphrase) { diff --git a/lib/src/packet/secret_subkey.dart b/lib/src/packet/secret_subkey.dart index d746b99e..efc59cf4 100644 --- a/lib/src/packet/secret_subkey.dart +++ b/lib/src/packet/secret_subkey.dart @@ -11,6 +11,7 @@ import '../enum/ecc.dart'; import '../enum/key_algorithm.dart'; import '../enum/packet_type.dart'; import '../enum/rsa_key_size.dart'; +import '../enum/s2k_usage.dart'; import '../enum/symmetric_algorithm.dart'; import '../type/subkey_packet.dart'; import 'public_subkey.dart'; @@ -79,17 +80,19 @@ class SecretSubkeyPacket extends SecretKeyPacket implements SubkeyPacketInterfac @override encrypt( final String passphrase, - final SymmetricAlgorithm symmetric, + final SymmetricAlgorithm symmetric, [ final AeadAlgorithm? aead, - ) { + ]) { if (secretKeyMaterial != null) { + final record = encryptKeyMaterial(passphrase, symmetric, aead); return SecretSubkeyPacket( publicKey as PublicSubkeyPacket, - encryptKeyMaterial(passphrase, symmetric, aead), - s2kUsage: s2kUsage, + record.cipherText, + s2kUsage: aead != null ? S2kUsage.aeadProtect : S2kUsage.cfb, symmetric: symmetric, - s2k: s2k, - iv: iv, + aead: aead, + s2k: record.s2k, + iv: record.iv, secretKeyMaterial: secretKeyMaterial, ); } else { diff --git a/lib/src/type/secret_key_packet.dart b/lib/src/type/secret_key_packet.dart index d75c8bb3..c8d39253 100644 --- a/lib/src/type/secret_key_packet.dart +++ b/lib/src/type/secret_key_packet.dart @@ -33,9 +33,9 @@ abstract class SecretKeyPacketInterface extends KeyPacketInterface { SecretKeyPacketInterface encrypt( String passphrase, - SymmetricAlgorithm symmetric, + SymmetricAlgorithm symmetric, [ AeadAlgorithm? aead, - ); + ]); SecretKeyPacketInterface decrypt(String passphrase); } diff --git a/test/packet/key_packet_test.dart b/test/packet/key_packet_test.dart index f66113c4..c4fd7bda 100644 --- a/test/packet/key_packet_test.dart +++ b/test/packet/key_packet_test.dart @@ -8,7 +8,7 @@ import 'package:dart_pg/src/packet/key/public_material.dart'; import 'package:test/test.dart'; void main() { - group('Public key', () { + group('Read public key', () { test('RSA keys', () { const keyPacket = ''' BGc/4FEBCACZXe5kmfKWChtAGdoYRxRkEcfm0Ne431ovB4QS3zIOoclfyA7jgyq1UAhKbsYhZmTK @@ -303,7 +303,7 @@ BGc/+dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGSVO0lI/BV }); }); - group('Secret key', () { + group('Decrypt secret key', () { const passphrase = 'password'; test('RSA keys', () { @@ -702,4 +702,6 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt expect(secretSubkey.isSubkey, isTrue); }); }); + + group('Generate secret key', () {}); } From e2d47df41a6437ec036711715b5a9a93738a41bd Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sat, 23 Nov 2024 16:53:50 +0700 Subject: [PATCH 050/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/secret_key.dart | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 93d158ac..33760e0e 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -280,19 +280,24 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { S2kInterface s2k, }) encryptKeyMaterial( final String passphrase, - final SymmetricAlgorithm symmetric, + final SymmetricAlgorithm symmetric, [ final AeadAlgorithm? aead, - ) { + ]) { if (passphrase.isEmpty) { throw ArgumentError('passphrase are required for key encryption'); } - assert(s2kUsage != S2kUsage.none); Helper.assertSymmetric(symmetric); final aeadProtect = aead != null; if (aeadProtect && keyVersion != KeyVersion.v6.value) { throw ArgumentError('Using AEAD with version $keyVersion of the key packet is not allowed.'); } - final s2k = aeadProtect ? Helper.stringToKey(S2kType.argon2) : Helper.stringToKey(S2kType.iterated); + final s2k = aeadProtect || Config.useV6Key + ? Helper.stringToKey( + S2kType.argon2, + ) + : Helper.stringToKey( + S2kType.iterated, + ); final random = Helper.secureRandom(); final iv = random.nextBytes(symmetric.blockSize); final kek = _produceEncryptionKey( From b58f997cd935231c48fdb662b5c5e26d2101cd33 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sat, 23 Nov 2024 17:34:25 +0700 Subject: [PATCH 051/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/secret_key.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 33760e0e..ed796802 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -288,10 +288,11 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { } Helper.assertSymmetric(symmetric); final aeadProtect = aead != null; - if (aeadProtect && keyVersion != KeyVersion.v6.value) { + final isV6 = keyVersion != KeyVersion.v6.value; + if (aeadProtect && !isV6) { throw ArgumentError('Using AEAD with version $keyVersion of the key packet is not allowed.'); } - final s2k = aeadProtect || Config.useV6Key + final s2k = aeadProtect || isV6 ? Helper.stringToKey( S2kType.argon2, ) From 13c151035cb641bc7ff50f4a12b2b8d0b7f28179 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sat, 23 Nov 2024 18:53:10 +0700 Subject: [PATCH 052/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/secret_key.dart | 18 +- test/packet/key_packet_test.dart | 293 ++++++++++++++++++++++++++++++- 2 files changed, 301 insertions(+), 10 deletions(-) diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index ed796802..e28a6f50 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -43,7 +43,7 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { final S2kUsage s2kUsage; - final SymmetricAlgorithm symmetric; + final SymmetricAlgorithm? symmetric; @override final AeadAlgorithm? aead; @@ -58,8 +58,8 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { SecretKeyPacket( this.publicKey, this.keyData, { - this.s2kUsage = S2kUsage.cfb, - this.symmetric = SymmetricAlgorithm.aes128, + this.s2kUsage = S2kUsage.none, + this.symmetric, this.aead, this.s2k, this.iv, @@ -343,13 +343,13 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { if (isEncrypted) { final kek = _produceEncryptionKey( passphrase, - symmetric, + symmetric!, type, s2k: s2k, aead: aead, ); if (aead != null) { - final cipher = aead!.cipherEngine(kek, symmetric); + final cipher = aead!.cipherEngine(kek, symmetric!); clearText = cipher.decrypt( keyData, iv!, @@ -358,12 +358,12 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { ...publicKey.data, ])); } else { - final cipher = BufferedCipher(symmetric.cfbCipherEngine) + final cipher = BufferedCipher(symmetric!.cfbCipherEngine) ..init( false, ParametersWithIV( KeyParameter(kek), - iv ?? Uint8List(symmetric.blockSize), + iv ?? Uint8List(symmetric!.blockSize), ), ); @@ -391,7 +391,7 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { final isV6 = publicKey.keyVersion == KeyVersion.v6.value; if (isEncrypted) { final optBytes = Uint8List.fromList([ - symmetric.value, + symmetric!.value, ...aead != null ? [aead!.value] : [], ...isV6 ? [s2k!.length] : [], ...s2k!.toBytes, @@ -427,7 +427,7 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { bool get isDecrypted => secretKeyMaterial != null; @override - bool get isEncrypted => s2kUsage != S2kUsage.none; + bool get isEncrypted => s2kUsage != S2kUsage.none && symmetric != null; @override bool get isEncryptionKey => publicKey.isEncryptionKey; diff --git a/test/packet/key_packet_test.dart b/test/packet/key_packet_test.dart index c4fd7bda..afcebe89 100644 --- a/test/packet/key_packet_test.dart +++ b/test/packet/key_packet_test.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/enum/ecc.dart'; import 'package:dart_pg/src/enum/key_algorithm.dart'; +import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; import 'package:dart_pg/src/packet/base.dart'; import 'package:dart_pg/src/packet/key/public_material.dart'; import 'package:test/test.dart'; @@ -703,5 +704,295 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt }); }); - group('Generate secret key', () {}); + group('Generate secret key', () { + const passphrase = 'password'; + + test('RSA keys', () { + final secretKey = SecretKeyPacket.generate( + KeyAlgorithm.rsaEncryptSign, + ); + expect(secretKey.isEncrypted, isFalse); + expect(secretKey.keyStrength, 2048); + expect(secretKey.keyVersion, 4); + + final encryptedSecretKey = secretKey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretKey.isEncrypted, isTrue); + + final decryptedSecretKey = SecretKeyPacket.fromBytes( + encryptedSecretKey.data, + ).decrypt(passphrase); + expect(secretKey.fingerprint, decryptedSecretKey.fingerprint); + + final secretSubkey = SecretSubkeyPacket.generate( + KeyAlgorithm.rsaEncryptSign, + ); + expect(secretSubkey.isEncrypted, isFalse); + expect(secretSubkey.keyStrength, 2048); + expect(secretSubkey.keyVersion, 4); + + final encryptedSecretSubkey = secretSubkey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretSubkey.isEncrypted, isTrue); + + final decryptedSecretSubkey = SecretSubkeyPacket.fromBytes( + encryptedSecretSubkey.data, + ).decrypt(passphrase); + expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); + }); + + test('ECDSA NIST P-384 keys', () { + final secretKey = SecretKeyPacket.generate( + KeyAlgorithm.ecdsa, + curve: Ecc.secp384r1, + ); + expect(secretKey.keyMaterial is ECDSAPublicMaterial, isTrue); + expect(secretKey.isEncrypted, isFalse); + expect(secretKey.keyStrength, 384); + expect(secretKey.keyVersion, 4); + + final encryptedSecretKey = secretKey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretKey.isEncrypted, isTrue); + + final decryptedSecretKey = SecretKeyPacket.fromBytes( + encryptedSecretKey.data, + ).decrypt(passphrase); + expect(secretKey.fingerprint, decryptedSecretKey.fingerprint); + + final secretSubkey = SecretSubkeyPacket.generate( + KeyAlgorithm.ecdsa, + curve: Ecc.secp384r1, + ); + expect(secretSubkey.keyMaterial is ECDSAPublicMaterial, isTrue); + expect(secretSubkey.isEncrypted, isFalse); + expect(secretSubkey.keyStrength, 384); + expect(secretSubkey.keyVersion, 4); + + final encryptedSecretSubkey = secretSubkey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretSubkey.isEncrypted, isTrue); + + final decryptedSecretSubkey = SecretSubkeyPacket.fromBytes( + encryptedSecretSubkey.data, + ).decrypt(passphrase); + expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); + }); + + test('ECDSA Brainpool P-512 keys', () { + final secretKey = SecretKeyPacket.generate( + KeyAlgorithm.ecdsa, + curve: Ecc.brainpoolP512r1, + ); + expect(secretKey.keyMaterial is ECDSAPublicMaterial, isTrue); + expect(secretKey.isEncrypted, isFalse); + expect(secretKey.keyStrength, 512); + expect(secretKey.keyVersion, 4); + + final encryptedSecretKey = secretKey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretKey.isEncrypted, isTrue); + + final decryptedSecretKey = SecretKeyPacket.fromBytes( + encryptedSecretKey.data, + ).decrypt(passphrase); + expect(secretKey.fingerprint, decryptedSecretKey.fingerprint); + + final secretSubkey = SecretSubkeyPacket.generate( + KeyAlgorithm.ecdsa, + curve: Ecc.brainpoolP512r1, + ); + expect(secretSubkey.keyMaterial is ECDSAPublicMaterial, isTrue); + expect(secretSubkey.isEncrypted, isFalse); + expect(secretSubkey.keyStrength, 512); + expect(secretSubkey.keyVersion, 4); + + final encryptedSecretSubkey = secretSubkey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretSubkey.isEncrypted, isTrue); + + final decryptedSecretSubkey = SecretSubkeyPacket.fromBytes( + encryptedSecretSubkey.data, + ).decrypt(passphrase); + expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); + }); + + test('EdDSA legacy keys', () { + final secretKey = SecretKeyPacket.generate( + KeyAlgorithm.eddsaLegacy, + ); + expect(secretKey.keyMaterial is EdDSALegacyPublicMaterial, isTrue); + expect(secretKey.isEncrypted, isFalse); + expect(secretKey.keyStrength, 255); + expect(secretKey.keyVersion, 4); + + final encryptedSecretKey = secretKey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretKey.isEncrypted, isTrue); + + final decryptedSecretKey = SecretKeyPacket.fromBytes( + encryptedSecretKey.data, + ).decrypt(passphrase); + expect(secretKey.fingerprint, decryptedSecretKey.fingerprint); + + final secretSubkey = SecretSubkeyPacket.generate( + KeyAlgorithm.eddsaLegacy, + ); + expect(secretSubkey.keyMaterial is EdDSALegacyPublicMaterial, isTrue); + expect(secretSubkey.isEncrypted, isFalse); + expect(secretSubkey.keyStrength, 255); + expect(secretSubkey.keyVersion, 4); + + final encryptedSecretSubkey = secretSubkey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretSubkey.isEncrypted, isTrue); + + final decryptedSecretSubkey = SecretSubkeyPacket.fromBytes( + encryptedSecretSubkey.data, + ).decrypt(passphrase); + expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); + }); + + test('ECDH NIST P-384 keys', () { + final secretKey = SecretKeyPacket.generate( + KeyAlgorithm.ecdh, + curve: Ecc.secp384r1, + ); + expect(secretKey.keyMaterial is ECDHPublicMaterial, isTrue); + expect(secretKey.isEncrypted, isFalse); + expect(secretKey.keyStrength, 384); + expect(secretKey.keyVersion, 4); + + final encryptedSecretKey = secretKey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretKey.isEncrypted, isTrue); + + final decryptedSecretKey = SecretKeyPacket.fromBytes( + encryptedSecretKey.data, + ).decrypt(passphrase); + expect(secretKey.fingerprint, decryptedSecretKey.fingerprint); + + final secretSubkey = SecretSubkeyPacket.generate( + KeyAlgorithm.ecdh, + curve: Ecc.secp384r1, + ); + expect(secretSubkey.keyMaterial is ECDHPublicMaterial, isTrue); + expect(secretSubkey.isEncrypted, isFalse); + expect(secretSubkey.keyStrength, 384); + expect(secretSubkey.keyVersion, 4); + + final encryptedSecretSubkey = secretSubkey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretSubkey.isEncrypted, isTrue); + + final decryptedSecretSubkey = SecretSubkeyPacket.fromBytes( + encryptedSecretSubkey.data, + ).decrypt(passphrase); + expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); + }); + + test('ECDH Brainpool P-512 keys', () { + final secretKey = SecretKeyPacket.generate( + KeyAlgorithm.ecdh, + curve: Ecc.brainpoolP512r1, + ); + expect(secretKey.keyMaterial is ECDHPublicMaterial, isTrue); + expect(secretKey.isEncrypted, isFalse); + expect(secretKey.keyStrength, 512); + expect(secretKey.keyVersion, 4); + + final encryptedSecretKey = secretKey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretKey.isEncrypted, isTrue); + + final decryptedSecretKey = SecretKeyPacket.fromBytes( + encryptedSecretKey.data, + ).decrypt(passphrase); + expect(secretKey.fingerprint, decryptedSecretKey.fingerprint); + + final secretSubkey = SecretSubkeyPacket.generate( + KeyAlgorithm.ecdh, + curve: Ecc.brainpoolP512r1, + ); + expect(secretSubkey.keyMaterial is ECDHPublicMaterial, isTrue); + expect(secretSubkey.isEncrypted, isFalse); + expect(secretSubkey.keyStrength, 512); + expect(secretSubkey.keyVersion, 4); + + final encryptedSecretSubkey = secretSubkey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretSubkey.isEncrypted, isTrue); + + final decryptedSecretSubkey = SecretSubkeyPacket.fromBytes( + encryptedSecretSubkey.data, + ).decrypt(passphrase); + expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); + }); + + test('ECDH Curve 25519 keys', () { + final secretKey = SecretKeyPacket.generate( + KeyAlgorithm.ecdh, + curve: Ecc.curve25519, + ); + expect(secretKey.keyMaterial is ECDHPublicMaterial, isTrue); + expect(secretKey.isEncrypted, isFalse); + expect(secretKey.keyStrength, 255); + expect(secretKey.keyVersion, 4); + + final encryptedSecretKey = secretKey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretKey.isEncrypted, isTrue); + + final decryptedSecretKey = SecretKeyPacket.fromBytes( + encryptedSecretKey.data, + ).decrypt(passphrase); + expect(secretKey.fingerprint, decryptedSecretKey.fingerprint); + + final secretSubkey = SecretSubkeyPacket.generate( + KeyAlgorithm.ecdh, + curve: Ecc.curve25519, + ); + expect(secretSubkey.keyMaterial is ECDHPublicMaterial, isTrue); + expect(secretSubkey.isEncrypted, isFalse); + expect(secretSubkey.keyStrength, 255); + expect(secretSubkey.keyVersion, 4); + + final encryptedSecretSubkey = secretSubkey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretSubkey.isEncrypted, isTrue); + + final decryptedSecretSubkey = SecretSubkeyPacket.fromBytes( + encryptedSecretSubkey.data, + ).decrypt(passphrase); + expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); + }); + }); } From ee9766b762a5f2014d1922fdd39eaba9fafc82ad Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 25 Nov 2024 08:33:13 +0700 Subject: [PATCH 053/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/helpers.dart | 2 +- lib/src/packet/secret_key.dart | 1 - lib/src/type/secret_key_packet.dart | 3 - test/packet/key_packet_test.dart | 160 ++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+), 5 deletions(-) diff --git a/lib/src/common/helpers.dart b/lib/src/common/helpers.dart index aa6d7491..333e3df9 100644 --- a/lib/src/common/helpers.dart +++ b/lib/src/common/helpers.dart @@ -85,7 +85,7 @@ final class Helper { _secureRandom.nextBytes(Argon2S2k.saltLength), ), _ => GenericS2k( - _secureRandom.nextBytes(Argon2S2k.saltLength), + _secureRandom.nextBytes(GenericS2k.saltLength), ), }; } diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index e28a6f50..43f4a30b 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -45,7 +45,6 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { final SymmetricAlgorithm? symmetric; - @override final AeadAlgorithm? aead; final S2kInterface? s2k; diff --git a/lib/src/type/secret_key_packet.dart b/lib/src/type/secret_key_packet.dart index c8d39253..cffc4b17 100644 --- a/lib/src/type/secret_key_packet.dart +++ b/lib/src/type/secret_key_packet.dart @@ -19,9 +19,6 @@ abstract class SecretKeyPacketInterface extends KeyPacketInterface { /// Get secret key material SecretKeyMaterialInterface? get secretKeyMaterial; - /// Get aead algorithm - AeadAlgorithm? get aead; - /// Secret key packed is encrypted bool get isEncrypted; diff --git a/test/packet/key_packet_test.dart b/test/packet/key_packet_test.dart index afcebe89..696935c1 100644 --- a/test/packet/key_packet_test.dart +++ b/test/packet/key_packet_test.dart @@ -994,5 +994,165 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt ).decrypt(passphrase); expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); }); + + test('X25519 keys', () { + final secretKey = SecretKeyPacket.generate( + KeyAlgorithm.x25519, + ); + expect(secretKey.keyMaterial is MontgomeryPublicMaterial, isTrue); + expect(secretKey.isEncrypted, isFalse); + expect(secretKey.keyStrength, 255); + expect(secretKey.keyVersion, 6); + + final encryptedSecretKey = secretKey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretKey.isEncrypted, isTrue); + + final decryptedSecretKey = SecretKeyPacket.fromBytes( + encryptedSecretKey.data, + ).decrypt(passphrase); + expect(secretKey.fingerprint, decryptedSecretKey.fingerprint); + + final secretSubkey = SecretSubkeyPacket.generate( + KeyAlgorithm.x25519, + ); + expect(secretSubkey.keyMaterial is MontgomeryPublicMaterial, isTrue); + expect(secretSubkey.isEncrypted, isFalse); + expect(secretSubkey.keyStrength, 255); + expect(secretSubkey.keyVersion, 6); + + final encryptedSecretSubkey = secretSubkey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretSubkey.isEncrypted, isTrue); + + final decryptedSecretSubkey = SecretSubkeyPacket.fromBytes( + encryptedSecretSubkey.data, + ).decrypt(passphrase); + expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); + }); + + test('X448 keys', () { + final secretKey = SecretKeyPacket.generate( + KeyAlgorithm.x448, + ); + expect(secretKey.keyMaterial is MontgomeryPublicMaterial, isTrue); + expect(secretKey.isEncrypted, isFalse); + expect(secretKey.keyStrength, 448); + expect(secretKey.keyVersion, 6); + + final encryptedSecretKey = secretKey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretKey.isEncrypted, isTrue); + + final decryptedSecretKey = SecretKeyPacket.fromBytes( + encryptedSecretKey.data, + ).decrypt(passphrase); + expect(secretKey.fingerprint, decryptedSecretKey.fingerprint); + + final secretSubkey = SecretSubkeyPacket.generate( + KeyAlgorithm.x448, + ); + expect(secretSubkey.keyMaterial is MontgomeryPublicMaterial, isTrue); + expect(secretSubkey.isEncrypted, isFalse); + expect(secretSubkey.keyStrength, 448); + expect(secretSubkey.keyVersion, 6); + + final encryptedSecretSubkey = secretSubkey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretSubkey.isEncrypted, isTrue); + + final decryptedSecretSubkey = SecretSubkeyPacket.fromBytes( + encryptedSecretSubkey.data, + ).decrypt(passphrase); + expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); + }); + + test('ed25519 keys', () { + final secretKey = SecretKeyPacket.generate( + KeyAlgorithm.ed25519, + ); + expect(secretKey.keyMaterial is EdDSAPublicMaterial, isTrue); + expect(secretKey.isEncrypted, isFalse); + expect(secretKey.keyStrength, 255); + expect(secretKey.keyVersion, 6); + + final encryptedSecretKey = secretKey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretKey.isEncrypted, isTrue); + + final decryptedSecretKey = SecretKeyPacket.fromBytes( + encryptedSecretKey.data, + ).decrypt(passphrase); + expect(secretKey.fingerprint, decryptedSecretKey.fingerprint); + + final secretSubkey = SecretSubkeyPacket.generate( + KeyAlgorithm.ed25519, + ); + expect(secretSubkey.keyMaterial is EdDSAPublicMaterial, isTrue); + expect(secretSubkey.isEncrypted, isFalse); + expect(secretSubkey.keyStrength, 255); + expect(secretSubkey.keyVersion, 6); + + final encryptedSecretSubkey = secretSubkey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretSubkey.isEncrypted, isTrue); + + final decryptedSecretSubkey = SecretSubkeyPacket.fromBytes( + encryptedSecretSubkey.data, + ).decrypt(passphrase); + expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); + }); + + test('ed448 keys', () { + final secretKey = SecretKeyPacket.generate( + KeyAlgorithm.ed448, + ); + expect(secretKey.keyMaterial is EdDSAPublicMaterial, isTrue); + expect(secretKey.isEncrypted, isFalse); + expect(secretKey.keyStrength, 448); + expect(secretKey.keyVersion, 6); + + final encryptedSecretKey = secretKey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretKey.isEncrypted, isTrue); + + final decryptedSecretKey = SecretKeyPacket.fromBytes( + encryptedSecretKey.data, + ).decrypt(passphrase); + expect(secretKey.fingerprint, decryptedSecretKey.fingerprint); + + final secretSubkey = SecretSubkeyPacket.generate( + KeyAlgorithm.ed448, + ); + expect(secretSubkey.keyMaterial is EdDSAPublicMaterial, isTrue); + expect(secretSubkey.isEncrypted, isFalse); + expect(secretSubkey.keyStrength, 448); + expect(secretSubkey.keyVersion, 6); + + final encryptedSecretSubkey = secretSubkey.encrypt( + passphrase, + SymmetricAlgorithm.aes128, + ); + expect(encryptedSecretSubkey.isEncrypted, isTrue); + + final decryptedSecretSubkey = SecretSubkeyPacket.fromBytes( + encryptedSecretSubkey.data, + ).decrypt(passphrase); + expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); + }); }); } From 335ebc234e2cd7bb251dbf09ebd2f098e46c25a0 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 25 Nov 2024 08:45:12 +0700 Subject: [PATCH 054/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/secret_key.dart | 14 +++++++++++--- test/packet/key_packet_test.dart | 5 +++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 43f4a30b..8c047948 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -287,9 +287,11 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { } Helper.assertSymmetric(symmetric); final aeadProtect = aead != null; - final isV6 = keyVersion != KeyVersion.v6.value; + final isV6 = keyVersion == KeyVersion.v6.value; if (aeadProtect && !isV6) { - throw ArgumentError('Using AEAD with version $keyVersion of the key packet is not allowed.'); + throw ArgumentError( + 'Using AEAD with version $keyVersion of the key packet is not allowed.', + ); } final s2k = aeadProtect || isV6 ? Helper.stringToKey( @@ -299,7 +301,13 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { S2kType.iterated, ); final random = Helper.secureRandom(); - final iv = random.nextBytes(symmetric.blockSize); + final iv = aeadProtect + ? random.nextBytes( + aead.ivLength, + ) + : random.nextBytes( + symmetric.blockSize, + ); final kek = _produceEncryptionKey( passphrase, symmetric, diff --git a/test/packet/key_packet_test.dart b/test/packet/key_packet_test.dart index 696935c1..adb64fae 100644 --- a/test/packet/key_packet_test.dart +++ b/test/packet/key_packet_test.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/aead_algorithm.dart'; import 'package:dart_pg/src/enum/ecc.dart'; import 'package:dart_pg/src/enum/key_algorithm.dart'; import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; @@ -1007,6 +1008,7 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt final encryptedSecretKey = secretKey.encrypt( passphrase, SymmetricAlgorithm.aes128, + AeadAlgorithm.gcm, ); expect(encryptedSecretKey.isEncrypted, isTrue); @@ -1026,6 +1028,7 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt final encryptedSecretSubkey = secretSubkey.encrypt( passphrase, SymmetricAlgorithm.aes128, + AeadAlgorithm.gcm, ); expect(encryptedSecretSubkey.isEncrypted, isTrue); @@ -1127,6 +1130,7 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt final encryptedSecretKey = secretKey.encrypt( passphrase, SymmetricAlgorithm.aes128, + AeadAlgorithm.gcm, ); expect(encryptedSecretKey.isEncrypted, isTrue); @@ -1146,6 +1150,7 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt final encryptedSecretSubkey = secretSubkey.encrypt( passphrase, SymmetricAlgorithm.aes128, + AeadAlgorithm.gcm, ); expect(encryptedSecretSubkey.isEncrypted, isTrue); From 5db0330732ee7449f0aed3b5284b1d0b8c756acb Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 25 Nov 2024 08:50:51 +0700 Subject: [PATCH 055/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/key_packet_test.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/packet/key_packet_test.dart b/test/packet/key_packet_test.dart index adb64fae..0e55145f 100644 --- a/test/packet/key_packet_test.dart +++ b/test/packet/key_packet_test.dart @@ -1008,7 +1008,6 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt final encryptedSecretKey = secretKey.encrypt( passphrase, SymmetricAlgorithm.aes128, - AeadAlgorithm.gcm, ); expect(encryptedSecretKey.isEncrypted, isTrue); @@ -1028,7 +1027,6 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt final encryptedSecretSubkey = secretSubkey.encrypt( passphrase, SymmetricAlgorithm.aes128, - AeadAlgorithm.gcm, ); expect(encryptedSecretSubkey.isEncrypted, isTrue); @@ -1050,6 +1048,7 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt final encryptedSecretKey = secretKey.encrypt( passphrase, SymmetricAlgorithm.aes128, + AeadAlgorithm.eax, ); expect(encryptedSecretKey.isEncrypted, isTrue); @@ -1069,6 +1068,7 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt final encryptedSecretSubkey = secretSubkey.encrypt( passphrase, SymmetricAlgorithm.aes128, + AeadAlgorithm.eax, ); expect(encryptedSecretSubkey.isEncrypted, isTrue); @@ -1090,6 +1090,7 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt final encryptedSecretKey = secretKey.encrypt( passphrase, SymmetricAlgorithm.aes128, + AeadAlgorithm.ocb, ); expect(encryptedSecretKey.isEncrypted, isTrue); @@ -1109,6 +1110,7 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt final encryptedSecretSubkey = secretSubkey.encrypt( passphrase, SymmetricAlgorithm.aes128, + AeadAlgorithm.ocb, ); expect(encryptedSecretSubkey.isEncrypted, isTrue); From 7b38ef61ef55dd1250240de3b06d8ee55dc24927 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 25 Nov 2024 10:49:08 +0700 Subject: [PATCH 056/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/aead_encrypted_data.dart | 2 +- lib/src/packet/compressed_data.dart | 2 +- lib/src/packet/literal_data.dart | 2 +- lib/src/packet/marker.dart | 2 +- lib/src/packet/one_pass_signature.dart | 2 +- lib/src/packet/padding.dart | 2 +- lib/src/packet/public_key.dart | 2 +- lib/src/packet/public_key_encrypted_session_key.dart | 2 +- lib/src/packet/public_subkey.dart | 2 +- lib/src/packet/secret_key.dart | 2 +- lib/src/packet/secret_subkey.dart | 2 +- lib/src/packet/signature.dart | 2 +- lib/src/packet/sym_encrypted_data.dart | 2 +- lib/src/packet/sym_encrypted_integrity_protected_data.dart | 2 +- lib/src/packet/sym_encrypted_session_key.dart | 2 +- lib/src/packet/trust.dart | 2 +- lib/src/packet/user_attribute.dart | 2 +- lib/src/packet/user_id.dart | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/src/packet/aead_encrypted_data.dart b/lib/src/packet/aead_encrypted_data.dart index 2a9f1d39..fa9aa747 100644 --- a/lib/src/packet/aead_encrypted_data.dart +++ b/lib/src/packet/aead_encrypted_data.dart @@ -15,7 +15,7 @@ import 'base.dart'; import 'packet_list.dart'; /// Implementation of the Symmetrically Encrypted Authenticated Encryption with -/// Additional Data (AEAD) Protected Data Packet(Tag 20) +/// Additional Data (AEAD) Protected Data Packet - Type 20 /// Author Nguyen Van Nguyen class AeadEncryptedDataPacket extends BasePacket implements EncryptedDataPacketInterface { static const version = 1; diff --git a/lib/src/packet/compressed_data.dart b/lib/src/packet/compressed_data.dart index 8e36836f..2f8808ad 100644 --- a/lib/src/packet/compressed_data.dart +++ b/lib/src/packet/compressed_data.dart @@ -11,7 +11,7 @@ import '../enum/compression_algorithm.dart'; import 'base.dart'; import 'packet_list.dart'; -/// Implementation of the Compressed Data Packet (Tag 8) +/// Implementation of the Compressed Data (COMP) Packet - Type 11 /// Author Nguyen Van Nguyen class CompressedDataPacket extends BasePacket { /// Default zip/zlib compression level, between 1 and 9 diff --git a/lib/src/packet/literal_data.dart b/lib/src/packet/literal_data.dart index d01f8fe8..6d6317de 100644 --- a/lib/src/packet/literal_data.dart +++ b/lib/src/packet/literal_data.dart @@ -12,7 +12,7 @@ import '../enum/literal_format.dart'; import '../type/literal_data.dart'; import 'base.dart'; -/// Implementation of the Literal Data Packet (Tag 11) +/// Implementation of the Literal Data (LIT) Packet - Type 11 /// Author Nguyen Van Nguyen class LiteralDataPacket extends BasePacket implements LiteralDataInterface { @override diff --git a/lib/src/packet/marker.dart b/lib/src/packet/marker.dart index 61ff88de..cfb3d316 100644 --- a/lib/src/packet/marker.dart +++ b/lib/src/packet/marker.dart @@ -9,7 +9,7 @@ import 'dart:typed_data'; import 'base.dart'; -/// Implementation of the strange "Marker packet" (Tag 10) +/// Implementation of the Marker (MARKER) Packet - Type 10 /// Author Nguyen Van Nguyen class MarkerPacket extends BasePacket { static const marker = 'PGP'; diff --git a/lib/src/packet/one_pass_signature.dart b/lib/src/packet/one_pass_signature.dart index bf02e8e9..f4c4292c 100644 --- a/lib/src/packet/one_pass_signature.dart +++ b/lib/src/packet/one_pass_signature.dart @@ -12,7 +12,7 @@ import '../enum/signature_type.dart'; import '../type/signature_packet.dart'; import 'base.dart'; -/// Implementation an OpenPGP One-Pass Signature Packet (Tag 4). +/// Implementation an OpenPGP One-Pass (OPS) Signature Packet - Type 4. /// Author Nguyen Van Nguyen class OnePassSignaturePacket extends BasePacket { final int version; diff --git a/lib/src/packet/padding.dart b/lib/src/packet/padding.dart index d11bfd53..f8db1392 100644 --- a/lib/src/packet/padding.dart +++ b/lib/src/packet/padding.dart @@ -9,7 +9,7 @@ import 'dart:typed_data'; import '../common/helpers.dart'; import 'base.dart'; -/// Implementation of the Padding Packet (Type 21) +/// Implementation of the Padding (PADDING) Packet - Type 21. /// Author Nguyen Van Nguyen class PaddingPacket extends BasePacket { static const paddingMin = 16; diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index 1e113b54..ecaf3d2c 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -18,7 +18,7 @@ import '../type/subkey_packet.dart'; import 'base.dart'; import 'key/public_material.dart'; -/// Implementation of the Public Key Packet (Type 6) +/// Implementation of the Public Key (PUBKEY) Packet - Type 6 /// Author Nguyen Van Nguyen class PublicKeyPacket extends BasePacket implements KeyPacketInterface { static const keyIDSize = 8; diff --git a/lib/src/packet/public_key_encrypted_session_key.dart b/lib/src/packet/public_key_encrypted_session_key.dart index 53201e8e..2978506f 100644 --- a/lib/src/packet/public_key_encrypted_session_key.dart +++ b/lib/src/packet/public_key_encrypted_session_key.dart @@ -16,7 +16,7 @@ import 'base.dart'; import 'key/public_material.dart'; import 'key/session_key_cryptor.dart'; -/// Implementation of the Public-Key Encrypted Session Key (PKESK) Packet (Type 1) +/// Implementation of the Public-Key Encrypted Session Key (PKESK) Packet - Type 1 /// Author Nguyen Van Nguyen class PublicKeyEncryptedSessionKeyPacket extends BasePacket { final int version; diff --git a/lib/src/packet/public_subkey.dart b/lib/src/packet/public_subkey.dart index 7dcd838d..b3f283b8 100644 --- a/lib/src/packet/public_subkey.dart +++ b/lib/src/packet/public_subkey.dart @@ -10,7 +10,7 @@ import '../enum/packet_type.dart'; import '../type/subkey_packet.dart'; import 'public_key.dart'; -/// Implementation of the Public Subkey Packet (Type 14) +/// Implementation of the Public Subkey (PUBSUBKEY) Packet - Type 14 /// Author Nguyen Van Nguyen class PublicSubkeyPacket extends PublicKeyPacket implements SubkeyPacketInterface { @override diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 8c047948..d3a2a52f 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -33,7 +33,7 @@ import '../type/secret_key_packet.dart'; import '../type/subkey_packet.dart'; import 'base.dart'; -/// Implementation of the Secret Key Packet (Type 5) +/// Implementation of the Secret Key (SECKEY) Packet - Type 5 /// Author Nguyen Van Nguyen class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { @override diff --git a/lib/src/packet/secret_subkey.dart b/lib/src/packet/secret_subkey.dart index efc59cf4..e8bbd821 100644 --- a/lib/src/packet/secret_subkey.dart +++ b/lib/src/packet/secret_subkey.dart @@ -17,7 +17,7 @@ import '../type/subkey_packet.dart'; import 'public_subkey.dart'; import 'secret_key.dart'; -/// Implementation of the Secret Subkey Packet (Type 7) +/// Implementation of the Secret Subkey (SECSUBKEY) Packet - Type 7 /// Author Nguyen Van Nguyen class SecretSubkeyPacket extends SecretKeyPacket implements SubkeyPacketInterface { @override diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 5edc2174..b494bd5b 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -21,7 +21,7 @@ import 'base.dart'; import 'signature_subpacket.dart'; import 'subpacket_reader.dart'; -/// Implementation of the Signature Packet (Tag 2) +/// Implementation of the Signature (SIG) Packet - Type 2 /// Author Nguyen Van Nguyen class SignaturePacket extends BasePacket implements SignaturePacketInterface { @override diff --git a/lib/src/packet/sym_encrypted_data.dart b/lib/src/packet/sym_encrypted_data.dart index f9ccf02f..b12fda72 100644 --- a/lib/src/packet/sym_encrypted_data.dart +++ b/lib/src/packet/sym_encrypted_data.dart @@ -16,7 +16,7 @@ import '../enum/symmetric_algorithm.dart'; import 'base.dart'; import 'packet_list.dart'; -/// Implementation of the Symmetrically Encrypted Data Packet (Tag 9)) +/// Implementation of the Symmetrically Encrypted Data (SED) Packet - Type 9 /// The encrypted contents will consist of more OpenPGP packets. /// Author Nguyen Van Nguyen class SymEncryptedDataPacket extends BasePacket implements EncryptedDataPacketInterface { diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index 79c29e12..136553e7 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -19,7 +19,7 @@ import '../type/packet_list.dart'; import 'base.dart'; import 'packet_list.dart'; -/// Implementation of the Sym. Encrypted Integrity Protected Data Packet (Tag 18) +/// Implementation of the Sym. Encrypted Integrity Protected Data (SEIPD) Packet - Type 18 /// Author Nguyen Van Nguyen class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements EncryptedDataPacketInterface { static const saltSize = 32; diff --git a/lib/src/packet/sym_encrypted_session_key.dart b/lib/src/packet/sym_encrypted_session_key.dart index 11f3c051..bedb3345 100644 --- a/lib/src/packet/sym_encrypted_session_key.dart +++ b/lib/src/packet/sym_encrypted_session_key.dart @@ -20,7 +20,7 @@ import '../type/session_key.dart'; import 'base.dart'; import 'key/session_key.dart'; -/// Implementation of the Symmetric Key Encrypted Session Key Packet (Tag 3) +/// Implementation of the Symmetric Key Encrypted Session Key (SKESK) Packet - Type 3 /// Author Nguyen Van Nguyen class SymEncryptedSessionKeyPacket extends BasePacket { final int version; diff --git a/lib/src/packet/trust.dart b/lib/src/packet/trust.dart index 238518ad..a53e2975 100644 --- a/lib/src/packet/trust.dart +++ b/lib/src/packet/trust.dart @@ -8,7 +8,7 @@ import 'dart:typed_data'; import 'base.dart'; -/// Implementation of the Trust Packet (Tag 12) +/// Implementation of the Trust (TRUST) Packet - Type 12 /// Author Nguyen Van Nguyen class TrustPacket extends BasePacket { final Uint8List levelAndTrustAmount; diff --git a/lib/src/packet/user_attribute.dart b/lib/src/packet/user_attribute.dart index 0398c9de..68139033 100644 --- a/lib/src/packet/user_attribute.dart +++ b/lib/src/packet/user_attribute.dart @@ -13,7 +13,7 @@ import 'image_user_attribute.dart'; import 'subpacket_reader.dart'; import 'user_attribute_subpacket.dart'; -/// Implementation of the User Attribute Packet (Type 17) +/// Implementation of the User Attribute (UAT) Packet - Type 17 /// Author Nguyen Van Nguyen class UserAttributePacket extends BasePacket implements UserIDPacketInterface { final List attributes; diff --git a/lib/src/packet/user_id.dart b/lib/src/packet/user_id.dart index 75d0aeb7..f605a260 100644 --- a/lib/src/packet/user_id.dart +++ b/lib/src/packet/user_id.dart @@ -11,7 +11,7 @@ import '../common/helpers.dart'; import '../type/user_id_packet.dart'; import 'base.dart'; -/// Implementation of the User ID Packet (Tag 13) +/// Implementation of the User ID (UID) Packet - Type 13 /// Author Nguyen Van Nguyen class UserIDPacket extends BasePacket implements UserIDPacketInterface { final String userID; From d202155390b861b72d6e889ae18ab7fd71d7afd3 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 25 Nov 2024 14:23:56 +0700 Subject: [PATCH 057/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/extensions.dart | 3 +- lib/src/common/helpers.dart | 32 +++++++++++-------- lib/src/cryptor/asymmetric/dsa.dart | 2 +- lib/src/cryptor/asymmetric/elgamal.dart | 2 +- lib/src/packet/aead_encrypted_data.dart | 2 +- lib/src/packet/key/ec_secret_material.dart | 2 +- lib/src/packet/key/ecdh_secret_material.dart | 2 +- .../packet/key/ecdh_session_key_cryptor.dart | 4 +-- .../key/eddsa_legacy_secret_material.dart | 2 +- lib/src/packet/key/eddsa_secret_material.dart | 2 +- .../key/montgomery_secret_material.dart | 3 +- lib/src/packet/key/rsa_secret_material.dart | 2 +- lib/src/packet/padding.dart | 2 +- lib/src/packet/secret_key.dart | 5 ++- lib/src/packet/signature.dart | 2 +- lib/src/packet/signature/notation_data.dart | 2 +- ...ym_encrypted_integrity_protected_data.dart | 2 +- lib/src/packet/sym_encrypted_session_key.dart | 2 +- test/crypto/ecc_test.dart | 5 ++- test/packet/key_wrap_test.dart | 24 +++++++------- 20 files changed, 52 insertions(+), 50 deletions(-) diff --git a/lib/src/common/extensions.dart b/lib/src/common/extensions.dart index f023f51d..5423dc6a 100644 --- a/lib/src/common/extensions.dart +++ b/lib/src/common/extensions.dart @@ -112,7 +112,6 @@ extension IntExt on int { } } - /// String extension /// Author Nguyen Van Nguyen extension StringExt on String { @@ -217,6 +216,8 @@ extension Uint8ListExt on Uint8List { return result; } + BigInt toUnsignedBigInt() => toBigIntWithSign(1); + DateTime toDateTime() => DateTime.fromMillisecondsSinceEpoch( unpack32() * 1000, ); diff --git a/lib/src/common/helpers.dart b/lib/src/common/helpers.dart index 333e3df9..6221af4f 100644 --- a/lib/src/common/helpers.dart +++ b/lib/src/common/helpers.dart @@ -23,7 +23,7 @@ export 'extensions.dart'; final class Helper { static final _random = Random.secure(); - static final _secureRandom = SecureRandom('Fortuna') + static final secureRandom = SecureRandom('Fortuna') ..seed( KeyParameter( Uint8List.fromList( @@ -40,12 +40,10 @@ final class Helper { return bytes.sublist(2, ((bitLength + 7) >> 3) + 2).toBigIntWithSign(1); } - static SecureRandom secureRandom() => _secureRandom; - static Uint8List generatePrefix([ final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, ]) { - final prefix = _secureRandom.nextBytes(symmetric.blockSize); + final prefix = randomBytes(symmetric.blockSize); return Uint8List.fromList([ ...prefix, prefix[prefix.length - 2], @@ -56,7 +54,7 @@ final class Helper { static Uint8List generateEncryptionKey([ final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, ]) => - _secureRandom.nextBytes((symmetric.keySize + 7) >> 3); + randomBytes((symmetric.keySize + 7) >> 3); static Uint8List hashDigest( final Uint8List input, [ @@ -65,15 +63,21 @@ final class Helper { return Digest(hash.digestName).process(input); } - static BigInt randomBigInt( - final BigInt min, - final BigInt max, { - SecureRandom? random, - }) { - random = random ?? secureRandom(); + static String generatePassword([final int length = 32]) { + return List.generate( + length, + ((_) => _random.nextInt(126 - 40) + 40), + ).map((char) => String.fromCharCode(char)).join(); + } + + static Uint8List randomBytes(final int length) { + return secureRandom.nextBytes(length); + } + + static BigInt randomBigInt(final BigInt min, final BigInt max) { BigInt k; do { - k = random.nextBigInteger(max.bitLength); + k = secureRandom.nextBigInteger(max.bitLength); } while (k.compareTo(min) <= 0 || k.compareTo(max) >= 0); return k; } @@ -82,10 +86,10 @@ final class Helper { assert(type != S2kType.simple); return switch (type) { S2kType.argon2 => Argon2S2k( - _secureRandom.nextBytes(Argon2S2k.saltLength), + randomBytes(Argon2S2k.saltLength), ), _ => GenericS2k( - _secureRandom.nextBytes(GenericS2k.saltLength), + randomBytes(GenericS2k.saltLength), ), }; } diff --git a/lib/src/cryptor/asymmetric/dsa.dart b/lib/src/cryptor/asymmetric/dsa.dart index 408a4371..88bd545f 100644 --- a/lib/src/cryptor/asymmetric/dsa.dart +++ b/lib/src/cryptor/asymmetric/dsa.dart @@ -33,7 +33,7 @@ class DSASigner implements Signer { _random = params.random; params = params.parameters; } else { - _random = Helper.secureRandom(); + _random = Helper.secureRandom; } if (params is AsymmetricKeyParameter) { _key = params.key; diff --git a/lib/src/cryptor/asymmetric/elgamal.dart b/lib/src/cryptor/asymmetric/elgamal.dart index 3abd18a9..533ea0a6 100644 --- a/lib/src/cryptor/asymmetric/elgamal.dart +++ b/lib/src/cryptor/asymmetric/elgamal.dart @@ -30,7 +30,7 @@ class ElGamalEngine implements AsymmetricBlockCipher { _random = params.random; params = params.parameters; } else { - _random = Helper.secureRandom(); + _random = Helper.secureRandom; } if (params is AsymmetricKeyParameter) { _key = params.key; diff --git a/lib/src/packet/aead_encrypted_data.dart b/lib/src/packet/aead_encrypted_data.dart index fa9aa747..067c7b04 100644 --- a/lib/src/packet/aead_encrypted_data.dart +++ b/lib/src/packet/aead_encrypted_data.dart @@ -85,7 +85,7 @@ class AeadEncryptedDataPacket extends BasePacket implements EncryptedDataPacketI final AeadAlgorithm aead = AeadAlgorithm.ocb, final int chunkSize = 12, }) { - final iv = Helper.secureRandom().nextBytes(aead.ivLength); + final iv = Helper.randomBytes(aead.ivLength); return AeadEncryptedDataPacket( symmetric, aead, diff --git a/lib/src/packet/key/ec_secret_material.dart b/lib/src/packet/key/ec_secret_material.dart index 172c4a0c..77d6a9e5 100644 --- a/lib/src/packet/key/ec_secret_material.dart +++ b/lib/src/packet/key/ec_secret_material.dart @@ -31,7 +31,7 @@ abstract class ECSecretMaterial implements KeyMaterialInterface { ECKeyGeneratorParameters( ECDomainParameters(curve.toLowerCase()), ), - Helper.secureRandom(), + Helper.secureRandom, ), ); return keyGen.generateKeyPair(); diff --git a/lib/src/packet/key/ecdh_secret_material.dart b/lib/src/packet/key/ecdh_secret_material.dart index 2c53b564..ddbd1254 100644 --- a/lib/src/packet/key/ecdh_secret_material.dart +++ b/lib/src/packet/key/ecdh_secret_material.dart @@ -35,7 +35,7 @@ class ECDHSecretMaterial extends ECSecretMaterial implements SecretKeyMaterialIn factory ECDHSecretMaterial.generate(final Ecc curve) { switch (curve) { case Ecc.curve25519: - final secret = Helper.secureRandom().nextBytes( + final secret = Helper.randomBytes( TweetNaCl.secretKeyLength, ); diff --git a/lib/src/packet/key/ecdh_session_key_cryptor.dart b/lib/src/packet/key/ecdh_session_key_cryptor.dart index 1e4509ef..a52b4609 100644 --- a/lib/src/packet/key/ecdh_session_key_cryptor.dart +++ b/lib/src/packet/key/ecdh_session_key_cryptor.dart @@ -70,7 +70,7 @@ class ECDHSessionKeyCryptor extends SessionKeyCryptor { switch (key.curve) { case Ecc.curve25519: final privateKey = nacl.PrivateKey.fromSeed( - Helper.secureRandom().nextBytes(TweetNaCl.seedSize), + Helper.randomBytes(TweetNaCl.seedSize), ); ephemeralKey = privateKey.publicKey.asTypedList.toBigIntWithSign(1); sharedKey = TweetNaCl.crypto_scalarmult( @@ -91,7 +91,7 @@ class ECDHSessionKeyCryptor extends SessionKeyCryptor { ..init( ParametersWithRandom( ECKeyGeneratorParameters(parameters), - Helper.secureRandom(), + Helper.secureRandom, ), ); final keyPair = keyGen.generateKeyPair(); diff --git a/lib/src/packet/key/eddsa_legacy_secret_material.dart b/lib/src/packet/key/eddsa_legacy_secret_material.dart index ac0a9bd0..3616324e 100644 --- a/lib/src/packet/key/eddsa_legacy_secret_material.dart +++ b/lib/src/packet/key/eddsa_legacy_secret_material.dart @@ -36,7 +36,7 @@ class EdDSALegacySecretMaterial implements SigningKeyMaterialInterface { ); factory EdDSALegacySecretMaterial.generate() { - final seed = Helper.secureRandom().nextBytes(TweetNaCl.seedSize); + final seed = Helper.randomBytes(TweetNaCl.seedSize); return EdDSALegacySecretMaterial( seed.toBigIntWithSign(1), EdDSALegacyPublicMaterial( diff --git a/lib/src/packet/key/eddsa_secret_material.dart b/lib/src/packet/key/eddsa_secret_material.dart index 59b6f417..bb8f9876 100644 --- a/lib/src/packet/key/eddsa_secret_material.dart +++ b/lib/src/packet/key/eddsa_secret_material.dart @@ -37,7 +37,7 @@ class EdDSASecretMaterial implements SigningKeyMaterialInterface { ); factory EdDSASecretMaterial.generate(final EdDSACurve curve) { - final secretKey = Helper.secureRandom().nextBytes(curve.payloadSize); + final secretKey = Helper.randomBytes(curve.payloadSize); final publicKey = switch (curve) { EdDSACurve.ed25519 => nacl.SigningKey.fromSeed( secretKey, diff --git a/lib/src/packet/key/montgomery_secret_material.dart b/lib/src/packet/key/montgomery_secret_material.dart index 42fb1d84..08e311d2 100644 --- a/lib/src/packet/key/montgomery_secret_material.dart +++ b/lib/src/packet/key/montgomery_secret_material.dart @@ -82,8 +82,7 @@ class MontgomerySecretMaterial implements SecretKeyMaterialInterface { static Uint8List _generateSecretKey(final MontgomeryCurve curve) { final payloadSize = curve.payloadSize; - final random = Helper.secureRandom(); - final key = random.nextBytes(payloadSize); + final key = Helper.randomBytes(payloadSize); switch (curve) { case MontgomeryCurve.x25519: // The lowest three bits must be 0 diff --git a/lib/src/packet/key/rsa_secret_material.dart b/lib/src/packet/key/rsa_secret_material.dart index 493828a5..75d2cc4c 100644 --- a/lib/src/packet/key/rsa_secret_material.dart +++ b/lib/src/packet/key/rsa_secret_material.dart @@ -88,7 +88,7 @@ class RSASecretMaterial implements SigningKeyMaterialInterface { keySize.bits, mrTests, ), - Helper.secureRandom(), + Helper.secureRandom, ), ); final keyPair = keyGen.generateKeyPair(); diff --git a/lib/src/packet/padding.dart b/lib/src/packet/padding.dart index f8db1392..a383369c 100644 --- a/lib/src/packet/padding.dart +++ b/lib/src/packet/padding.dart @@ -21,7 +21,7 @@ class PaddingPacket extends BasePacket { factory PaddingPacket.createPadding(int lengh) { assert(paddingMin <= lengh && lengh <= paddingMax); - return PaddingPacket(Helper.secureRandom().nextBytes(lengh)); + return PaddingPacket(Helper.randomBytes(lengh)); } @override diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index d3a2a52f..d05ef27f 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -300,12 +300,11 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { : Helper.stringToKey( S2kType.iterated, ); - final random = Helper.secureRandom(); final iv = aeadProtect - ? random.nextBytes( + ? Helper.randomBytes( aead.ivLength, ) - : random.nextBytes( + : Helper.randomBytes( symmetric.blockSize, ); final kek = _produceEncryptionKey( diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index b494bd5b..a80bcab6 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -194,7 +194,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { hashedSubpackets.add(KeyExpirationTime.fromTime(keyExpirationTime)); } final salt = version == 6 - ? Helper.secureRandom().nextBytes( + ? Helper.randomBytes( hashAlgorithm.saltSize, ) : Uint8List(0); diff --git a/lib/src/packet/signature/notation_data.dart b/lib/src/packet/signature/notation_data.dart index 3b5d7720..1847a87f 100644 --- a/lib/src/packet/signature/notation_data.dart +++ b/lib/src/packet/signature/notation_data.dart @@ -39,7 +39,7 @@ class NotationData extends SignatureSubpacket { int saltSize, { final bool critical = false, }) { - final salt = Helper.secureRandom().nextBytes(saltSize); + final salt = Helper.randomBytes(saltSize); final nameData = utf8.encode(saltName); final nameLength = min(nameData.length, 0xffff); return NotationData( diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index 136553e7..ed5d57ab 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -137,7 +137,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc Helper.assertSymmetric(symmetric); final version = aeadProtect || Config.useV6Key ? 2 : 1; - final salt = aeadProtect ? Helper.secureRandom().nextBytes(saltSize) : null; + final salt = aeadProtect ? Helper.randomBytes(saltSize) : null; final chunkSize = aeadProtect ? Config.aeadChunkSize : 0; final Uint8List encrypted; diff --git a/lib/src/packet/sym_encrypted_session_key.dart b/lib/src/packet/sym_encrypted_session_key.dart index bedb3345..df19d216 100644 --- a/lib/src/packet/sym_encrypted_session_key.dart +++ b/lib/src/packet/sym_encrypted_session_key.dart @@ -157,7 +157,7 @@ class SymEncryptedSessionKeyPacket extends BasePacket { final kek = Helper.hkdf(key, symmetric.keySizeInByte, info: adata); - iv = Helper.secureRandom().nextBytes(aead.ivLength); + iv = Helper.randomBytes(aead.ivLength); final cipher = aead.cipherEngine(kek, symmetric); encrypted = cipher.encrypt(sessionKey.encryptionKey, iv, adata); } else { diff --git a/test/crypto/ecc_test.dart b/test/crypto/ecc_test.dart index a246922a..9400535b 100644 --- a/test/crypto/ecc_test.dart +++ b/test/crypto/ecc_test.dart @@ -14,11 +14,10 @@ void main() { ); test('Test ECDH', () { - final random = Helper.secureRandom(); for (var i = 0; i < 100; i++) { /// Generate ephemeral private keys - final kA = random.nextBytes(X448.payloadSize); - final kB = random.nextBytes(X448.payloadSize); + final kA = Helper.randomBytes(X448.payloadSize); + final kB = Helper.randomBytes(X448.payloadSize); /// Publish their public keys final qA = X448.scalarMultBase(kA); diff --git a/test/packet/key_wrap_test.dart b/test/packet/key_wrap_test.dart index 5cf88016..74f2809a 100644 --- a/test/packet/key_wrap_test.dart +++ b/test/packet/key_wrap_test.dart @@ -110,8 +110,8 @@ void main() { expect(wrappedKey128, equals(wrappedKey128128)); expect(unwrappedKey128, equals(keyData128)); - final key = Helper.secureRandom().nextBytes(16); - final keyData = Helper.secureRandom().nextBytes(32); + final key = Helper.randomBytes(16); + final keyData = Helper.randomBytes(32); final wrappedKey = aes.wrap(key, keyData); final unwrappedKey = aes.unwrap(key, wrappedKey); expect(unwrappedKey, equals(keyData)); @@ -190,8 +190,8 @@ void main() { expect(wrappedKey192, equals(wrappedKey192192)); expect(unwrappedKey192, equals(keyData192)); - final key = Helper.secureRandom().nextBytes(24); - final keyData = Helper.secureRandom().nextBytes(32); + final key = Helper.randomBytes(24); + final keyData = Helper.randomBytes(32); final wrappedKey = aes.wrap(key, keyData); final unwrappedKey = aes.unwrap(key, wrappedKey); expect(unwrappedKey, equals(keyData)); @@ -317,8 +317,8 @@ void main() { expect(wrappedKey256, equals(wrappedKey256256)); expect(unwrappedKey256, equals(keyData256)); - final key = Helper.secureRandom().nextBytes(32); - final keyData = Helper.secureRandom().nextBytes(32); + final key = Helper.randomBytes(32); + final keyData = Helper.randomBytes(32); final wrappedKey = aes.wrap(key, keyData); final unwrappedKey = aes.unwrap(key, wrappedKey); expect(unwrappedKey, equals(keyData)); @@ -331,8 +331,8 @@ void main() { final unwrappedKey128 = camellia.unwrap(key128, wrappedKey128); expect(unwrappedKey128, equals(keyData128)); - final key = Helper.secureRandom().nextBytes(16); - final keyData = Helper.secureRandom().nextBytes(32); + final key = Helper.randomBytes(16); + final keyData = Helper.randomBytes(32); final wrappedKey = camellia.wrap(key, keyData); final unwrappedKey = camellia.unwrap(key, wrappedKey); expect(unwrappedKey, equals(keyData)); @@ -349,8 +349,8 @@ void main() { final unwrappedKey192 = camellia.unwrap(key192, wrappedKey192); expect(unwrappedKey192, equals(keyData192)); - final key = Helper.secureRandom().nextBytes(24); - final keyData = Helper.secureRandom().nextBytes(32); + final key = Helper.randomBytes(24); + final keyData = Helper.randomBytes(32); final wrappedKey = camellia.wrap(key, keyData); final unwrappedKey = camellia.unwrap(key, wrappedKey); expect(unwrappedKey, equals(keyData)); @@ -371,8 +371,8 @@ void main() { final unwrappedKey256 = camellia.unwrap(key256, wrappedKey256); expect(unwrappedKey256, equals(keyData256)); - final key = Helper.secureRandom().nextBytes(32); - final keyData = Helper.secureRandom().nextBytes(32); + final key = Helper.randomBytes(32); + final keyData = Helper.randomBytes(32); final wrappedKey = camellia.wrap(key, keyData); final unwrappedKey = camellia.unwrap(key, wrappedKey); expect(unwrappedKey, equals(keyData)); From ebf19676ed59099d75caf2d97e8bcecd3ce6f631 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 25 Nov 2024 14:29:38 +0700 Subject: [PATCH 058/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/helpers.dart | 47 ++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/lib/src/common/helpers.dart b/lib/src/common/helpers.dart index 6221af4f..70dfd757 100644 --- a/lib/src/common/helpers.dart +++ b/lib/src/common/helpers.dart @@ -23,7 +23,7 @@ export 'extensions.dart'; final class Helper { static final _random = Random.secure(); - static final secureRandom = SecureRandom('Fortuna') + static get secureRandom => SecureRandom('Fortuna') ..seed( KeyParameter( Uint8List.fromList( @@ -35,10 +35,12 @@ final class Helper { ), ); - static BigInt readMPI(final Uint8List bytes) { - final bitLength = bytes.sublist(0, 2).unpack16(); - return bytes.sublist(2, ((bitLength + 7) >> 3) + 2).toBigIntWithSign(1); - } + static BigInt readMPI(final Uint8List bytes) => bytes + .sublist( + 2, + ((bytes.sublist(0, 2).unpack16() + 7) >> 3) + 2, + ) + .toBigIntWithSign(1); static Uint8List generatePrefix([ final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, @@ -59,20 +61,17 @@ final class Helper { static Uint8List hashDigest( final Uint8List input, [ final HashAlgorithm hash = HashAlgorithm.sha256, - ]) { - return Digest(hash.digestName).process(input); - } + ]) => + Digest(hash.digestName).process(input); - static String generatePassword([final int length = 32]) { - return List.generate( - length, - ((_) => _random.nextInt(126 - 40) + 40), - ).map((char) => String.fromCharCode(char)).join(); - } + static String generatePassword([final int length = 32]) => List.generate( + length, + ((_) => _random.nextInt(126 - 40) + 40), + ).map((char) => String.fromCharCode(char)).join(); - static Uint8List randomBytes(final int length) { - return secureRandom.nextBytes(length); - } + static Uint8List randomBytes(final int length) => secureRandom.nextBytes( + length, + ); static BigInt randomBigInt(final BigInt min, final BigInt max) { BigInt k; @@ -116,13 +115,17 @@ final class Helper { } static assertHash(final HashAlgorithm hash) { - assert(hash != HashAlgorithm.md5 && hash != HashAlgorithm.sha1 && hash != HashAlgorithm.ripemd160); + assert( + hash != HashAlgorithm.md5 && hash != HashAlgorithm.sha1 && hash != HashAlgorithm.ripemd160, + ); } static assertSymmetric(final SymmetricAlgorithm symmetric) { - assert(symmetric != SymmetricAlgorithm.plaintext && - symmetric != SymmetricAlgorithm.cast5 && - symmetric != SymmetricAlgorithm.idea && - symmetric != SymmetricAlgorithm.tripledes); + assert( + symmetric != SymmetricAlgorithm.plaintext && + symmetric != SymmetricAlgorithm.cast5 && + symmetric != SymmetricAlgorithm.idea && + symmetric != SymmetricAlgorithm.tripledes, + ); } } From 4d7cd4bb80a9ce7fd4da74d03336cb8bf982e0fd Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 25 Nov 2024 15:36:18 +0700 Subject: [PATCH 059/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/config.dart | 3 ++- lib/src/enum/key_algorithm.dart | 3 ++- lib/src/enum/profile.dart | 12 ++++++++++++ .../sym_encrypted_integrity_protected_data.dart | 3 ++- 4 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 lib/src/enum/profile.dart diff --git a/lib/src/common/config.dart b/lib/src/common/config.dart index 151b69c5..2c09be5a 100644 --- a/lib/src/common/config.dart +++ b/lib/src/common/config.dart @@ -7,6 +7,7 @@ library; import '../enum/aead_algorithm.dart'; import '../enum/compression_algorithm.dart'; import '../enum/hash_algorithm.dart'; +import '../enum/profile.dart'; import '../enum/symmetric_algorithm.dart'; /// Configuration class @@ -24,7 +25,7 @@ final class Config { static bool aeadProtect = false; - static bool useV6Key = false; + static Profile useProfile = Profile.rfc4880; static HashAlgorithm preferredHash = HashAlgorithm.sha256; diff --git a/lib/src/enum/key_algorithm.dart b/lib/src/enum/key_algorithm.dart index e30bb5b9..4254a513 100644 --- a/lib/src/enum/key_algorithm.dart +++ b/lib/src/enum/key_algorithm.dart @@ -6,6 +6,7 @@ library; import '../common/config.dart'; import 'key_version.dart'; +import 'profile.dart'; /// Public key algorithms enum /// Author Nguyen Van Nguyen @@ -70,6 +71,6 @@ enum KeyAlgorithm { int get keyVersion => switch (this) { x25519 || x448 || ed25519 || ed448 => KeyVersion.v6.value, - _ => Config.useV6Key ? KeyVersion.v6.value : KeyVersion.v4.value, + _ => Config.useProfile == Profile.rfc9580 ? KeyVersion.v6.value : KeyVersion.v4.value, }; } diff --git a/lib/src/enum/profile.dart b/lib/src/enum/profile.dart new file mode 100644 index 00000000..093a2a43 --- /dev/null +++ b/lib/src/enum/profile.dart @@ -0,0 +1,12 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +/// OpenPGP profiles enum +/// Author Nguyen Van Nguyen +enum Profile { + rfc4880, + rfc9580; +} diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index ed5d57ab..1e341f72 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -6,6 +6,7 @@ library; import 'dart:typed_data'; +import 'package:dart_pg/src/enum/profile.dart'; import 'package:pointycastle/api.dart'; import '../common/config.dart'; @@ -136,7 +137,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc }) { Helper.assertSymmetric(symmetric); - final version = aeadProtect || Config.useV6Key ? 2 : 1; + final version = aeadProtect || Config.useProfile == Profile.rfc9580 ? 2 : 1; final salt = aeadProtect ? Helper.randomBytes(saltSize) : null; final chunkSize = aeadProtect ? Config.aeadChunkSize : 0; From 040d6c1f2dc7ec2a49aeb62102edb53c57fce446 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 25 Nov 2024 15:47:04 +0700 Subject: [PATCH 060/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/config.dart | 2 +- lib/src/enum/profile.dart | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/common/config.dart b/lib/src/common/config.dart index 2c09be5a..b915c323 100644 --- a/lib/src/common/config.dart +++ b/lib/src/common/config.dart @@ -25,7 +25,7 @@ final class Config { static bool aeadProtect = false; - static Profile useProfile = Profile.rfc4880; + static Profile useProfile = Profile.rfc6637; static HashAlgorithm preferredHash = HashAlgorithm.sha256; diff --git a/lib/src/enum/profile.dart b/lib/src/enum/profile.dart index 093a2a43..cb629076 100644 --- a/lib/src/enum/profile.dart +++ b/lib/src/enum/profile.dart @@ -8,5 +8,7 @@ library; /// Author Nguyen Van Nguyen enum Profile { rfc4880, + rfc5581, + rfc6637, rfc9580; } From 0dbabbf7fb00175735b81228dbe2850aa50ba913 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 25 Nov 2024 16:04:47 +0700 Subject: [PATCH 061/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/config.dart | 2 +- lib/src/enum/profile.dart | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/src/common/config.dart b/lib/src/common/config.dart index b915c323..2c09be5a 100644 --- a/lib/src/common/config.dart +++ b/lib/src/common/config.dart @@ -25,7 +25,7 @@ final class Config { static bool aeadProtect = false; - static Profile useProfile = Profile.rfc6637; + static Profile useProfile = Profile.rfc4880; static HashAlgorithm preferredHash = HashAlgorithm.sha256; diff --git a/lib/src/enum/profile.dart b/lib/src/enum/profile.dart index cb629076..5dcc7dd9 100644 --- a/lib/src/enum/profile.dart +++ b/lib/src/enum/profile.dart @@ -7,8 +7,13 @@ library; /// OpenPGP profiles enum /// Author Nguyen Van Nguyen enum Profile { + /// RFC 4880 & 5581 & 6637 profile + /// https://www.rfc-editor.org/rfc/rfc4880 + /// https://www.rfc-editor.org/rfc/rfc5581 + /// https://www.rfc-editor.org/rfc/rfc6637 rfc4880, - rfc5581, - rfc6637, + + /// RFC 9580 profile + /// https://www.rfc-editor.org/rfc/rfc9580 rfc9580; } From d5c5d6539c9ccafba1fd3d14b9e46751fdeaa49e Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 25 Nov 2024 16:23:37 +0700 Subject: [PATCH 062/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/helpers.dart | 30 +++++++++++++++++++++--------- lib/src/enum/hash_algorithm.dart | 5 ++++- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/src/common/helpers.dart b/lib/src/common/helpers.dart index 70dfd757..4dd714bc 100644 --- a/lib/src/common/helpers.dart +++ b/lib/src/common/helpers.dart @@ -8,11 +8,14 @@ import 'dart:math'; import 'dart:typed_data'; import 'package:pointycastle/export.dart'; +import '../enum/ecc.dart'; +import '../enum/profile.dart'; import '../type/s2k.dart'; import '../enum/s2k_type.dart'; import '../enum/hash_algorithm.dart'; import '../enum/symmetric_algorithm.dart'; import 'argon2_s2k.dart'; +import 'config.dart'; import 'extensions.dart'; import 'generic_s2k.dart'; @@ -115,17 +118,26 @@ final class Helper { } static assertHash(final HashAlgorithm hash) { - assert( - hash != HashAlgorithm.md5 && hash != HashAlgorithm.sha1 && hash != HashAlgorithm.ripemd160, - ); + assert(hash != HashAlgorithm.unknown && + hash != HashAlgorithm.md5 && + hash != HashAlgorithm.sha1 && + hash != HashAlgorithm.ripemd160); } static assertSymmetric(final SymmetricAlgorithm symmetric) { - assert( - symmetric != SymmetricAlgorithm.plaintext && - symmetric != SymmetricAlgorithm.cast5 && - symmetric != SymmetricAlgorithm.idea && - symmetric != SymmetricAlgorithm.tripledes, - ); + assert(symmetric != SymmetricAlgorithm.plaintext && + symmetric != SymmetricAlgorithm.cast5 && + symmetric != SymmetricAlgorithm.idea && + symmetric != SymmetricAlgorithm.tripledes); + } + + static assertEcc(final Ecc ecc) { + if (Config.useProfile == Profile.rfc4880) { + assert(ecc != Ecc.brainpoolP256r1 && + ecc != Ecc.brainpoolP384r1 && + ecc != Ecc.brainpoolP512r1 && + ecc != Ecc.ed25519 && + ecc != Ecc.curve25519); + } } } diff --git a/lib/src/enum/hash_algorithm.dart b/lib/src/enum/hash_algorithm.dart index cde8fde5..94fb3c51 100644 --- a/lib/src/enum/hash_algorithm.dart +++ b/lib/src/enum/hash_algorithm.dart @@ -7,6 +7,7 @@ library; /// Hash algorithms enum /// Author Nguyen Van Nguyen enum HashAlgorithm { + unknown(0), md5(1), sha1(2), ripemd160(3), @@ -23,6 +24,7 @@ enum HashAlgorithm { /// pointy castle digest name String get digestName => switch (this) { + unknown => 'Unknown', md5 => 'MD5', sha1 => 'SHA-1', ripemd160 => 'RIPEMD-160', @@ -35,6 +37,7 @@ enum HashAlgorithm { }; int get digestSize => switch (this) { + unknown => 0, md5 => 16, sha1 || ripemd160 => 20, sha224 => 28, @@ -44,7 +47,7 @@ enum HashAlgorithm { }; int get saltSize => switch (this) { - md5 || sha1 || ripemd160 => 0, + unknown || md5 || sha1 || ripemd160 => 0, sha224 || sha256 || sha3_256 => 16, sha384 => 24, sha512 || sha3_512 => 32, From f91545e71e5b799ab5a01cb844ac001ecb6a212c Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 26 Nov 2024 10:34:01 +0700 Subject: [PATCH 063/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/sym_encrypted_session_key.dart | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/src/packet/sym_encrypted_session_key.dart b/lib/src/packet/sym_encrypted_session_key.dart index df19d216..1451f7ef 100644 --- a/lib/src/packet/sym_encrypted_session_key.dart +++ b/lib/src/packet/sym_encrypted_session_key.dart @@ -9,10 +9,12 @@ import 'dart:typed_data'; import 'package:pointycastle/api.dart'; import '../common/argon2_s2k.dart'; +import '../common/config.dart'; import '../common/generic_s2k.dart'; import '../common/helpers.dart'; import '../cryptor/symmetric/buffered_cipher.dart'; import '../enum/aead_algorithm.dart'; +import '../enum/profile.dart'; import '../enum/s2k_type.dart'; import '../enum/symmetric_algorithm.dart'; import '../type/s2k.dart'; @@ -130,8 +132,17 @@ class SymEncryptedSessionKeyPacket extends BasePacket { final AeadAlgorithm aead = AeadAlgorithm.gcm, final bool aeadProtect = false, }) { - final version = aeadProtect && sessionKey != null ? 6 : 4; - final s2k = aeadProtect + Helper.assertSymmetric(symmetric); + final version = switch (Config.useProfile) { + Profile.rfc4880 => 4, + Profile.rfc9580 => 6, + }; + if (aeadProtect && sessionKey != null && version == 4) { + throw ArgumentError( + 'Using AEAD with version $version SKESK packet is not allowed.', + ); + } + final s2k = version == 6 ? Helper.stringToKey( S2kType.argon2, ) @@ -245,6 +256,7 @@ class SymEncryptedSessionKeyPacket extends BasePacket { iv, encrypted, symmetric: symmetric, + aead: aead, sessionKey: sessionKey, ); } From 2d88f7db1e3a090c4ea2822e47df52babe3e825b Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 26 Nov 2024 10:40:32 +0700 Subject: [PATCH 064/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/config.dart | 2 +- lib/src/common/helpers.dart | 2 +- lib/src/enum/key_algorithm.dart | 2 +- lib/src/enum/{profile.dart => preset_profile.dart} | 4 ++-- lib/src/packet/sym_encrypted_integrity_protected_data.dart | 4 ++-- lib/src/packet/sym_encrypted_session_key.dart | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) rename lib/src/enum/{profile.dart => preset_profile.dart} (90%) diff --git a/lib/src/common/config.dart b/lib/src/common/config.dart index 2c09be5a..555c7f40 100644 --- a/lib/src/common/config.dart +++ b/lib/src/common/config.dart @@ -25,7 +25,7 @@ final class Config { static bool aeadProtect = false; - static Profile useProfile = Profile.rfc4880; + static PresetProfile useProfile = PresetProfile.rfc4880; static HashAlgorithm preferredHash = HashAlgorithm.sha256; diff --git a/lib/src/common/helpers.dart b/lib/src/common/helpers.dart index 4dd714bc..6ce8416b 100644 --- a/lib/src/common/helpers.dart +++ b/lib/src/common/helpers.dart @@ -132,7 +132,7 @@ final class Helper { } static assertEcc(final Ecc ecc) { - if (Config.useProfile == Profile.rfc4880) { + if (Config.useProfile == PresetProfile.rfc4880) { assert(ecc != Ecc.brainpoolP256r1 && ecc != Ecc.brainpoolP384r1 && ecc != Ecc.brainpoolP512r1 && diff --git a/lib/src/enum/key_algorithm.dart b/lib/src/enum/key_algorithm.dart index 4254a513..e038c494 100644 --- a/lib/src/enum/key_algorithm.dart +++ b/lib/src/enum/key_algorithm.dart @@ -71,6 +71,6 @@ enum KeyAlgorithm { int get keyVersion => switch (this) { x25519 || x448 || ed25519 || ed448 => KeyVersion.v6.value, - _ => Config.useProfile == Profile.rfc9580 ? KeyVersion.v6.value : KeyVersion.v4.value, + _ => Config.useProfile == PresetProfile.rfc9580 ? KeyVersion.v6.value : KeyVersion.v4.value, }; } diff --git a/lib/src/enum/profile.dart b/lib/src/enum/preset_profile.dart similarity index 90% rename from lib/src/enum/profile.dart rename to lib/src/enum/preset_profile.dart index 5dcc7dd9..ade55a04 100644 --- a/lib/src/enum/profile.dart +++ b/lib/src/enum/preset_profile.dart @@ -4,9 +4,9 @@ library; -/// OpenPGP profiles enum +/// OpenPGP preset profiles enum /// Author Nguyen Van Nguyen -enum Profile { +enum PresetProfile { /// RFC 4880 & 5581 & 6637 profile /// https://www.rfc-editor.org/rfc/rfc4880 /// https://www.rfc-editor.org/rfc/rfc5581 diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index 1e341f72..63a13314 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -6,7 +6,6 @@ library; import 'dart:typed_data'; -import 'package:dart_pg/src/enum/profile.dart'; import 'package:pointycastle/api.dart'; import '../common/config.dart'; @@ -14,6 +13,7 @@ import '../common/helpers.dart'; import '../cryptor/symmetric/buffered_cipher.dart'; import '../enum/aead_algorithm.dart'; import '../enum/hash_algorithm.dart'; +import '../enum/preset_profile.dart'; import '../enum/symmetric_algorithm.dart'; import '../type/encrypted_data_packet.dart'; import '../type/packet_list.dart'; @@ -137,7 +137,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc }) { Helper.assertSymmetric(symmetric); - final version = aeadProtect || Config.useProfile == Profile.rfc9580 ? 2 : 1; + final version = aeadProtect || Config.useProfile == PresetProfile.rfc9580 ? 2 : 1; final salt = aeadProtect ? Helper.randomBytes(saltSize) : null; final chunkSize = aeadProtect ? Config.aeadChunkSize : 0; diff --git a/lib/src/packet/sym_encrypted_session_key.dart b/lib/src/packet/sym_encrypted_session_key.dart index 1451f7ef..6f279e35 100644 --- a/lib/src/packet/sym_encrypted_session_key.dart +++ b/lib/src/packet/sym_encrypted_session_key.dart @@ -14,7 +14,7 @@ import '../common/generic_s2k.dart'; import '../common/helpers.dart'; import '../cryptor/symmetric/buffered_cipher.dart'; import '../enum/aead_algorithm.dart'; -import '../enum/profile.dart'; +import '../enum/preset_profile.dart'; import '../enum/s2k_type.dart'; import '../enum/symmetric_algorithm.dart'; import '../type/s2k.dart'; @@ -134,8 +134,8 @@ class SymEncryptedSessionKeyPacket extends BasePacket { }) { Helper.assertSymmetric(symmetric); final version = switch (Config.useProfile) { - Profile.rfc4880 => 4, - Profile.rfc9580 => 6, + PresetProfile.rfc4880 => 4, + PresetProfile.rfc9580 => 6, }; if (aeadProtect && sessionKey != null && version == 4) { throw ArgumentError( From 39ce6d904e9ec06b084a22fdd71cbdf652775957 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 26 Nov 2024 10:43:20 +0700 Subject: [PATCH 065/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/config.dart | 4 ++-- lib/src/common/helpers.dart | 4 ++-- lib/src/enum/key_algorithm.dart | 4 ++-- lib/src/enum/{preset_profile.dart => preset_rfc.dart} | 2 +- .../packet/sym_encrypted_integrity_protected_data.dart | 4 ++-- lib/src/packet/sym_encrypted_session_key.dart | 8 ++++---- 6 files changed, 13 insertions(+), 13 deletions(-) rename lib/src/enum/{preset_profile.dart => preset_rfc.dart} (96%) diff --git a/lib/src/common/config.dart b/lib/src/common/config.dart index 555c7f40..406e2872 100644 --- a/lib/src/common/config.dart +++ b/lib/src/common/config.dart @@ -7,7 +7,7 @@ library; import '../enum/aead_algorithm.dart'; import '../enum/compression_algorithm.dart'; import '../enum/hash_algorithm.dart'; -import '../enum/profile.dart'; +import '../enum/preset_rfc.dart'; import '../enum/symmetric_algorithm.dart'; /// Configuration class @@ -25,7 +25,7 @@ final class Config { static bool aeadProtect = false; - static PresetProfile useProfile = PresetProfile.rfc4880; + static PresetRfc presetRfc = PresetRfc.rfc4880; static HashAlgorithm preferredHash = HashAlgorithm.sha256; diff --git a/lib/src/common/helpers.dart b/lib/src/common/helpers.dart index 6ce8416b..2a6ca6c4 100644 --- a/lib/src/common/helpers.dart +++ b/lib/src/common/helpers.dart @@ -9,7 +9,7 @@ import 'dart:typed_data'; import 'package:pointycastle/export.dart'; import '../enum/ecc.dart'; -import '../enum/profile.dart'; +import '../enum/preset_rfc.dart'; import '../type/s2k.dart'; import '../enum/s2k_type.dart'; import '../enum/hash_algorithm.dart'; @@ -132,7 +132,7 @@ final class Helper { } static assertEcc(final Ecc ecc) { - if (Config.useProfile == PresetProfile.rfc4880) { + if (Config.presetRfc == PresetRfc.rfc4880) { assert(ecc != Ecc.brainpoolP256r1 && ecc != Ecc.brainpoolP384r1 && ecc != Ecc.brainpoolP512r1 && diff --git a/lib/src/enum/key_algorithm.dart b/lib/src/enum/key_algorithm.dart index e038c494..bc3f4b11 100644 --- a/lib/src/enum/key_algorithm.dart +++ b/lib/src/enum/key_algorithm.dart @@ -6,7 +6,7 @@ library; import '../common/config.dart'; import 'key_version.dart'; -import 'profile.dart'; +import 'preset_rfc.dart'; /// Public key algorithms enum /// Author Nguyen Van Nguyen @@ -71,6 +71,6 @@ enum KeyAlgorithm { int get keyVersion => switch (this) { x25519 || x448 || ed25519 || ed448 => KeyVersion.v6.value, - _ => Config.useProfile == PresetProfile.rfc9580 ? KeyVersion.v6.value : KeyVersion.v4.value, + _ => Config.presetRfc == PresetRfc.rfc9580 ? KeyVersion.v6.value : KeyVersion.v4.value, }; } diff --git a/lib/src/enum/preset_profile.dart b/lib/src/enum/preset_rfc.dart similarity index 96% rename from lib/src/enum/preset_profile.dart rename to lib/src/enum/preset_rfc.dart index ade55a04..811be823 100644 --- a/lib/src/enum/preset_profile.dart +++ b/lib/src/enum/preset_rfc.dart @@ -6,7 +6,7 @@ library; /// OpenPGP preset profiles enum /// Author Nguyen Van Nguyen -enum PresetProfile { +enum PresetRfc { /// RFC 4880 & 5581 & 6637 profile /// https://www.rfc-editor.org/rfc/rfc4880 /// https://www.rfc-editor.org/rfc/rfc5581 diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index 63a13314..fc76dcb0 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -13,7 +13,7 @@ import '../common/helpers.dart'; import '../cryptor/symmetric/buffered_cipher.dart'; import '../enum/aead_algorithm.dart'; import '../enum/hash_algorithm.dart'; -import '../enum/preset_profile.dart'; +import '../enum/preset_rfc.dart'; import '../enum/symmetric_algorithm.dart'; import '../type/encrypted_data_packet.dart'; import '../type/packet_list.dart'; @@ -137,7 +137,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc }) { Helper.assertSymmetric(symmetric); - final version = aeadProtect || Config.useProfile == PresetProfile.rfc9580 ? 2 : 1; + final version = aeadProtect || Config.presetRfc == PresetRfc.rfc9580 ? 2 : 1; final salt = aeadProtect ? Helper.randomBytes(saltSize) : null; final chunkSize = aeadProtect ? Config.aeadChunkSize : 0; diff --git a/lib/src/packet/sym_encrypted_session_key.dart b/lib/src/packet/sym_encrypted_session_key.dart index 6f279e35..49b55eda 100644 --- a/lib/src/packet/sym_encrypted_session_key.dart +++ b/lib/src/packet/sym_encrypted_session_key.dart @@ -14,7 +14,7 @@ import '../common/generic_s2k.dart'; import '../common/helpers.dart'; import '../cryptor/symmetric/buffered_cipher.dart'; import '../enum/aead_algorithm.dart'; -import '../enum/preset_profile.dart'; +import '../enum/preset_rfc.dart'; import '../enum/s2k_type.dart'; import '../enum/symmetric_algorithm.dart'; import '../type/s2k.dart'; @@ -133,9 +133,9 @@ class SymEncryptedSessionKeyPacket extends BasePacket { final bool aeadProtect = false, }) { Helper.assertSymmetric(symmetric); - final version = switch (Config.useProfile) { - PresetProfile.rfc4880 => 4, - PresetProfile.rfc9580 => 6, + final version = switch (Config.presetRfc) { + PresetRfc.rfc4880 => 4, + PresetRfc.rfc9580 => 6, }; if (aeadProtect && sessionKey != null && version == 4) { throw ArgumentError( From 8a7e04ac45a6d47369caa5ca35d0c4c1054ecdb8 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 26 Nov 2024 10:48:22 +0700 Subject: [PATCH 066/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/enum/preset_rfc.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/enum/preset_rfc.dart b/lib/src/enum/preset_rfc.dart index 811be823..18e6053f 100644 --- a/lib/src/enum/preset_rfc.dart +++ b/lib/src/enum/preset_rfc.dart @@ -4,16 +4,16 @@ library; -/// OpenPGP preset profiles enum +/// OpenPGP preset RFCs enum /// Author Nguyen Van Nguyen enum PresetRfc { - /// RFC 4880 & 5581 & 6637 profile + /// RFC 4880 & 5581 & 6637 /// https://www.rfc-editor.org/rfc/rfc4880 /// https://www.rfc-editor.org/rfc/rfc5581 /// https://www.rfc-editor.org/rfc/rfc6637 rfc4880, - /// RFC 9580 profile + /// RFC 9580 /// https://www.rfc-editor.org/rfc/rfc9580 rfc9580; } From 38ee9e6be889ea029d60f410f6d21f11ccc8a2f3 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 26 Nov 2024 11:04:52 +0700 Subject: [PATCH 067/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/aead_encrypted_data.dart | 17 ---------- lib/src/packet/sym_encrypted_data.dart | 15 --------- ...ym_encrypted_integrity_protected_data.dart | 33 +++++++------------ lib/src/type/encrypted_data_packet.dart | 6 ---- 4 files changed, 12 insertions(+), 59 deletions(-) diff --git a/lib/src/packet/aead_encrypted_data.dart b/lib/src/packet/aead_encrypted_data.dart index 067c7b04..5b268121 100644 --- a/lib/src/packet/aead_encrypted_data.dart +++ b/lib/src/packet/aead_encrypted_data.dart @@ -114,23 +114,6 @@ class AeadEncryptedDataPacket extends BasePacket implements EncryptedDataPacketI ...encrypted, ]); - @override - AeadEncryptedDataPacket encrypt( - final Uint8List key, { - final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - }) { - if (packets != null && packets!.isNotEmpty) { - return AeadEncryptedDataPacket.encryptPackets( - key, - packets!, - symmetric: symmetric, - aead: aead, - chunkSize: chunkSize, - ); - } - return this; - } - @override AeadEncryptedDataPacket decrypt( final Uint8List key, { diff --git a/lib/src/packet/sym_encrypted_data.dart b/lib/src/packet/sym_encrypted_data.dart index b12fda72..a92d5d9d 100644 --- a/lib/src/packet/sym_encrypted_data.dart +++ b/lib/src/packet/sym_encrypted_data.dart @@ -61,21 +61,6 @@ class SymEncryptedDataPacket extends BasePacket implements EncryptedDataPacketIn @override Uint8List get data => encrypted; - @override - SymEncryptedDataPacket encrypt( - final Uint8List key, { - final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - }) { - if (packets != null && packets!.isNotEmpty) { - return SymEncryptedDataPacket.encryptPackets( - key, - packets!, - symmetric: symmetric, - ); - } - return this; - } - @override SymEncryptedDataPacket decrypt( final Uint8List key, { diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index fc76dcb0..6be32ed3 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -132,12 +132,20 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc final Uint8List key, final PacketListInterface packets, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - final AeadAlgorithm? aead, + final AeadAlgorithm aead = AeadAlgorithm.gcm, final bool aeadProtect = false, }) { Helper.assertSymmetric(symmetric); - final version = aeadProtect || Config.presetRfc == PresetRfc.rfc9580 ? 2 : 1; + final version = switch (Config.presetRfc) { + PresetRfc.rfc4880 => 1, + PresetRfc.rfc9580 => 2, + }; + if (aeadProtect && version == 1) { + throw ArgumentError( + 'Using AEAD with version $version SEIPD packet is not allowed.', + ); + } final salt = aeadProtect ? Helper.randomBytes(saltSize) : null; final chunkSize = aeadProtect ? Config.aeadChunkSize : 0; @@ -148,7 +156,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc key, packets.encode(), symmetric: symmetric, - aead: aead ?? Config.preferredAead, + aead: aead, chunkSizeByte: chunkSize, salt: salt, ); @@ -177,7 +185,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc encrypted, packets: packets, symmetric: version == 2 ? symmetric : null, - aead: aeadProtect ? aead : null, + aead: aead, chunkSize: chunkSize, salt: salt, ); @@ -193,23 +201,6 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc ...encrypted, ]); - @override - SymEncryptedIntegrityProtectedDataPacket encrypt( - final Uint8List key, { - final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - }) { - if (packets != null && packets!.isNotEmpty) { - return SymEncryptedIntegrityProtectedDataPacket.encryptPackets( - key, - packets!, - symmetric: symmetric, - aead: aead, - aeadProtect: aead != null, - ); - } - return this; - } - @override SymEncryptedIntegrityProtectedDataPacket decrypt( final Uint8List key, { diff --git a/lib/src/type/encrypted_data_packet.dart b/lib/src/type/encrypted_data_packet.dart index 3d3bfd8a..3fc887cb 100644 --- a/lib/src/type/encrypted_data_packet.dart +++ b/lib/src/type/encrypted_data_packet.dart @@ -18,12 +18,6 @@ abstract class EncryptedDataPacketInterface { /// Decrypted packets contained within. PacketListInterface? get packets; - /// Encrypt the payload in the packet. - EncryptedDataPacketInterface encrypt( - final Uint8List key, { - final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - }); - /// Decrypt the encrypted data contained in the packet. EncryptedDataPacketInterface decrypt( final Uint8List key, { From 93213097285df031e6960fb63e5dc4fe53865d6b Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 26 Nov 2024 11:16:35 +0700 Subject: [PATCH 068/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/sym_encrypted_integrity_protected_data.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index 6be32ed3..213b3b4e 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -185,7 +185,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc encrypted, packets: packets, symmetric: version == 2 ? symmetric : null, - aead: aead, + aead: aeadProtect ? aead : null, chunkSize: chunkSize, salt: salt, ); From dcc36bb20c2c9d49836b87ac7725b27830c058af Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 26 Nov 2024 11:52:48 +0700 Subject: [PATCH 069/202] WIP Signed-off-by: Nguyen Van Nguyen --- .../packet/sym_encrypted_integrity_protected_data.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index 213b3b4e..f52c862e 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -139,7 +139,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc final version = switch (Config.presetRfc) { PresetRfc.rfc4880 => 1, - PresetRfc.rfc9580 => 2, + PresetRfc.rfc9580 => aeadProtect ? 2 : 1, }; if (aeadProtect && version == 1) { throw ArgumentError( @@ -185,7 +185,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc encrypted, packets: packets, symmetric: version == 2 ? symmetric : null, - aead: aeadProtect ? aead : null, + aead: version == 2 ? aead : null, chunkSize: chunkSize, salt: salt, ); @@ -248,7 +248,10 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc if (!verifyHash) { throw StateError('Modification detected.'); } - packetBytes = toHash.sublist(cipherSymmetric.blockSize + 2, toHash.length - 2); + packetBytes = toHash.sublist( + cipherSymmetric.blockSize + 2, + toHash.length - 2, + ); } return SymEncryptedIntegrityProtectedDataPacket( version, From 447451792e98456e95c9149b59f92e4e5f686c7b Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 26 Nov 2024 11:56:38 +0700 Subject: [PATCH 070/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/key_packet_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/packet/key_packet_test.dart b/test/packet/key_packet_test.dart index 0e55145f..369aa540 100644 --- a/test/packet/key_packet_test.dart +++ b/test/packet/key_packet_test.dart @@ -706,7 +706,7 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt }); group('Generate secret key', () { - const passphrase = 'password'; + final passphrase = Helper.generatePassword(); test('RSA keys', () { final secretKey = SecretKeyPacket.generate( From cdbf0133cc56e33af9c6094b11e686225f6d74e1 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 26 Nov 2024 13:26:41 +0700 Subject: [PATCH 071/202] WIP Signed-off-by: Nguyen Van Nguyen --- .../sym_encrypted_integrity_protected_data.dart | 15 +++------------ lib/src/packet/sym_encrypted_session_key.dart | 13 ++----------- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index f52c862e..20086c81 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -13,7 +13,6 @@ import '../common/helpers.dart'; import '../cryptor/symmetric/buffered_cipher.dart'; import '../enum/aead_algorithm.dart'; import '../enum/hash_algorithm.dart'; -import '../enum/preset_rfc.dart'; import '../enum/symmetric_algorithm.dart'; import '../type/encrypted_data_packet.dart'; import '../type/packet_list.dart'; @@ -137,15 +136,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc }) { Helper.assertSymmetric(symmetric); - final version = switch (Config.presetRfc) { - PresetRfc.rfc4880 => 1, - PresetRfc.rfc9580 => aeadProtect ? 2 : 1, - }; - if (aeadProtect && version == 1) { - throw ArgumentError( - 'Using AEAD with version $version SEIPD packet is not allowed.', - ); - } + final version = aeadProtect ? 2 : 1; final salt = aeadProtect ? Helper.randomBytes(saltSize) : null; final chunkSize = aeadProtect ? Config.aeadChunkSize : 0; @@ -184,8 +175,8 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc version, encrypted, packets: packets, - symmetric: version == 2 ? symmetric : null, - aead: version == 2 ? aead : null, + symmetric: aeadProtect ? symmetric : null, + aead: aeadProtect ? aead : null, chunkSize: chunkSize, salt: salt, ); diff --git a/lib/src/packet/sym_encrypted_session_key.dart b/lib/src/packet/sym_encrypted_session_key.dart index 49b55eda..61c5a007 100644 --- a/lib/src/packet/sym_encrypted_session_key.dart +++ b/lib/src/packet/sym_encrypted_session_key.dart @@ -9,12 +9,10 @@ import 'dart:typed_data'; import 'package:pointycastle/api.dart'; import '../common/argon2_s2k.dart'; -import '../common/config.dart'; import '../common/generic_s2k.dart'; import '../common/helpers.dart'; import '../cryptor/symmetric/buffered_cipher.dart'; import '../enum/aead_algorithm.dart'; -import '../enum/preset_rfc.dart'; import '../enum/s2k_type.dart'; import '../enum/symmetric_algorithm.dart'; import '../type/s2k.dart'; @@ -133,15 +131,8 @@ class SymEncryptedSessionKeyPacket extends BasePacket { final bool aeadProtect = false, }) { Helper.assertSymmetric(symmetric); - final version = switch (Config.presetRfc) { - PresetRfc.rfc4880 => 4, - PresetRfc.rfc9580 => 6, - }; - if (aeadProtect && sessionKey != null && version == 4) { - throw ArgumentError( - 'Using AEAD with version $version SKESK packet is not allowed.', - ); - } + + final version = aeadProtect && sessionKey != null ? 6 : 4; final s2k = version == 6 ? Helper.stringToKey( S2kType.argon2, From f5b1d0303f63ec22d34579897d33b13fccd134e0 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 26 Nov 2024 14:38:38 +0700 Subject: [PATCH 072/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/sym_encrypted_integrity_protected_data.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index 20086c81..b63b41cd 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -282,7 +282,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc final derivedKey = Helper.hkdf( key, - keySize, + keySize + ivLength, info: aData, salt: salt, ); @@ -318,7 +318,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc ); chunkData = chunkData.sublist(size); - nonce.setAll(ivLength - 4, chunkIndex.pack32()); + nonce.setAll(ivLength - 4, (++chunkIndex).pack32()); } /// For encryption: empty final chunk From f8ccc909e7ad2e0ce0ad74fe5d2c9ec9db4eb655 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 26 Nov 2024 14:42:49 +0700 Subject: [PATCH 073/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/encryption_test.dart | 120 +++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 test/packet/encryption_test.dart diff --git a/test/packet/encryption_test.dart b/test/packet/encryption_test.dart new file mode 100644 index 00000000..6e511b4c --- /dev/null +++ b/test/packet/encryption_test.dart @@ -0,0 +1,120 @@ +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/aead_algorithm.dart'; +import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; +import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/packet_list.dart'; +import 'package:dart_pg/src/type/literal_data.dart'; +import 'package:faker/faker.dart'; +import 'package:test/test.dart'; + +void main() { + group('Aead encrypted decryption', () { + const literalText = 'Hello, world!\n'; + + test('Decrypt eax', () { + final bytes = + '0107010eb732379f73c4928de25facfe6517ec105dc11a81dc0cb8a2f6f3d90016384a56fc821ae11ae8dbcb49862655dea88d06a81486801b0ff387bd2eab013de1259586906eab2476' + .hexToBytes(); + + final aepd = AeadEncryptedDataPacket.fromBytes(bytes); + expect(aepd.symmetric, SymmetricAlgorithm.aes128); + expect(aepd.aead, AeadAlgorithm.eax); + expect(aepd.chunkSize, 14); + expect(aepd.iv.toHexadecimal(), 'b732379f73c4928de25facfe6517ec10'); + + final decryptAepd = aepd.decrypt('86f1efb86952329f24acd3bfd0e5346d'.hexToBytes()); + final literalData = decryptAepd.packets!.elementAt(0) as LiteralDataInterface; + expect(literalData.binary, literalText.toBytes()); + }); + + test('Decrypt ocb', () { + final bytes = + '0107020e5ed2bc1e470abe8f1d644c7a6c8a567b0f7701196611a154ba9c2574cd056284a8ef68035c623d93cc708a43211bb6eaf2b27f7c18d571bcd83b20add3a08b73af15b9a098' + .hexToBytes(); + + final aepd = AeadEncryptedDataPacket.fromBytes(bytes); + expect(aepd.symmetric, SymmetricAlgorithm.aes128); + expect(aepd.aead, AeadAlgorithm.ocb); + expect(aepd.chunkSize, 14); + expect(aepd.iv.toHexadecimal(), '5ed2bc1e470abe8f1d644c7a6c8a56'); + + final decryptAepd = aepd.decrypt('d1f01ba30e130aa7d2582c16e050ae44'.hexToBytes()); + final literalData = decryptAepd.packets!.elementAt(0) as LiteralDataInterface; + expect(literalData.binary, literalText.toBytes()); + }); + }); + + group('Symmetrically decryption', () {}); + + group('Symmetrically encryption', () { + final literalData = LiteralDataPacket.fromText( + faker.randomGenerator.string(1000), + ); + final packets = PacketList([literalData]); + final key = Helper.generateEncryptionKey( + SymmetricAlgorithm.aes128, + ); // encryption key + + test('Encrypt SED', () { + final encrypted = SymEncryptedDataPacket.encryptPackets( + key, + packets, + symmetric: SymmetricAlgorithm.aes128, + ); + final encrypt = SymEncryptedDataPacket.fromBytes( + encrypted.data, + ); + + expect( + () => encrypt.decrypt( + key, + symmetric: SymmetricAlgorithm.aes128, + ), + throwsStateError, + ); + }); + + test('Encrypt V1 SEIPD', () { + final encrypted = SymEncryptedIntegrityProtectedDataPacket.encryptPackets( + key, + packets, + symmetric: SymmetricAlgorithm.aes128, + ); + expect(encrypted.version, 1); + expect(encrypted.symmetric, isNull); + expect(encrypted.aead, isNull); + + final decrypted = SymEncryptedIntegrityProtectedDataPacket.fromBytes( + encrypted.data, + ).decrypt( + key, + symmetric: SymmetricAlgorithm.aes128, + ); + final ldPacket = decrypted.packets!.elementAt(0); + expect(ldPacket.data, equals(literalData.data)); + }); + + test('Encrypt V2 SEIPD', () { + final encrypted = SymEncryptedIntegrityProtectedDataPacket.encryptPackets( + key, + packets, + symmetric: SymmetricAlgorithm.aes128, + aead: AeadAlgorithm.gcm, + aeadProtect: true, + ); + expect(encrypted.version, 2); + expect(encrypted.symmetric, SymmetricAlgorithm.aes128); + expect(encrypted.aead, AeadAlgorithm.gcm); + + final decrypted = SymEncryptedIntegrityProtectedDataPacket.fromBytes( + encrypted.data, + ).decrypt(key); + final ldPacket = decrypted.packets!.elementAt(0); + expect(ldPacket.data, equals(literalData.data)); + }); + }); + + group('Password protected session key', () {}); + + group('Public key protected session key', () {}); +} From 45aa3d6dd24cd75890eafd37aedd73306111ce8b Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 26 Nov 2024 15:23:09 +0700 Subject: [PATCH 074/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/encryption_test.dart | 140 ++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 1 deletion(-) diff --git a/test/packet/encryption_test.dart b/test/packet/encryption_test.dart index 6e511b4c..9f8c0ad6 100644 --- a/test/packet/encryption_test.dart +++ b/test/packet/encryption_test.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; + +import 'package:dart_pg/src/common/argon2_s2k.dart'; import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/enum/aead_algorithm.dart'; import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; @@ -44,7 +47,142 @@ void main() { }); }); - group('Symmetrically decryption', () {}); + group('Symmetrically decryption', () { + const passphrase = 'password'; + const literalText = "Hello, world!"; + + test('Encrypted using aead eax', () { + final skesk = SymEncryptedSessionKeyPacket.fromBytes(base64.decode( + 'Bh4HAQsDCKWuV50fxdgr/2kiT5GZk7NQb6O1mmpzz/jF78X0HFf7VOHCJoFdeCj1+SxFTrZevgCrWYbGjm58VQ==', + )).decrypt(passphrase); + expect(skesk.symmetric, SymmetricAlgorithm.aes128); + expect(skesk.aead, AeadAlgorithm.eax); + + final sessionKey = skesk.sessionKey!; + expect( + sessionKey.encryptionKey, + '3881bafe985412459b86c36f98cb9a5e'.hexToBytes(), + ); + + final seipd = SymEncryptedIntegrityProtectedDataPacket.fromBytes(base64.decode( + 'AgcBBp/5DjsyGWTzpCkTyNzGYZMlAVIn77fq6qSfBMLmdBddSj0ibtavy5yprBIsFHDhHGPUwKskHGqTitSL+ZpambkLuoMl3mEEdUAlireVmpWtBR3alusVQx3+9fXiJVyngmFUbjOa', + )).decrypt(sessionKey.encryptionKey); + expect(seipd.symmetric, SymmetricAlgorithm.aes128); + expect(seipd.aead, AeadAlgorithm.eax); + + final literalData = seipd.packets!.elementAt(0) as LiteralDataInterface; + expect(literalData.binary, literalText.toBytes()); + }); + + test('Encrypted using aead ocb', () { + final skesk = SymEncryptedSessionKeyPacket.fromBytes(base64.decode( + 'Bh0HAgsDCFaimNL142RT/8/MXBFmTtudtCWQ19xGsHJBthLDgSz/++oA8jR7JWQRI/iHrmDU/WFOCDfYGdNs', + )).decrypt(passphrase); + expect(skesk.symmetric, SymmetricAlgorithm.aes128); + expect(skesk.aead, AeadAlgorithm.ocb); + + final sessionKey = skesk.sessionKey!; + expect( + sessionKey.encryptionKey, + '28e79ab82397d3c63de24ac217d7b791'.hexToBytes(), + ); + + final seipd = SymEncryptedIntegrityProtectedDataPacket.fromBytes(base64.decode( + 'AgcCBiCmYfcx/JowMrViMyYCfjpdjbV0jr7/CwxZENCezdZB/5/ThWJ1gDW8SXVM4b8//6fa0KO4EE9RM89CpBAKg+70yhtIAaiEa/QrzafIzp1l4hLzAcvNmP3K3mlKh3rUJHMj9uhX', + )).decrypt(sessionKey.encryptionKey); + expect(seipd.symmetric, SymmetricAlgorithm.aes128); + expect(seipd.aead, AeadAlgorithm.ocb); + + final literalData = seipd.packets!.elementAt(0) as LiteralDataInterface; + expect(literalData.binary, literalText.toBytes()); + }); + + test('Encrypted using aead gcm', () { + final skesk = SymEncryptedSessionKeyPacket.fromBytes(base64.decode( + 'BhoHAwsDCOnTl4WyBwAI/7QufEg+9IhEV8s3Jrmz25/3duX02aQJUuJEcpiFGr//dSbfLdVUQXV5p3mf', + )).decrypt(passphrase); + expect(skesk.symmetric, SymmetricAlgorithm.aes128); + expect(skesk.aead, AeadAlgorithm.gcm); + + final sessionKey = skesk.sessionKey!; + expect( + sessionKey.encryptionKey, + '1936fc8568980274bb900d8319360c77'.hexToBytes(), + ); + + final seipd = SymEncryptedIntegrityProtectedDataPacket.fromBytes(base64.decode( + 'AgcDBvy5RJC8uYu9ydEGxgkCZpQPcuie3CG1WWsVdrEB7Q+f/G/G1lu/0k3NB5CWbm0ehaMAU3hMsdi2oGme8SFVp7KtYlhTG1dlH9d3eRL6leNdm0Ahb2mkwkjbKP9DMfFjKQc5nm/5', + )).decrypt(sessionKey.encryptionKey); + expect(seipd.symmetric, SymmetricAlgorithm.aes128); + expect(seipd.aead, AeadAlgorithm.gcm); + + final literalData = seipd.packets!.elementAt(0) as LiteralDataInterface; + expect(literalData.binary, literalText.toBytes()); + }); + + test('V4 SKESK Using Argon2 with AES-128', () { + final skesk = SymEncryptedSessionKeyPacket.fromBytes(base64.decode( + 'BAcEnFL4PCf5XlDVNUQOzf8xNgEEFZ5S/K0izz+VZULLp5TvhAsR', + )).decrypt(passphrase); + expect(skesk.symmetric, SymmetricAlgorithm.aes128); + expect(skesk.s2k is Argon2S2k, isTrue); + + final sessionKey = skesk.sessionKey!; + expect( + sessionKey.encryptionKey, + '01fe16bbacfd1e7b78ef3b865187374f'.hexToBytes(), + ); + expect(sessionKey.symmetric, SymmetricAlgorithm.aes128); + + final seipd = SymEncryptedIntegrityProtectedDataPacket.fromBytes(base64.decode( + 'AZgYpj5gnPi7oX4MOUME6vk1FBe38okh/ibiY6UrIL+6otumcslkydOrejv0bEFN0h07OEdd8DempXiZPMU=', + )).decrypt(sessionKey.encryptionKey, symmetric: sessionKey.symmetric); + final literalData = seipd.packets!.elementAt(0) as LiteralDataInterface; + expect(literalData.binary, literalText.toBytes()); + }); + + test('V4 SKESK Using Argon2 with AES-192', () { + final skesk = SymEncryptedSessionKeyPacket.fromBytes(base64.decode( + 'BAgE4UysRxU0WRipYtyjR+FD+AEEFYcyydr2txRvP6ZqSD3fx/5naFUuVQSy8Bc=', + )).decrypt(passphrase); + expect(skesk.symmetric, SymmetricAlgorithm.aes192); + expect(skesk.s2k is Argon2S2k, isTrue); + + final sessionKey = skesk.sessionKey!; + expect( + sessionKey.encryptionKey, + '27006dae68e509022ce45a14e569e91001c2955af8dfe194'.hexToBytes(), + ); + expect(sessionKey.symmetric, SymmetricAlgorithm.aes192); + + final seipd = SymEncryptedIntegrityProtectedDataPacket.fromBytes(base64.decode( + 'AdJ1Sw56PRYiKZjCvHg+2bnq02s33AJJoyBexBI4QKATFRkyez2gldJldRysLVg77Mwwfgl2n/d572WciAM=', + )).decrypt(sessionKey.encryptionKey, symmetric: sessionKey.symmetric); + final literalData = seipd.packets!.elementAt(0) as LiteralDataInterface; + expect(literalData.binary, literalText.toBytes()); + }); + + test('V4 SKESK Using Argon2 with AES-256', () { + final skesk = SymEncryptedSessionKeyPacket.fromBytes(base64.decode( + 'BAkEuHiVICBv95nGiCxCRaZifAEEFZ2fZeyrWoHQpZvVGkP2ejP+a6JJUhqRrutt2Jml3sxo/A==', + )).decrypt(passphrase); + expect(skesk.symmetric, SymmetricAlgorithm.aes256); + expect(skesk.s2k is Argon2S2k, isTrue); + + final sessionKey = skesk.sessionKey!; + expect( + sessionKey.encryptionKey, + 'bbeda55b9aae63dac45d4f49d89dacf4af37fefc13bab2f1f8e18fb74580d8b0'.hexToBytes(), + ); + expect(sessionKey.symmetric, SymmetricAlgorithm.aes256); + + final seipd = SymEncryptedIntegrityProtectedDataPacket.fromBytes(base64.decode( + 'AfirtbIE3SaPO19Vq7qe5dMCcqWZbNtVMHeu5vZKBetHnnx/yveQ9brJYlzhJvGskCUJma43+iur/T1sKjE=', + )).decrypt(sessionKey.encryptionKey, symmetric: sessionKey.symmetric); + final literalData = seipd.packets!.elementAt(0) as LiteralDataInterface; + expect(literalData.binary, literalText.toBytes()); + }); + }); group('Symmetrically encryption', () { final literalData = LiteralDataPacket.fromText( From a89f9a0ed84a0ebb6fa79e934ce8a5809bf060f5 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 26 Nov 2024 16:34:26 +0700 Subject: [PATCH 075/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/encryption_test.dart | 105 ++++++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 8 deletions(-) diff --git a/test/packet/encryption_test.dart b/test/packet/encryption_test.dart index 9f8c0ad6..d18be25c 100644 --- a/test/packet/encryption_test.dart +++ b/test/packet/encryption_test.dart @@ -1,10 +1,12 @@ import 'dart:convert'; import 'package:dart_pg/src/common/argon2_s2k.dart'; +import 'package:dart_pg/src/common/config.dart'; import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/enum/aead_algorithm.dart'; import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/key/session_key.dart'; import 'package:dart_pg/src/packet/packet_list.dart'; import 'package:dart_pg/src/type/literal_data.dart'; import 'package:faker/faker.dart'; @@ -48,13 +50,13 @@ void main() { }); group('Symmetrically decryption', () { - const passphrase = 'password'; + const password = 'password'; const literalText = "Hello, world!"; test('Encrypted using aead eax', () { final skesk = SymEncryptedSessionKeyPacket.fromBytes(base64.decode( 'Bh4HAQsDCKWuV50fxdgr/2kiT5GZk7NQb6O1mmpzz/jF78X0HFf7VOHCJoFdeCj1+SxFTrZevgCrWYbGjm58VQ==', - )).decrypt(passphrase); + )).decrypt(password); expect(skesk.symmetric, SymmetricAlgorithm.aes128); expect(skesk.aead, AeadAlgorithm.eax); @@ -77,7 +79,7 @@ void main() { test('Encrypted using aead ocb', () { final skesk = SymEncryptedSessionKeyPacket.fromBytes(base64.decode( 'Bh0HAgsDCFaimNL142RT/8/MXBFmTtudtCWQ19xGsHJBthLDgSz/++oA8jR7JWQRI/iHrmDU/WFOCDfYGdNs', - )).decrypt(passphrase); + )).decrypt(password); expect(skesk.symmetric, SymmetricAlgorithm.aes128); expect(skesk.aead, AeadAlgorithm.ocb); @@ -100,7 +102,7 @@ void main() { test('Encrypted using aead gcm', () { final skesk = SymEncryptedSessionKeyPacket.fromBytes(base64.decode( 'BhoHAwsDCOnTl4WyBwAI/7QufEg+9IhEV8s3Jrmz25/3duX02aQJUuJEcpiFGr//dSbfLdVUQXV5p3mf', - )).decrypt(passphrase); + )).decrypt(password); expect(skesk.symmetric, SymmetricAlgorithm.aes128); expect(skesk.aead, AeadAlgorithm.gcm); @@ -123,7 +125,7 @@ void main() { test('V4 SKESK Using Argon2 with AES-128', () { final skesk = SymEncryptedSessionKeyPacket.fromBytes(base64.decode( 'BAcEnFL4PCf5XlDVNUQOzf8xNgEEFZ5S/K0izz+VZULLp5TvhAsR', - )).decrypt(passphrase); + )).decrypt(password); expect(skesk.symmetric, SymmetricAlgorithm.aes128); expect(skesk.s2k is Argon2S2k, isTrue); @@ -144,7 +146,7 @@ void main() { test('V4 SKESK Using Argon2 with AES-192', () { final skesk = SymEncryptedSessionKeyPacket.fromBytes(base64.decode( 'BAgE4UysRxU0WRipYtyjR+FD+AEEFYcyydr2txRvP6ZqSD3fx/5naFUuVQSy8Bc=', - )).decrypt(passphrase); + )).decrypt(password); expect(skesk.symmetric, SymmetricAlgorithm.aes192); expect(skesk.s2k is Argon2S2k, isTrue); @@ -165,7 +167,7 @@ void main() { test('V4 SKESK Using Argon2 with AES-256', () { final skesk = SymEncryptedSessionKeyPacket.fromBytes(base64.decode( 'BAkEuHiVICBv95nGiCxCRaZifAEEFZ2fZeyrWoHQpZvVGkP2ejP+a6JJUhqRrutt2Jml3sxo/A==', - )).decrypt(passphrase); + )).decrypt(password); expect(skesk.symmetric, SymmetricAlgorithm.aes256); expect(skesk.s2k is Argon2S2k, isTrue); @@ -252,7 +254,94 @@ void main() { }); }); - group('Password protected session key', () {}); + group('Password protected session key', () { + final password = Helper.generatePassword(); + final literalText = faker.randomGenerator.string(1000); + + test('Encrypt with null session key', () { + final skesk = SymEncryptedSessionKeyPacket.encryptSessionKey( + password, + symmetric: Config.preferredSymmetric, + ); + final sessionKey = skesk.sessionKey!; + final seipd = SymEncryptedIntegrityProtectedDataPacket.encryptPackets( + sessionKey.encryptionKey, + PacketList([LiteralDataPacket.fromText(literalText)]), + symmetric: sessionKey.symmetric, + ); + expect(sessionKey.symmetric, skesk.symmetric); + + final packets = PacketList.decode(PacketList([skesk, seipd]).encode()); + final decryptSkesk = (packets[0] as SymEncryptedSessionKeyPacket).decrypt(password); + final decryptSeipd = (packets[1] as SymEncryptedIntegrityProtectedDataPacket).decrypt( + decryptSkesk.sessionKey!.encryptionKey, + symmetric: decryptSkesk.sessionKey!.symmetric, + ); + final literalData = decryptSeipd.packets!.elementAt(0) as LiteralDataInterface; + expect(literalData.text, literalText); + }); + + test('Encrypt with session key', () { + final sessionKey = SessionKey.produceKey(Config.preferredSymmetric); + final skesk = SymEncryptedSessionKeyPacket.encryptSessionKey( + password, + sessionKey: sessionKey, + symmetric: SymmetricAlgorithm.aes256, + ); + final seipd = SymEncryptedIntegrityProtectedDataPacket.encryptPackets( + sessionKey.encryptionKey, + PacketList([LiteralDataPacket.fromText(literalText)]), + symmetric: sessionKey.symmetric, + ); + expect(skesk.symmetric, SymmetricAlgorithm.aes256); + + final packets = PacketList.decode(PacketList([skesk, seipd]).encode()); + final decryptSkesk = (packets[0] as SymEncryptedSessionKeyPacket).decrypt(password); + final decryptSeipd = (packets[1] as SymEncryptedIntegrityProtectedDataPacket).decrypt( + decryptSkesk.sessionKey!.encryptionKey, + symmetric: decryptSkesk.sessionKey!.symmetric, + ); + final literalData = decryptSeipd.packets!.elementAt(0) as LiteralDataInterface; + expect( + decryptSkesk.symmetric, + SymmetricAlgorithm.aes256, + ); + expect(literalData.text, literalText); + }); + + test('Aead encrypt with session key', () { + final sessionKey = SessionKey.produceKey(Config.preferredSymmetric); + final skesk = SymEncryptedSessionKeyPacket.encryptSessionKey( + password, + sessionKey: sessionKey, + symmetric: SymmetricAlgorithm.aes256, + aead: Config.preferredAead, + aeadProtect: true, + ); + final seipd = SymEncryptedIntegrityProtectedDataPacket.encryptPackets( + sessionKey.encryptionKey, + PacketList([LiteralDataPacket.fromText(literalText)]), + symmetric: sessionKey.symmetric, + aead: Config.preferredAead, + aeadProtect: true, + ); + expect(skesk.version, 6); + expect(skesk.symmetric, SymmetricAlgorithm.aes256); + expect(skesk.aead, Config.preferredAead); + expect(seipd.version, 2); + expect(seipd.symmetric, sessionKey.symmetric); + expect(seipd.aead, Config.preferredAead); + + final packets = PacketList.decode(PacketList([skesk, seipd]).encode()); + final decryptSkesk = (packets[0] as SymEncryptedSessionKeyPacket).decrypt(password); + final decryptSeipd = (packets[1] as SymEncryptedIntegrityProtectedDataPacket).decrypt( + decryptSkesk.sessionKey!.encryptionKey, + symmetric: decryptSkesk.sessionKey!.symmetric, + ); + final literalData = decryptSeipd.packets!.elementAt(0) as LiteralDataInterface; + expect(literalData.text, literalText); + }); + }); group('Public key protected session key', () {}); } From 497e6a0b6b9b63490b19d8c26a0fd70f137f386a Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 26 Nov 2024 17:00:03 +0700 Subject: [PATCH 076/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/encryption_test.dart | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/test/packet/encryption_test.dart b/test/packet/encryption_test.dart index d18be25c..a5436c59 100644 --- a/test/packet/encryption_test.dart +++ b/test/packet/encryption_test.dart @@ -28,7 +28,7 @@ void main() { expect(aepd.iv.toHexadecimal(), 'b732379f73c4928de25facfe6517ec10'); final decryptAepd = aepd.decrypt('86f1efb86952329f24acd3bfd0e5346d'.hexToBytes()); - final literalData = decryptAepd.packets!.elementAt(0) as LiteralDataInterface; + final literalData = decryptAepd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); }); @@ -44,7 +44,7 @@ void main() { expect(aepd.iv.toHexadecimal(), '5ed2bc1e470abe8f1d644c7a6c8a56'); final decryptAepd = aepd.decrypt('d1f01ba30e130aa7d2582c16e050ae44'.hexToBytes()); - final literalData = decryptAepd.packets!.elementAt(0) as LiteralDataInterface; + final literalData = decryptAepd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); }); }); @@ -72,7 +72,7 @@ void main() { expect(seipd.symmetric, SymmetricAlgorithm.aes128); expect(seipd.aead, AeadAlgorithm.eax); - final literalData = seipd.packets!.elementAt(0) as LiteralDataInterface; + final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); }); @@ -95,7 +95,7 @@ void main() { expect(seipd.symmetric, SymmetricAlgorithm.aes128); expect(seipd.aead, AeadAlgorithm.ocb); - final literalData = seipd.packets!.elementAt(0) as LiteralDataInterface; + final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); }); @@ -118,7 +118,7 @@ void main() { expect(seipd.symmetric, SymmetricAlgorithm.aes128); expect(seipd.aead, AeadAlgorithm.gcm); - final literalData = seipd.packets!.elementAt(0) as LiteralDataInterface; + final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); }); @@ -139,7 +139,7 @@ void main() { final seipd = SymEncryptedIntegrityProtectedDataPacket.fromBytes(base64.decode( 'AZgYpj5gnPi7oX4MOUME6vk1FBe38okh/ibiY6UrIL+6otumcslkydOrejv0bEFN0h07OEdd8DempXiZPMU=', )).decrypt(sessionKey.encryptionKey, symmetric: sessionKey.symmetric); - final literalData = seipd.packets!.elementAt(0) as LiteralDataInterface; + final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); }); @@ -160,7 +160,7 @@ void main() { final seipd = SymEncryptedIntegrityProtectedDataPacket.fromBytes(base64.decode( 'AdJ1Sw56PRYiKZjCvHg+2bnq02s33AJJoyBexBI4QKATFRkyez2gldJldRysLVg77Mwwfgl2n/d572WciAM=', )).decrypt(sessionKey.encryptionKey, symmetric: sessionKey.symmetric); - final literalData = seipd.packets!.elementAt(0) as LiteralDataInterface; + final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); }); @@ -181,7 +181,7 @@ void main() { final seipd = SymEncryptedIntegrityProtectedDataPacket.fromBytes(base64.decode( 'AfirtbIE3SaPO19Vq7qe5dMCcqWZbNtVMHeu5vZKBetHnnx/yveQ9brJYlzhJvGskCUJma43+iur/T1sKjE=', )).decrypt(sessionKey.encryptionKey, symmetric: sessionKey.symmetric); - final literalData = seipd.packets!.elementAt(0) as LiteralDataInterface; + final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); }); }); @@ -272,12 +272,12 @@ void main() { expect(sessionKey.symmetric, skesk.symmetric); final packets = PacketList.decode(PacketList([skesk, seipd]).encode()); - final decryptSkesk = (packets[0] as SymEncryptedSessionKeyPacket).decrypt(password); - final decryptSeipd = (packets[1] as SymEncryptedIntegrityProtectedDataPacket).decrypt( + final decryptSkesk = packets.whereType().first.decrypt(password); + final decryptSeipd = packets.whereType().first.decrypt( decryptSkesk.sessionKey!.encryptionKey, symmetric: decryptSkesk.sessionKey!.symmetric, ); - final literalData = decryptSeipd.packets!.elementAt(0) as LiteralDataInterface; + final literalData = decryptSeipd.packets!.whereType().first; expect(literalData.text, literalText); }); @@ -296,12 +296,12 @@ void main() { expect(skesk.symmetric, SymmetricAlgorithm.aes256); final packets = PacketList.decode(PacketList([skesk, seipd]).encode()); - final decryptSkesk = (packets[0] as SymEncryptedSessionKeyPacket).decrypt(password); - final decryptSeipd = (packets[1] as SymEncryptedIntegrityProtectedDataPacket).decrypt( + final decryptSkesk = packets.whereType().first.decrypt(password); + final decryptSeipd = packets.whereType().first.decrypt( decryptSkesk.sessionKey!.encryptionKey, symmetric: decryptSkesk.sessionKey!.symmetric, ); - final literalData = decryptSeipd.packets!.elementAt(0) as LiteralDataInterface; + final literalData = decryptSeipd.packets!.whereType().first; expect( decryptSkesk.symmetric, SymmetricAlgorithm.aes256, @@ -333,12 +333,12 @@ void main() { expect(seipd.aead, Config.preferredAead); final packets = PacketList.decode(PacketList([skesk, seipd]).encode()); - final decryptSkesk = (packets[0] as SymEncryptedSessionKeyPacket).decrypt(password); - final decryptSeipd = (packets[1] as SymEncryptedIntegrityProtectedDataPacket).decrypt( + final decryptSkesk = packets.whereType().first.decrypt(password); + final decryptSeipd = packets.whereType().first.decrypt( decryptSkesk.sessionKey!.encryptionKey, symmetric: decryptSkesk.sessionKey!.symmetric, ); - final literalData = decryptSeipd.packets!.elementAt(0) as LiteralDataInterface; + final literalData = decryptSeipd.packets!.whereType().first; expect(literalData.text, literalText); }); }); From 771d9dcb09c0ff235a29aa077062742d0a83bf83 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 27 Nov 2024 10:45:56 +0700 Subject: [PATCH 077/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/key_wrapper.dart | 10 +- test/packet/encryption_test.dart | 164 ++++++++++++++++++++++++++-- 2 files changed, 157 insertions(+), 17 deletions(-) diff --git a/lib/src/packet/key/key_wrapper.dart b/lib/src/packet/key/key_wrapper.dart index 4d5fec21..8aef4ec1 100644 --- a/lib/src/packet/key/key_wrapper.dart +++ b/lib/src/packet/key/key_wrapper.dart @@ -15,10 +15,7 @@ export 'camellia_key_wrapper.dart'; /// An implementation of the key wrapper based on RFC 3394. /// Author Nguyen Van Nguyen abstract class KeyWrapper { - static final _iv = Uint8List.fromList([ - 0xa6, 0xa6, 0xa6, 0xa6, // 0 - 3 - 0xa6, 0xa6, 0xa6, 0xa6 - ]); + static const iv = [0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6, 0xa6]; final BlockCipher cipher; @@ -41,7 +38,7 @@ abstract class KeyWrapper { } cipher.init(true, KeyParameter(kek)); - final a = Uint8List.fromList(_iv); + final a = Uint8List.fromList(iv); final r = Uint8List.fromList(key); final n = key.length ~/ 8; for (var j = 0; j <= 5; j++) { @@ -91,8 +88,7 @@ abstract class KeyWrapper { r.setAll((i - 1) * 8, buffer.sublist(8, 16)); } } - - if (!_iv.equals(a)) { + if (!a.equals(Uint8List.fromList(iv))) { throw StateError('Integrity check failed.'); } diff --git a/test/packet/encryption_test.dart b/test/packet/encryption_test.dart index a5436c59..205e5be7 100644 --- a/test/packet/encryption_test.dart +++ b/test/packet/encryption_test.dart @@ -4,6 +4,7 @@ import 'package:dart_pg/src/common/argon2_s2k.dart'; import 'package:dart_pg/src/common/config.dart'; import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/enum/aead_algorithm.dart'; +import 'package:dart_pg/src/enum/key_algorithm.dart'; import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; import 'package:dart_pg/src/packet/base.dart'; import 'package:dart_pg/src/packet/key/session_key.dart'; @@ -274,9 +275,9 @@ void main() { final packets = PacketList.decode(PacketList([skesk, seipd]).encode()); final decryptSkesk = packets.whereType().first.decrypt(password); final decryptSeipd = packets.whereType().first.decrypt( - decryptSkesk.sessionKey!.encryptionKey, - symmetric: decryptSkesk.sessionKey!.symmetric, - ); + decryptSkesk.sessionKey!.encryptionKey, + symmetric: decryptSkesk.sessionKey!.symmetric, + ); final literalData = decryptSeipd.packets!.whereType().first; expect(literalData.text, literalText); }); @@ -298,9 +299,9 @@ void main() { final packets = PacketList.decode(PacketList([skesk, seipd]).encode()); final decryptSkesk = packets.whereType().first.decrypt(password); final decryptSeipd = packets.whereType().first.decrypt( - decryptSkesk.sessionKey!.encryptionKey, - symmetric: decryptSkesk.sessionKey!.symmetric, - ); + decryptSkesk.sessionKey!.encryptionKey, + symmetric: decryptSkesk.sessionKey!.symmetric, + ); final literalData = decryptSeipd.packets!.whereType().first; expect( decryptSkesk.symmetric, @@ -335,13 +336,156 @@ void main() { final packets = PacketList.decode(PacketList([skesk, seipd]).encode()); final decryptSkesk = packets.whereType().first.decrypt(password); final decryptSeipd = packets.whereType().first.decrypt( - decryptSkesk.sessionKey!.encryptionKey, - symmetric: decryptSkesk.sessionKey!.symmetric, - ); + decryptSkesk.sessionKey!.encryptionKey, + symmetric: decryptSkesk.sessionKey!.symmetric, + ); final literalData = decryptSeipd.packets!.whereType().first; expect(literalData.text, literalText); }); }); - group('Public key protected session key', () {}); + group('Public key protected session key', () { + const literalText = 'Hello World :)'; + + test('Decrypt with RSA subkey', () { + final subkeyData = ''' +BF2lnPIBDADWML9cbGMrp12CtF9b2P6z9TTT74S8iyBOzaSvdGDQY/sUtZXRg21HWamXnn9sSXvI +DEINOQ6A9QxdxoqWdCHrOuW3ofneYXoG+zeKc4dC86wa1TR2q9vW+RMXSO4uImA+Uzula/6k1Dog +Df28qhCxMwG/i/m9g1c/0aApuDyKdQ1PXsHHNlgd/Dn6rrd5y2AObaifV7wIhEJnvqgFXDN2RXGj +LeCOHV4Q2WTYPg/S4k1nMXVDwZXrvIsA0YwIMgIT86Rafp1qKlgPNbiIlC1g9RY/iFaGN2b4Ir6G +DohBQSfZW2+LXoPZuVE/wGlQ01rh827KVZW4lXvqsge+wtnWlszcselGATyzqOK9LdHPdZGzROZY +I2e8c+paLNDdVPL6vdRBUnkCaEkOtl1mr2JpQi5nTU+gTX4IeInC7E+1a9UDF/Y85ybUz8XV8rUn +R76UqVC7KidNepdHbZjjXCt8/Zo+Tec9JNbYNQB/e9ExmDntmlHEsSEQzFwzj8sxH48AEQEAAQAL +/RdgsLI0vko4dTNb3oCW2Y3ouIBdRx6RDNCtD0l7KUn1b6UeAKEieB3ugl0jFoNKLfFyrQ7maFfY +5yWhEuVC/aTAA+ycCDqmZvw2FSTOYTgEgodXN+ev8EmxW80Rz7VHWTvUN9FhTSTOaR2wiT47TaEZ +kRpH+9Ucbbxwc8u56RmvlulPzVSh8NItAmMDCNGJSg2pGFtz5vkC/oB2Rb54BsHk6HdH/ZdlSywJ +nkEf3PhGbO5TbpobmfJl3MIVHPSUCITpKTvsK3g8rBBZAHLCm5ED91A1LANYBcfaWzM09La9aGat +muEAbcUoR8z/6Yeyi6TfuqrY5LN8Qf8o04Ghpra8DPLUntMA9UcF0vv9aAR6V050O033jhlALuCg +qDkC7+hC6Slm0QS1roj8DHWxqb1RQCzvPpLygIPf6/v58szNhkB2PidyYeScT4iZx31zWYyCi2xW +yD3UijciQbms11vMSGv/5hBzCOTnL7GQSnJQCKey184xIylyMB8A3E5/aQYA6XTikxzrSdyVCsdK +1lbAx+sKuoilfxU1UmOY5czfqbjhLLJBdS38Z/SbCYoQFTt8jbKekb5CrcjZRWs2glSrQwJpI6Ie +tFzcXDUaq4Ap8HaW4HRz+r/4b2WOeGWiY8+i9nvLpzRJGC8Q7fHJgMhj4B9l4C+4IhyKfY67EGkt +/NYJ2MDV+sBNWhIpILWPdZ6iY8D5YmxfWr0smEKDLRKjP9VI04wdinQ/zIrSuXz9Fab7l6TWGXnZ +squFDjMHZIjTBgDq35jnk9KJYC60zE+R/ewp92MW5Nd7lxzbv2VGWTXvLPkLbi31JSgCbSxFLrTd +905VkGOdthVdJte0vm/NMSYm98mVSbXlbq64eR0lhwDPs4IUALwnrIOQi12tnTTJEwpmSoAgb3wy +2bmW4xcJ8AiOrvzYTmPXoCrlH0gP43v0V2k/JuVcS2DMcKkFygKcw/O379LSz5VSBL4xLGn/fdrR +RBUjT3BtZf46XeQFV8OpCrn/OVJCbWEdcdjJEA66mNUF/1zMNNJLfMvZDGVK3tfWggK3JqK/oQ4U +SRNxreECaw/c/2yAhEsOG+M9g24A/18/SF7AP6/XCojNYeUDLV77ocn1XjNNXp4cTBmzh94I6qhj +SsrMdFDgxhMK+gFfYQHfEVlzHISS2hMSvSeUkWsEoOu9TmwFNuEnWLzEPWeJAENvtUXlGc396IEj +EWbTE/ndfIE+i/dP8vgD2SGqAKyz4XmyABqt/Ry5idusd89FgIK6QNZDbI1xF5KImRjyyiBqHt4a +'''; + final packetListData = ''' +wcDMA3wvqk35PDeyAQv/UXZYWGSxURvRk1E/ONY6EjQGdTEVgcpFzSpxU+KFss8eByzz4gQSG2mD +NY19lplr395XIwZOjkW0SvZZyZ5fWoL8cCZmtsK4wzwTAv6pILHEsAu0lTX1SiS40sBPiN/G+gxH +5jdPWqS44glBb5TqtaXi4MUk/XW/TQlXwk7btk+GWDwn9k75vsSosKwdIiLeY4+opqZBzwrSq47d +yux7J5VbNsGLmUELG7vvYJPopv61c1k/V2OsuHhLTtYmdH0zwK5yaHBUlMyCzoR0RNMoNqPNY5aW +mcall4wG29sh0VPSc/SWmiWfJps6CC1m46enpYSRf6VtosrJfyoR/xyL6pS6SSf3AYlSeJIfRYrN +WUlZSqHRuD/11Hh9WQgbyikT0/HWN6MKUYaC5ozOr4w0KIsCOk2vdOU6ZCbwcZlm+ZJFfE8T1PGu ++osbpZiwXzhbxUX0vcK0IR9he0cxoLcsl/sp+ff0mvtD84oKHZy+WmBDORlQeGLOB3Bi1FGD8n3O +0j8B5NnjKZXdIKpjGUT2T6o+xPsJIufIVinzhlMReyqQ7d5gVNKAsuFQKzAcBv/hOIQnAabSiF4r +2QYTOt7WX7s= +'''; + + final subkey = SecretSubkeyPacket.fromBytes( + base64.decode( + subkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect(subkey.keyAlgorithm, KeyAlgorithm.rsaEncryptSign); + + final packetList = PacketList.decode( + base64.decode( + packetListData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final pkesk = packetList.whereType().first.decrypt(subkey); + final sessionKey = pkesk.sessionKey!; + final seipd = packetList.whereType().first.decrypt( + sessionKey.encryptionKey, + symmetric: sessionKey.symmetric, + ); + final literalData = seipd.packets!.whereType().first; + expect(literalData.binary, literalText.toBytes()); + }); + + test('Decrypt with ElGamal subkey', () { + final subkeyData = ''' +BF3+CmgQDADZhdKTM3ms3XpXnQke83FgaIBtP1g1qhqpCfg50WiPS0kjiMC0OJz2vh59nusbBLzg +I//Y1VMhKfIWYbqMcIY+lWbseHjl52rqW6AaJ0TH4NgVt7vhyVeJt0k/NnxvNhMd0587KXmfpDxr +wBqc/l5cVB+p0rL8vs8kxojHXAi5V3koM0UjREWs5Jpj/XU9LhEoyXZkeJC/pes1u6UKoFYn7dFI +P49Kkd1kb+1bNfdPYtA0JpcGzYgeMNOvdWJwn43dNhxoeuXfmAEhA8LdzT0C0O+7akXOKWrfhXJ8 +MTBqvPgWZYx7MNuQx/ejIMZHl+Iaf7hG976ILH+NCGiKkhidd9GIuA/WteHiQbXLyfiQ4n8P12q9 ++4dq6ybUM65tnozRyyN+1m3rU2a/+Ly3JCh4TeO27w+cxMWkaeHyTQaJVMbMbDpXduVd32MA33UV +NH5/KXMVczVi5asVjuKDSojJDV1QwX8izZNl1t+AI0L3balCabV0SFhlfnBEUj1my1sMAIfl/H7J +QB1nxW7/bNZMfHBYn9fqAZMupr0KZ8OrlQOpgUXO5bA3gcn6vI65qTUIbBIolQFIDvkcTFu/Sdpa +D6y7L6kQO8XRUAs9T1VSRJC0fJHXRg7YVY57cAS2ltgNHCl2vVnARtvcvogZDmL/gI0dsna7fJR5 +ewM0C+ulVIRwiMDTVE8I4qZ/nxINmnjIN0/EaEzzDprXz591CvbZ/ZwnTGB8+VvMVs74VSwSAq+f +pBMuFtpjDjOzut1AN6NYdXzaE/gr6tv0XCSdh1X26jibvsyAaVT7jK8mcYRhovePCMjdsf1qig06 +Xpdu9UDM3OiZiZpM7uanrEUC7jfK4bJ30r7UTiTsJBNE7FNn5F21CNX3mFKwSYyDv3adC8NIFbjH +B85Dul/eQLuv1+by72cGUQ3XYextDxi+7H+V3mrlFoiUPX2PN9VHr6EnNuPZmdTJCziSwB8gdPNN +0u21HFL2VNFORXHa9tSehIHLpNgXWZ/qdE+lKbWuJnGeRHj4FAv+MQaafW0uHF+N8MDm8UWPvf4V +d0UJ0UpIjRWl2hTV+BHkNfvZlBRhhQIphNiKRe/Wap0f/lW2Gm2uS0KgByjjNXEzTiwrte2GX65M +6F6Lz8N31kt1Iig1xGOuv+6HmxTNR8gL2K5PdJeJn8PTJWrRS7+BY8Hdkgb+wVpzE5cCvpFiG/P0 +yqfBdLWxVPlPI7dchDkmx4iAhHJX9J/gX/hC6L3AzPNJqNPAKy20wYp/ruTbbwBolW/4ikWij460 +JrvBsm6Sp81A3ebaiN9XkJygLOyhGyhMieGulCYz6AahAFcECtPXGTcordV1mJth8yjF4gZfDQyg +0nMW4Yr49yeFXcRMUw1yzN3Q9v2zzqDuFi2lGYTXYmVqLYzM9KbLO2WxE/21xnBjLsl09l/FdA/b +hdZq3t4/apbFOeQQ/j/AphvzWbsJnhG9Q7+d3VoDlz0gFiSduCYIAAq8dUOJNjrUTkZsL1pOIjhY +jCMi2uiKS6RQkT6nvuumPF/D/VTnUGeZAAD+KHmMi2GfZkSvVig6xSzwIGHKVOxFkrpVLhkIStzK +Xa0PYg== +'''; + final packetListData = ''' +wcJOA92wTJQbq0qsEAv/RmVvuYpXwWuEKi8FGkD/6brX96NaAYVY9NM6tzj2Cgk4QxPSlUQJQvGD +wP48EWfOGvXXJZxW6vwLnW9pdXKBBIfrYkU+3vjvrvUSqucP6JZpMIXcQD15heD98gpdA7Tws1fn +iTgVV0L1pRj0BBimQMahlMAXMV2HirUzG9edLl+/6w/JaFnuHquOwdP6bfqmnUdmG/x9z6hntMHY +b6E+eUM7LKhI+OyThnys+h/A/3BbdDsynVpYi7PUxvfOathoTYHEx6AmaA7rRtuPdAADKX04Du9v +73jdvxfiKX2lgxlIUxRCofgI+kohMsUqGKFROeJLunIXa9iJni2X0AXZO+7rWhZTC9ZPYlhP0K23 +TMcr4eTUqaQgaNfHhac15uF5J7mKGZsq5NguLYHf0ovwXtcwXq1TeZQTSHj3o1P+QvtcGaZF5FDF +rcyN2Z0oQkK4wdZl/z1wevqjOW6Fpy9kq2RF2zhAa9v3zXSeRr+ic8wOZAD8XPn1lP0q46YB/Oaw +C/9lsHZuduT0YBeG3l0cOEAPF4y78tA32wA3RcwmqDKk/Lp2u7tXejORCEqRqzLQ7rSlCcfivBoD +QczTuCM071hM8DQ33ENpGfW3w3/uqHRXDjaOKSld0bcPgmiHpLbOzAwyxgHM3SqwAZofj+Sw36/K +dkFpURmnQY89m/ELChBbfUWFbnXSsLOCHq0dj3FnDGTmNqQLCT/qte62YWVkgVghSSH6OhTmHZ81 +XUQI+RLHt3lvhF3YuUvdOd+/1WstKXwtTEMpmailGEyhbTnYeqV5rPo1NhaYvI2ieYzU6M3pz7hU +4ebMatDirZTy/o19unwJCAXQuhfVTblWHp8cvPB0BlkImhHr94AGDTAUwZCzP6+aNmWpoX3fBlDB +D7jv3XI21vufPF0FgXAvo/TOIZya+EmIY106HP2ySgaLCG++YGSU/DBbXIA9H5aGpA+MPiZmw8HO +sHn9xYLcI33N28qWQkPjvNkvYyMjuGb94ReXiN+SvuaMUFQpXjJGqw3PALnSQwGkJ4dgWQKW+gP3 +heUWSSwsi+sdLtKcnQQfj/RDqmhO9tmfk8sRTu3Myp9tYJLnjngOxsNEMoRRgo7eBSLVjUQlQu8= +'''; + + final subkey = SecretSubkeyPacket.fromBytes( + base64.decode( + subkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect(subkey.keyAlgorithm, KeyAlgorithm.elgamal); + + final packetList = PacketList.decode( + base64.decode( + packetListData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final pkesk = packetList.whereType().first.decrypt(subkey); + final sessionKey = pkesk.sessionKey!; + final seipd = packetList.whereType().first.decrypt( + sessionKey.encryptionKey, + symmetric: sessionKey.symmetric, + ); + final comPacket = seipd.packets!.whereType().first; + final literalData = comPacket.packets.whereType().first; + expect(literalData.binary, literalText.toBytes()); + }); + + test('Decrypt with ECDH subkey', () {}); + }); } From 5c97fc1e85c2339271f9f4f0284222eede1d10b8 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 27 Nov 2024 11:00:39 +0700 Subject: [PATCH 078/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/ecdh_session_key_cryptor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/packet/key/ecdh_session_key_cryptor.dart b/lib/src/packet/key/ecdh_session_key_cryptor.dart index a52b4609..51bf81bc 100644 --- a/lib/src/packet/key/ecdh_session_key_cryptor.dart +++ b/lib/src/packet/key/ecdh_session_key_cryptor.dart @@ -134,7 +134,7 @@ class ECDHSessionKeyCryptor extends SessionKeyCryptor { Uint8List.fromList( key.d.toUnsignedBytes().reversed.toList(), ), - ephemeralKey.toUnsignedBytes(), + ephemeralKey.toUnsignedBytes().sublist(1), ); break; case Ecc.ed25519: From 3db28d273c11c4d392bb21a22ebbc02f3988715c Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 27 Nov 2024 11:12:16 +0700 Subject: [PATCH 079/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/encryption_test.dart | 39 +++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/test/packet/encryption_test.dart b/test/packet/encryption_test.dart index 205e5be7..0984675a 100644 --- a/test/packet/encryption_test.dart +++ b/test/packet/encryption_test.dart @@ -486,6 +486,43 @@ heUWSSwsi+sdLtKcnQQfj/RDqmhO9tmfk8sRTu3Myp9tYJLnjngOxsNEMoRRgo7eBSLVjUQlQu8= expect(literalData.binary, literalText.toBytes()); }); - test('Decrypt with ECDH subkey', () {}); + test('Decrypt with ECDH subkey', () { + final subkeyData = ''' +BFxHBOkSCisGAQQBl1UBBQEBB0BC/wYhratJPOCptcKkMNgyIpFWK0KzLbTfHewT356+IgMBCAcA +AP9/8RTxulNe64U7qvtO4JhL2hWCn8UQerIAGIlukzE6UBCu +'''; + final packetListData = ''' +wV4DR2b2udXyHrYSAQdAbADLqbFjAj5vsaZrsEQyPE0f9MkFeEopzPJ5DhpBKVow0JijpGDNhQ53 +UuPfdlmTJQxTfOasai86MJvGTZbaiZq2MYBHn1w1UkAibx5RUX3r0j8B0WUc//ZJCcpU440qFbrD +SdJraH1GfEeeMdV9t8623Gu3xkQ4hXf+figKNUWdq+kwHGqbQQNoeai1TYYNCuY= +'''; + + final subkey = SecretSubkeyPacket.fromBytes( + base64.decode( + subkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); + + final packetList = PacketList.decode( + base64.decode( + packetListData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final pkesk = packetList.whereType().first.decrypt(subkey); + final sessionKey = pkesk.sessionKey!; + final seipd = packetList.whereType().first.decrypt( + sessionKey.encryptionKey, + symmetric: sessionKey.symmetric, + ); + final literalData = seipd.packets!.whereType().first; + expect(literalData.binary, literalText.toBytes()); + }); }); } From df77b1f6a0f435780d034cc9191d38347ba3b895 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 27 Nov 2024 11:24:54 +0700 Subject: [PATCH 080/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/ecdh_session_key_cryptor.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/src/packet/key/ecdh_session_key_cryptor.dart b/lib/src/packet/key/ecdh_session_key_cryptor.dart index 51bf81bc..aa56071c 100644 --- a/lib/src/packet/key/ecdh_session_key_cryptor.dart +++ b/lib/src/packet/key/ecdh_session_key_cryptor.dart @@ -72,7 +72,10 @@ class ECDHSessionKeyCryptor extends SessionKeyCryptor { final privateKey = nacl.PrivateKey.fromSeed( Helper.randomBytes(TweetNaCl.seedSize), ); - ephemeralKey = privateKey.publicKey.asTypedList.toBigIntWithSign(1); + ephemeralKey = Uint8List.fromList([ + 0x40, + ...privateKey.publicKey.asTypedList, + ]).toUnsignedBigInt(); sharedKey = TweetNaCl.crypto_scalarmult( Uint8List(TweetNaCl.sharedKeyLength), privateKey.asTypedList, From be771a19b4d1fe1363a810eddd25ed47273fc855 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 28 Nov 2024 09:14:19 +0700 Subject: [PATCH 081/202] WIP Signed-off-by: Nguyen Van Nguyen --- .../public_key_encrypted_session_key.dart | 4 +- lib/src/packet/secret_key.dart | 10 +++-- test/packet/encryption_test.dart | 40 +++++++++++++++++++ 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/lib/src/packet/public_key_encrypted_session_key.dart b/lib/src/packet/public_key_encrypted_session_key.dart index 2978506f..6f68d1b5 100644 --- a/lib/src/packet/public_key_encrypted_session_key.dart +++ b/lib/src/packet/public_key_encrypted_session_key.dart @@ -91,11 +91,11 @@ class PublicKeyEncryptedSessionKeyPacket extends BasePacket { bytes.sublist(pos), ), KeyAlgorithm.x25519 => MontgomerySessionKeyCryptor.fromBytes( - bytes, + bytes.sublist(pos), MontgomeryCurve.x25519, ), KeyAlgorithm.x448 => MontgomerySessionKeyCryptor.fromBytes( - bytes, + bytes.sublist(pos), MontgomeryCurve.x448, ), _ => throw UnsupportedError( diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index d05ef27f..34475c8d 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -210,10 +210,12 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { SecretKeyMaterialInterface? keyMaterial; var keyData = bytes.sublist(pos); if (s2kUsage == S2kUsage.none) { - final checksum = keyData.sublist(keyData.length - 2); - keyData = keyData.sublist(0, keyData.length - 2); - if (!checksum.equals(_computeChecksum(keyData))) { - throw StateError('Key checksum mismatch!'); + if (!isV6) { + final checksum = keyData.sublist(keyData.length - 2); + keyData = keyData.sublist(0, keyData.length - 2); + if (!checksum.equals(_computeChecksum(keyData))) { + throw StateError('Key checksum mismatch!'); + } } keyMaterial = _readKeyMaterial( keyData, diff --git a/test/packet/encryption_test.dart b/test/packet/encryption_test.dart index 0984675a..362cf643 100644 --- a/test/packet/encryption_test.dart +++ b/test/packet/encryption_test.dart @@ -524,5 +524,45 @@ SdJraH1GfEeeMdV9t8623Gu3xkQ4hXf+figKNUWdq+kwHGqbQQNoeai1TYYNCuY= final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); }); + + test('Decrypt with x25519 subkey', () { + final subkeyData = ''' +BmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u +/tVY6a//1q0NWC1X+yui3O24 +'''; + final packetListData = ''' +wW0GIQYSyD8ecG9jCP4VGkF3Q6HwM3kOk+mXhIjR2zeNqZMIhRmYQIdvyFFkbvQontVWA5ukwSV2 +G16sUGyAgCirPA+1ISgjo8WL43phtu5TkL7Z59/eXc9IogZy/V+V+SkcSc1vF0WQ2F8kX/Az0loC +CQIM3eSIHALVilESDk12PS37M9JkaseDvMWVtfEOE9b1lG3aYJXruqm0GZAV8DoyM/HCQBTOGa8m +UQi6XlKOsfRPH6ozJjwLn7nPdfz5pHPtR0QljShJqeM= +'''; + + final subkey = SecretSubkeyPacket.fromBytes( + base64.decode( + subkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect(subkey.keyAlgorithm, KeyAlgorithm.x25519); + + final packetList = PacketList.decode( + base64.decode( + packetListData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final pkesk = packetList.whereType().first.decrypt(subkey); + final sessionKey = pkesk.sessionKey!; + final seipd = packetList.whereType().first.decrypt( + sessionKey.encryptionKey, + symmetric: sessionKey.symmetric, + ); + final literalData = seipd.packets!.whereType().first; + expect(literalData.binary, literalText.toBytes()); + }); }); } From 77a9cdaa14a0e898405327809acd8b86f778562b Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 28 Nov 2024 13:26:12 +0700 Subject: [PATCH 082/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/encryption_test.dart | 49 +++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/test/packet/encryption_test.dart b/test/packet/encryption_test.dart index 362cf643..4fb5d971 100644 --- a/test/packet/encryption_test.dart +++ b/test/packet/encryption_test.dart @@ -525,7 +525,7 @@ SdJraH1GfEeeMdV9t8623Gu3xkQ4hXf+figKNUWdq+kwHGqbQQNoeai1TYYNCuY= expect(literalData.binary, literalText.toBytes()); }); - test('Decrypt with x25519 subkey', () { + test('Decrypt with x25519 rfc9580 subkey', () { final subkeyData = ''' BmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u /tVY6a//1q0NWC1X+yui3O24 @@ -564,5 +564,52 @@ UQi6XlKOsfRPH6ozJjwLn7nPdfz5pHPtR0QljShJqeM= final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); }); + + test('Decrypt with x448 rfc9580 subkey', () { + final subkeyData = ''' +BmUai2IaAAAAON1puvWiEA6dtJRSRWG1Qz8tV2diAMuGZqNhnU3tNZjR+oSkyTYsujbXrkc6aF11 +E95MHVNZu7AHAHyUpzdzMVTNhwCC9nTxPlJRQeN8iGN4l5jjJ3Q5kN/M/DW9t6AJzu33htP5cagh +BsL8GXscLICI +'''; + final packetListData = ''' +wXUGIQZh4qTsn8glFgGNbIdCTl8gH2OtkI/PAGCQ0gi9s9k/rhrDhXo7kUKDJ39FfNp3kmAaM24C +e3bcYXwLy0gF2i6rxfL20D+g3cxv0i3CuXQCgcbojTN/8KY8ExiVXdfo+OWIZ5XndtyMpJW28BiL +Hru+n9bSwM8CBwMMENh7cT8lILXteh885FrUUD1QJMtD7xJUn2y78cVGgFSIkLbvFPDerB37xuht +MRkykuWgbUoJH/kcgBPdeCoYzJmfLV9FyATv0/AYq0yWpQ0VUfNLTFyeHIGxz7NHvrzJSrOy1Gm3 +1PXqWvb4sBROjnOXoAk12JdPudz3l1QZT/DX947f4h6hwkVv7RRT0oOS2pMaz/mekRuD6utUcpsj +FQ/MEDphnhOsB4RH0il8YPVc9DCnf3GhSs66h+Z699MXHBaUmdtiN1IgoEgLfb/900U2TfI6dvrv +C56WIMA8EA1COvLGc9Ge4owW0UE8jIuqWLzA2nVg5belbzhNnOEh9b1cOcDUh8CfBuXqHEi/ANMU +OMmaIGfcHfQFVu5v/UMcLxcH/fSVF6DvtOxEoUxASWBSmp6yC4A778BFuDFXb+/T8FjuJBaUj9rC +SkYqt1TYVKG1XZPI4OdIvGtneo+vH/CqF6bxlLWU6oskZ5SE+xJblmmO01ObM9JRi9D8jZnXedTH +ExAnXHXIb8I= +'''; + + final subkey = SecretSubkeyPacket.fromBytes( + base64.decode( + subkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect(subkey.keyAlgorithm, KeyAlgorithm.x448); + + final packetList = PacketList.decode( + base64.decode( + packetListData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final pkesk = packetList.whereType().first.decrypt(subkey); + final sessionKey = pkesk.sessionKey!; + final seipd = packetList.whereType().first.decrypt( + sessionKey.encryptionKey, + symmetric: sessionKey.symmetric, + ); + final literalData = seipd.packets!.whereType().first; + expect(literalData.binary, 'Hello there'.toBytes()); + }); }); } From 1665a44066ecc79248b9de9ab17cafea4e181df0 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 28 Nov 2024 14:13:55 +0700 Subject: [PATCH 083/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/encryption_test.dart | 183 +++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/test/packet/encryption_test.dart b/test/packet/encryption_test.dart index 4fb5d971..32be1fe4 100644 --- a/test/packet/encryption_test.dart +++ b/test/packet/encryption_test.dart @@ -346,6 +346,7 @@ void main() { group('Public key protected session key', () { const literalText = 'Hello World :)'; + final sessionKey = SessionKey.produceKey(); test('Decrypt with RSA subkey', () { final subkeyData = ''' @@ -611,5 +612,187 @@ ExAnXHXIb8I= final literalData = seipd.packets!.whereType().first; expect(literalData.binary, 'Hello there'.toBytes()); }); + + test('Encrypt session key with RSA subkey', () { + final publicSubkeyData = ''' +BF2lnPIBDADWML9cbGMrp12CtF9b2P6z9TTT74S8iyBOzaSvdGDQY/sUtZXRg21HWamXnn9sSXvI +DEINOQ6A9QxdxoqWdCHrOuW3ofneYXoG+zeKc4dC86wa1TR2q9vW+RMXSO4uImA+Uzula/6k1Dog +Df28qhCxMwG/i/m9g1c/0aApuDyKdQ1PXsHHNlgd/Dn6rrd5y2AObaifV7wIhEJnvqgFXDN2RXGj +LeCOHV4Q2WTYPg/S4k1nMXVDwZXrvIsA0YwIMgIT86Rafp1qKlgPNbiIlC1g9RY/iFaGN2b4Ir6G +DohBQSfZW2+LXoPZuVE/wGlQ01rh827KVZW4lXvqsge+wtnWlszcselGATyzqOK9LdHPdZGzROZY +I2e8c+paLNDdVPL6vdRBUnkCaEkOtl1mr2JpQi5nTU+gTX4IeInC7E+1a9UDF/Y85ybUz8XV8rUn +R76UqVC7KidNepdHbZjjXCt8/Zo+Tec9JNbYNQB/e9ExmDntmlHEsSEQzFwzj8sxH48AEQEAAQ== +'''; + final secretSubkeyData = ''' +BF2lnPIBDADWML9cbGMrp12CtF9b2P6z9TTT74S8iyBOzaSvdGDQY/sUtZXRg21HWamXnn9sSXvI +DEINOQ6A9QxdxoqWdCHrOuW3ofneYXoG+zeKc4dC86wa1TR2q9vW+RMXSO4uImA+Uzula/6k1Dog +Df28qhCxMwG/i/m9g1c/0aApuDyKdQ1PXsHHNlgd/Dn6rrd5y2AObaifV7wIhEJnvqgFXDN2RXGj +LeCOHV4Q2WTYPg/S4k1nMXVDwZXrvIsA0YwIMgIT86Rafp1qKlgPNbiIlC1g9RY/iFaGN2b4Ir6G +DohBQSfZW2+LXoPZuVE/wGlQ01rh827KVZW4lXvqsge+wtnWlszcselGATyzqOK9LdHPdZGzROZY +I2e8c+paLNDdVPL6vdRBUnkCaEkOtl1mr2JpQi5nTU+gTX4IeInC7E+1a9UDF/Y85ybUz8XV8rUn +R76UqVC7KidNepdHbZjjXCt8/Zo+Tec9JNbYNQB/e9ExmDntmlHEsSEQzFwzj8sxH48AEQEAAQAL +/RdgsLI0vko4dTNb3oCW2Y3ouIBdRx6RDNCtD0l7KUn1b6UeAKEieB3ugl0jFoNKLfFyrQ7maFfY +5yWhEuVC/aTAA+ycCDqmZvw2FSTOYTgEgodXN+ev8EmxW80Rz7VHWTvUN9FhTSTOaR2wiT47TaEZ +kRpH+9Ucbbxwc8u56RmvlulPzVSh8NItAmMDCNGJSg2pGFtz5vkC/oB2Rb54BsHk6HdH/ZdlSywJ +nkEf3PhGbO5TbpobmfJl3MIVHPSUCITpKTvsK3g8rBBZAHLCm5ED91A1LANYBcfaWzM09La9aGat +muEAbcUoR8z/6Yeyi6TfuqrY5LN8Qf8o04Ghpra8DPLUntMA9UcF0vv9aAR6V050O033jhlALuCg +qDkC7+hC6Slm0QS1roj8DHWxqb1RQCzvPpLygIPf6/v58szNhkB2PidyYeScT4iZx31zWYyCi2xW +yD3UijciQbms11vMSGv/5hBzCOTnL7GQSnJQCKey184xIylyMB8A3E5/aQYA6XTikxzrSdyVCsdK +1lbAx+sKuoilfxU1UmOY5czfqbjhLLJBdS38Z/SbCYoQFTt8jbKekb5CrcjZRWs2glSrQwJpI6Ie +tFzcXDUaq4Ap8HaW4HRz+r/4b2WOeGWiY8+i9nvLpzRJGC8Q7fHJgMhj4B9l4C+4IhyKfY67EGkt +/NYJ2MDV+sBNWhIpILWPdZ6iY8D5YmxfWr0smEKDLRKjP9VI04wdinQ/zIrSuXz9Fab7l6TWGXnZ +squFDjMHZIjTBgDq35jnk9KJYC60zE+R/ewp92MW5Nd7lxzbv2VGWTXvLPkLbi31JSgCbSxFLrTd +905VkGOdthVdJte0vm/NMSYm98mVSbXlbq64eR0lhwDPs4IUALwnrIOQi12tnTTJEwpmSoAgb3wy +2bmW4xcJ8AiOrvzYTmPXoCrlH0gP43v0V2k/JuVcS2DMcKkFygKcw/O379LSz5VSBL4xLGn/fdrR +RBUjT3BtZf46XeQFV8OpCrn/OVJCbWEdcdjJEA66mNUF/1zMNNJLfMvZDGVK3tfWggK3JqK/oQ4U +SRNxreECaw/c/2yAhEsOG+M9g24A/18/SF7AP6/XCojNYeUDLV77ocn1XjNNXp4cTBmzh94I6qhj +SsrMdFDgxhMK+gFfYQHfEVlzHISS2hMSvSeUkWsEoOu9TmwFNuEnWLzEPWeJAENvtUXlGc396IEj +EWbTE/ndfIE+i/dP8vgD2SGqAKyz4XmyABqt/Ry5idusd89FgIK6QNZDbI1xF5KImRjyyiBqHt4a +'''; + + final publicSubkey = PublicSubkeyPacket.fromBytes( + base64.decode( + publicSubkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final secretSubkey = SecretSubkeyPacket.fromBytes( + base64.decode( + secretSubkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect(publicSubkey.keyAlgorithm, KeyAlgorithm.rsaEncryptSign); + expect(publicSubkey.fingerprint, secretSubkey.fingerprint); + + final encryptedPkesk = PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( + publicSubkey, + sessionKey, + ); + final decryptedPkesk = PublicKeyEncryptedSessionKeyPacket.fromBytes( + encryptedPkesk.data, + ).decrypt(secretSubkey); + expect(decryptedPkesk.sessionKey!.encryptionKey, sessionKey.encryptionKey); + }); + + test('Encrypt session key with ECDH subkey', () { + final publicSubkeyData = ''' +BFxHBOkSCisGAQQBl1UBBQEBB0BC/wYhratJPOCptcKkMNgyIpFWK0KzLbTfHewT356+IgMBCAc= +'''; + final secretSubkeyData = ''' +BFxHBOkSCisGAQQBl1UBBQEBB0BC/wYhratJPOCptcKkMNgyIpFWK0KzLbTfHewT356+IgMBCAcA +AP9/8RTxulNe64U7qvtO4JhL2hWCn8UQerIAGIlukzE6UBCu +'''; + + final publicSubkey = PublicSubkeyPacket.fromBytes( + base64.decode( + publicSubkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final secretSubkey = SecretSubkeyPacket.fromBytes( + base64.decode( + secretSubkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect(publicSubkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(publicSubkey.fingerprint, secretSubkey.fingerprint); + + final encryptedPkesk = PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( + publicSubkey, + sessionKey, + ); + final decryptedPkesk = PublicKeyEncryptedSessionKeyPacket.fromBytes( + encryptedPkesk.data, + ).decrypt(secretSubkey); + expect(decryptedPkesk.sessionKey!.encryptionKey, sessionKey.encryptionKey); + }); + + test('Encrypt session key with x25519 rfc9580 subkey', () { + final publicSubkeyData = ''' +BmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1 +'''; + final secretSubkeyData = ''' +BmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u +/tVY6a//1q0NWC1X+yui3O24 +'''; + + final publicSubkey = PublicSubkeyPacket.fromBytes( + base64.decode( + publicSubkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final secretSubkey = SecretSubkeyPacket.fromBytes( + base64.decode( + secretSubkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect(publicSubkey.keyAlgorithm, KeyAlgorithm.x25519); + expect(publicSubkey.fingerprint, secretSubkey.fingerprint); + + final encryptedPkesk = PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( + publicSubkey, + sessionKey, + ); + final decryptedPkesk = PublicKeyEncryptedSessionKeyPacket.fromBytes( + encryptedPkesk.data, + ).decrypt(secretSubkey); + expect(decryptedPkesk.sessionKey!.encryptionKey, sessionKey.encryptionKey); + }); + + test('Encrypt session key with x448 rfc9580 subkey', () { + final publicSubkeyData = ''' +BmUai2IaAAAAON1puvWiEA6dtJRSRWG1Qz8tV2diAMuGZqNhnU3tNZjR+oSkyTYsujbXrkc6aF11 +E95MHVNZu7AH +'''; + final secretSubkeyData = ''' +BmUai2IaAAAAON1puvWiEA6dtJRSRWG1Qz8tV2diAMuGZqNhnU3tNZjR+oSkyTYsujbXrkc6aF11 +E95MHVNZu7AHAHyUpzdzMVTNhwCC9nTxPlJRQeN8iGN4l5jjJ3Q5kN/M/DW9t6AJzu33htP5cagh +BsL8GXscLICI +'''; + + final publicSubkey = PublicSubkeyPacket.fromBytes( + base64.decode( + publicSubkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final secretSubkey = SecretSubkeyPacket.fromBytes( + base64.decode( + secretSubkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect(publicSubkey.keyAlgorithm, KeyAlgorithm.x448); + expect(publicSubkey.fingerprint, secretSubkey.fingerprint); + + final encryptedPkesk = PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( + publicSubkey, + sessionKey, + ); + final decryptedPkesk = PublicKeyEncryptedSessionKeyPacket.fromBytes( + encryptedPkesk.data, + ).decrypt(secretSubkey); + expect(decryptedPkesk.sessionKey!.encryptionKey, sessionKey.encryptionKey); + }); }); } From a722ebef6272ce95a78053a61c6fa566bed182d1 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 29 Nov 2024 08:36:20 +0700 Subject: [PATCH 084/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/cryptor/dh/x448.dart | 225 ++++++++++++++++++ .../{ecc/x448.dart => math/fp448.dart} | 218 +---------------- .../key/montgomery_secret_material.dart | 2 +- test/crypto/{ecc_test.dart => x448_test.dart} | 2 +- 4 files changed, 229 insertions(+), 218 deletions(-) create mode 100644 lib/src/cryptor/dh/x448.dart rename lib/src/cryptor/{ecc/x448.dart => math/fp448.dart} (75%) rename test/crypto/{ecc_test.dart => x448_test.dart} (99%) diff --git a/lib/src/cryptor/dh/x448.dart b/lib/src/cryptor/dh/x448.dart new file mode 100644 index 00000000..785aca29 --- /dev/null +++ b/lib/src/cryptor/dh/x448.dart @@ -0,0 +1,225 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../math/fp448.dart'; + +/// Implementation of rfc-7748 x448 +/// Author Nguyen Van Nguyen +final class X448 { + static const payloadSize = 56; + static const a24 = 39082; // (156326 + 2) % 4; + static const mask_8 = 0xff; + + /// Calculate a scalar point multiplication from base scalar + static Uint8List scalarMultBase( + final Uint8List k, [ + final int kOff = 0, + ]) { + final u = Uint8List(payloadSize); + u[0] = 5; + return X448.scalarMult(k, u, kOff); + } + + /// Calculate a generic scalar point multiplication + static Uint8List scalarMult( + final Uint8List k, + final Uint8List u, [ + final int kOff = 0, + final int uOff = 0, + ]) { + final n = _decodeScalar(k, kOff); + + final x1 = decode(u, uOff); + final x2 = Fp448.create(); + x2.setAll(0, x1); + final z2 = Fp448.create(); + z2[0] = 1; + final x3 = Fp448.create(); + x3[0] = 1; + final z3 = Fp448.create(); + + final t1 = Fp448.create(); + final t2 = Fp448.create(); + + var bit = 447, swap = 1; + do { + Fp448.add(x3, z3, t1); + Fp448.sub(x3, z3, x3); + + Fp448.add(x2, z2, z3); + Fp448.sub(x2, z2, x2); + + Fp448.mul(t1, x2, t1); + Fp448.mul(x3, z3, x3); + + Fp448.sqr(z3, z3); + Fp448.sqr(x2, x2); + + Fp448.sub(z3, x2, t2); + Fp448.mulA24(t2, a24, z2); + Fp448.add(z2, x2, z2); + Fp448.mul(z2, t2, z2); + Fp448.mul(x2, z3, x2); + + Fp448.sub(t1, x3, z3); + Fp448.add(t1, x3, x3); + Fp448.sqr(x3, x3); + Fp448.sqr(z3, z3); + Fp448.mul(z3, x1, z3); + + --bit; + + final word = bit >>> 5; + final shift = bit & 0x1F; + final kt = (n[word] >>> shift) & 1; + swap ^= kt; + Fp448.cswap(swap, x2, x3); + Fp448.cswap(swap, z2, z3); + swap = kt; + } while (bit >= 2); + + for (var i = 0; i < 2; ++i) { + _pointDouble(x2, z2); + } + + Fp448.inv(z2, z2); + Fp448.mul(x2, z2, x2); + Fp448.normalize(x2); + + return encode(x2); + } + + static Uint32List decode( + final Uint8List bytes, [ + final int off = 0, + ]) { + final z = Fp448.create(); + _decode56(bytes, z, off); + _decode56(bytes, z, off + 7, 2); + _decode56(bytes, z, off + 14, 4); + _decode56(bytes, z, off + 21, 6); + _decode56(bytes, z, off + 28, 8); + _decode56(bytes, z, off + 35, 10); + _decode56(bytes, z, off + 42, 12); + _decode56(bytes, z, off + 49, 14); + return z; + } + + static Uint8List encode( + final Uint32List x, [ + final int off = 0, + ]) { + final z = Uint8List(payloadSize); + _encode56(x, z, off); + _encode56(x, z, off + 2, 7); + _encode56(x, z, off + 4, 14); + _encode56(x, z, off + 6, 21); + _encode56(x, z, off + 8, 28); + _encode56(x, z, off + 10, 35); + _encode56(x, z, off + 12, 42); + _encode56(x, z, off + 14, 49); + return z; + } + + static void _pointDouble( + final Uint32List x, + final Uint32List z, + ) { + final a = Fp448.create(); + final b = Fp448.create(); + + Fp448.add(x, z, a); + Fp448.sub(x, z, b); + Fp448.sqr(a, a); + Fp448.sqr(b, b); + Fp448.mul(a, b, x); + Fp448.sub(a, b, a); + Fp448.mulA24(a, a24, z); + Fp448.add(z, b, z); + Fp448.mul(z, a, z); + } + + static Uint32List _decodeScalar( + final Uint8List bytes, + final int off, + ) { + final n = Uint32List(14); + for (var i = 0; i < 14; ++i) { + n[i] = _decode32(bytes, off + i * 4); + } + + n[0] &= 0xfffffffc; + n[13] |= 0x80000000; + return n; + } + + static int _decode24( + final Uint8List bytes, [ + final int off = 0, + ]) { + var n = bytes[off]; + n |= bytes[off + 1] << 8; + n |= bytes[off + 2] << 16; + return n; + } + + static int _decode32( + final Uint8List bytes, [ + final int off = 0, + ]) { + var n = bytes[off]; + n |= bytes[off + 1] << 8; + n |= bytes[off + 2] << 16; + n |= bytes[off + 3] << 24; + return n; + } + + static void _decode56( + final Uint8List bytes, + final Uint32List z, [ + final int off = 0, + final int zOff = 0, + ]) { + final lo = _decode32(bytes, off); + final hi = _decode24(bytes, off + 4); + z[zOff] = lo & Fp448.m28; + z[zOff + 1] = (lo >>> 28) | (hi << 4); + } + + static void _encode24( + final int n, + final Uint8List bytes, [ + final int off = 0, + ]) { + bytes[off] = n & mask_8; + bytes[off + 1] = (n >>> 8) & mask_8; + bytes[off + 2] = (n >>> 16) & mask_8; + } + + static void _encode32( + final int n, + final Uint8List bytes, [ + final int off = 0, + ]) { + bytes[off] = n & mask_8; + bytes[off + 1] = (n >>> 8) & mask_8; + bytes[off + 2] = (n >>> 16) & mask_8; + bytes[off + 3] = (n >>> 24) & mask_8; + } + + static void _encode56( + final Uint32List x, + final Uint8List bytes, [ + final int xOff = 0, + final int off = 0, + ]) { + final lo = x[xOff], hi = x[xOff + 1]; + _encode32(lo | (hi << 28), bytes, off); + _encode24(hi >>> 4, bytes, off + 4); + } +} diff --git a/lib/src/cryptor/ecc/x448.dart b/lib/src/cryptor/math/fp448.dart similarity index 75% rename from lib/src/cryptor/ecc/x448.dart rename to lib/src/cryptor/math/fp448.dart index 2545cfbc..4102b6a5 100644 --- a/lib/src/cryptor/ecc/x448.dart +++ b/lib/src/cryptor/math/fp448.dart @@ -6,223 +6,9 @@ library; import 'dart:typed_data'; -/// Implementation of rfc-7748 x448 +/// Implementation of rfc-7748 x448 field point /// Author Nguyen Van Nguyen -final class X448 { - static const payloadSize = 56; - static const a24 = 39082; // (156326 + 2) % 4; - static const mask_8 = 0xff; - - /// Calculate a scalar point multiplication from base scalar - static Uint8List scalarMultBase( - final Uint8List k, [ - final int kOff = 0, - ]) { - final u = Uint8List(payloadSize); - u[0] = 5; - return X448.scalarMult(k, u, kOff); - } - - /// Calculate a generic scalar point multiplication - static Uint8List scalarMult( - final Uint8List k, - final Uint8List u, [ - final int kOff = 0, - final int uOff = 0, - ]) { - final n = _decodeScalar(k, kOff); - - final x1 = decode(u, uOff); - final x2 = X448Field.create(); - x2.setAll(0, x1); - final z2 = X448Field.create(); - z2[0] = 1; - final x3 = X448Field.create(); - x3[0] = 1; - final z3 = X448Field.create(); - - final t1 = X448Field.create(); - final t2 = X448Field.create(); - - var bit = 447, swap = 1; - do { - X448Field.add(x3, z3, t1); - X448Field.sub(x3, z3, x3); - - X448Field.add(x2, z2, z3); - X448Field.sub(x2, z2, x2); - - X448Field.mul(t1, x2, t1); - X448Field.mul(x3, z3, x3); - - X448Field.sqr(z3, z3); - X448Field.sqr(x2, x2); - - X448Field.sub(z3, x2, t2); - X448Field.mulA24(t2, a24, z2); - X448Field.add(z2, x2, z2); - X448Field.mul(z2, t2, z2); - X448Field.mul(x2, z3, x2); - - X448Field.sub(t1, x3, z3); - X448Field.add(t1, x3, x3); - X448Field.sqr(x3, x3); - X448Field.sqr(z3, z3); - X448Field.mul(z3, x1, z3); - - --bit; - - final word = bit >>> 5; - final shift = bit & 0x1F; - final kt = (n[word] >>> shift) & 1; - swap ^= kt; - X448Field.cswap(swap, x2, x3); - X448Field.cswap(swap, z2, z3); - swap = kt; - } while (bit >= 2); - - for (var i = 0; i < 2; ++i) { - _pointDouble(x2, z2); - } - - X448Field.inv(z2, z2); - X448Field.mul(x2, z2, x2); - X448Field.normalize(x2); - - return encode(x2); - } - - static Uint32List decode( - final Uint8List bytes, [ - final int off = 0, - ]) { - final z = X448Field.create(); - _decode56(bytes, z, off); - _decode56(bytes, z, off + 7, 2); - _decode56(bytes, z, off + 14, 4); - _decode56(bytes, z, off + 21, 6); - _decode56(bytes, z, off + 28, 8); - _decode56(bytes, z, off + 35, 10); - _decode56(bytes, z, off + 42, 12); - _decode56(bytes, z, off + 49, 14); - return z; - } - - static Uint8List encode( - final Uint32List x, [ - final int off = 0, - ]) { - final z = Uint8List(payloadSize); - _encode56(x, z, off); - _encode56(x, z, off + 2, 7); - _encode56(x, z, off + 4, 14); - _encode56(x, z, off + 6, 21); - _encode56(x, z, off + 8, 28); - _encode56(x, z, off + 10, 35); - _encode56(x, z, off + 12, 42); - _encode56(x, z, off + 14, 49); - return z; - } - - static void _pointDouble( - final Uint32List x, - final Uint32List z, - ) { - final a = X448Field.create(); - final b = X448Field.create(); - - X448Field.add(x, z, a); - X448Field.sub(x, z, b); - X448Field.sqr(a, a); - X448Field.sqr(b, b); - X448Field.mul(a, b, x); - X448Field.sub(a, b, a); - X448Field.mulA24(a, a24, z); - X448Field.add(z, b, z); - X448Field.mul(z, a, z); - } - - static Uint32List _decodeScalar( - final Uint8List bytes, - final int off, - ) { - final n = Uint32List(14); - for (var i = 0; i < 14; ++i) { - n[i] = _decode32(bytes, off + i * 4); - } - - n[0] &= 0xfffffffc; - n[13] |= 0x80000000; - return n; - } - - static int _decode24( - final Uint8List bytes, [ - final int off = 0, - ]) { - var n = bytes[off]; - n |= bytes[off + 1] << 8; - n |= bytes[off + 2] << 16; - return n; - } - - static int _decode32( - final Uint8List bytes, [ - final int off = 0, - ]) { - var n = bytes[off]; - n |= bytes[off + 1] << 8; - n |= bytes[off + 2] << 16; - n |= bytes[off + 3] << 24; - return n; - } - - static void _decode56( - final Uint8List bytes, - final Uint32List z, [ - final int off = 0, - final int zOff = 0, - ]) { - final lo = _decode32(bytes, off); - final hi = _decode24(bytes, off + 4); - z[zOff] = lo & X448Field.m28; - z[zOff + 1] = (lo >>> 28) | (hi << 4); - } - - static void _encode24( - final int n, - final Uint8List bytes, [ - final int off = 0, - ]) { - bytes[off] = n & mask_8; - bytes[off + 1] = (n >>> 8) & mask_8; - bytes[off + 2] = (n >>> 16) & mask_8; - } - - static void _encode32( - final int n, - final Uint8List bytes, [ - final int off = 0, - ]) { - bytes[off] = n & mask_8; - bytes[off + 1] = (n >>> 8) & mask_8; - bytes[off + 2] = (n >>> 16) & mask_8; - bytes[off + 3] = (n >>> 24) & mask_8; - } - - static void _encode56( - final Uint32List x, - final Uint8List bytes, [ - final int xOff = 0, - final int off = 0, - ]) { - final lo = x[xOff], hi = x[xOff + 1]; - _encode32(lo | (hi << 28), bytes, off); - _encode24(hi >>> 4, bytes, off + 4); - } -} - -final class X448Field { +class Fp448 { /// Field element size static const size = 16; diff --git a/lib/src/packet/key/montgomery_secret_material.dart b/lib/src/packet/key/montgomery_secret_material.dart index 08e311d2..a6f5db0f 100644 --- a/lib/src/packet/key/montgomery_secret_material.dart +++ b/lib/src/packet/key/montgomery_secret_material.dart @@ -10,7 +10,7 @@ import 'package:pinenacl/tweetnacl.dart'; import '../../common/helpers.dart'; import '../../type/secret_key_material.dart'; -import '../../cryptor/ecc/x448.dart'; +import '../../cryptor/dh/x448.dart'; import '../../enum/montgomery_curve.dart'; import 'montgomery_public_material.dart'; diff --git a/test/crypto/ecc_test.dart b/test/crypto/x448_test.dart similarity index 99% rename from test/crypto/ecc_test.dart rename to test/crypto/x448_test.dart index 9400535b..6db90396 100644 --- a/test/crypto/ecc_test.dart +++ b/test/crypto/x448_test.dart @@ -1,5 +1,5 @@ import 'package:dart_pg/src/common/helpers.dart'; -import 'package:dart_pg/src/cryptor/ecc/x448.dart'; +import 'package:dart_pg/src/cryptor/dh/x448.dart'; import 'package:test/test.dart'; void main() { From d6b053bd75aecd253c026ecaaac77622d34c3d5a Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 29 Nov 2024 08:53:13 +0700 Subject: [PATCH 085/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/cryptor/math/fp448.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/cryptor/math/fp448.dart b/lib/src/cryptor/math/fp448.dart index 4102b6a5..58ff0e6f 100644 --- a/lib/src/cryptor/math/fp448.dart +++ b/lib/src/cryptor/math/fp448.dart @@ -6,7 +6,7 @@ library; import 'dart:typed_data'; -/// Implementation of rfc-7748 x448 field point +/// Implementation of rfc-7748 x448 field element /// Author Nguyen Van Nguyen class Fp448 { /// Field element size From 3763cb366de0c7ecb2dafa354f2d93d41074de04 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 29 Nov 2024 09:19:46 +0700 Subject: [PATCH 086/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/cryptor/math/fp448.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/cryptor/math/fp448.dart b/lib/src/cryptor/math/fp448.dart index 58ff0e6f..c6212d8b 100644 --- a/lib/src/cryptor/math/fp448.dart +++ b/lib/src/cryptor/math/fp448.dart @@ -8,7 +8,7 @@ import 'dart:typed_data'; /// Implementation of rfc-7748 x448 field element /// Author Nguyen Van Nguyen -class Fp448 { +final class Fp448 { /// Field element size static const size = 16; From 399456ed80a52fcf8dbd05fcbbac501f5f7e9edf Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 29 Nov 2024 10:37:38 +0700 Subject: [PATCH 087/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/ecdh_secret_material.dart | 14 +++++--------- lib/src/packet/key/ecdh_session_key_cryptor.dart | 2 +- .../packet/key/eddsa_legacy_secret_material.dart | 6 +++--- lib/src/packet/key/elgamal_secret_material.dart | 5 ++++- lib/src/packet/key/montgomery_secret_material.dart | 7 +++---- lib/src/packet/key/rsa_secret_material.dart | 2 -- lib/src/packet/key/rsa_session_key_cryptor.dart | 2 +- 7 files changed, 17 insertions(+), 21 deletions(-) diff --git a/lib/src/packet/key/ecdh_secret_material.dart b/lib/src/packet/key/ecdh_secret_material.dart index ddbd1254..1bf78e19 100644 --- a/lib/src/packet/key/ecdh_secret_material.dart +++ b/lib/src/packet/key/ecdh_secret_material.dart @@ -5,7 +5,7 @@ library; import 'dart:typed_data'; -import 'package:pinenacl/ed25519.dart' as nacl; +import 'package:pinenacl/x25519.dart' as nacl; import 'package:pinenacl/tweetnacl.dart'; import 'package:pointycastle/pointycastle.dart'; @@ -50,13 +50,13 @@ class ECDHSecretMaterial extends ECSecretMaterial implements SecretKeyMaterialIn return ECDHSecretMaterial( Uint8List.fromList( privateKey.asTypedList.reversed.toList(), - ).toBigIntWithSign(1), + ).toUnsignedBigInt(), ECDHPublicMaterial( curve.asn1Oid, Uint8List.fromList([ 0x40, ...privateKey.publicKey.asTypedList, - ]).toBigIntWithSign(1), + ]).toUnsignedBigInt(), curve.hashAlgorithm, curve.symmetricAlgorithm, ), @@ -73,11 +73,7 @@ class ECDHSecretMaterial extends ECSecretMaterial implements SecretKeyMaterialIn privateKey.d!, ECDHPublicMaterial( curve.asn1Oid, - q - .getEncoded( - q.isCompressed, - ) - .toBigIntWithSign(1), + q.getEncoded(q.isCompressed).toUnsignedBigInt(), curve.hashAlgorithm, curve.symmetricAlgorithm, ), @@ -99,7 +95,7 @@ class ECDHSecretMaterial extends ECSecretMaterial implements SecretKeyMaterialIn 0x40, ...privateKey.publicKey.asTypedList, ]); - return publicMaterial.q.compareTo(dG.toBigIntWithSign(1)) == 0; + return publicMaterial.q.compareTo(dG.toUnsignedBigInt()) == 0; case Ecc.ed25519: return false; default: diff --git a/lib/src/packet/key/ecdh_session_key_cryptor.dart b/lib/src/packet/key/ecdh_session_key_cryptor.dart index aa56071c..54739364 100644 --- a/lib/src/packet/key/ecdh_session_key_cryptor.dart +++ b/lib/src/packet/key/ecdh_session_key_cryptor.dart @@ -109,7 +109,7 @@ class ECDHSessionKeyCryptor extends SessionKeyCryptor { )) .toUnsignedBytes(); final publicKey = keyPair.publicKey as ECPublicKey; - ephemeralKey = publicKey.Q!.getEncoded(false).toBigIntWithSign(1); + ephemeralKey = publicKey.Q!.getEncoded(false).toUnsignedBigInt(); } final keyWrapper = _selectKeyWrapper(key.kdfSymmetric); return ECDHSessionKeyCryptor( diff --git a/lib/src/packet/key/eddsa_legacy_secret_material.dart b/lib/src/packet/key/eddsa_legacy_secret_material.dart index 3616324e..fdcf587d 100644 --- a/lib/src/packet/key/eddsa_legacy_secret_material.dart +++ b/lib/src/packet/key/eddsa_legacy_secret_material.dart @@ -38,13 +38,13 @@ class EdDSALegacySecretMaterial implements SigningKeyMaterialInterface { factory EdDSALegacySecretMaterial.generate() { final seed = Helper.randomBytes(TweetNaCl.seedSize); return EdDSALegacySecretMaterial( - seed.toBigIntWithSign(1), + seed.toUnsignedBigInt(), EdDSALegacyPublicMaterial( Ecc.ed25519.asn1Oid, Uint8List.fromList([ 0x40, ...nacl.SigningKey.fromSeed(seed).verifyKey.asTypedList, - ]).toBigIntWithSign(1)), + ]).toUnsignedBigInt()), ); } @@ -55,7 +55,7 @@ class EdDSALegacySecretMaterial implements SigningKeyMaterialInterface { 0x40, ...signingKey.verifyKey.asTypedList, ]); - return publicMaterial.q.compareTo(dG.toBigIntWithSign(1)) == 0; + return publicMaterial.q.compareTo(dG.toUnsignedBigInt()) == 0; } @override diff --git a/lib/src/packet/key/elgamal_secret_material.dart b/lib/src/packet/key/elgamal_secret_material.dart index 9f167245..776c05e5 100644 --- a/lib/src/packet/key/elgamal_secret_material.dart +++ b/lib/src/packet/key/elgamal_secret_material.dart @@ -69,7 +69,10 @@ class ElGamalSecretMaterial implements SecretKeyMaterialInterface { // Re-derive public key y' = g ** x mod p // Expect y == y' // Blinded exponentiation computes g**{r(p-1) + x} to compare to y - final r = Helper.randomBigInt(BigInt.two << (pSize - 1), BigInt.two << pSize); + final r = Helper.randomBigInt( + BigInt.two << (pSize - 1), + BigInt.two << pSize, + ); final rqx = ((publicMaterial.prime - BigInt.one) * r) + exponent; return publicMaterial.exponent.compareTo( publicMaterial.generator.modPow(rqx, publicMaterial.prime), diff --git a/lib/src/packet/key/montgomery_secret_material.dart b/lib/src/packet/key/montgomery_secret_material.dart index a6f5db0f..08ecb1f5 100644 --- a/lib/src/packet/key/montgomery_secret_material.dart +++ b/lib/src/packet/key/montgomery_secret_material.dart @@ -4,8 +4,7 @@ library; -import 'dart:typed_data'; -import 'package:pinenacl/ed25519.dart' as nacl; +import 'package:pinenacl/x25519.dart'; import 'package:pinenacl/tweetnacl.dart'; import '../../common/helpers.dart'; @@ -36,7 +35,7 @@ class MontgomerySecretMaterial implements SecretKeyMaterialInterface { factory MontgomerySecretMaterial.generate(final MontgomeryCurve curve) { final secretKey = _generateSecretKey(curve); final publicKey = switch (curve) { - MontgomeryCurve.x25519 => nacl.PrivateKey( + MontgomeryCurve.x25519 => PrivateKey( secretKey, ).publicKey.asTypedList, MontgomeryCurve.x448 => X448.scalarMultBase(secretKey), @@ -59,7 +58,7 @@ class MontgomerySecretMaterial implements SecretKeyMaterialInterface { @override bool get isValid { final publicKey = switch (publicMaterial.curve) { - MontgomeryCurve.x25519 => nacl.PrivateKey( + MontgomeryCurve.x25519 => PrivateKey( secretKey, ).publicKey.asTypedList, MontgomeryCurve.x448 => X448.scalarMultBase(secretKey), diff --git a/lib/src/packet/key/rsa_secret_material.dart b/lib/src/packet/key/rsa_secret_material.dart index 75d2cc4c..48ba170a 100644 --- a/lib/src/packet/key/rsa_secret_material.dart +++ b/lib/src/packet/key/rsa_secret_material.dart @@ -110,12 +110,10 @@ class RSASecretMaterial implements SigningKeyMaterialInterface { bool get isValid { // expect pq = n if ((primeP * primeQ).compareTo(publicMaterial.modulus) != 0) { - print('expect pq = n'); return false; } // expect p*u = 1 mod q if (((primeP * coefficient) % primeQ).compareTo(BigInt.one) != 0) { - print('expect p*u = 1 mod q'); return false; } diff --git a/lib/src/packet/key/rsa_session_key_cryptor.dart b/lib/src/packet/key/rsa_session_key_cryptor.dart index 6d973419..04f5677e 100644 --- a/lib/src/packet/key/rsa_session_key_cryptor.dart +++ b/lib/src/packet/key/rsa_session_key_cryptor.dart @@ -38,7 +38,7 @@ class RSASessionKeyCryptor extends SessionKeyCryptor { PublicKeyParameter(key.publicKey), ), sessionKey, - ).toBigIntWithSign(1), + ).toUnsignedBigInt(), ); } From 0d601f297580f646cbd161f6aab0507be582dee7 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 29 Nov 2024 11:08:22 +0700 Subject: [PATCH 088/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/signature/embedded_signature.dart | 5 ++++- lib/src/packet/signature/notation_data.dart | 6 +++++- lib/src/packet/signature/preferred_aead_algorithms.dart | 8 +++++++- .../signature/preferred_compression_algorithms.dart | 9 +++++++-- lib/src/packet/signature/preferred_hash_algorithms.dart | 8 +++++++- 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/lib/src/packet/signature/embedded_signature.dart b/lib/src/packet/signature/embedded_signature.dart index e304844d..31394a77 100644 --- a/lib/src/packet/signature/embedded_signature.dart +++ b/lib/src/packet/signature/embedded_signature.dart @@ -16,5 +16,8 @@ class EmbeddedSignature extends SignatureSubpacket { EmbeddedSignature(final Uint8List data, {super.critical, super.isLong}) : super(SignatureSubpacketType.embeddedSignature, data); - factory EmbeddedSignature.fromSignature(final SignaturePacket signature) => EmbeddedSignature(signature.data); + factory EmbeddedSignature.fromSignature( + final SignaturePacket signature, + ) => + EmbeddedSignature(signature.data); } diff --git a/lib/src/packet/signature/notation_data.dart b/lib/src/packet/signature/notation_data.dart index 1847a87f..67305656 100644 --- a/lib/src/packet/signature/notation_data.dart +++ b/lib/src/packet/signature/notation_data.dart @@ -22,7 +22,11 @@ class NotationData extends SignatureSubpacket { static const headerNameLength = 2; static const headerValueLength = 2; - NotationData(final Uint8List data, {super.critical, super.isLong}) : super(SignatureSubpacketType.notationData, data); + NotationData( + final Uint8List data, { + super.critical, + super.isLong, + }) : super(SignatureSubpacketType.notationData, data); factory NotationData.fromNotation( final bool humanReadable, diff --git a/lib/src/packet/signature/preferred_aead_algorithms.dart b/lib/src/packet/signature/preferred_aead_algorithms.dart index a888e65c..642cc8f6 100644 --- a/lib/src/packet/signature/preferred_aead_algorithms.dart +++ b/lib/src/packet/signature/preferred_aead_algorithms.dart @@ -23,5 +23,11 @@ class PreferredAeadAlgorithms extends SignatureSubpacket { ); List get preferences => - data.map((pref) => AeadAlgorithm.values.firstWhere((alg) => alg.value == pref)).toList(); + data + .map( + (pref) => AeadAlgorithm.values.firstWhere( + (alg) => alg.value == pref, + ), + ) + .toList(); } diff --git a/lib/src/packet/signature/preferred_compression_algorithms.dart b/lib/src/packet/signature/preferred_compression_algorithms.dart index 6266273b..e08ac3d7 100644 --- a/lib/src/packet/signature/preferred_compression_algorithms.dart +++ b/lib/src/packet/signature/preferred_compression_algorithms.dart @@ -23,6 +23,11 @@ class PreferredCompressionAlgorithms extends SignatureSubpacket { data, ); - List get preferences => - data.map((pref) => CompressionAlgorithm.values.firstWhere((alg) => alg.value == pref)).toList(); + List get preferences => data + .map( + (pref) => CompressionAlgorithm.values.firstWhere( + (alg) => alg.value == pref, + ), + ) + .toList(); } diff --git a/lib/src/packet/signature/preferred_hash_algorithms.dart b/lib/src/packet/signature/preferred_hash_algorithms.dart index d441d247..0a292192 100644 --- a/lib/src/packet/signature/preferred_hash_algorithms.dart +++ b/lib/src/packet/signature/preferred_hash_algorithms.dart @@ -21,5 +21,11 @@ class PreferredHashAlgorithms extends SignatureSubpacket { }) : super(SignatureSubpacketType.preferredHashAlgorithms, data); List get preferences => - data.map((pref) => HashAlgorithm.values.firstWhere((alg) => alg.value == pref)).toList(); + data + .map( + (pref) => HashAlgorithm.values.firstWhere( + (alg) => alg.value == pref, + ), + ) + .toList(); } From d14e3c7d0039c6de959ec49300a77f5649d3acbc Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 29 Nov 2024 11:49:01 +0700 Subject: [PATCH 089/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/signature/notation_data.dart | 27 ++++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/lib/src/packet/signature/notation_data.dart b/lib/src/packet/signature/notation_data.dart index 67305656..2330dfd5 100644 --- a/lib/src/packet/signature/notation_data.dart +++ b/lib/src/packet/signature/notation_data.dart @@ -43,18 +43,18 @@ class NotationData extends SignatureSubpacket { int saltSize, { final bool critical = false, }) { - final salt = Helper.randomBytes(saltSize); - final nameData = utf8.encode(saltName); + final valueData = Helper.generatePassword(saltSize).toBytes(); + final nameData = saltName.toBytes(); final nameLength = min(nameData.length, 0xffff); return NotationData( Uint8List.fromList([ ...[0, 0, 0, 0], (nameLength >> 8) & 0xff, (nameLength >> 0) & 0xff, - (salt.length >> 8) & 0xff, - (salt.length >> 0) & 0xff, + (valueData.length >> 8) & 0xff, + (valueData.length >> 0) & 0xff, ...nameData, - ...salt, + ...valueData, ]), critical: critical, ); @@ -62,18 +62,22 @@ class NotationData extends SignatureSubpacket { bool get isHumanReadable => data[0] == 0x80; - String get notationName { + String get notationName => utf8.decode(nameData); + + String get notationValue => utf8.decode(valueData); + + Uint8List get nameData { final nameLength = (((data[headerFlagLength] & 0xff) << 8) + (data[headerFlagLength + 1] & 0xff)); final nameOffset = headerFlagLength + headerNameLength + headerValueLength; - return utf8.decode(data.sublist(nameOffset, nameOffset + nameLength)); + return data.sublist(nameOffset, nameOffset + nameLength); } - String get notationValue { + Uint8List get valueData { final nameLength = (((data[headerFlagLength] & 0xff) << 8) + (data[headerFlagLength + 1] & 0xff)); final valueLength = (((data[headerFlagLength + headerNameLength] & 0xff) << 8) + (data[headerFlagLength + headerNameLength + 1] & 0xff)); final valueOffset = headerFlagLength + headerNameLength + headerValueLength + nameLength; - return utf8.decode(data.sublist(valueOffset, valueOffset + valueLength)); + return data.sublist(valueOffset, valueOffset + valueLength); } static Uint8List _notationToBytes( @@ -81,13 +85,12 @@ class NotationData extends SignatureSubpacket { final String notationName, final String notationValue, ) { - final nameData = utf8.encode(notationName); + final nameData = notationName.toBytes(); final nameLength = min(nameData.length, 0xffff); if (nameLength != nameData.length) { throw ArgumentError('notationName exceeds maximum length.'); } - - final valueData = utf8.encode(notationValue); + final valueData = notationValue.toBytes(); final valueLength = min(valueData.length, 0xffff); if (valueLength != valueData.length) { throw ArgumentError('notationValue exceeds maximum length.'); From 94e54993eaf1b2f7db00956636e3276cedd44a77 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 29 Nov 2024 13:50:00 +0700 Subject: [PATCH 090/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/signature.dart | 9 +- test/packet/signature_test.dart | 256 ++++++++++++++++++++++++++++++++ 2 files changed, 262 insertions(+), 3 deletions(-) create mode 100644 test/packet/signature_test.dart diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index a80bcab6..1556a8ca 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -208,6 +208,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { ]); final message = Uint8List.fromList([ + ...salt, ...dataToSign, ...signatureData, ..._calculateTrailer( @@ -297,8 +298,8 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { bool isExpired([final DateTime? time]) { final timestamp = time?.millisecondsSinceEpoch ?? DateTime.now().millisecondsSinceEpoch; final creation = creationTime?.millisecondsSinceEpoch ?? 0; - final expiration = expirationTime?.millisecondsSinceEpoch ?? 0; - return !(creation < timestamp && timestamp < expiration); + final expiration = expirationTime?.millisecondsSinceEpoch ?? DateTime.now().millisecondsSinceEpoch; + return !(creation <= timestamp && timestamp <= expiration); } @override @@ -307,7 +308,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { final Uint8List dataToVerify, [ final DateTime? time, ]) { - if (issuerKeyID != verifyKey.keyID) { + if (!issuerKeyID.equals(verifyKey.keyID)) { throw ArgumentError('Signature was not issued by the given public key.'); } if (keyAlgorithm != verifyKey.keyAlgorithm) { @@ -320,6 +321,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { } final message = Uint8List.fromList([ + ...salt, ...dataToVerify, ...signatureData, ..._calculateTrailer( @@ -327,6 +329,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { signatureData.length, ) ]); + final hash = Helper.hashDigest(message, hashAlgorithm); if (signedHashValue[0] != hash[0] || signedHashValue[1] != hash[1]) { throw StateError('Signed digest did not match!'); diff --git a/test/packet/signature_test.dart b/test/packet/signature_test.dart new file mode 100644 index 00000000..85308d4a --- /dev/null +++ b/test/packet/signature_test.dart @@ -0,0 +1,256 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/key_flag.dart'; +import 'package:dart_pg/src/enum/signature_subpacket_type.dart'; +import 'package:dart_pg/src/enum/support_feature.dart'; +import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/signature_subpacket.dart'; +import 'package:dart_pg/src/packet/subpacket_reader.dart'; +import 'package:test/test.dart'; + +void main() { + group('Subpacket', () { + test('Reader', () { + final initSubpackets = SignatureSubpacketType.values + .map( + (type) => SignatureSubpacket(type, Helper.randomBytes(100)), + ) + .toList(); + final bytes = Uint8List.fromList( + initSubpackets + .map( + (subpacket) => subpacket.encode(), + ) + .expand((byte) => byte) + .toList(), + ); + final subpackets = []; + var offset = 0; + while (offset < bytes.length) { + final reader = SubpacketReader.read(bytes, offset); + offset = reader.offset; + final data = reader.data; + if (data.isNotEmpty) { + final critical = ((reader.type & 0x80) != 0); + final type = SignatureSubpacketType.values.firstWhere( + (type) => type.value == (reader.type & 0x7f), + ); + subpackets.add(SignatureSubpacket( + type, + data, + critical: critical, + isLong: reader.isLong, + )); + } + } + + expect(initSubpackets.length, subpackets.length); + for (final subpacket in initSubpackets) { + final index = initSubpackets.indexOf(subpacket); + expect(subpacket.type, subpackets[index].type); + expect(subpacket.data, equals(subpackets[index].data)); + } + }); + + test('KeyFlag', () { + final keyFlags = KeyFlags.fromFlags( + KeyFlag.certifyKeys.value | + KeyFlag.signData.value | + KeyFlag.encryptCommunication.value | + KeyFlag.encryptStorage.value | + KeyFlag.splitPrivateKey.value | + KeyFlag.authentication.value | + KeyFlag.sharedPrivateKey.value, + ); + for (final flag in KeyFlag.values) { + expect(keyFlags.flags & flag.value, flag.value); + } + expect(keyFlags.isCertifyKeys, isTrue); + expect(keyFlags.isSignData, isTrue); + expect(keyFlags.isEncryptCommunication, isTrue); + expect(keyFlags.isEncryptStorage, isTrue); + }); + + test('Features', () { + final features = Features.fromFeatures( + SupportFeature.version1SEIPD.value | + SupportFeature.aeadEncrypted.value | + SupportFeature.version5PublicKey.value | + SupportFeature.version2SEIPD.value, + ); + expect(features.supportVersion2SEIPD, isTrue); + expect(features.supportAeadEncrypted, isTrue); + expect(features.supportVersion5PublicKey, isTrue); + expect(features.supportVersion2SEIPD, isTrue); + }); + + test('Salt notation', () { + final saltNotation = NotationData.saltNotation(16); + expect(saltNotation.notationName, NotationData.saltName); + expect(saltNotation.valueData.length, 16); + }); + }); + + group('Verification', () { + const literalText = 'Hello World :)'; + + test('RSA key', () { + const keyData = ''' +BF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv/seOXpgecTdOcVtt +fzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz/56fW2O0F23qIRd8UUJp5IIlN4RD +dRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0 +kIzDxrEqW+7Ba8nocQlecMF3X5KMN5kp2zraLv9dlBBpWW43XktjcCZgMy20SouraVma8Je/ECwU +WYUiAZxLIlMv9CurEOtxUw6N3RdOtLmYZS9uEnn5y1UkF88o8Nku890uk6BrewFzJyLAx5wRZ4F0 +qV/yq36UWQ0JB/AUGhHVPdFf6pl6eaxBwT5GXvbBUibtf8YI2og5RsgTWtXfU7ebSGXrl5ZMpbA6 +mbfhd0R8aPxWfmDWiIOhBufhMCvUHh1sApMKVZnvIff9/0Dca3wbvLIwa3T4CyshfT0AEQEAAQ== +'''; + const signatureData = ''' +BAEBCgBtBYJnOHr4CZD7/MgqAV5zMEUUAAAAAAAcACBzYWx0QG5vdGF0aW9ucy5vcGVucGdwanMu +b3JnOjEa9EOdbhBhQV8InW6o4SNTRRHi1SlZZb7xXxYI7U4WIQTRpm4aI7GCyZgPeIz7/MgqAV5z +MAAAy7IL/17BbwLPAXrf6xhjeYU7JJrLODcW9sbEz0NmmnppO2AsYAGYFtBz/K4USDLGV9EeyVX+ +hsbcsOXV+nS07ZbY3PhJU2/xyVba/iEVzde0GkOSfI87VZ7WW26jytsnSYjN8uvXJG3pHTjHPiD9 +krfOCoUlN/CEp85tkS4nZ68x6eI/6PYtR/S4e4DWGtF5uB3Y3xLAbgPX03v0/sonjs21By9i7uy0 +JZI0/nsU0mJDU04p8jOKhYBy3W4O2GboZ8T3YEnpeDxpkbi6ZEVrMi/j3MfgDrl46IQxK8Xaru+E +LugRLCSgY5lrz+6G8v0d2oU/YpwLs7XpRgDszOCSp1aJZoP4wqMqqE8COzq2dJc52b0ysYsxmxtr +nk3eLE5XsXU7oSq1HMFQkxMS7NiEbG8FPpwGdd+4NOOZj6Wli/DzMfBwf1sGj6wcvGLlLMGGHScc +j1AUElFDMJNJEXHK6cgf51OphuFdfgqUmymRmCPh2FehrykW6sUL2YcV1igpUB1wRQ== +'''; + + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + keyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final signature = SignaturePacket.fromBytes( + base64.decode( + signatureData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + + expect(signature.verify(publicKey, literalText.toBytes()), isTrue); + }); + + test('DSA key', () { + const keyData = ''' +BF3+CmgRDADZhdKTM3ms3XpXnQke83FgaIBtP1g1qhqpCfg50WiPS0kjiMC0OJz2vh59nusbBLzg +I//Y1VMhKfIWYbqMcIY+lWbseHjl52rqW6AaJ0TH4NgVt7vhyVeJt0k/NnxvNhMd0587KXmfpDxr +wBqc/l5cVB+p0rL8vs8kxojHXAi5V3koM0UjREWs5Jpj/XU9LhEoyXZkeJC/pes1u6UKoFYn7dFI +P49Kkd1kb+1bNfdPYtA0JpcGzYgeMNOvdWJwn43dNhxoeuXfmAEhA8LdzT0C0O+7akXOKWrfhXJ8 +MTBqvPgWZYx7MNuQx/ejIMZHl+Iaf7hG976ILH+NCGiKkhidd9GIuA/WteHiQbXLyfiQ4n8P12q9 ++4dq6ybUM65tnozRyyN+1m3rU2a/+Ly3JCh4TeO27w+cxMWkaeHyTQaJVMbMbDpXduVd32MA33UV +NH5/KXMVczVi5asVjuKDSojJDV1QwX8izZNl1t+AI0L3balCabV0SFhlfnBEUj1my1sBAMOSO/I6 +7BvBS3IPHZWXHjgclhs26mPzRlZLryAUWR2DDACH5fx+yUAdZ8Vu/2zWTHxwWJ/X6gGTLqa9CmfD +q5UDqYFFzuWwN4HJ+ryOuak1CGwSKJUBSA75HExbv0naWg+suy+pEDvF0VALPU9VUkSQtHyR10YO +2FWOe3AEtpbYDRwpdr1ZwEbb3L6IGQ5i/4CNHbJ2u3yUeXsDNAvrpVSEcIjA01RPCOKmf58SDZp4 +yDdPxGhM8w6a18+fdQr22f2cJ0xgfPlbzFbO+FUsEgKvn6QTLhbaYw4zs7rdQDejWHV82hP4K+rb +9FwknYdV9uo4m77MgGlU+4yvJnGEYaL3jwjI3bH9aooNOl6XbvVAzNzomYmaTO7mp6xFAu43yuGy +d9K+1E4k7CQTROxTZ+RdtQjV95hSsEmMg792nQvDSBW4xwfOQ7pf3kC7r9fm8u9nBlEN12HsbQ8Y +vux/ld5q5RaIlD19jzfVR6+hJzbj2ZnUyQs4ksAfIHTzTdLttRxS9lTRTkVx2vbUnoSBy6TYF1mf +6nRPpSm1riZxnkR4+BQL/0rUAxwegTNIG/5M612s2a45QvYK1turZ7spI1RGitJUIjBXUuR76jIs +yqagIhBl5nEsQ4HLv8OQ3EgJ5T9gldLFpHNczLxBQnnNwfPoD2e0kC/iy0rfiNX8HWpTgQpbzAos +Lj5/E0iNlildynIhuqBosyRWFqGva0O6qioL90srlzlfKCloe9R9w3HizjCbf59yEspuJt9iHVNO +POW2Wj5ub0KTiJPp9vBmrFaB79/IlgojpQoYvQ77Hx5A9CJqpaMCHGOW6Uz9euN1ozzETEkIPtL8 +XAxcogfpe2JKE1uS7ugxsKEGEDfxOQFKAGV0XFtIx50vFCr2vQro0WB858CGN47dCxChhNUxNtGc +11JNEkNv/X7hKtRf/5VCmnazGWwNK47cqZ7GJfEBnElD7s/tQvTC5Qp7lg9gEt47TUX0bjzUTCxN +vLosuKL9+J1Wln1myRpff/5ZOAnZTPHR+AbX4bRB4sK5zijQe4139Dn2oRYK+EIYoBAxFxSOzehP +IQ== +'''; + const signatureData = ''' +BAARCgBvBYJnOHrmCRCbp4ncdtaEmkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBn +cC5vcmfMz7rhDJDNvMygIAtM2lpYtNV/Uo1hX/2TeWCVF09y4xYhBHH/2gBECeXdsMPo8Zunidx2 +1oSaAABjEAEAkWtxWQf/FdyetrTz9auRmtoGmXOSSmyD8FCeaZTTS6YBAKO4IcYMynJnEzMRpTvK +EzSE9lH3RInTOtPd+ICKWrA7 +'''; + + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + keyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final signature = SignaturePacket.fromBytes( + base64.decode( + signatureData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + + expect(signature.verify(publicKey, literalText.toBytes()), isTrue); + }); + + test('EdDSA legacy key', () { + const keyData = ''' +BFxHBOkWCSsGAQQB2kcPAQEHQK41sJNxQKsohWxQSk+E813FQaj0wd4Js5Qv1G+ztbtd +'''; + const signatureData = ''' +BAEWCgBtBYJnOHrpCZDyMVUMT0fjjkUUAAAAAAAcACBzYWx0QG5vdGF0aW9ucy5vcGVucGdwanMu +b3JnzMEymyavaUnENgbL9jraEbtusliXCI43HBlvu7fvL54WIQTrhbtfozp14V6UTmPyMVUMT0fj +jgAApJYBAJfdeqUkAmab94505yS83p2JkPMqJEMbrH9G0LdjPkRaAP4//ZOdoZm7IoOyjelKc0LC +emD753kKao1uctpHT1WHDw== +'''; + + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + keyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final signature = SignaturePacket.fromBytes( + base64.decode( + signatureData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + + expect(signature.verify(publicKey, literalText.toBytes()), isTrue); + }); + + test('Ed25519 key', () { + const keyData = ''' +BmOHf+MbAAAAIPlNp7tI1gph5WdwamWH0DMZmbudiRoIJC6thFQ9+JWj +'''; + const signatureData = ''' +BgEbCgAAACkFgmc4euIioQbLGGxPBgmml+TVLfpscisMHx4nwYpWcI9lJewnutmsyQAAAADkJyCU +jQRajkQCbyj5lwWuYQL8v+POcIyOEQPiE0ieuHH2CyqwLxNq8KQ06K8+4PLazLhcdk8A34nfuiGS +CcHUtYDD2WgFQiCPnL6DfYYdS2ttmRWtQkCGmw6oumQ0LHU7ig4= +'''; + + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + keyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final signature = SignaturePacket.fromBytes( + base64.decode( + signatureData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + + expect(signature.verify(publicKey, literalText.toBytes()), isTrue); + }); + }); + + group('Signing', () {}); +} From df959132d18e425fbc3762111b974ea97ad0560a Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 29 Nov 2024 14:58:45 +0700 Subject: [PATCH 091/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/type/key.dart | 46 ++++++++++++++++++++++++++++++ lib/src/type/packet_container.dart | 14 +++++++++ lib/src/type/subkey.dart | 40 ++++++++++++++++++++++++++ lib/src/type/user.dart | 29 +++++++++++++++++++ 4 files changed, 129 insertions(+) create mode 100644 lib/src/type/key.dart create mode 100644 lib/src/type/packet_container.dart create mode 100644 lib/src/type/subkey.dart create mode 100644 lib/src/type/user.dart diff --git a/lib/src/type/key.dart b/lib/src/type/key.dart new file mode 100644 index 00000000..5cefc06e --- /dev/null +++ b/lib/src/type/key.dart @@ -0,0 +1,46 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../enum/key_algorithm.dart'; +import 'armorable.dart'; +import 'key_packet.dart'; +import 'packet_container.dart'; +import 'signature_packet.dart'; +import 'subkey.dart'; +import 'user.dart'; + +/// Transferable key interface +/// That represents a key packet, the relevant signatures, users and subkeys. +/// Author Nguyen Van Nguyen +abstract class KeyInterface implements ArmorableInterface, PacketContainerInterface { + KeyPacketInterface get keyPacket; + + KeyInterface get publicKey; + + int get version; + + DateTime? get creationTime; + + DateTime? get expirationTime; + + KeyAlgorithm get keyAlgorithm; + + Uint8List get fingerprint; + + Uint8List get keyID; + + int get keyStrength; + + Iterable get revocationSignatures; + + Iterable get directSignatures; + + Iterable get users; + + Iterable get subkeys; +} diff --git a/lib/src/type/packet_container.dart b/lib/src/type/packet_container.dart new file mode 100644 index 00000000..d7dcdf74 --- /dev/null +++ b/lib/src/type/packet_container.dart @@ -0,0 +1,14 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'packet_list.dart'; + +/// Packet container interface +/// Author Nguyen Van Nguyen +abstract class PacketContainerInterface { + /// Get packet list + PacketListInterface get packetList; +} diff --git a/lib/src/type/subkey.dart b/lib/src/type/subkey.dart new file mode 100644 index 00000000..df7d7179 --- /dev/null +++ b/lib/src/type/subkey.dart @@ -0,0 +1,40 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import '../enum/key_algorithm.dart'; +import 'key.dart'; +import 'packet_container.dart'; +import 'signature_packet.dart'; +import 'subkey_packet.dart'; + +/// Subkey interface +/// That represents a subkey packet and the relevant signatures. +/// Author Nguyen Van Nguyen +abstract class SubkeyInterface extends PacketContainerInterface { + KeyInterface get mainKey; + + SubkeyPacketInterface get keyPacket; + + int get version; + + DateTime? get creationTime; + + DateTime? get expirationTime; + + KeyAlgorithm get keyAlgorithm; + + Uint8List get fingerprint; + + Uint8List get keyID; + + int get keyStrength; + + Iterable get revocationSignatures; + + Iterable get bindingSignatures; +} diff --git a/lib/src/type/user.dart b/lib/src/type/user.dart new file mode 100644 index 00000000..4ef9d1a8 --- /dev/null +++ b/lib/src/type/user.dart @@ -0,0 +1,29 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'key.dart'; +import 'packet_container.dart'; +import 'signature_packet.dart'; +import 'user_id_packet.dart'; + +/// OpenPGP user interface +/// That represents an user ID or attribute packet and the relevant signatures. +/// Author Nguyen Van Nguyen +abstract class UserInterface extends PacketContainerInterface { + KeyInterface get mainKey; + + UserIDPacketInterface get userIDPacket; + + String get userID; + + bool get isPrimary; + + Iterable get revocationCertifications; + + Iterable get selfCertifications; + + Iterable get otherCertifications; +} From 26551b4710761e06c490db27c2faf7b159cfff9b Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 29 Nov 2024 15:59:50 +0700 Subject: [PATCH 092/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/encryption_test.dart | 167 ++++++++++++++++++++++++++++++- 1 file changed, 165 insertions(+), 2 deletions(-) diff --git a/test/packet/encryption_test.dart b/test/packet/encryption_test.dart index 32be1fe4..49282516 100644 --- a/test/packet/encryption_test.dart +++ b/test/packet/encryption_test.dart @@ -487,7 +487,89 @@ heUWSSwsi+sdLtKcnQQfj/RDqmhO9tmfk8sRTu3Myp9tYJLnjngOxsNEMoRRgo7eBSLVjUQlQu8= expect(literalData.binary, literalText.toBytes()); }); - test('Decrypt with ECDH subkey', () { + test('Decrypt with ECDH NIST P-384 subkey', () { + final subkeyData = ''' +BGc/+YkSBSuBBAAiAwME6+LPwcbdvoEdxPA2L002pKXp8Nt7PxIsjUegQRw2bTQXMlu5zplsUNpr +WJZ1W/iqLhmEH8eLONxcuzVEQMb3dCImxBzmL3Y5HxGGti81EsU7JBIZxHKhl85RY78HdHgCAwEJ +CQABf19UJNkpedSjrDT3bwSSzOlI8/uyJzKkqF5z26O2i+UbQ/VYIK78pSWB5/YNCfpTHRn5 +'''; + final packetListData = ''' +wY4DWimQzmvWOyASAwME8gf7ALFjHdnr+1HOfeaBR7LJ3cSQBXB+vSd1d1Tt7nk6aKvCTrkDg8FN +n4V8crQpq5O47id1cWX5RaxxPoqN0wVeqFcDwfRKjAkR6hheaUnnAkrkN873nH6QkQTKJm7bICWq +PXVtziQ8IyqoGshWNSwpWcPZl9KW2mUBGVIpOtCg0j8BYZTkYDl0D/a62/c1QUyqjpXqgGhzXvuS +23BdpGx/IkjjyOVV3mSvG54iZDtqtfYL5WWys9Euw7n2zOWb8E4= +'''; + + final subkey = SecretSubkeyPacket.fromBytes( + base64.decode( + subkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); + + final packetList = PacketList.decode( + base64.decode( + packetListData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final pkesk = packetList.whereType().first.decrypt(subkey); + final sessionKey = pkesk.sessionKey!; + final seipd = packetList.whereType().first.decrypt( + sessionKey.encryptionKey, + symmetric: sessionKey.symmetric, + ); + final literalData = seipd.packets!.whereType().first; + expect(literalData.binary, literalText.toBytes()); + }); + + test('Decrypt with ECDH Brainpool P-256 subkey', () { + final subkeyData = ''' +BGc/+dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGSVO0lI/BV +2JJsuQqI0Pne08Y7og4bhyv9S+D+wcU8sv0DAQgHAAEAoE4oBDyVgNFV3b7pSIJe+mrkuJYxel3E +JezusZoe4R0RBg== +'''; + final packetListData = ''' +wW4DZbU70a/wXgYSAgMEho+Zrglsg+H4OtMqAI96cGR9lPCWr3q5DhnCAM2kIGofREOFwOm3BIcZ +1QPGtziET3wRmVxF7bSu3i7Sxgq/TSBdQdIy4GS4UpC2LP+bwgyXRgJBMFWa0EL86TcwcAwn49I/ +AXzl+1DZczLIfN+EPS+dkDwlf4BiX82RZby5klmHzg8r3VqtMkyAwwoGgxn1JXzoWKUINsFBOG6J +dJb40maS +'''; + + final subkey = SecretSubkeyPacket.fromBytes( + base64.decode( + subkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); + + final packetList = PacketList.decode( + base64.decode( + packetListData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final pkesk = packetList.whereType().first.decrypt(subkey); + final sessionKey = pkesk.sessionKey!; + final seipd = packetList.whereType().first.decrypt( + sessionKey.encryptionKey, + symmetric: sessionKey.symmetric, + ); + final literalData = seipd.packets!.whereType().first; + expect(literalData.binary, literalText.toBytes()); + }); + + test('Decrypt with ECDH x25519 subkey', () { final subkeyData = ''' BFxHBOkSCisGAQQBl1UBBQEBB0BC/wYhratJPOCptcKkMNgyIpFWK0KzLbTfHewT356+IgMBCAcA AP9/8RTxulNe64U7qvtO4JhL2hWCn8UQerIAGIlukzE6UBCu @@ -679,7 +761,88 @@ EWbTE/ndfIE+i/dP8vgD2SGqAKyz4XmyABqt/Ry5idusd89FgIK6QNZDbI1xF5KImRjyyiBqHt4a expect(decryptedPkesk.sessionKey!.encryptionKey, sessionKey.encryptionKey); }); - test('Encrypt session key with ECDH subkey', () { + test('Encrypt session key with ECDH NIST P-384 subkey', () { + final publicSubkeyData = ''' +BGc/+YkSBSuBBAAiAwME6+LPwcbdvoEdxPA2L002pKXp8Nt7PxIsjUegQRw2bTQXMlu5zplsUNpr +WJZ1W/iqLhmEH8eLONxcuzVEQMb3dCImxBzmL3Y5HxGGti81EsU7JBIZxHKhl85RY78HdHgCAwEJ +CQ== +'''; + final secretSubkeyData = ''' +BGc/+YkSBSuBBAAiAwME6+LPwcbdvoEdxPA2L002pKXp8Nt7PxIsjUegQRw2bTQXMlu5zplsUNpr +WJZ1W/iqLhmEH8eLONxcuzVEQMb3dCImxBzmL3Y5HxGGti81EsU7JBIZxHKhl85RY78HdHgCAwEJ +CQABf19UJNkpedSjrDT3bwSSzOlI8/uyJzKkqF5z26O2i+UbQ/VYIK78pSWB5/YNCfpTHRn5 +'''; + + final publicSubkey = PublicSubkeyPacket.fromBytes( + base64.decode( + publicSubkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final secretSubkey = SecretSubkeyPacket.fromBytes( + base64.decode( + secretSubkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect(publicSubkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(publicSubkey.fingerprint, secretSubkey.fingerprint); + + final encryptedPkesk = PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( + publicSubkey, + sessionKey, + ); + final decryptedPkesk = PublicKeyEncryptedSessionKeyPacket.fromBytes( + encryptedPkesk.data, + ).decrypt(secretSubkey); + expect(decryptedPkesk.sessionKey!.encryptionKey, sessionKey.encryptionKey); + }); + + test('Encrypt session key with ECDH Brainpool P-256 subkey', () { + final publicSubkeyData = ''' +BGc/+dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGSVO0lI/BV +2JJsuQqI0Pne08Y7og4bhyv9S+D+wcU8sv0DAQgH +'''; + final secretSubkeyData = ''' +BGc/+dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGSVO0lI/BV +2JJsuQqI0Pne08Y7og4bhyv9S+D+wcU8sv0DAQgHAAEAoE4oBDyVgNFV3b7pSIJe+mrkuJYxel3E +JezusZoe4R0RBg== +'''; + + final publicSubkey = PublicSubkeyPacket.fromBytes( + base64.decode( + publicSubkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final secretSubkey = SecretSubkeyPacket.fromBytes( + base64.decode( + secretSubkeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + expect(publicSubkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(publicSubkey.fingerprint, secretSubkey.fingerprint); + + final encryptedPkesk = PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( + publicSubkey, + sessionKey, + ); + final decryptedPkesk = PublicKeyEncryptedSessionKeyPacket.fromBytes( + encryptedPkesk.data, + ).decrypt(secretSubkey); + expect(decryptedPkesk.sessionKey!.encryptionKey, sessionKey.encryptionKey); + }); + + test('Encrypt session key with ECDH x25519 subkey', () { final publicSubkeyData = ''' BFxHBOkSCisGAQQBl1UBBQEBB0BC/wYhratJPOCptcKkMNgyIpFWK0KzLbTfHewT356+IgMBCAc= '''; From 0e294998692cd11daf134ea3683cc888160caf8b Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 29 Nov 2024 16:24:52 +0700 Subject: [PATCH 093/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/signature_test.dart | 71 +++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/test/packet/signature_test.dart b/test/packet/signature_test.dart index 85308d4a..1c1d78c3 100644 --- a/test/packet/signature_test.dart +++ b/test/packet/signature_test.dart @@ -96,7 +96,7 @@ void main() { group('Verification', () { const literalText = 'Hello World :)'; - test('RSA key', () { + test('Verify with RSA key', () { const keyData = ''' BF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv/seOXpgecTdOcVtt fzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz/56fW2O0F23qIRd8UUJp5IIlN4RD @@ -138,7 +138,7 @@ j1AUElFDMJNJEXHK6cgf51OphuFdfgqUmymRmCPh2FehrykW6sUL2YcV1igpUB1wRQ== expect(signature.verify(publicKey, literalText.toBytes()), isTrue); }); - test('DSA key', () { + test('Verify with DSA key', () { const keyData = ''' BF3+CmgRDADZhdKTM3ms3XpXnQke83FgaIBtP1g1qhqpCfg50WiPS0kjiMC0OJz2vh59nusbBLzg I//Y1VMhKfIWYbqMcIY+lWbseHjl52rqW6AaJ0TH4NgVt7vhyVeJt0k/NnxvNhMd0587KXmfpDxr @@ -190,7 +190,70 @@ EzSE9lH3RInTOtPd+ICKWrA7 expect(signature.verify(publicKey, literalText.toBytes()), isTrue); }); - test('EdDSA legacy key', () { + test('Verify with ECDSA NIST P-384 key', () { + const keyData = ''' +BGc/+YkTBSuBBAAiAwMEd3FJXIrlQksBiwOLB+ksANWsTQzFNxqpxzjpglaDNKjgF/RXVXj9A6os +ao0WJtqkKFKZvkASvOCgOaBn3GDUZmF8GJwuuLNOci0JC/N+g5nFd/BeXNJNyECMIbllcLt9 +'''; + const signatureData = ''' +BAATCQBdBQJnSYcNFiEEoyUQfma8qz7qQHVQOW7Li7htGSIJEDluy4u4bRkiNRQAAAAAABQAGHNh +bHRAcGhwLW9wZW5wZ3Aub3Jn67K0PLenjKjdnfzO74JVwHinZH47yg9GAAA0CgF8Dth3ap+Dc8+3 +4OamLRo8MRCH2fBbjRNPxtKjz1ZG9NDOF6KePoSv57ijpwPjXpIuAX0RhdUWB1HX6lY/f2zcVetC +CqiCSnlWWoGYsnbr2P8E9ra3g7s5O9vEM0v78eEYaNs= +'''; + + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + keyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final signature = SignaturePacket.fromBytes( + base64.decode( + signatureData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + + expect(signature.verify(publicKey, literalText.toBytes()), isTrue); + }); + + test('Verify with ECDSA Brainpool P-256 key', () { + const keyData = ''' +BGc/+dATCSskAwMCCAEBBwIDBJ15ari0PCq317awmdNNIwz+yOZ18yUCg8LOAmYEaRAqAh1HmAnS +K5d4i1CX2M2/UKup7f/KD/o5Y6oid+VuTZQ= +'''; + const signatureData = ''' +BAATCABVBQJnSYZUFiEEzRtbFClPgL5lzvvvmVEhn8nelXgJEJlRIZ/J3pV4LRQAAAAAABQAEHNh +bHRAcGhwLW9wZW5wZ3Aub3JnxGTgM/wcO2Hyp0OjyEEnVwAAXiwA/3ZoYHQHEJnc279Wu4YgTGNH +HEfWo+l0t+wTCKJUh9iuAQCcmwa2Jh5BJNJd9ezMwPyH/uCgYyyemg9S1J5xNKFepw== +'''; + + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + keyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final signature = SignaturePacket.fromBytes( + base64.decode( + signatureData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + + expect(signature.verify(publicKey, literalText.toBytes()), isTrue); + }); + + test('Verify with EdDSA legacy key', () { const keyData = ''' BFxHBOkWCSsGAQQB2kcPAQEHQK41sJNxQKsohWxQSk+E813FQaj0wd4Js5Qv1G+ztbtd '''; @@ -221,7 +284,7 @@ emD753kKao1uctpHT1WHDw== expect(signature.verify(publicKey, literalText.toBytes()), isTrue); }); - test('Ed25519 key', () { + test('Verify with Ed25519 key', () { const keyData = ''' BmOHf+MbAAAAIPlNp7tI1gph5WdwamWH0DMZmbudiRoIJC6thFQ9+JWj '''; From 8e66a0d54f8ab59a545cd7a785cbefa444322e6e Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 29 Nov 2024 17:20:55 +0700 Subject: [PATCH 094/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/signature_test.dart | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/packet/signature_test.dart b/test/packet/signature_test.dart index 1c1d78c3..8997c1ff 100644 --- a/test/packet/signature_test.dart +++ b/test/packet/signature_test.dart @@ -313,6 +313,38 @@ CcHUtYDD2WgFQiCPnL6DfYYdS2ttmRWtQkCGmw6oumQ0LHU7ig4= expect(signature.verify(publicKey, literalText.toBytes()), isTrue); }); + + test('Verify with Ed448 key', () { + const keyData = ''' +BmbzftccAAAAOR3+OzyG7CwqCJopfjLl1hr1L8xJb4yIqt3ya0SH/pNXF63tthhChdNUdqtTGppE +g6KH7Dz3jXlFAA== +'''; + const signatureData = ''' +BgAcCAAAADMFAmdJlAEiIQajZpX32QU9YiluDfY6DUi6J3MNnD1PcbgqWMinyQ/qIAkQo2aV99kF +PWIAAAAAUIoQbFoa0AGrM/71CxsKoDRGUBDJuaNAY5eefCAWYJyMKaQHktcakEk22D9FFV/jonlA +1Wc7R087rKOb7OF7ikAFCvJZNm04770BgPph3pY2wRU82uD/J5P+DBi7pKj63EXAPzla/jmY3cs9 +sU42Y3hMyl3ZAZyV2uFHbqAUz+KbMO4oAA== +'''; + + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + keyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final signature = SignaturePacket.fromBytes( + base64.decode( + signatureData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + + expect(signature.verify(publicKey, literalText.toBytes()), isTrue); + }); }); group('Signing', () {}); From 0cb5e799ac41bda5dad454ee6806b482cf6e169c Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 29 Nov 2024 17:25:51 +0700 Subject: [PATCH 095/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/signature_test.dart | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/packet/signature_test.dart b/test/packet/signature_test.dart index 8997c1ff..af275ead 100644 --- a/test/packet/signature_test.dart +++ b/test/packet/signature_test.dart @@ -347,5 +347,17 @@ sU42Y3hMyl3ZAZyV2uFHbqAUz+KbMO4oAA== }); }); - group('Signing', () {}); + group('Signing', () { + test('Sign with RSA key', () {}); + + test('Sign with ECDSA NIST P-384 key', () {}); + + test('Sign with ECDSA Brainpool P-256 key', () {}); + + test('Sign with EdDSA legacy key', () {}); + + test('Sign with Ed25519 key', () {}); + + test('Sign with Ed448 key', () {}); + }); } From 84dff4c8a14da0a2dd9588f3e725ad22f681983a Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 2 Dec 2024 09:26:01 +0700 Subject: [PATCH 096/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/eddsa_secret_material.dart | 1 + test/packet/key_packet_test.dart | 16 +- test/packet/signature_test.dart | 279 +++++++++++++++++- 3 files changed, 280 insertions(+), 16 deletions(-) diff --git a/lib/src/packet/key/eddsa_secret_material.dart b/lib/src/packet/key/eddsa_secret_material.dart index bb8f9876..b934732b 100644 --- a/lib/src/packet/key/eddsa_secret_material.dart +++ b/lib/src/packet/key/eddsa_secret_material.dart @@ -83,6 +83,7 @@ class EdDSASecretMaterial implements SigningKeyMaterialInterface { .sign( Helper.hashDigest(message, hash), ) + .signature .asTypedList, EdDSACurve.ed448 => EdPrivateKey.fromBytes( secretKey, diff --git a/test/packet/key_packet_test.dart b/test/packet/key_packet_test.dart index 369aa540..5f069fa6 100644 --- a/test/packet/key_packet_test.dart +++ b/test/packet/key_packet_test.dart @@ -250,7 +250,7 @@ BGc/+dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGSVO0lI/BV expect(subkeyMaterial.curve, Ecc.curve25519); }); - test('Curve 25519 keys', () { + test('Curve 25519 rfc9580 keys', () { const keyPacket = 'BmOHf+MbAAAAIPlNp7tI1gph5WdwamWH0DMZmbudiRoIJC6thFQ9+JWj'; final publicKey = PublicKeyPacket.fromBytes( base64.decode(keyPacket), @@ -277,7 +277,7 @@ BGc/+dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGSVO0lI/BV expect(publicSubkey.isSubkey, isTrue); }); - test('Curve 448 keys', () { + test('Curve 448 rfc9580 keys', () { const keyPacket = 'BmbzbxMcAAAAOclr6WO01hRcPLq6+/O0G+HA8hfV+fWyej4w7y9j6Im19Y5lOhbn99B0mZ0i6ggJLxcf/wPqwG3hAA=='; final publicKey = PublicKeyPacket.fromBytes( base64.decode(keyPacket), @@ -606,7 +606,7 @@ gWiiTYzGt55CPH+6z9IdUnM/22Y2nOVfXg== expect(subkeyMaterial.curve, Ecc.curve25519); }); - test('Curve 25519 keys', () { + test('RFC9580 Curve 25519 keys', () { final passphrase = 'correct horse battery staple'; final keyPacket = ''' BmOHf+MbAAAAIPlNp7tI1gph5WdwamWH0DMZmbudiRoIJC6thFQ9+JWj/SYJAhQEXW/XHJ4JbR62 @@ -654,7 +654,7 @@ DqYVRdwUzAEEFS4Typ/05yT7HC6x34YCCUGvktXKv+W6nfHFC8dcVKOMDaFpd+g3rFQZF0MQcjr6 expect(secretSubkey.isSubkey, isTrue); }); - test('Curve 448 keys', () { + test('RFC9580 Curve 448 keys', () { final passphrase = 'Ax@2bGh;SxD&"A_;El%mPIvLx_!#3Aik'; final keyPacket = ''' BmbzftccAAAAOR3+OzyG7CwqCJopfjLl1hr1L8xJb4yIqt3ya0SH/pNXF63tthhChdNUdqtTGppE @@ -996,7 +996,7 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); }); - test('X25519 keys', () { + test('RFC9580 X25519 keys', () { final secretKey = SecretKeyPacket.generate( KeyAlgorithm.x25519, ); @@ -1036,7 +1036,7 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); }); - test('X448 keys', () { + test('RFC9580 X448 keys', () { final secretKey = SecretKeyPacket.generate( KeyAlgorithm.x448, ); @@ -1078,7 +1078,7 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); }); - test('ed25519 keys', () { + test('RFC9580 Ed25519 keys', () { final secretKey = SecretKeyPacket.generate( KeyAlgorithm.ed25519, ); @@ -1120,7 +1120,7 @@ qMBOq2Y5lwP1KQ6i4kdyb8vb8YPEEJlZVGW4fmbbWuiNWJq3xjFQlBnOvUypeyqXIoJ2C/fS8+Vt expect(secretSubkey.fingerprint, decryptedSecretSubkey.fingerprint); }); - test('ed448 keys', () { + test('RFC9580 Ed448 keys', () { final secretKey = SecretKeyPacket.generate( KeyAlgorithm.ed448, ); diff --git a/test/packet/signature_test.dart b/test/packet/signature_test.dart index af275ead..68f8a518 100644 --- a/test/packet/signature_test.dart +++ b/test/packet/signature_test.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/enum/key_flag.dart'; import 'package:dart_pg/src/enum/signature_subpacket_type.dart'; +import 'package:dart_pg/src/enum/signature_type.dart'; import 'package:dart_pg/src/enum/support_feature.dart'; import 'package:dart_pg/src/packet/base.dart'; import 'package:dart_pg/src/packet/signature_subpacket.dart'; @@ -93,6 +94,61 @@ void main() { }); }); + group('One-Pass Signature', () { + test('Version 3', () { + const signatureData = ''' +BAEBCgBtBYJnOHr4CZD7/MgqAV5zMEUUAAAAAAAcACBzYWx0QG5vdGF0aW9ucy5vcGVucGdwanMu +b3JnOjEa9EOdbhBhQV8InW6o4SNTRRHi1SlZZb7xXxYI7U4WIQTRpm4aI7GCyZgPeIz7/MgqAV5z +MAAAy7IL/17BbwLPAXrf6xhjeYU7JJrLODcW9sbEz0NmmnppO2AsYAGYFtBz/K4USDLGV9EeyVX+ +hsbcsOXV+nS07ZbY3PhJU2/xyVba/iEVzde0GkOSfI87VZ7WW26jytsnSYjN8uvXJG3pHTjHPiD9 +krfOCoUlN/CEp85tkS4nZ68x6eI/6PYtR/S4e4DWGtF5uB3Y3xLAbgPX03v0/sonjs21By9i7uy0 +JZI0/nsU0mJDU04p8jOKhYBy3W4O2GboZ8T3YEnpeDxpkbi6ZEVrMi/j3MfgDrl46IQxK8Xaru+E +LugRLCSgY5lrz+6G8v0d2oU/YpwLs7XpRgDszOCSp1aJZoP4wqMqqE8COzq2dJc52b0ysYsxmxtr +nk3eLE5XsXU7oSq1HMFQkxMS7NiEbG8FPpwGdd+4NOOZj6Wli/DzMfBwf1sGj6wcvGLlLMGGHScc +j1AUElFDMJNJEXHK6cgf51OphuFdfgqUmymRmCPh2FehrykW6sUL2YcV1igpUB1wRQ== +'''; + final signature = SignaturePacket.fromBytes( + base64.decode( + signatureData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + + final ops = OnePassSignaturePacket.fromSignature(signature); + expect(ops.version, 3); + expect(ops.signatureType, signature.signatureType); + expect(ops.hashAlgorithm, signature.hashAlgorithm); + expect(ops.keyAlgorithm, signature.keyAlgorithm); + expect(ops.issuerKeyID, signature.issuerKeyID); + }); + + test('Version 6', () { + const signatureData = ''' +BgEbCgAAACkFgmc4euIioQbLGGxPBgmml+TVLfpscisMHx4nwYpWcI9lJewnutmsyQAAAADkJyCU +jQRajkQCbyj5lwWuYQL8v+POcIyOEQPiE0ieuHH2CyqwLxNq8KQ06K8+4PLazLhcdk8A34nfuiGS +CcHUtYDD2WgFQiCPnL6DfYYdS2ttmRWtQkCGmw6oumQ0LHU7ig4= +'''; + final signature = SignaturePacket.fromBytes( + base64.decode( + signatureData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + + final ops = OnePassSignaturePacket.fromSignature(signature); + expect(ops.version, 6); + expect(ops.signatureType, signature.signatureType); + expect(ops.hashAlgorithm, signature.hashAlgorithm); + expect(ops.salt, signature.salt); + expect(ops.issuerFingerprint, signature.issuerFingerprint); + expect(ops.issuerKeyID, signature.issuerKeyID); + }); + }); + group('Verification', () { const literalText = 'Hello World :)'; @@ -284,7 +340,7 @@ emD753kKao1uctpHT1WHDw== expect(signature.verify(publicKey, literalText.toBytes()), isTrue); }); - test('Verify with Ed25519 key', () { + test('Verify with Ed25519 rfc9580 key', () { const keyData = ''' BmOHf+MbAAAAIPlNp7tI1gph5WdwamWH0DMZmbudiRoIJC6thFQ9+JWj '''; @@ -314,7 +370,7 @@ CcHUtYDD2WgFQiCPnL6DfYYdS2ttmRWtQkCGmw6oumQ0LHU7ig4= expect(signature.verify(publicKey, literalText.toBytes()), isTrue); }); - test('Verify with Ed448 key', () { + test('Verify with Ed448 rfc9580 key', () { const keyData = ''' BmbzftccAAAAOR3+OzyG7CwqCJopfjLl1hr1L8xJb4yIqt3ya0SH/pNXF63tthhChdNUdqtTGppE g6KH7Dz3jXlFAA== @@ -348,16 +404,223 @@ sU42Y3hMyl3ZAZyV2uFHbqAUz+KbMO4oAA== }); group('Signing', () { - test('Sign with RSA key', () {}); + const literalText = 'Hello World :)'; - test('Sign with ECDSA NIST P-384 key', () {}); + test('Sign with RSA key', () { + const secretKeyData = ''' +BGc/4FEBCACZXe5kmfKWChtAGdoYRxRkEcfm0Ne431ovB4QS3zIOoclfyA7jgyq1UAhKbsYhZmTK +xx0FO5/HKOO2ojJU3AeKbLP+xhH8yVdEK5JiySvX/16Tz27d+r5WGLkjZMYZAxWW9hdfurajYp8v +cdACvErKMXShXUR9npCKH5O0QBagxMg6vczfJfNG1qWJ9aeFO7yffUt9n+39ru3Cc67EAp7m29Wo +wto15xmeXCPkYEgE+BM+86AHGqihOERjPCzWeFVLRuCwj2YjmZvtMh9A2gx5K6dMQ5viMDsTSoB0 +DEZzvY+Q7gr7/qiWCpSfmy3gvmReOvMzk8uI7+Nt6vdJia4vABEBAAEAB/oDLokNzxuJgcRP9n3p +MJoJMkz0LwElARb1GYjjtVdHKSX2TKwEHi7hSalqXwwah7c44y4yKhi4SWU9KD9JNiA+x977PsqV +dMcHfRntAyokw/h0+5Q3Ub16J+8CNlPvyjDOQpvBrQd6jZAqmeNehXHyV8ebTI8gRg08nOwYenJ5 +KTYIZTFaZ2kvVkV1ihbaUk5AGQsFsh8e3Y+Zh2KfjJfl2ykng23SMocF99Lth7ityO8LYFSk1iNk +IeLqAxC3yblEtr2QYSlOwmXjU5Mos7PGHuBZjvEpwDqyZTuAMFhN0e/iPKtejFp6u5OOtABwUE7s +rB9tQ7DnB5nXfaprzemFBADDRLSHU4ICg8lGwBFF+XtlQ+pmv3AawiwjV0DUcCxDu3YUBbnyZPIe +J5CAIzLE7syNFgL2y4LB2396ej5weEE+1bmbKDQZ3YDKqI02qy540dqPdEtikW8tLUZbWkwooTLk ++Ltv8USNc1dyHYdHFgv6m0fUOmgEjJJpkQEUsv8m9QQAyREGq2qL9TGY2NHAKBGQprzm8KduW1Z+ +3sN0BkcFJDMsYaH05dekh5o/p/b1cc22hhaNUtiK9goqNUPXqLTZwU/8w7piblPzcSx7xWP5yWbh +s43PqR7I56PHuZ5HNk8IfxFnFxSfr+eUzoHkSuZeXcuuvdOBXJOqLCa7t9bwYhMD/0G/N+W0KEve +Wk723L6iG1cWrX7uKQLo9L4zdvn1CknANvGWD8UBjDk4QJKl4EFE1FWp/uFjQ9mcP7u/F3S+Ekhv +nXllXNZ1lBvdnmW0xkPwd3MJFpQ269Q46Fgw2t5mQ2B44qvAEXfs7XvpHQOkpBJnpPVDKvXbciXf +Sl6rNjITOic= +'''; + const publicKeyData = ''' +BGc/4FEBCACZXe5kmfKWChtAGdoYRxRkEcfm0Ne431ovB4QS3zIOoclfyA7jgyq1UAhKbsYhZmTK +xx0FO5/HKOO2ojJU3AeKbLP+xhH8yVdEK5JiySvX/16Tz27d+r5WGLkjZMYZAxWW9hdfurajYp8v +cdACvErKMXShXUR9npCKH5O0QBagxMg6vczfJfNG1qWJ9aeFO7yffUt9n+39ru3Cc67EAp7m29Wo +wto15xmeXCPkYEgE+BM+86AHGqihOERjPCzWeFVLRuCwj2YjmZvtMh9A2gx5K6dMQ5viMDsTSoB0 +DEZzvY+Q7gr7/qiWCpSfmy3gvmReOvMzk8uI7+Nt6vdJia4vABEBAAE= +'''; + final secretKey = SecretKeyPacket.fromBytes( + base64.decode( + secretKeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + publicKeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final signature = SignaturePacket.createSignature( + secretKey, + SignatureType.standalone, + literalText.toBytes(), + ); + expect(signature.verify(publicKey, literalText.toBytes()), isTrue); + }); - test('Sign with ECDSA Brainpool P-256 key', () {}); + test('Sign with ECDSA NIST P-384 key', () { + const secretKeyData = ''' +BGc/+YkTBSuBBAAiAwMEd3FJXIrlQksBiwOLB+ksANWsTQzFNxqpxzjpglaDNKjgF/RXVXj9A6os +ao0WJtqkKFKZvkASvOCgOaBn3GDUZmF8GJwuuLNOci0JC/N+g5nFd/BeXNJNyECMIbllcLt9AAGA +45H1wvi7/byiPz3owPhTAh0Pxc/T/U+MiGt/qGO3dVKRfIwlBVMbMb0JDMkBSARKGCU= +'''; + const publicKeyData = ''' +BGc/+YkTBSuBBAAiAwMEd3FJXIrlQksBiwOLB+ksANWsTQzFNxqpxzjpglaDNKjgF/RXVXj9A6os +ao0WJtqkKFKZvkASvOCgOaBn3GDUZmF8GJwuuLNOci0JC/N+g5nFd/BeXNJNyECMIbllcLt9 +'''; + final secretKey = SecretKeyPacket.fromBytes( + base64.decode( + secretKeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + publicKeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final signature = SignaturePacket.createSignature( + secretKey, + SignatureType.standalone, + literalText.toBytes(), + ); + expect(signature.verify(publicKey, literalText.toBytes()), isTrue); + }); - test('Sign with EdDSA legacy key', () {}); + test('Sign with ECDSA Brainpool P-256 key', () { + const secretKeyData = ''' +BGc/+dATCSskAwMCCAEBBwIDBJ15ari0PCq317awmdNNIwz+yOZ18yUCg8LOAmYEaRAqAh1HmAnS +K5d4i1CX2M2/UKup7f/KD/o5Y6oid+VuTZQAAQCAetZiozf4F0b5trgEPGOsbT4PYke6PlfnRjlP +s4Zxog7L +'''; + const publicKeyData = ''' +BGc/+dATCSskAwMCCAEBBwIDBJ15ari0PCq317awmdNNIwz+yOZ18yUCg8LOAmYEaRAqAh1HmAnS +K5d4i1CX2M2/UKup7f/KD/o5Y6oid+VuTZQ= +'''; + final secretKey = SecretKeyPacket.fromBytes( + base64.decode( + secretKeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + publicKeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final signature = SignaturePacket.createSignature( + secretKey, + SignatureType.standalone, + literalText.toBytes(), + ); + expect(signature.verify(publicKey, literalText.toBytes()), isTrue); + }); - test('Sign with Ed25519 key', () {}); + test('Sign with EdDSA legacy key', () { + const secretKeyData = ''' +BGc/+gYWCSsGAQQB2kcPAQEHQGUAHTxwcB6TD72goFQpLf3jAqdbm1cZwv1N2mjBffEgAAD/fBmt +MQFsViG6cDhKa8Mjx3gK1PRMy9CXs375s8jmjlERTA== +'''; + const publicKeyData = ''' +BGc/+gYWCSsGAQQB2kcPAQEHQGUAHTxwcB6TD72goFQpLf3jAqdbm1cZwv1N2mjBffEg +'''; + final secretKey = SecretKeyPacket.fromBytes( + base64.decode( + secretKeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + publicKeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final signature = SignaturePacket.createSignature( + secretKey, + SignatureType.standalone, + literalText.toBytes(), + ); + expect(signature.verify(publicKey, literalText.toBytes()), isTrue); + }); - test('Sign with Ed448 key', () {}); + test('Sign with Ed25519 rfc9580 key', () { + const secretKeyData = ''' +BmOHf+MbAAAAIPlNp7tI1gph5WdwamWH0DMZmbudiRoIJC6thFQ9+JWjABlygXsSvnB+jV9YbOYT +YSAdNE6yZqLIL95oNXYrZbC3 +'''; + const publicKeyData = ''' +BmOHf+MbAAAAIPlNp7tI1gph5WdwamWH0DMZmbudiRoIJC6thFQ9+JWj +'''; + final secretKey = SecretKeyPacket.fromBytes( + base64.decode( + secretKeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + publicKeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final signature = SignaturePacket.createSignature( + secretKey, + SignatureType.standalone, + literalText.toBytes(), + ); + expect(signature.verify(publicKey, literalText.toBytes()), isTrue); + }); + + test('Sign with Ed448 rfc9580 key', () { + const secretKeyData = ''' +BmbzftccAAAAOR3+OzyG7CwqCJopfjLl1hr1L8xJb4yIqt3ya0SH/pNXF63tthhChdNUdqtTGppE +g6KH7Dz3jXlFAAD8C816Koa0GGjpC5lWzhe5Ve8u3Q567f44XWum3y63z8fldhY1jvB2ixSGZtNc +yFlK9T9y2q2KGTA= +'''; + const publicKeyData = ''' +BmbzftccAAAAOR3+OzyG7CwqCJopfjLl1hr1L8xJb4yIqt3ya0SH/pNXF63tthhChdNUdqtTGppE +g6KH7Dz3jXlFAA== +'''; + final secretKey = SecretKeyPacket.fromBytes( + base64.decode( + secretKeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final publicKey = PublicKeyPacket.fromBytes( + base64.decode( + publicKeyData.replaceAll( + RegExp(r'\r?\n', multiLine: true), + '', + ), + ), + ); + final signature = SignaturePacket.createSignature( + secretKey, + SignatureType.standalone, + literalText.toBytes(), + ); + expect(signature.verify(publicKey, literalText.toBytes()), isTrue); + }); }); } From e04b25dcd77aef1bfa2f97850a54aa8a115b1ecf Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 2 Dec 2024 09:54:58 +0700 Subject: [PATCH 097/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/helpers.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/common/helpers.dart b/lib/src/common/helpers.dart index 2a6ca6c4..15286245 100644 --- a/lib/src/common/helpers.dart +++ b/lib/src/common/helpers.dart @@ -26,7 +26,7 @@ export 'extensions.dart'; final class Helper { static final _random = Random.secure(); - static get secureRandom => SecureRandom('Fortuna') + static SecureRandom get secureRandom => SecureRandom('Fortuna') ..seed( KeyParameter( Uint8List.fromList( From d5693db7a1d4672af8a044c08cca3dee2ee2271d Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 2 Dec 2024 11:03:53 +0700 Subject: [PATCH 098/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/base.dart | 2 +- lib/src/packet/signature.dart | 3 +++ lib/src/packet/signature/key_expiration_time.dart | 2 +- lib/src/type/signature_packet.dart | 2 ++ lib/src/type/subkey.dart | 4 ++-- lib/src/type/user.dart | 2 +- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/src/packet/base.dart b/lib/src/packet/base.dart index 771ec0eb..644da793 100644 --- a/lib/src/packet/base.dart +++ b/lib/src/packet/base.dart @@ -32,7 +32,7 @@ export 'trust.dart'; export 'user_attribute.dart'; export 'user_id.dart'; -/// Base packet abstract class +/// Base abstract packet class /// Author Nguyen Van Nguyen abstract class BasePacket implements PacketInterface { static const partialMinSize = 512; diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 1556a8ca..6bbeda8a 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -245,6 +245,9 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { @override DateTime? get expirationTime => getSubpacket()?.expirationTime; + @override + int get keyExpirationTime => getSubpacket()?.expiry ?? 0; + @override bool get isCertRevocation => signatureType == SignatureType.certRevocation; diff --git a/lib/src/packet/signature/key_expiration_time.dart b/lib/src/packet/signature/key_expiration_time.dart index 4429b66b..9453cee4 100644 --- a/lib/src/packet/signature/key_expiration_time.dart +++ b/lib/src/packet/signature/key_expiration_time.dart @@ -30,5 +30,5 @@ class KeyExpirationTime extends SignatureSubpacket { KeyExpirationTime(seconds.pack32(), critical: critical); /// Return the number of seconds after creation time a key is valid for. - int get time => data.unpack32(); + int get expiry => data.unpack32(); } diff --git a/lib/src/type/signature_packet.dart b/lib/src/type/signature_packet.dart index 303df25c..1de978f6 100644 --- a/lib/src/type/signature_packet.dart +++ b/lib/src/type/signature_packet.dart @@ -40,6 +40,8 @@ abstract class SignaturePacketInterface extends PacketInterface { DateTime? get expirationTime; + int get keyExpirationTime; + Uint8List get issuerKeyID; Uint8List get issuerFingerprint; diff --git a/lib/src/type/subkey.dart b/lib/src/type/subkey.dart index df7d7179..0e371569 100644 --- a/lib/src/type/subkey.dart +++ b/lib/src/type/subkey.dart @@ -16,11 +16,11 @@ import 'subkey_packet.dart'; /// That represents a subkey packet and the relevant signatures. /// Author Nguyen Van Nguyen abstract class SubkeyInterface extends PacketContainerInterface { - KeyInterface get mainKey; + KeyInterface? get mainKey; SubkeyPacketInterface get keyPacket; - int get version; + int get keyVersion; DateTime? get creationTime; diff --git a/lib/src/type/user.dart b/lib/src/type/user.dart index 4ef9d1a8..688f2779 100644 --- a/lib/src/type/user.dart +++ b/lib/src/type/user.dart @@ -13,7 +13,7 @@ import 'user_id_packet.dart'; /// That represents an user ID or attribute packet and the relevant signatures. /// Author Nguyen Van Nguyen abstract class UserInterface extends PacketContainerInterface { - KeyInterface get mainKey; + KeyInterface? get mainKey; UserIDPacketInterface get userIDPacket; From 09511968932a0a10c41ad30275cf6f8d20339b8f Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 2 Dec 2024 11:14:23 +0700 Subject: [PATCH 099/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/type/user.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/src/type/user.dart b/lib/src/type/user.dart index 688f2779..9a297747 100644 --- a/lib/src/type/user.dart +++ b/lib/src/type/user.dart @@ -21,9 +21,9 @@ abstract class UserInterface extends PacketContainerInterface { bool get isPrimary; - Iterable get revocationCertifications; + Iterable get revocationSignatures; - Iterable get selfCertifications; + Iterable get selfSignatures; - Iterable get otherCertifications; + Iterable get otherSignatures; } From cb1e3e132738d0bef12e13b132714db19d0e047d Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 2 Dec 2024 13:07:57 +0700 Subject: [PATCH 100/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/type/key.dart | 4 ++-- lib/src/type/subkey.dart | 6 +++--- lib/src/type/user.dart | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/type/key.dart b/lib/src/type/key.dart index 5cefc06e..f3b10759 100644 --- a/lib/src/type/key.dart +++ b/lib/src/type/key.dart @@ -18,13 +18,13 @@ import 'user.dart'; /// That represents a key packet, the relevant signatures, users and subkeys. /// Author Nguyen Van Nguyen abstract class KeyInterface implements ArmorableInterface, PacketContainerInterface { - KeyPacketInterface get keyPacket; + KeyPacketInterface? get keyPacket; KeyInterface get publicKey; int get version; - DateTime? get creationTime; + DateTime get creationTime; DateTime? get expirationTime; diff --git a/lib/src/type/subkey.dart b/lib/src/type/subkey.dart index 0e371569..22837be9 100644 --- a/lib/src/type/subkey.dart +++ b/lib/src/type/subkey.dart @@ -16,13 +16,13 @@ import 'subkey_packet.dart'; /// That represents a subkey packet and the relevant signatures. /// Author Nguyen Van Nguyen abstract class SubkeyInterface extends PacketContainerInterface { - KeyInterface? get mainKey; + KeyInterface get mainKey; SubkeyPacketInterface get keyPacket; - int get keyVersion; + int get version; - DateTime? get creationTime; + DateTime get creationTime; DateTime? get expirationTime; diff --git a/lib/src/type/user.dart b/lib/src/type/user.dart index 9a297747..bfa5804e 100644 --- a/lib/src/type/user.dart +++ b/lib/src/type/user.dart @@ -13,7 +13,7 @@ import 'user_id_packet.dart'; /// That represents an user ID or attribute packet and the relevant signatures. /// Author Nguyen Van Nguyen abstract class UserInterface extends PacketContainerInterface { - KeyInterface? get mainKey; + KeyInterface get mainKey; UserIDPacketInterface get userIDPacket; From 724c0a19a6555f42d0c6be7a1795449b6d0a8192 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 2 Dec 2024 15:09:33 +0700 Subject: [PATCH 101/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/packet_list.dart | 2 +- lib/src/type/key.dart | 2 +- lib/src/type/subkey.dart | 8 ++++++++ lib/src/type/user.dart | 4 ++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/src/packet/packet_list.dart b/lib/src/packet/packet_list.dart index e5d716e7..5365abed 100644 --- a/lib/src/packet/packet_list.dart +++ b/lib/src/packet/packet_list.dart @@ -79,7 +79,7 @@ class PacketList extends ListBase implements PacketListInterfac .toList(growable: false), ); - PacketList filterByTypes([final List tags = const []]) { + PacketListInterface filterByTypes([final List tags = const []]) { if (tags.isNotEmpty) { return PacketList(packets.where((packet) => tags.contains(packet.type))); } diff --git a/lib/src/type/key.dart b/lib/src/type/key.dart index f3b10759..16e0f312 100644 --- a/lib/src/type/key.dart +++ b/lib/src/type/key.dart @@ -18,7 +18,7 @@ import 'user.dart'; /// That represents a key packet, the relevant signatures, users and subkeys. /// Author Nguyen Van Nguyen abstract class KeyInterface implements ArmorableInterface, PacketContainerInterface { - KeyPacketInterface? get keyPacket; + KeyPacketInterface get keyPacket; KeyInterface get publicKey; diff --git a/lib/src/type/subkey.dart b/lib/src/type/subkey.dart index 22837be9..4a2d7a57 100644 --- a/lib/src/type/subkey.dart +++ b/lib/src/type/subkey.dart @@ -34,7 +34,15 @@ abstract class SubkeyInterface extends PacketContainerInterface { int get keyStrength; + bool get isSigningKey; + + bool get isEncryptionKey; + Iterable get revocationSignatures; Iterable get bindingSignatures; + + bool isRevoked([final DateTime? time]); + + bool verify([final DateTime? time]); } diff --git a/lib/src/type/user.dart b/lib/src/type/user.dart index bfa5804e..fbe0c2ef 100644 --- a/lib/src/type/user.dart +++ b/lib/src/type/user.dart @@ -26,4 +26,8 @@ abstract class UserInterface extends PacketContainerInterface { Iterable get selfSignatures; Iterable get otherSignatures; + + bool isRevoked([final DateTime? time]); + + bool verify([final DateTime? time]); } From b50efe98c83091ffc50639ade229616f68d0bb02 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 2 Dec 2024 16:29:49 +0700 Subject: [PATCH 102/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/type/key_packet.dart | 2 +- lib/src/type/packet.dart | 2 +- lib/src/type/secret_key_packet.dart | 2 +- lib/src/type/signature_packet.dart | 2 +- lib/src/type/subkey_packet.dart | 2 +- lib/src/type/user_id_packet.dart | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/type/key_packet.dart b/lib/src/type/key_packet.dart index 0230f7ed..cf77da5f 100644 --- a/lib/src/type/key_packet.dart +++ b/lib/src/type/key_packet.dart @@ -12,7 +12,7 @@ import 'packet.dart'; /// Key packet interface /// Author Nguyen Van Nguyen -abstract class KeyPacketInterface extends PacketInterface { +abstract interface class KeyPacketInterface implements PacketInterface { /// Get key version int get keyVersion; diff --git a/lib/src/type/packet.dart b/lib/src/type/packet.dart index 97be5f1c..69d3cce8 100644 --- a/lib/src/type/packet.dart +++ b/lib/src/type/packet.dart @@ -10,7 +10,7 @@ import '../enum/packet_type.dart'; /// Packet interface /// Author Nguyen Van Nguyen -abstract class PacketInterface { +abstract interface class PacketInterface { /// Get packet type PacketType get type; diff --git a/lib/src/type/secret_key_packet.dart b/lib/src/type/secret_key_packet.dart index cffc4b17..054cc1b8 100644 --- a/lib/src/type/secret_key_packet.dart +++ b/lib/src/type/secret_key_packet.dart @@ -12,7 +12,7 @@ import 'secret_key_material.dart'; /// Secret key packet interface /// Author Nguyen Van Nguyen -abstract class SecretKeyPacketInterface extends KeyPacketInterface { +abstract class SecretKeyPacketInterface implements KeyPacketInterface { /// Get public key packet KeyPacketInterface get publicKey; diff --git a/lib/src/type/signature_packet.dart b/lib/src/type/signature_packet.dart index 1de978f6..4892f1e2 100644 --- a/lib/src/type/signature_packet.dart +++ b/lib/src/type/signature_packet.dart @@ -15,7 +15,7 @@ import 'subpacket.dart'; /// Signature packet interface /// Author Nguyen Van Nguyen -abstract class SignaturePacketInterface extends PacketInterface { +abstract class SignaturePacketInterface implements PacketInterface { int get version; SignatureType get signatureType; diff --git a/lib/src/type/subkey_packet.dart b/lib/src/type/subkey_packet.dart index c2fbdd3f..18c606bd 100644 --- a/lib/src/type/subkey_packet.dart +++ b/lib/src/type/subkey_packet.dart @@ -8,4 +8,4 @@ import 'key_packet.dart'; /// Subkey packet interface /// Author Nguyen Van Nguyen -abstract class SubkeyPacketInterface extends KeyPacketInterface {} +abstract class SubkeyPacketInterface implements KeyPacketInterface {} diff --git a/lib/src/type/user_id_packet.dart b/lib/src/type/user_id_packet.dart index 49152f3e..98fed750 100644 --- a/lib/src/type/user_id_packet.dart +++ b/lib/src/type/user_id_packet.dart @@ -10,7 +10,7 @@ import 'packet.dart'; /// User ID packet interface /// Author Nguyen Van Nguyen -abstract class UserIDPacketInterface extends PacketInterface { +abstract class UserIDPacketInterface implements PacketInterface { /// Get bytes for sign Uint8List get signBytes; } From 5e1636912e8edd3520ca819ad8684a5cb742b0e4 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 2 Dec 2024 16:36:36 +0700 Subject: [PATCH 103/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/type/aead.dart | 2 +- lib/src/type/armorable.dart | 2 +- lib/src/type/encrypted_data_packet.dart | 2 +- lib/src/type/key_material.dart | 2 +- lib/src/type/literal_data.dart | 2 +- lib/src/type/packet_container.dart | 2 +- lib/src/type/packet_list.dart | 2 +- lib/src/type/s2k.dart | 2 +- lib/src/type/secret_key_material.dart | 2 +- lib/src/type/session_key.dart | 2 +- lib/src/type/subkey.dart | 2 +- lib/src/type/subpacket.dart | 2 +- lib/src/type/user.dart | 2 +- lib/src/type/verification_key_material.dart | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/src/type/aead.dart b/lib/src/type/aead.dart index 06258275..40d8989b 100644 --- a/lib/src/type/aead.dart +++ b/lib/src/type/aead.dart @@ -8,7 +8,7 @@ import 'dart:typed_data'; /// AEAD Authenticated-Encryption interface /// Author Nguyen Van Nguyen -abstract class AeadInterface { +abstract interface class AeadInterface { /// Encrypt plaintext input. Uint8List encrypt( final Uint8List plainText, diff --git a/lib/src/type/armorable.dart b/lib/src/type/armorable.dart index 00bf09ab..1c067cfb 100644 --- a/lib/src/type/armorable.dart +++ b/lib/src/type/armorable.dart @@ -6,7 +6,7 @@ library; /// Armorable interface /// Author Nguyen Van Nguyen -abstract class ArmorableInterface { +abstract interface class ArmorableInterface { /// Return ASCII armored text String armor(); } diff --git a/lib/src/type/encrypted_data_packet.dart b/lib/src/type/encrypted_data_packet.dart index 3fc887cb..77d31ef8 100644 --- a/lib/src/type/encrypted_data_packet.dart +++ b/lib/src/type/encrypted_data_packet.dart @@ -11,7 +11,7 @@ import 'packet_list.dart'; /// Encrypted data packet interface /// Author Nguyen Van Nguyen -abstract class EncryptedDataPacketInterface { +abstract interface class EncryptedDataPacketInterface { /// Encrypted data Uint8List get encrypted; diff --git a/lib/src/type/key_material.dart b/lib/src/type/key_material.dart index 77bcccbe..51a93128 100644 --- a/lib/src/type/key_material.dart +++ b/lib/src/type/key_material.dart @@ -8,7 +8,7 @@ import 'dart:typed_data'; /// Key material interface /// Author Nguyen Van Nguyen -abstract class KeyMaterialInterface { +abstract interface class KeyMaterialInterface { /// Get key strength int get keyStrength; diff --git a/lib/src/type/literal_data.dart b/lib/src/type/literal_data.dart index 4856baf3..fa06deca 100644 --- a/lib/src/type/literal_data.dart +++ b/lib/src/type/literal_data.dart @@ -10,7 +10,7 @@ import 'package:dart_pg/src/enum/literal_format.dart'; /// Literal data interface /// Author Nguyen Van Nguyen -abstract class LiteralDataInterface { +abstract interface class LiteralDataInterface { LiteralFormat get format; String get filename; diff --git a/lib/src/type/packet_container.dart b/lib/src/type/packet_container.dart index d7dcdf74..fb417fe1 100644 --- a/lib/src/type/packet_container.dart +++ b/lib/src/type/packet_container.dart @@ -8,7 +8,7 @@ import 'packet_list.dart'; /// Packet container interface /// Author Nguyen Van Nguyen -abstract class PacketContainerInterface { +abstract interface class PacketContainerInterface { /// Get packet list PacketListInterface get packetList; } diff --git a/lib/src/type/packet_list.dart b/lib/src/type/packet_list.dart index e9d82f32..f6c293ca 100644 --- a/lib/src/type/packet_list.dart +++ b/lib/src/type/packet_list.dart @@ -10,7 +10,7 @@ import 'packet.dart'; /// Packet list interface /// Author Nguyen Van Nguyen -abstract class PacketListInterface extends Iterable { +abstract interface class PacketListInterface extends Iterable { /// Get packets Iterable get packets; diff --git a/lib/src/type/s2k.dart b/lib/src/type/s2k.dart index d652c186..92440607 100644 --- a/lib/src/type/s2k.dart +++ b/lib/src/type/s2k.dart @@ -10,7 +10,7 @@ import '../enum/s2k_type.dart'; /// String-to-key interface /// Author Nguyen Van Nguyen -abstract class S2kInterface { +abstract interface class S2kInterface { /// Get S2K type S2kType get type; diff --git a/lib/src/type/secret_key_material.dart b/lib/src/type/secret_key_material.dart index 557630c9..44459281 100644 --- a/lib/src/type/secret_key_material.dart +++ b/lib/src/type/secret_key_material.dart @@ -8,7 +8,7 @@ import 'key_material.dart'; /// Secret key material interface /// Author Nguyen Van Nguyen -abstract class SecretKeyMaterialInterface extends KeyMaterialInterface { +abstract class SecretKeyMaterialInterface implements KeyMaterialInterface { /// Get public key material KeyMaterialInterface get publicMaterial; diff --git a/lib/src/type/session_key.dart b/lib/src/type/session_key.dart index 655ca65a..08759dd1 100644 --- a/lib/src/type/session_key.dart +++ b/lib/src/type/session_key.dart @@ -10,7 +10,7 @@ import '../enum/symmetric_algorithm.dart'; /// Session key interface /// Author Nguyen Van Nguyen -abstract class SessionKeyInterface { +abstract interface class SessionKeyInterface { Uint8List get encryptionKey; SymmetricAlgorithm get symmetric; diff --git a/lib/src/type/subkey.dart b/lib/src/type/subkey.dart index 4a2d7a57..ce02d16d 100644 --- a/lib/src/type/subkey.dart +++ b/lib/src/type/subkey.dart @@ -15,7 +15,7 @@ import 'subkey_packet.dart'; /// Subkey interface /// That represents a subkey packet and the relevant signatures. /// Author Nguyen Van Nguyen -abstract class SubkeyInterface extends PacketContainerInterface { +abstract class SubkeyInterface implements PacketContainerInterface { KeyInterface get mainKey; SubkeyPacketInterface get keyPacket; diff --git a/lib/src/type/subpacket.dart b/lib/src/type/subpacket.dart index 76f1d86d..4f19266c 100644 --- a/lib/src/type/subpacket.dart +++ b/lib/src/type/subpacket.dart @@ -10,7 +10,7 @@ import '../enum/signature_subpacket_type.dart'; /// Subpacket interface /// Author Nguyen Van Nguyen -abstract class SubpacketInterface { +abstract interface class SubpacketInterface { /// Get sub-packet type SignatureSubpacketType get type; diff --git a/lib/src/type/user.dart b/lib/src/type/user.dart index fbe0c2ef..1039ac32 100644 --- a/lib/src/type/user.dart +++ b/lib/src/type/user.dart @@ -12,7 +12,7 @@ import 'user_id_packet.dart'; /// OpenPGP user interface /// That represents an user ID or attribute packet and the relevant signatures. /// Author Nguyen Van Nguyen -abstract class UserInterface extends PacketContainerInterface { +abstract class UserInterface implements PacketContainerInterface { KeyInterface get mainKey; UserIDPacketInterface get userIDPacket; diff --git a/lib/src/type/verification_key_material.dart b/lib/src/type/verification_key_material.dart index 62e36b4c..2b328d84 100644 --- a/lib/src/type/verification_key_material.dart +++ b/lib/src/type/verification_key_material.dart @@ -11,7 +11,7 @@ import 'key_material.dart'; /// Verification key material interface /// Author Nguyen Van Nguyen -abstract class VerificationKeyMaterial extends KeyMaterialInterface { +abstract class VerificationKeyMaterial implements KeyMaterialInterface { /// Verify a signature with message bool verify( final Uint8List message, From b712c154a116dc679228a0029bb567c41db34e4f Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 2 Dec 2024 17:13:24 +0700 Subject: [PATCH 104/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/type/key.dart | 2 +- lib/src/type/secret_key_material.dart | 2 +- lib/src/type/secret_key_packet.dart | 2 +- lib/src/type/session_key_cryptor.dart | 2 +- lib/src/type/signature_packet.dart | 2 +- lib/src/type/signing_key_material.dart | 2 +- lib/src/type/subkey.dart | 2 +- lib/src/type/subkey_packet.dart | 2 +- lib/src/type/user.dart | 2 +- lib/src/type/user_id_packet.dart | 2 +- lib/src/type/verification_key_material.dart | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/src/type/key.dart b/lib/src/type/key.dart index 16e0f312..d186b939 100644 --- a/lib/src/type/key.dart +++ b/lib/src/type/key.dart @@ -17,7 +17,7 @@ import 'user.dart'; /// Transferable key interface /// That represents a key packet, the relevant signatures, users and subkeys. /// Author Nguyen Van Nguyen -abstract class KeyInterface implements ArmorableInterface, PacketContainerInterface { +abstract interface class KeyInterface implements ArmorableInterface, PacketContainerInterface { KeyPacketInterface get keyPacket; KeyInterface get publicKey; diff --git a/lib/src/type/secret_key_material.dart b/lib/src/type/secret_key_material.dart index 44459281..f1dc9c20 100644 --- a/lib/src/type/secret_key_material.dart +++ b/lib/src/type/secret_key_material.dart @@ -8,7 +8,7 @@ import 'key_material.dart'; /// Secret key material interface /// Author Nguyen Van Nguyen -abstract class SecretKeyMaterialInterface implements KeyMaterialInterface { +abstract interface class SecretKeyMaterialInterface implements KeyMaterialInterface { /// Get public key material KeyMaterialInterface get publicMaterial; diff --git a/lib/src/type/secret_key_packet.dart b/lib/src/type/secret_key_packet.dart index 054cc1b8..91167c4a 100644 --- a/lib/src/type/secret_key_packet.dart +++ b/lib/src/type/secret_key_packet.dart @@ -12,7 +12,7 @@ import 'secret_key_material.dart'; /// Secret key packet interface /// Author Nguyen Van Nguyen -abstract class SecretKeyPacketInterface implements KeyPacketInterface { +abstract interface class SecretKeyPacketInterface implements KeyPacketInterface { /// Get public key packet KeyPacketInterface get publicKey; diff --git a/lib/src/type/session_key_cryptor.dart b/lib/src/type/session_key_cryptor.dart index 904d87cc..1db0bd46 100644 --- a/lib/src/type/session_key_cryptor.dart +++ b/lib/src/type/session_key_cryptor.dart @@ -10,7 +10,7 @@ import 'secret_key_material.dart'; /// Session key cryptor interface /// Author Nguyen Van Nguyen -abstract class SessionKeyCryptorInterface { +abstract interface class SessionKeyCryptorInterface { Uint8List encode(); Uint8List decrypt(SecretKeyMaterialInterface key); diff --git a/lib/src/type/signature_packet.dart b/lib/src/type/signature_packet.dart index 4892f1e2..252647f1 100644 --- a/lib/src/type/signature_packet.dart +++ b/lib/src/type/signature_packet.dart @@ -15,7 +15,7 @@ import 'subpacket.dart'; /// Signature packet interface /// Author Nguyen Van Nguyen -abstract class SignaturePacketInterface implements PacketInterface { +abstract interface class SignaturePacketInterface implements PacketInterface { int get version; SignatureType get signatureType; diff --git a/lib/src/type/signing_key_material.dart b/lib/src/type/signing_key_material.dart index 8015beea..dca479c4 100644 --- a/lib/src/type/signing_key_material.dart +++ b/lib/src/type/signing_key_material.dart @@ -11,7 +11,7 @@ import '../enum/hash_algorithm.dart'; /// Signing key material interface /// Author Nguyen Van Nguyen -abstract class SigningKeyMaterialInterface extends SecretKeyMaterialInterface { +abstract interface class SigningKeyMaterialInterface implements SecretKeyMaterialInterface { /// Sign a message and return signature Uint8List sign( final Uint8List message, diff --git a/lib/src/type/subkey.dart b/lib/src/type/subkey.dart index ce02d16d..a8a3437b 100644 --- a/lib/src/type/subkey.dart +++ b/lib/src/type/subkey.dart @@ -15,7 +15,7 @@ import 'subkey_packet.dart'; /// Subkey interface /// That represents a subkey packet and the relevant signatures. /// Author Nguyen Van Nguyen -abstract class SubkeyInterface implements PacketContainerInterface { +abstract interface class SubkeyInterface implements PacketContainerInterface { KeyInterface get mainKey; SubkeyPacketInterface get keyPacket; diff --git a/lib/src/type/subkey_packet.dart b/lib/src/type/subkey_packet.dart index 18c606bd..17986107 100644 --- a/lib/src/type/subkey_packet.dart +++ b/lib/src/type/subkey_packet.dart @@ -8,4 +8,4 @@ import 'key_packet.dart'; /// Subkey packet interface /// Author Nguyen Van Nguyen -abstract class SubkeyPacketInterface implements KeyPacketInterface {} +abstract interface class SubkeyPacketInterface implements KeyPacketInterface {} diff --git a/lib/src/type/user.dart b/lib/src/type/user.dart index 1039ac32..4184f172 100644 --- a/lib/src/type/user.dart +++ b/lib/src/type/user.dart @@ -12,7 +12,7 @@ import 'user_id_packet.dart'; /// OpenPGP user interface /// That represents an user ID or attribute packet and the relevant signatures. /// Author Nguyen Van Nguyen -abstract class UserInterface implements PacketContainerInterface { +abstract interface class UserInterface implements PacketContainerInterface { KeyInterface get mainKey; UserIDPacketInterface get userIDPacket; diff --git a/lib/src/type/user_id_packet.dart b/lib/src/type/user_id_packet.dart index 98fed750..46becea0 100644 --- a/lib/src/type/user_id_packet.dart +++ b/lib/src/type/user_id_packet.dart @@ -10,7 +10,7 @@ import 'packet.dart'; /// User ID packet interface /// Author Nguyen Van Nguyen -abstract class UserIDPacketInterface implements PacketInterface { +abstract interface class UserIDPacketInterface implements PacketInterface { /// Get bytes for sign Uint8List get signBytes; } diff --git a/lib/src/type/verification_key_material.dart b/lib/src/type/verification_key_material.dart index 2b328d84..e9074c05 100644 --- a/lib/src/type/verification_key_material.dart +++ b/lib/src/type/verification_key_material.dart @@ -11,7 +11,7 @@ import 'key_material.dart'; /// Verification key material interface /// Author Nguyen Van Nguyen -abstract class VerificationKeyMaterial implements KeyMaterialInterface { +abstract interface class VerificationKeyMaterial implements KeyMaterialInterface { /// Verify a signature with message bool verify( final Uint8List message, From 0e24ce6cb5b3c8599470bf13645924b615c3052e Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 2 Dec 2024 17:30:24 +0700 Subject: [PATCH 105/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base.dart | 196 ++++++++++++++++++++++++++++++++++ lib/src/key/public_key.dart | 32 ++++++ lib/src/key/subkey.dart | 130 ++++++++++++++++++++++ lib/src/key/user.dart | 103 ++++++++++++++++++ lib/src/type/private_key.dart | 31 ++++++ lib/src/type/subkey.dart | 3 + lib/src/type/user.dart | 3 + 7 files changed, 498 insertions(+) create mode 100644 lib/src/key/base.dart create mode 100644 lib/src/key/public_key.dart create mode 100644 lib/src/key/subkey.dart create mode 100644 lib/src/key/user.dart create mode 100644 lib/src/type/private_key.dart diff --git a/lib/src/key/base.dart b/lib/src/key/base.dart new file mode 100644 index 00000000..9817774f --- /dev/null +++ b/lib/src/key/base.dart @@ -0,0 +1,196 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'package:dart_pg/src/enum/key_algorithm.dart'; +import 'package:dart_pg/src/enum/signature_type.dart'; +import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/packet_list.dart'; +import 'package:dart_pg/src/type/key_packet.dart'; +import 'package:dart_pg/src/type/packet_list.dart'; +import 'package:dart_pg/src/type/signature_packet.dart'; +import 'package:dart_pg/src/type/subkey.dart'; +import 'package:dart_pg/src/type/subkey_packet.dart'; +import 'package:dart_pg/src/type/user.dart'; + +import '../type/key.dart'; +import 'subkey.dart'; +import 'user.dart'; + +/// Base abstract OpenPGP key class +/// Author Nguyen Van Nguyen +abstract class Base implements KeyInterface { + @override + late final KeyPacketInterface keyPacket; + + @override + late final Iterable revocationSignatures; + + @override + late final Iterable directSignatures; + + @override + late final Iterable users; + + @override + late final Iterable subkeys; + + Base(final PacketListInterface packetList) { + _readPacketList(packetList); + } + + @override + DateTime get creationTime => keyPacket.creationTime; + + @override + DateTime? get expirationTime { + final time = keyExpiration(directSignatures.toList()); + if (time == null) { + for (final user in users) { + if (user.isPrimary) { + return keyExpiration(user.selfSignatures.toList()); + } + } + } + return time; + } + + @override + Uint8List get fingerprint => keyPacket.fingerprint; + + @override + KeyAlgorithm get keyAlgorithm => keyPacket.keyAlgorithm; + + @override + Uint8List get keyID => keyPacket.keyID; + + @override + int get keyStrength => keyPacket.keyStrength; + + @override + int get version => keyPacket.keyVersion; + + @override + PacketListInterface get packetList => PacketList([ + keyPacket, + ...revocationSignatures, + ...directSignatures, + ...users.map((user) => user.packetList).expand((packet) => packet), + ...subkeys.map((subkey) => subkey.packetList).expand((packet) => packet), + ]); + + _readPacketList(final PacketListInterface packetList) { + final revocationSignatures = []; + final directSignatures = []; + final users = []; + final subkeys = []; + + KeyPacketInterface? keyPacket; + Subkey? subkey; + User? user; + Uint8List? primaryKeyID; + for (final packet in packetList) { + switch (packet.type) { + case PacketType.publicKey: + case PacketType.secretKey: + if (keyPacket != null) { + throw StateError('Key block contains multiple key packets'); + } + if (packet is KeyPacketInterface) { + keyPacket = packet; + primaryKeyID = packet.keyID; + } + break; + case PacketType.publicSubkey: + case PacketType.secretSubkey: + if (packet is SubkeyPacketInterface) { + subkey = Subkey( + this, + packet, + ); + subkeys.add(subkey); + } + user = null; + break; + case PacketType.userID: + case PacketType.userAttribute: + if (packet is UserIDPacket) { + user = User( + this, + packet, + ); + users.add(user); + } + break; + case PacketType.signature: + if (packet is SignaturePacket) { + switch (packet.signatureType) { + case SignatureType.certGeneric: + case SignatureType.certPersona: + case SignatureType.certCasual: + case SignatureType.certPositive: + if (packet.issuerKeyID == primaryKeyID) { + user?.selfSignatures.add(packet); + } else { + user?.otherSignatures.add(packet); + } + break; + case SignatureType.certRevocation: + user?.revocationSignatures.add(packet); + break; + case SignatureType.subkeyBinding: + subkey?.bindingSignatures.add(packet); + break; + case SignatureType.subkeyRevocation: + subkey?.revocationSignatures.add(packet); + break; + case SignatureType.directKey: + directSignatures.add(packet); + break; + case SignatureType.keyRevocation: + revocationSignatures.add(packet); + break; + default: + } + } + break; + default: + } + } + + if (keyPacket == null) { + throw StateError('Key packet not found in packet list'); + } + + this.keyPacket = keyPacket; + this.revocationSignatures = revocationSignatures; + this.directSignatures = directSignatures; + this.users = users.where((user) => user.verify()); + this.subkeys = subkeys.where((subkey) => subkey.verify()); + } + + static DateTime? keyExpiration( + final List signatures, + ) { + signatures.sort( + (a, b) => b.creationTime!.compareTo( + a.creationTime!, + ), + ); + for (final signature in signatures) { + if (signature.keyExpirationTime > 0) { + final creationTime = signature.creationTime!; + return creationTime.add(Duration( + seconds: signature.keyExpirationTime, + )); + } else if (signature.expirationTime != null) { + return signature.expirationTime; + } + } + return null; + } +} diff --git a/lib/src/key/public_key.dart b/lib/src/key/public_key.dart new file mode 100644 index 00000000..ce5b0e2f --- /dev/null +++ b/lib/src/key/public_key.dart @@ -0,0 +1,32 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'package:dart_pg/src/common/armor.dart'; +import 'package:dart_pg/src/enum/armor_type.dart'; +import 'package:dart_pg/src/packet/packet_list.dart'; + +import '../type/key.dart'; +import 'base.dart'; + +/// OpenPGP public key class +/// Author Nguyen Van Nguyen +class PublicKey extends Base { + PublicKey(super.packetList); + + factory PublicKey.fromArmored(final String armored) { + final armor = Armor.decode(armored); + if (armor.type != ArmorType.publicKey) { + throw ArgumentError('Armored text not of public key type'); + } + return PublicKey(PacketList.decode(armor.data)); + } + + @override + String armor() => Armor.encode(ArmorType.publicKey, packetList.encode()); + + @override + KeyInterface get publicKey => this; +} diff --git a/lib/src/key/subkey.dart b/lib/src/key/subkey.dart new file mode 100644 index 00000000..6032a494 --- /dev/null +++ b/lib/src/key/subkey.dart @@ -0,0 +1,130 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'package:dart_pg/src/enum/key_algorithm.dart'; +import 'package:dart_pg/src/key/base.dart'; +import 'package:dart_pg/src/packet/packet_list.dart'; +import 'package:dart_pg/src/packet/signature/key_flags.dart'; +import 'package:dart_pg/src/type/key.dart'; +import 'package:dart_pg/src/type/packet_list.dart'; +import 'package:dart_pg/src/type/signature_packet.dart'; +import 'package:dart_pg/src/type/subkey.dart'; +import 'package:dart_pg/src/type/subkey_packet.dart'; + +/// OpenPGP subkey class +/// Author Nguyen Van Nguyen +final class Subkey implements SubkeyInterface { + @override + final KeyInterface mainKey; + + @override + final SubkeyPacketInterface keyPacket; + + @override + final List revocationSignatures; + + @override + final List bindingSignatures; + + Subkey( + this.mainKey, + this.keyPacket, { + this.revocationSignatures = const [], + this.bindingSignatures = const [], + }); + + @override + DateTime get creationTime => keyPacket.creationTime; + + @override + DateTime? get expirationTime => Base.keyExpiration(bindingSignatures); + + @override + Uint8List get fingerprint => keyPacket.fingerprint; + + @override + KeyAlgorithm get keyAlgorithm => keyPacket.keyAlgorithm; + + @override + Uint8List get keyID => keyPacket.keyID; + + @override + int get keyStrength => keyPacket.keyStrength; + + @override + bool get isEncryptionKey { + if (keyPacket.isEncryptionKey) { + for (final signature in bindingSignatures) { + final keyFlags = signature.getSubpacket(); + if (keyFlags != null && !(keyFlags.isEncryptStorage || keyFlags.isEncryptCommunication)) { + return false; + } + } + } + return keyPacket.isEncryptionKey; + } + + @override + bool get isSigningKey { + if (keyPacket.isSigningKey) { + for (final signature in bindingSignatures) { + final keyFlags = signature.getSubpacket(); + if (keyFlags != null && !(keyFlags.isSignData)) { + return false; + } + } + } + return keyPacket.isSigningKey; + } + + @override + PacketListInterface get packetList => PacketList([ + keyPacket, + ...revocationSignatures, + ...bindingSignatures, + ]); + + @override + int get version => keyPacket.keyVersion; + + @override + bool isRevoked([ + final DateTime? time, + ]) { + for (final revocation in revocationSignatures) { + if (revocation.verify( + mainKey.keyPacket, + Uint8List.fromList([ + ...mainKey.keyPacket.signBytes, + ...keyPacket.signBytes, + ]), + time, + )) { + return true; + } + } + return false; + } + + @override + bool verify([DateTime? time]) { + for (final signature in bindingSignatures) { + if (signature.verify( + mainKey.keyPacket, + Uint8List.fromList([ + ...mainKey.keyPacket.signBytes, + ...keyPacket.signBytes, + ]), + time, + )) { + return true; + } + } + return false; + } +} diff --git a/lib/src/key/user.dart b/lib/src/key/user.dart new file mode 100644 index 00000000..5f75c5dd --- /dev/null +++ b/lib/src/key/user.dart @@ -0,0 +1,103 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/packet_list.dart'; +import 'package:dart_pg/src/type/key.dart'; +import 'package:dart_pg/src/type/packet_list.dart'; +import 'package:dart_pg/src/type/signature_packet.dart'; +import 'package:dart_pg/src/type/user.dart'; +import 'package:dart_pg/src/type/user_id_packet.dart'; + +/// OpenPGP user class +/// Author Nguyen Van Nguyen +final class User implements UserInterface { + @override + final KeyInterface mainKey; + + @override + final UserIDPacketInterface userIDPacket; + + @override + final List selfSignatures; + + @override + final List otherSignatures; + + @override + final List revocationSignatures; + + User( + this.mainKey, + this.userIDPacket, { + this.selfSignatures = const [], + this.otherSignatures = const [], + this.revocationSignatures = const [], + }); + + @override + bool get isPrimary { + final signatures = selfSignatures.toList(); + signatures.sort( + (a, b) => b.creationTime!.compareTo( + a.creationTime!, + ), + ); + for (final signature in signatures) { + if (signature.isPrimaryUserID) { + return true; + } + } + return false; + } + + @override + PacketListInterface get packetList => PacketList([ + userIDPacket, + ...revocationSignatures, + ...selfSignatures, + ...otherSignatures, + ]); + + @override + String get userID => (userIDPacket is UserIDPacket) ? (userIDPacket as UserIDPacket).userID : ""; + + @override + bool isRevoked([DateTime? time]) { + for (final revocation in revocationSignatures) { + if (revocation.verify( + mainKey.keyPacket, + Uint8List.fromList([ + ...mainKey.keyPacket.signBytes, + ...userIDPacket.signBytes, + ]), + time, + )) { + return true; + } + } + return false; + } + + @override + bool verify([DateTime? time]) { + for (final signature in selfSignatures) { + if (signature.verify( + mainKey.keyPacket, + Uint8List.fromList([ + ...mainKey.keyPacket.signBytes, + ...userIDPacket.signBytes, + ]), + time, + )) { + return true; + } + } + return false; + } +} diff --git a/lib/src/type/private_key.dart b/lib/src/type/private_key.dart new file mode 100644 index 00000000..3035b55e --- /dev/null +++ b/lib/src/type/private_key.dart @@ -0,0 +1,31 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'package:dart_pg/src/type/key.dart'; + +/// OpenPGP private key interface +/// Author Nguyen Van Nguyen +abstract interface class PrivateKeyInterface implements KeyInterface { + bool get isEncrypted; + + bool get isDecrypted; + + bool get aeadProtected; + + /// Lock a private key with the given passphrase. + /// This method does not change the original key. + PrivateKeyInterface encrypt( + final String passphrase, [ + final Iterable subkeyPassphrases = const [], + ]); + + /// Unlock a private key with the given passphrase. + /// This method does not change the original key. + PrivateKeyInterface decrypt( + final String passphrase, [ + final Iterable subkeyPassphrases = const [], + ]); +} diff --git a/lib/src/type/subkey.dart b/lib/src/type/subkey.dart index a8a3437b..3c0050a5 100644 --- a/lib/src/type/subkey.dart +++ b/lib/src/type/subkey.dart @@ -42,7 +42,10 @@ abstract interface class SubkeyInterface implements PacketContainerInterface { Iterable get bindingSignatures; + /// Check if a subkey is revoked bool isRevoked([final DateTime? time]); + /// Verify subkey. + /// Check for existence and validity of binding signature. bool verify([final DateTime? time]); } diff --git a/lib/src/type/user.dart b/lib/src/type/user.dart index 4184f172..3b06b515 100644 --- a/lib/src/type/user.dart +++ b/lib/src/type/user.dart @@ -27,7 +27,10 @@ abstract interface class UserInterface implements PacketContainerInterface { Iterable get otherSignatures; + /// Check if a given certificate of the user is revoked bool isRevoked([final DateTime? time]); + /// Verify user. + /// Check for existence and validity of self signature. bool verify([final DateTime? time]); } From 7ad586a9d92be62bb084a1c67b6dfc2ed6f9959c Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 3 Dec 2024 09:29:53 +0700 Subject: [PATCH 106/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base.dart | 15 ++- lib/src/key/private_key.dart | 145 ++++++++++++++++++++++++++++ lib/src/key/public_key.dart | 2 +- lib/src/packet/secret_key.dart | 3 + lib/src/packet/secret_subkey.dart | 4 +- lib/src/type/private_key.dart | 5 +- lib/src/type/secret_key_packet.dart | 3 + 7 files changed, 165 insertions(+), 12 deletions(-) create mode 100644 lib/src/key/private_key.dart diff --git a/lib/src/key/base.dart b/lib/src/key/base.dart index 9817774f..fcb52703 100644 --- a/lib/src/key/base.dart +++ b/lib/src/key/base.dart @@ -10,14 +10,13 @@ import 'package:dart_pg/src/enum/key_algorithm.dart'; import 'package:dart_pg/src/enum/signature_type.dart'; import 'package:dart_pg/src/packet/base.dart'; import 'package:dart_pg/src/packet/packet_list.dart'; +import 'package:dart_pg/src/type/key.dart'; import 'package:dart_pg/src/type/key_packet.dart'; import 'package:dart_pg/src/type/packet_list.dart'; import 'package:dart_pg/src/type/signature_packet.dart'; import 'package:dart_pg/src/type/subkey.dart'; import 'package:dart_pg/src/type/subkey_packet.dart'; import 'package:dart_pg/src/type/user.dart'; - -import '../type/key.dart'; import 'subkey.dart'; import 'user.dart'; @@ -28,16 +27,16 @@ abstract class Base implements KeyInterface { late final KeyPacketInterface keyPacket; @override - late final Iterable revocationSignatures; + late final List revocationSignatures; @override - late final Iterable directSignatures; + late final List directSignatures; @override - late final Iterable users; + late final List users; @override - late final Iterable subkeys; + late final List subkeys; Base(final PacketListInterface packetList) { _readPacketList(packetList); @@ -169,8 +168,8 @@ abstract class Base implements KeyInterface { this.keyPacket = keyPacket; this.revocationSignatures = revocationSignatures; this.directSignatures = directSignatures; - this.users = users.where((user) => user.verify()); - this.subkeys = subkeys.where((subkey) => subkey.verify()); + this.users = users.where((user) => user.verify()).toList(); + this.subkeys = subkeys.where((subkey) => subkey.verify()).toList(); } static DateTime? keyExpiration( diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart new file mode 100644 index 00000000..799befb1 --- /dev/null +++ b/lib/src/key/private_key.dart @@ -0,0 +1,145 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'package:dart_pg/src/common/armor.dart'; +import 'package:dart_pg/src/common/config.dart'; +import 'package:dart_pg/src/enum/aead_algorithm.dart'; +import 'package:dart_pg/src/enum/armor_type.dart'; +import 'package:dart_pg/src/enum/hash_algorithm.dart'; +import 'package:dart_pg/src/key/base.dart'; +import 'package:dart_pg/src/key/public_key.dart'; +import 'package:dart_pg/src/key/subkey.dart'; +import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/packet_list.dart'; +import 'package:dart_pg/src/type/key.dart'; +import 'package:dart_pg/src/type/packet.dart'; +import 'package:dart_pg/src/type/private_key.dart'; +import 'package:dart_pg/src/type/secret_key_packet.dart'; + +final class PrivateKey extends Base implements PrivateKeyInterface { + PrivateKey(super.packetList); + + @override + SecretKeyPacketInterface get keyPacket => super.keyPacket as SecretKeyPacketInterface; + + @override + bool get isDecrypted => keyPacket.isDecrypted; + + @override + bool get isEncrypted => keyPacket.isEncrypted; + + @override + bool get aeadProtected => keyPacket.aeadProtected; + + @override + HashAlgorithm get preferredHash => keyPacket.preferredHash; + + @override + KeyInterface get publicKey { + final packets = []; + for (final packet in packetList) { + switch (packet.type) { + case PacketType.secretKey: + if (packet is SecretKeyPacket) { + packets.add(packet.publicKey); + } + break; + case PacketType.secretSubkey: + if (packet is SecretSubkeyPacket) { + packets.add(packet.publicKey); + } + break; + default: + packets.add(packet); + } + } + return PublicKey(PacketList(packets)); + } + + @override + String armor() => Armor.encode(ArmorType.privateKey, packetList.encode()); + + @override + PrivateKeyInterface encrypt( + final String passphrase, [ + final Iterable subkeyPassphrases = const [], + ]) { + if (passphrase.isEmpty) { + throw ArgumentError('Passphrase are required for key encryption.'); + } + if (!keyPacket.isDecrypted) { + throw StateError('Private key must be decrypted before encrypting.'); + } + + AeadAlgorithm? aead; + if (version == 6 && Config.aeadProtect) { + aead = Config.preferredAead; + } + final subkeys = this.subkeys.map((subkey) { + final index = this.subkeys.indexOf(subkey); + final subkeyPass = (index < subkeyPassphrases.length) ? subkeyPassphrases.elementAt(index) : passphrase; + if (subkeyPass.isNotEmpty && subkey.keyPacket is SecretSubkeyPacket) { + final keyPacket = (subkey.keyPacket as SecretSubkeyPacket); + return Subkey( + this, + keyPacket.encrypt( + subkeyPass, + Config.preferredSymmetric, + aead, + ), + revocationSignatures: subkey.revocationSignatures.toList(), + bindingSignatures: subkey.bindingSignatures.toList(), + ); + } else { + return subkey; + } + }); + + return PrivateKey(PacketList([ + keyPacket.encrypt( + passphrase, + Config.preferredSymmetric, + aead, + ), + ...revocationSignatures, + ...directSignatures, + ...users.map((user) => user.packetList).expand((packet) => packet), + ...subkeys.map((subkey) => subkey.packetList).expand((packet) => packet), + ])); + } + + @override + PrivateKeyInterface decrypt( + String passphrase, [ + Iterable subkeyPassphrases = const [], + ]) { + if (passphrase.isEmpty) { + throw ArgumentError('Passphrase are required for key decryption,'); + } + final subkeys = this.subkeys.map((subkey) { + final index = this.subkeys.indexOf(subkey); + final subkeyPass = (index < subkeyPassphrases.length) ? subkeyPassphrases.elementAt(index) : passphrase; + if (subkeyPass.isNotEmpty && subkey.keyPacket is SecretSubkeyPacket) { + final keyPacket = (subkey.keyPacket as SecretSubkeyPacket); + return Subkey( + this, + keyPacket.decrypt(subkeyPass), + revocationSignatures: subkey.revocationSignatures.toList(), + bindingSignatures: subkey.bindingSignatures.toList(), + ); + } else { + return subkey; + } + }); + return PrivateKey(PacketList([ + keyPacket.decrypt(passphrase), + ...revocationSignatures, + ...directSignatures, + ...users.map((user) => user.packetList).expand((packet) => packet), + ...subkeys.map((subkey) => subkey.packetList).expand((packet) => packet), + ])); + } +} diff --git a/lib/src/key/public_key.dart b/lib/src/key/public_key.dart index ce5b0e2f..89df1af2 100644 --- a/lib/src/key/public_key.dart +++ b/lib/src/key/public_key.dart @@ -13,7 +13,7 @@ import 'base.dart'; /// OpenPGP public key class /// Author Nguyen Van Nguyen -class PublicKey extends Base { +final class PublicKey extends Base { PublicKey(super.packetList); factory PublicKey.fromArmored(final String armored) { diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 34475c8d..d2292570 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -461,6 +461,9 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { @override int get keyVersion => publicKey.keyVersion; + @override + bool get aeadProtected => aead != null; + @override HashAlgorithm get preferredHash { if ((keyMaterial is ECPublicMaterial)) { diff --git a/lib/src/packet/secret_subkey.dart b/lib/src/packet/secret_subkey.dart index e8bbd821..0c76d079 100644 --- a/lib/src/packet/secret_subkey.dart +++ b/lib/src/packet/secret_subkey.dart @@ -78,7 +78,7 @@ class SecretSubkeyPacket extends SecretKeyPacket implements SubkeyPacketInterfac } @override - encrypt( + SecretSubkeyPacket encrypt( final String passphrase, final SymmetricAlgorithm symmetric, [ final AeadAlgorithm? aead, @@ -101,7 +101,7 @@ class SecretSubkeyPacket extends SecretKeyPacket implements SubkeyPacketInterfac } @override - decrypt(final String passphrase) { + SecretSubkeyPacket decrypt(final String passphrase) { if (secretKeyMaterial == null) { return SecretSubkeyPacket( publicKey as PublicSubkeyPacket, diff --git a/lib/src/type/private_key.dart b/lib/src/type/private_key.dart index 3035b55e..60a68a16 100644 --- a/lib/src/type/private_key.dart +++ b/lib/src/type/private_key.dart @@ -4,7 +4,8 @@ library; -import 'package:dart_pg/src/type/key.dart'; +import '../enum/hash_algorithm.dart'; +import 'key.dart'; /// OpenPGP private key interface /// Author Nguyen Van Nguyen @@ -15,6 +16,8 @@ abstract interface class PrivateKeyInterface implements KeyInterface { bool get aeadProtected; + HashAlgorithm get preferredHash; + /// Lock a private key with the given passphrase. /// This method does not change the original key. PrivateKeyInterface encrypt( diff --git a/lib/src/type/secret_key_packet.dart b/lib/src/type/secret_key_packet.dart index 91167c4a..9e62de8d 100644 --- a/lib/src/type/secret_key_packet.dart +++ b/lib/src/type/secret_key_packet.dart @@ -25,6 +25,9 @@ abstract interface class SecretKeyPacketInterface implements KeyPacketInterface /// Secret key packed is decrypted bool get isDecrypted; + /// Secret key packed is aead protected + bool get aeadProtected; + /// Get preferred hash algorithm HashAlgorithm get preferredHash; From e25150085d92712ec467586c88288cf849e38408 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 3 Dec 2024 09:54:37 +0700 Subject: [PATCH 107/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/type/key.dart | 13 +++++++++++++ lib/src/type/literal_data.dart | 7 +++++++ lib/src/type/private_key.dart | 4 ++++ lib/src/type/secret_key_packet.dart | 8 +++++--- lib/src/type/session_key.dart | 2 ++ lib/src/type/session_key_cryptor.dart | 2 ++ lib/src/type/signature_packet.dart | 26 ++++++++++++++++++++++++++ lib/src/type/subkey.dart | 13 +++++++++++++ lib/src/type/user.dart | 7 +++++++ 9 files changed, 79 insertions(+), 3 deletions(-) diff --git a/lib/src/type/key.dart b/lib/src/type/key.dart index d186b939..a9fb1387 100644 --- a/lib/src/type/key.dart +++ b/lib/src/type/key.dart @@ -18,29 +18,42 @@ import 'user.dart'; /// That represents a key packet, the relevant signatures, users and subkeys. /// Author Nguyen Van Nguyen abstract interface class KeyInterface implements ArmorableInterface, PacketContainerInterface { + /// Return key packet KeyPacketInterface get keyPacket; + /// Return key as public key KeyInterface get publicKey; + /// Return key version int get version; + /// Get creation time DateTime get creationTime; + /// Return the expiration time of the key or null if key does not expire. DateTime? get expirationTime; + /// Get key algorithm KeyAlgorithm get keyAlgorithm; + /// Get fingerprint Uint8List get fingerprint; + /// Get key ID Uint8List get keyID; + /// Get key strength int get keyStrength; + /// Get revocation signatures Iterable get revocationSignatures; + /// Get direct signatures Iterable get directSignatures; + /// Get users Iterable get users; + /// Get subkeys Iterable get subkeys; } diff --git a/lib/src/type/literal_data.dart b/lib/src/type/literal_data.dart index fa06deca..b5851243 100644 --- a/lib/src/type/literal_data.dart +++ b/lib/src/type/literal_data.dart @@ -11,17 +11,24 @@ import 'package:dart_pg/src/enum/literal_format.dart'; /// Literal data interface /// Author Nguyen Van Nguyen abstract interface class LiteralDataInterface { + /// Get literal format LiteralFormat get format; + /// Get filename String get filename; + /// Get time DateTime get time; + /// Get binary data Uint8List get binary; + /// Get text data String get text; + /// Get header Uint8List get header; + /// Get sign bytes Uint8List get signBytes; } diff --git a/lib/src/type/private_key.dart b/lib/src/type/private_key.dart index 60a68a16..d12de4d1 100644 --- a/lib/src/type/private_key.dart +++ b/lib/src/type/private_key.dart @@ -10,12 +10,16 @@ import 'key.dart'; /// OpenPGP private key interface /// Author Nguyen Van Nguyen abstract interface class PrivateKeyInterface implements KeyInterface { + /// Private key is encrypted bool get isEncrypted; + /// Private key is decrypted bool get isDecrypted; + /// Private key is aead protected bool get aeadProtected; + /// Get preferred hash algorithm HashAlgorithm get preferredHash; /// Lock a private key with the given passphrase. diff --git a/lib/src/type/secret_key_packet.dart b/lib/src/type/secret_key_packet.dart index 9e62de8d..4d858166 100644 --- a/lib/src/type/secret_key_packet.dart +++ b/lib/src/type/secret_key_packet.dart @@ -19,23 +19,25 @@ abstract interface class SecretKeyPacketInterface implements KeyPacketInterface /// Get secret key material SecretKeyMaterialInterface? get secretKeyMaterial; - /// Secret key packed is encrypted + /// Secret key packet is encrypted bool get isEncrypted; - /// Secret key packed is decrypted + /// Secret key packet is decrypted bool get isDecrypted; - /// Secret key packed is aead protected + /// Secret key packet is aead protected bool get aeadProtected; /// Get preferred hash algorithm HashAlgorithm get preferredHash; + /// Encrypt secret key packet with passphrase SecretKeyPacketInterface encrypt( String passphrase, SymmetricAlgorithm symmetric, [ AeadAlgorithm? aead, ]); + /// Decrypt secret key packet with passphrase SecretKeyPacketInterface decrypt(String passphrase); } diff --git a/lib/src/type/session_key.dart b/lib/src/type/session_key.dart index 08759dd1..abd7c85b 100644 --- a/lib/src/type/session_key.dart +++ b/lib/src/type/session_key.dart @@ -11,8 +11,10 @@ import '../enum/symmetric_algorithm.dart'; /// Session key interface /// Author Nguyen Van Nguyen abstract interface class SessionKeyInterface { + /// Get encryption key Uint8List get encryptionKey; + /// Get algorithm to encrypt the message with SymmetricAlgorithm get symmetric; /// Checksum the encryption key diff --git a/lib/src/type/session_key_cryptor.dart b/lib/src/type/session_key_cryptor.dart index 1db0bd46..afb4ffbb 100644 --- a/lib/src/type/session_key_cryptor.dart +++ b/lib/src/type/session_key_cryptor.dart @@ -11,7 +11,9 @@ import 'secret_key_material.dart'; /// Session key cryptor interface /// Author Nguyen Van Nguyen abstract interface class SessionKeyCryptorInterface { + /// Serialize session key cryptor to bytes Uint8List encode(); + /// Decrypt session key by using secret key packet Uint8List decrypt(SecretKeyMaterialInterface key); } diff --git a/lib/src/type/signature_packet.dart b/lib/src/type/signature_packet.dart index 252647f1..d9c2d99c 100644 --- a/lib/src/type/signature_packet.dart +++ b/lib/src/type/signature_packet.dart @@ -16,54 +16,80 @@ import 'subpacket.dart'; /// Signature packet interface /// Author Nguyen Van Nguyen abstract interface class SignaturePacketInterface implements PacketInterface { + /// Get version int get version; + /// Get signature type SignatureType get signatureType; + /// Get key algorithm KeyAlgorithm get keyAlgorithm; + /// Get hash algorithm HashAlgorithm get hashAlgorithm; + /// Get hashed subpackets Iterable get hashedSubpackets; + /// Get unhashed subpackets Iterable get unhashedSubpackets; + /// Get signature data Uint8List get signatureData; + /// Get signed hash value Uint8List get signedHashValue; + /// Get salt value Uint8List get salt; + /// Get signature Uint8List get signature; + /// Get signature creation time DateTime? get creationTime; + /// Get signature expiration time DateTime? get expirationTime; + /// Get key expiration time int get keyExpirationTime; + /// Get issuer key ID Uint8List get issuerKeyID; + /// Get issuer key fingerprint Uint8List get issuerFingerprint; + /// Return is primary user ID bool get isPrimaryUserID; + /// Return is certification bool get isCertification; + /// Return is revocation certification bool get isCertRevocation; + /// Return is direct key bool get isDirectKey; + /// Return is key revocation bool get isKeyRevocation; + /// Return is subkey binding bool get isSubkeyBinding; + /// Return is subkey revocation bool get isSubkeyRevocation; + /// Get subpacket T? getSubpacket(); + /// Verify signature expiration date. + /// Use the given date for verification instead of the current time. bool isExpired([final DateTime? time]); + /// Verify the signature packet. bool verify( final KeyPacketInterface verifyKey, final Uint8List dataToVerify, [ diff --git a/lib/src/type/subkey.dart b/lib/src/type/subkey.dart index 3c0050a5..dbaca98d 100644 --- a/lib/src/type/subkey.dart +++ b/lib/src/type/subkey.dart @@ -16,30 +16,43 @@ import 'subkey_packet.dart'; /// That represents a subkey packet and the relevant signatures. /// Author Nguyen Van Nguyen abstract interface class SubkeyInterface implements PacketContainerInterface { + /// Get main key KeyInterface get mainKey; + /// Get key packet SubkeyPacketInterface get keyPacket; + /// Return key version int get version; + /// Get creation time DateTime get creationTime; + /// Get the expiration time of the subkey or null if subkey does not expire. DateTime? get expirationTime; + /// Get key algorithm KeyAlgorithm get keyAlgorithm; + /// Get fingerprint Uint8List get fingerprint; + /// Get key ID Uint8List get keyID; + /// Get key strength int get keyStrength; + /// Return subkey is signing or verification key bool get isSigningKey; + /// Return subkey is encryption or decryption key bool get isEncryptionKey; + /// Get revocation signatures Iterable get revocationSignatures; + /// Get binding signatures Iterable get bindingSignatures; /// Check if a subkey is revoked diff --git a/lib/src/type/user.dart b/lib/src/type/user.dart index 3b06b515..57de4236 100644 --- a/lib/src/type/user.dart +++ b/lib/src/type/user.dart @@ -13,18 +13,25 @@ import 'user_id_packet.dart'; /// That represents an user ID or attribute packet and the relevant signatures. /// Author Nguyen Van Nguyen abstract interface class UserInterface implements PacketContainerInterface { + /// Get main key KeyInterface get mainKey; + /// Get user ID packet UserIDPacketInterface get userIDPacket; + /// Get user ID String get userID; + /// Return user is primary bool get isPrimary; + /// Get revocation signatures Iterable get revocationSignatures; + /// Get self signatures Iterable get selfSignatures; + /// Get other signatures Iterable get otherSignatures; /// Check if a given certificate of the user is revoked From 12b6758c80c3aa356a56e99e053641caf7c8d1a9 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 3 Dec 2024 10:56:00 +0700 Subject: [PATCH 108/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/public_key.dart | 46 ++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/lib/src/key/public_key.dart b/lib/src/key/public_key.dart index 89df1af2..4f5a1afc 100644 --- a/lib/src/key/public_key.dart +++ b/lib/src/key/public_key.dart @@ -4,10 +4,13 @@ library; -import 'package:dart_pg/src/common/armor.dart'; -import 'package:dart_pg/src/enum/armor_type.dart'; -import 'package:dart_pg/src/packet/packet_list.dart'; +import 'dart:typed_data'; +import 'package:dart_pg/src/packet/base.dart'; + +import '../common/armor.dart'; +import '../enum/armor_type.dart'; +import '../packet/packet_list.dart'; import '../type/key.dart'; import 'base.dart'; @@ -16,6 +19,7 @@ import 'base.dart'; final class PublicKey extends Base { PublicKey(super.packetList); + /// Read public key from armored string factory PublicKey.fromArmored(final String armored) { final armor = Armor.decode(armored); if (armor.type != ArmorType.publicKey) { @@ -24,6 +28,42 @@ final class PublicKey extends Base { return PublicKey(PacketList.decode(armor.data)); } + /// Read public keys from armored string + /// Return one or multiple key objects + static Iterable readPublicKeys(final String armored) { + final armor = Armor.decode(armored); + if (armor.type != ArmorType.publicKey) { + throw ArgumentError('Armored text not of public key type'); + } + final publicKeys = []; + final packetList = PacketList.decode(armor.data); + final indexes = packetList.indexOfTypes([PacketType.publicKey]); + for (var i = 0; i < indexes.length; i++) { + if (indexes.asMap().containsKey(i + 1)) { + publicKeys.add( + PublicKey( + PacketList( + packetList.packets.sublist(indexes[i], indexes[i + 1]), + ), + ), + ); + } + } + return publicKeys; + } + + /// Armor multiple public key. + static String armorPublicKeys(Iterable keys) => Armor.encode( + ArmorType.publicKey, + Uint8List.fromList(keys + .map( + (key) => key.publicKey.packetList, + ) + .expand( + (packet) => packet.encode(), + ) + .toList())); + @override String armor() => Armor.encode(ArmorType.publicKey, packetList.encode()); From d43fac84e59d77434ed05dad6cd69a701eb65396 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 3 Dec 2024 11:03:21 +0700 Subject: [PATCH 109/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base.dart | 6 +++--- lib/src/key/user.dart | 4 ++-- lib/src/packet/signature.dart | 5 +++-- lib/src/type/signature_packet.dart | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/src/key/base.dart b/lib/src/key/base.dart index fcb52703..36ccde10 100644 --- a/lib/src/key/base.dart +++ b/lib/src/key/base.dart @@ -176,13 +176,13 @@ abstract class Base implements KeyInterface { final List signatures, ) { signatures.sort( - (a, b) => b.creationTime!.compareTo( - a.creationTime!, + (a, b) => b.creationTime.compareTo( + a.creationTime, ), ); for (final signature in signatures) { if (signature.keyExpirationTime > 0) { - final creationTime = signature.creationTime!; + final creationTime = signature.creationTime; return creationTime.add(Duration( seconds: signature.keyExpirationTime, )); diff --git a/lib/src/key/user.dart b/lib/src/key/user.dart index 5f75c5dd..af2dc7cf 100644 --- a/lib/src/key/user.dart +++ b/lib/src/key/user.dart @@ -44,8 +44,8 @@ final class User implements UserInterface { bool get isPrimary { final signatures = selfSignatures.toList(); signatures.sort( - (a, b) => b.creationTime!.compareTo( - a.creationTime!, + (a, b) => b.creationTime.compareTo( + a.creationTime, ), ); for (final signature in signatures) { diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 6bbeda8a..37cedee7 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -240,7 +240,8 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { ]); @override - DateTime? get creationTime => getSubpacket()?.creationTime; + DateTime get creationTime => + getSubpacket()?.creationTime ?? DateTime.fromMillisecondsSinceEpoch(0); @override DateTime? get expirationTime => getSubpacket()?.expirationTime; @@ -300,7 +301,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { @override bool isExpired([final DateTime? time]) { final timestamp = time?.millisecondsSinceEpoch ?? DateTime.now().millisecondsSinceEpoch; - final creation = creationTime?.millisecondsSinceEpoch ?? 0; + final creation = creationTime.millisecondsSinceEpoch; final expiration = expirationTime?.millisecondsSinceEpoch ?? DateTime.now().millisecondsSinceEpoch; return !(creation <= timestamp && timestamp <= expiration); } diff --git a/lib/src/type/signature_packet.dart b/lib/src/type/signature_packet.dart index d9c2d99c..d8614af5 100644 --- a/lib/src/type/signature_packet.dart +++ b/lib/src/type/signature_packet.dart @@ -47,7 +47,7 @@ abstract interface class SignaturePacketInterface implements PacketInterface { Uint8List get signature; /// Get signature creation time - DateTime? get creationTime; + DateTime get creationTime; /// Get signature expiration time DateTime? get expirationTime; From 990a568c98a2495f1c32db2b9d3d606c6e61bf0f Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 3 Dec 2024 11:13:01 +0700 Subject: [PATCH 110/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/private_key.dart | 16 ++++++++-------- lib/src/type/private_key.dart | 4 ++++ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index 799befb1..5862acdd 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -23,19 +23,19 @@ final class PrivateKey extends Base implements PrivateKeyInterface { PrivateKey(super.packetList); @override - SecretKeyPacketInterface get keyPacket => super.keyPacket as SecretKeyPacketInterface; + SecretKeyPacketInterface get secretKeyPacket => super.keyPacket as SecretKeyPacketInterface; @override - bool get isDecrypted => keyPacket.isDecrypted; + bool get isDecrypted => secretKeyPacket.isDecrypted; @override - bool get isEncrypted => keyPacket.isEncrypted; + bool get isEncrypted => secretKeyPacket.isEncrypted; @override - bool get aeadProtected => keyPacket.aeadProtected; + bool get aeadProtected => secretKeyPacket.aeadProtected; @override - HashAlgorithm get preferredHash => keyPacket.preferredHash; + HashAlgorithm get preferredHash => secretKeyPacket.preferredHash; @override KeyInterface get publicKey { @@ -70,7 +70,7 @@ final class PrivateKey extends Base implements PrivateKeyInterface { if (passphrase.isEmpty) { throw ArgumentError('Passphrase are required for key encryption.'); } - if (!keyPacket.isDecrypted) { + if (!secretKeyPacket.isDecrypted) { throw StateError('Private key must be decrypted before encrypting.'); } @@ -99,7 +99,7 @@ final class PrivateKey extends Base implements PrivateKeyInterface { }); return PrivateKey(PacketList([ - keyPacket.encrypt( + secretKeyPacket.encrypt( passphrase, Config.preferredSymmetric, aead, @@ -135,7 +135,7 @@ final class PrivateKey extends Base implements PrivateKeyInterface { } }); return PrivateKey(PacketList([ - keyPacket.decrypt(passphrase), + secretKeyPacket.decrypt(passphrase), ...revocationSignatures, ...directSignatures, ...users.map((user) => user.packetList).expand((packet) => packet), diff --git a/lib/src/type/private_key.dart b/lib/src/type/private_key.dart index d12de4d1..44e3385c 100644 --- a/lib/src/type/private_key.dart +++ b/lib/src/type/private_key.dart @@ -6,10 +6,14 @@ library; import '../enum/hash_algorithm.dart'; import 'key.dart'; +import 'secret_key_packet.dart'; /// OpenPGP private key interface /// Author Nguyen Van Nguyen abstract interface class PrivateKeyInterface implements KeyInterface { + /// Get secret key packet + SecretKeyPacketInterface get secretKeyPacket; + /// Private key is encrypted bool get isEncrypted; From 6a012bfd1d0f6a91878f2d26dc543cddb059f913 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 3 Dec 2024 11:36:39 +0700 Subject: [PATCH 111/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/private_key.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index 5862acdd..5cb977a1 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -9,6 +9,7 @@ import 'package:dart_pg/src/common/config.dart'; import 'package:dart_pg/src/enum/aead_algorithm.dart'; import 'package:dart_pg/src/enum/armor_type.dart'; import 'package:dart_pg/src/enum/hash_algorithm.dart'; +import 'package:dart_pg/src/enum/key_version.dart'; import 'package:dart_pg/src/key/base.dart'; import 'package:dart_pg/src/key/public_key.dart'; import 'package:dart_pg/src/key/subkey.dart'; @@ -75,12 +76,12 @@ final class PrivateKey extends Base implements PrivateKeyInterface { } AeadAlgorithm? aead; - if (version == 6 && Config.aeadProtect) { + if (version == KeyVersion.v6.value && Config.aeadProtect) { aead = Config.preferredAead; } final subkeys = this.subkeys.map((subkey) { final index = this.subkeys.indexOf(subkey); - final subkeyPass = (index < subkeyPassphrases.length) ? subkeyPassphrases.elementAt(index) : passphrase; + final subkeyPass = subkeyPassphrases.elementAtOrNull(index) ?? passphrase; if (subkeyPass.isNotEmpty && subkey.keyPacket is SecretSubkeyPacket) { final keyPacket = (subkey.keyPacket as SecretSubkeyPacket); return Subkey( @@ -121,7 +122,7 @@ final class PrivateKey extends Base implements PrivateKeyInterface { } final subkeys = this.subkeys.map((subkey) { final index = this.subkeys.indexOf(subkey); - final subkeyPass = (index < subkeyPassphrases.length) ? subkeyPassphrases.elementAt(index) : passphrase; + final subkeyPass = subkeyPassphrases.elementAtOrNull(index) ?? passphrase; if (subkeyPass.isNotEmpty && subkey.keyPacket is SecretSubkeyPacket) { final keyPacket = (subkey.keyPacket as SecretSubkeyPacket); return Subkey( From 40584d38a9e778659a73008a8175d138f0f6af86 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 3 Dec 2024 11:44:30 +0700 Subject: [PATCH 112/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/armor.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/common/armor.dart b/lib/src/common/armor.dart index 35bc5337..2e10e624 100644 --- a/lib/src/common/armor.dart +++ b/lib/src/common/armor.dart @@ -39,8 +39,9 @@ class Armor { static const splitPattern = r'^-----[^-]+-----$'; static const emptyLinePattern = r'^[ \f\r\t\u00a0\u2000-\u200a\u202f\u205f\u3000]*$'; static const headerPattern = r'^([^\s:]|[^\s:][^:]*[^\s:]): .+$'; - static const beginPattern = - r'^-----BEGIN PGP (MESSAGE, PART \d+\/\d+|MESSAGE, PART \d+|SIGNED MESSAGE|MESSAGE|PUBLIC KEY BLOCK|PRIVATE KEY BLOCK|SIGNATURE)-----$'; + static const beginPattern = r'^-----BEGIN PGP (MESSAGE, PART \d+\/\d+' + r'|MESSAGE, PART \d+|SIGNED MESSAGE|MESSAGE|' + r'PUBLIC KEY BLOCK|PRIVATE KEY BLOCK|SIGNATURE)-----$'; static const base64Chunk = 76; From 49ce73a22909977e5ca9ec3556e1f88943e7ff8f Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 3 Dec 2024 12:00:02 +0700 Subject: [PATCH 113/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/armor.dart | 33 +++++---------------------------- lib/src/enum/armor_type.dart | 32 +++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/lib/src/common/armor.dart b/lib/src/common/armor.dart index 2e10e624..2384c679 100644 --- a/lib/src/common/armor.dart +++ b/lib/src/common/armor.dart @@ -39,18 +39,19 @@ class Armor { static const splitPattern = r'^-----[^-]+-----$'; static const emptyLinePattern = r'^[ \f\r\t\u00a0\u2000-\u200a\u202f\u205f\u3000]*$'; static const headerPattern = r'^([^\s:]|[^\s:][^:]*[^\s:]): .+$'; - static const beginPattern = r'^-----BEGIN PGP (MESSAGE, PART \d+\/\d+' - r'|MESSAGE, PART \d+|SIGNED MESSAGE|MESSAGE|' - r'PUBLIC KEY BLOCK|PRIVATE KEY BLOCK|SIGNATURE)-----$'; static const base64Chunk = 76; + /// Armor type final ArmorType type; + /// Armor data final Uint8List data; + /// Armor headers final List headers; + /// Armor text final String text; Armor( @@ -74,7 +75,7 @@ class Armor { final lines = LineSplitter.split(armored); for (final line in lines) { if (type == null && splitPattern.hasMatch(line)) { - type = _getType(line); + type = ArmorType.fromBegin(line); } else { if (headerPattern.hasMatch(line)) { headers.add(line); @@ -194,30 +195,6 @@ class Armor { return result.join(); } - static ArmorType _getType(final String text) { - final matches = RegExp(beginPattern).allMatches(text); - if (matches.isEmpty) { - throw ArgumentError('Unknown ASCII armor type'); - } - final match = matches.elementAt(0)[0]!; - if (r'MESSAGE, PART \d+\/\d+'.hasMatch(match)) { - return ArmorType.multipartSection; - } else if (r'MESSAGE, PART \d+'.hasMatch(match)) { - return ArmorType.multipartLast; - } else if (r'SIGNED MESSAGE'.hasMatch(match)) { - return ArmorType.signedMessage; - } else if (r'MESSAGE'.hasMatch(match)) { - return ArmorType.message; - } else if (r'PUBLIC KEY BLOCK'.hasMatch(match)) { - return ArmorType.publicKey; - } else if (r'PRIVATE KEY BLOCK'.hasMatch(match)) { - return ArmorType.privateKey; - } else if (r'SIGNATURE'.hasMatch(match)) { - return ArmorType.signature; - } - return ArmorType.multipartSection; - } - static String _addHeader([final String customComment = '']) { final headers = []; headers.add('Version: $version\n'); diff --git a/lib/src/enum/armor_type.dart b/lib/src/enum/armor_type.dart index 74e41afe..e8cd2caa 100644 --- a/lib/src/enum/armor_type.dart +++ b/lib/src/enum/armor_type.dart @@ -4,6 +4,8 @@ library; +import '../common/extensions.dart'; + /// Armor types enum /// Author Nguyen Van Nguyen enum ArmorType { @@ -13,5 +15,33 @@ enum ArmorType { message, publicKey, privateKey, - signature, + signature; + + static const _beginPattern = r'^-----BEGIN PGP (MESSAGE, PART \d+\/\d+' + r'|MESSAGE, PART \d+|SIGNED MESSAGE|MESSAGE|' + r'PUBLIC KEY BLOCK|PRIVATE KEY BLOCK|SIGNATURE)-----$'; + + factory ArmorType.fromBegin(final String text) { + final matches = RegExp(_beginPattern).allMatches(text); + if (matches.isEmpty) { + throw ArgumentError('Unknown ASCII armor type'); + } + final match = matches.elementAt(0)[0]!; + if (r'MESSAGE, PART \d+\/\d+'.hasMatch(match)) { + return multipartSection; + } else if (r'MESSAGE, PART \d+'.hasMatch(match)) { + return multipartLast; + } else if (r'SIGNED MESSAGE'.hasMatch(match)) { + return signedMessage; + } else if (r'MESSAGE'.hasMatch(match)) { + return message; + } else if (r'PUBLIC KEY BLOCK'.hasMatch(match)) { + return publicKey; + } else if (r'PRIVATE KEY BLOCK'.hasMatch(match)) { + return privateKey; + } else if (r'SIGNATURE'.hasMatch(match)) { + return signature; + } + return multipartSection; + } } From 567afe8d3d8179ad7227b5587418bf5ff00cbdb4 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 3 Dec 2024 16:48:12 +0700 Subject: [PATCH 114/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base.dart | 4 ++-- lib/src/key/private_key.dart | 2 +- lib/src/key/public_key.dart | 2 +- lib/src/key/subkey.dart | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/key/base.dart b/lib/src/key/base.dart index 36ccde10..c2335a59 100644 --- a/lib/src/key/base.dart +++ b/lib/src/key/base.dart @@ -22,7 +22,7 @@ import 'user.dart'; /// Base abstract OpenPGP key class /// Author Nguyen Van Nguyen -abstract class Base implements KeyInterface { +abstract class BaseKey implements KeyInterface { @override late final KeyPacketInterface keyPacket; @@ -38,7 +38,7 @@ abstract class Base implements KeyInterface { @override late final List subkeys; - Base(final PacketListInterface packetList) { + BaseKey(final PacketListInterface packetList) { _readPacketList(packetList); } diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index 5cb977a1..ce035e74 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -20,7 +20,7 @@ import 'package:dart_pg/src/type/packet.dart'; import 'package:dart_pg/src/type/private_key.dart'; import 'package:dart_pg/src/type/secret_key_packet.dart'; -final class PrivateKey extends Base implements PrivateKeyInterface { +final class PrivateKey extends BaseKey implements PrivateKeyInterface { PrivateKey(super.packetList); @override diff --git a/lib/src/key/public_key.dart b/lib/src/key/public_key.dart index 4f5a1afc..4345e4c7 100644 --- a/lib/src/key/public_key.dart +++ b/lib/src/key/public_key.dart @@ -16,7 +16,7 @@ import 'base.dart'; /// OpenPGP public key class /// Author Nguyen Van Nguyen -final class PublicKey extends Base { +final class PublicKey extends BaseKey { PublicKey(super.packetList); /// Read public key from armored string diff --git a/lib/src/key/subkey.dart b/lib/src/key/subkey.dart index 6032a494..fbbf64a7 100644 --- a/lib/src/key/subkey.dart +++ b/lib/src/key/subkey.dart @@ -42,7 +42,7 @@ final class Subkey implements SubkeyInterface { DateTime get creationTime => keyPacket.creationTime; @override - DateTime? get expirationTime => Base.keyExpiration(bindingSignatures); + DateTime? get expirationTime => BaseKey.keyExpiration(bindingSignatures); @override Uint8List get fingerprint => keyPacket.fingerprint; From a1548d266b438be1f4c527da6dd57d9d9291f574 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 08:53:45 +0700 Subject: [PATCH 115/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base.dart | 8 +++++++- lib/src/key/user.dart | 8 ++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/src/key/base.dart b/lib/src/key/base.dart index c2335a59..5a689ee4 100644 --- a/lib/src/key/base.dart +++ b/lib/src/key/base.dart @@ -6,6 +6,7 @@ library; import 'dart:typed_data'; +import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/enum/key_algorithm.dart'; import 'package:dart_pg/src/enum/signature_type.dart'; import 'package:dart_pg/src/packet/base.dart'; @@ -110,6 +111,8 @@ abstract class BaseKey implements KeyInterface { subkey = Subkey( this, packet, + revocationSignatures: [], + bindingSignatures: [], ); subkeys.add(subkey); } @@ -121,6 +124,9 @@ abstract class BaseKey implements KeyInterface { user = User( this, packet, + revocationSignatures: [], + selfSignatures: [], + otherSignatures: [], ); users.add(user); } @@ -132,7 +138,7 @@ abstract class BaseKey implements KeyInterface { case SignatureType.certPersona: case SignatureType.certCasual: case SignatureType.certPositive: - if (packet.issuerKeyID == primaryKeyID) { + if (packet.issuerKeyID.equals(primaryKeyID!)) { user?.selfSignatures.add(packet); } else { user?.otherSignatures.add(packet); diff --git a/lib/src/key/user.dart b/lib/src/key/user.dart index af2dc7cf..fa4fcc48 100644 --- a/lib/src/key/user.dart +++ b/lib/src/key/user.dart @@ -24,20 +24,20 @@ final class User implements UserInterface { final UserIDPacketInterface userIDPacket; @override - final List selfSignatures; + final List revocationSignatures; @override - final List otherSignatures; + final List selfSignatures; @override - final List revocationSignatures; + final List otherSignatures; User( this.mainKey, this.userIDPacket, { + this.revocationSignatures = const [], this.selfSignatures = const [], this.otherSignatures = const [], - this.revocationSignatures = const [], }); @override From 661a389cd963470916cce0b02abf263d5edac065 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 11:05:51 +0700 Subject: [PATCH 116/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base.dart | 181 ++++++++++++++++++---------------- lib/src/key/private_key.dart | 8 +- lib/src/type/packet_list.dart | 4 +- lib/src/type/subkey.dart | 4 +- lib/src/type/user.dart | 6 +- 5 files changed, 107 insertions(+), 96 deletions(-) diff --git a/lib/src/key/base.dart b/lib/src/key/base.dart index 5a689ee4..9df5c0cf 100644 --- a/lib/src/key/base.dart +++ b/lib/src/key/base.dart @@ -8,8 +8,6 @@ import 'dart:typed_data'; import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/enum/key_algorithm.dart'; -import 'package:dart_pg/src/enum/signature_type.dart'; -import 'package:dart_pg/src/packet/base.dart'; import 'package:dart_pg/src/packet/packet_list.dart'; import 'package:dart_pg/src/type/key.dart'; import 'package:dart_pg/src/type/key_packet.dart'; @@ -18,6 +16,7 @@ import 'package:dart_pg/src/type/signature_packet.dart'; import 'package:dart_pg/src/type/subkey.dart'; import 'package:dart_pg/src/type/subkey_packet.dart'; import 'package:dart_pg/src/type/user.dart'; +import 'package:dart_pg/src/type/user_id_packet.dart'; import 'subkey.dart'; import 'user.dart'; @@ -84,97 +83,109 @@ abstract class BaseKey implements KeyInterface { ]); _readPacketList(final PacketListInterface packetList) { - final revocationSignatures = []; - final directSignatures = []; - final users = []; - final subkeys = []; + final keyPacketList = packetList.takeWhile( + (packet) => packet is KeyPacketInterface, + ); - KeyPacketInterface? keyPacket; - Subkey? subkey; - User? user; - Uint8List? primaryKeyID; - for (final packet in packetList) { - switch (packet.type) { - case PacketType.publicKey: - case PacketType.secretKey: - if (keyPacket != null) { - throw StateError('Key block contains multiple key packets'); - } - if (packet is KeyPacketInterface) { - keyPacket = packet; - primaryKeyID = packet.keyID; - } - break; - case PacketType.publicSubkey: - case PacketType.secretSubkey: - if (packet is SubkeyPacketInterface) { - subkey = Subkey( - this, - packet, - revocationSignatures: [], - bindingSignatures: [], - ); - subkeys.add(subkey); + if (keyPacketList.isEmpty) { + throw StateError('Key packet not found in packet list.'); + } + if (keyPacketList.length > 1) { + throw StateError('Key block contains multiple key packets.'); + } + keyPacket = keyPacketList.whereType().first; + + var remainPackets = packetList.skipWhile( + (packet) => packet is KeyPacketInterface, + ); + + revocationSignatures = remainPackets + .takeWhile((packet) { + if (packet is SignaturePacketInterface) { + return packet.isCertRevocation; } - user = null; - break; - case PacketType.userID: - case PacketType.userAttribute: - if (packet is UserIDPacket) { - user = User( - this, - packet, - revocationSignatures: [], - selfSignatures: [], - otherSignatures: [], - ); - users.add(user); + return false; + }) + .whereType() + .toList(); + remainPackets = remainPackets.skipWhile((packet) { + if (packet is SignaturePacketInterface) { + return packet.isKeyRevocation; + } + return false; + }); + + directSignatures = remainPackets + .takeWhile((packet) { + if (packet is SignaturePacketInterface) { + return packet.isDirectKey; } - break; - case PacketType.signature: - if (packet is SignaturePacket) { - switch (packet.signatureType) { - case SignatureType.certGeneric: - case SignatureType.certPersona: - case SignatureType.certCasual: - case SignatureType.certPositive: - if (packet.issuerKeyID.equals(primaryKeyID!)) { - user?.selfSignatures.add(packet); - } else { - user?.otherSignatures.add(packet); - } - break; - case SignatureType.certRevocation: - user?.revocationSignatures.add(packet); - break; - case SignatureType.subkeyBinding: - subkey?.bindingSignatures.add(packet); - break; - case SignatureType.subkeyRevocation: - subkey?.revocationSignatures.add(packet); - break; - case SignatureType.directKey: - directSignatures.add(packet); - break; - case SignatureType.keyRevocation: - revocationSignatures.add(packet); - break; - default: - } + return false; + }) + .whereType() + .toList(); + remainPackets = remainPackets.skipWhile((packet) { + if (packet is SignaturePacketInterface) { + return packet.isDirectKey; + } + return false; + }); + + User? user; + final users = []; + final userPackets = remainPackets.takeWhile((packet) { + return packet is! SubkeyPacketInterface; + }); + for (final packet in userPackets) { + if (packet is UserIDPacketInterface) { + user = User( + this, + packet, + revocationSignatures: [], + selfSignatures: [], + otherSignatures: [], + ); + users.add(user); + } + if (packet is SignaturePacketInterface) { + if (packet.isCertification) { + if (packet.issuerKeyID.equals(keyPacket.keyID)) { + user?.selfSignatures.add(packet); + } else { + user?.otherSignatures.add(packet); } - break; - default: + } + if (packet.isCertRevocation) { + user?.revocationSignatures.add(packet); + } } } + this.users = users.where((user) => user.verify()).toList(); - if (keyPacket == null) { - throw StateError('Key packet not found in packet list'); + Subkey? subkey; + final subkeys = []; + final subkeyPackets = remainPackets.skipWhile((packet) { + return packet is! SubkeyPacketInterface; + }); + for (final packet in subkeyPackets) { + if (packet is SubkeyPacketInterface) { + subkey = Subkey( + this, + packet, + revocationSignatures: [], + bindingSignatures: [], + ); + subkeys.add(subkey); + } + if (packet is SignaturePacketInterface) { + if (packet.isSubkeyRevocation) { + subkey?.revocationSignatures.add(packet); + } + if (packet.isSubkeyBinding) { + subkey?.bindingSignatures.add(packet); + } + } } - - this.keyPacket = keyPacket; - this.revocationSignatures = revocationSignatures; - this.directSignatures = directSignatures; - this.users = users.where((user) => user.verify()).toList(); this.subkeys = subkeys.where((subkey) => subkey.verify()).toList(); } diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index ce035e74..6e611d58 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -91,8 +91,8 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { Config.preferredSymmetric, aead, ), - revocationSignatures: subkey.revocationSignatures.toList(), - bindingSignatures: subkey.bindingSignatures.toList(), + revocationSignatures: subkey.revocationSignatures, + bindingSignatures: subkey.bindingSignatures, ); } else { return subkey; @@ -128,8 +128,8 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { return Subkey( this, keyPacket.decrypt(subkeyPass), - revocationSignatures: subkey.revocationSignatures.toList(), - bindingSignatures: subkey.bindingSignatures.toList(), + revocationSignatures: subkey.revocationSignatures, + bindingSignatures: subkey.bindingSignatures, ); } else { return subkey; diff --git a/lib/src/type/packet_list.dart b/lib/src/type/packet_list.dart index f6c293ca..1f7d852a 100644 --- a/lib/src/type/packet_list.dart +++ b/lib/src/type/packet_list.dart @@ -10,9 +10,9 @@ import 'packet.dart'; /// Packet list interface /// Author Nguyen Van Nguyen -abstract interface class PacketListInterface extends Iterable { +abstract interface class PacketListInterface implements List { /// Get packets - Iterable get packets; + List get packets; /// Serialize packets to bytes Uint8List encode(); diff --git a/lib/src/type/subkey.dart b/lib/src/type/subkey.dart index dbaca98d..549bfbd4 100644 --- a/lib/src/type/subkey.dart +++ b/lib/src/type/subkey.dart @@ -50,10 +50,10 @@ abstract interface class SubkeyInterface implements PacketContainerInterface { bool get isEncryptionKey; /// Get revocation signatures - Iterable get revocationSignatures; + List get revocationSignatures; /// Get binding signatures - Iterable get bindingSignatures; + List get bindingSignatures; /// Check if a subkey is revoked bool isRevoked([final DateTime? time]); diff --git a/lib/src/type/user.dart b/lib/src/type/user.dart index 57de4236..2f6be9b5 100644 --- a/lib/src/type/user.dart +++ b/lib/src/type/user.dart @@ -26,13 +26,13 @@ abstract interface class UserInterface implements PacketContainerInterface { bool get isPrimary; /// Get revocation signatures - Iterable get revocationSignatures; + List get revocationSignatures; /// Get self signatures - Iterable get selfSignatures; + List get selfSignatures; /// Get other signatures - Iterable get otherSignatures; + List get otherSignatures; /// Check if a given certificate of the user is revoked bool isRevoked([final DateTime? time]); From 160cc7c2b4c891ef004a967bbbcb2de53e96e2b4 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 12:32:36 +0700 Subject: [PATCH 117/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/key/key_reading_test.dart | 347 +++++++++++++++++++++++++++++++++ 1 file changed, 347 insertions(+) create mode 100644 test/key/key_reading_test.dart diff --git a/test/key/key_reading_test.dart b/test/key/key_reading_test.dart new file mode 100644 index 00000000..f1c8334f --- /dev/null +++ b/test/key/key_reading_test.dart @@ -0,0 +1,347 @@ +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/key_algorithm.dart'; +import 'package:dart_pg/src/key/public_key.dart'; +import 'package:test/test.dart'; + +void main() { + group('Key reading', () { + const userID = 'Dart Privacy Guard '; + test('RSA key', () { + const armored = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBGc/4FEBCACZXe5kmfKWChtAGdoYRxRkEcfm0Ne431ovB4QS3zIOoclfyA7j +gyq1UAhKbsYhZmTKxx0FO5/HKOO2ojJU3AeKbLP+xhH8yVdEK5JiySvX/16Tz27d ++r5WGLkjZMYZAxWW9hdfurajYp8vcdACvErKMXShXUR9npCKH5O0QBagxMg6vczf +JfNG1qWJ9aeFO7yffUt9n+39ru3Cc67EAp7m29Wowto15xmeXCPkYEgE+BM+86AH +GqihOERjPCzWeFVLRuCwj2YjmZvtMh9A2gx5K6dMQ5viMDsTSoB0DEZzvY+Q7gr7 +/qiWCpSfmy3gvmReOvMzk8uI7+Nt6vdJia4vABEBAAG0L0RhcnQgUHJpdmFjeSBH +dWFyZCA8ZGFydHBnQG9wZW5wZ3AuZXhhbXBsZS5jb20+iQFRBBMBCAA7FiEEXMzt +pU+RcIn4xIgABTI3KgKPL/UFAmc/4FECGwMFCwkIBwICIgIGFQoJCAsCBBYCAwEC +HgcCF4AACgkQBTI3KgKPL/UGsgf+MZ4jtjY6DLCSirAJkV31cvzawO3KgSBXGuBf +bnZxWniWwfzJF1n38HBMIpfx9wOpks7b6js5TuBaoguQ3xc8URrtywO4HavM3fd2 +kzWeKeePQQVsdmbBJS497MXHo+WiztAl+x7wTtAO08goSKefDuhst1G14AiUxeaZ +YPZpq8FdGe+LI6tTuc/FA/0P78g21P82WoWti23OpPKjDh7TS2T47DJ/dUEEavwV +6mT7rdLoRqrZtDj52rNzPOf4vyfBQe8X5ZQljSUcbUBqLE8LeD6z8AYpw/0Iisc+ +xg4TRq38ccCG6AOWCIAExWo2OgIZkU7tKURKstPXRAa2eQWEg7kBDQRnP+BRAQgA +lK7KSXI6cbgCihf9oD1iaIJE7pPmGvgVA44DyNwqYfAD5BX1XLfjTcEltcQ+gYP5 +Rkh9IXnV1Ew1cgiF3dTyPLVhIweNWkbtoVSfdPFAKOylZkk98zUkQpTSTzdH3+yl +euyuitz3Fotgs1efDWUgStpGakKc0EQhR6vDWKOjUJye8oWMR93X1uxdIVcDM3IM +PEorTUVtLzZSQyUHQG73h4fMN7mUPrRALO0LGVXVY9ie0RTjgA9SdzZZxMRN5nGs +MD4tiiyr/JRsztIyzT4hJyg+uC2zRPEA6/q9m0egseP3BCeal7h01BEm5T8JJD5V +t8L9rQ3UakGSshiziCntCwARAQABiQE2BBgBCAAgFiEEXMztpU+RcIn4xIgABTI3 +KgKPL/UFAmc/4FECGwwACgkQBTI3KgKPL/Uzpwf/f9uukiyNC4QNF7cfgSzoZMRQ +oOeg/LqKrk/Ig036EI65xwTHm8DG0fvejIpVPrmPM0nKVhBacTTU5M6nunN5D5RJ +FlO7GPjgZt0PmBEJ/VWiiwV9tww9hegW9pFRrVswM8lrocQ87XGcIxCrQi48UpHp +5Us0Q50tqWlrMVw2TehgPDLJtmpdta4OBG8DdfwugLIrCjD6yxvPYOvX0mQDlY5G +2v9w+m3t60KSt9okDs3LouipniCA6brtpcQ9XPw7w+Is3hSSk0g8Zbm2lVVZVRiP +hrmPD+bTB7WO025GVa9Kl9vs87rAeEVXtEXJiNxlTP8BGSRzz1ayKQ9Y4UcY2Q== +=S9XV +-----END PGP PUBLIC KEY BLOCK----- +'''; + final publicKey = PublicKey.fromArmored(armored); + final user = publicKey.users[0]; + final subkey = publicKey.subkeys[0]; + + expect( + publicKey.fingerprint.toHexadecimal(), + '5ccceda54f917089f8c488000532372a028f2ff5', + ); + expect(publicKey.version, 4); + expect(publicKey.keyAlgorithm, KeyAlgorithm.rsaEncryptSign); + expect(publicKey.keyStrength, 2048); + + expect( + subkey.fingerprint.toHexadecimal(), + '79fb82ac1204bdc854e87364316ef1539787254f', + ); + expect(subkey.version, 4); + expect(subkey.keyAlgorithm, KeyAlgorithm.rsaEncryptSign); + expect(subkey.keyStrength, 2048); + + expect(user.userID, userID); + }); + + test('DSA & ElGamal key', () { + const armored = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=3nnd +-----END PGP PUBLIC KEY BLOCK----- +'''; + final publicKey = PublicKey.fromArmored(armored); + final user = publicKey.users[0]; + final subkey = publicKey.subkeys[0]; + + expect( + publicKey.fingerprint.toHexadecimal(), + '9a83344a3711864c1f502094ba727d0f6b50c281', + ); + expect(publicKey.version, 4); + expect(publicKey.keyAlgorithm, KeyAlgorithm.dsa); + expect(publicKey.keyStrength, 2048); + + expect( + subkey.fingerprint.toHexadecimal(), + '8f1cb653b2c3a4808302b63b52c1ed36e9e22006', + ); + expect(subkey.version, 4); + expect(subkey.keyAlgorithm, KeyAlgorithm.elgamal); + expect(subkey.keyStrength, 2048); + + expect(user.userID, userID); + }); + + test('ECC NIST P-384 key', () { + const armored = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mG8EZz/5iRMFK4EEACIDAwR3cUlciuVCSwGLA4sH6SwA1axNDMU3GqnHOOmCVoM0 +qOAX9FdVeP0DqixqjRYm2qQoUpm+QBK84KA5oGfcYNRmYXwYnC64s05yLQkL836D +mcV38F5c0k3IQIwhuWVwu320L0RhcnQgUHJpdmFjeSBHdWFyZCA8ZGFydHBnQG9w +ZW5wZ3AuZXhhbXBsZS5jb20+iLMEExMJADsWIQSjJRB+ZryrPupAdVA5bsuLuG0Z +IgUCZz/5iQIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRA5bsuLuG0Z +InjBAX9bv51DhgTNGDot6gTEKJ7c0FypBuPZPlNOKMCshtvu36+jiJyMpGBaR/C+ +CB8wctkBgNT0/5Mj1e+UXqFAmflWmODnszrYpdiGSF2GJxVwOBLJeFVqPQtl2mSn +mKmhhoVy3bhzBGc/+YkSBSuBBAAiAwME6+LPwcbdvoEdxPA2L002pKXp8Nt7PxIs +jUegQRw2bTQXMlu5zplsUNprWJZ1W/iqLhmEH8eLONxcuzVEQMb3dCImxBzmL3Y5 +HxGGti81EsU7JBIZxHKhl85RY78HdHgCAwEJCYiYBBgTCQAgFiEEoyUQfma8qz7q +QHVQOW7Li7htGSIFAmc/+YkCGwwACgkQOW7Li7htGSJT3AGAgueSdnW+Lvrv2K9V +fqgHGB2NDYsdLdXOIHardOmkFmD3rTbjsjHkQrLCs/lCQkVhAYCxmcY10PcZBwYq +mzhn+ek2wQOTGM2wO3qJVZ9Z+z5kkQXH6JtyfkZATJgA/kougNs= +=K1/q +-----END PGP PUBLIC KEY BLOCK----- +'''; + final publicKey = PublicKey.fromArmored(armored); + final user = publicKey.users[0]; + final subkey = publicKey.subkeys[0]; + + expect( + publicKey.fingerprint.toHexadecimal(), + 'a325107e66bcab3eea407550396ecb8bb86d1922', + ); + expect(publicKey.version, 4); + expect(publicKey.keyAlgorithm, KeyAlgorithm.ecdsa); + expect(publicKey.keyStrength, 384); + + expect( + subkey.fingerprint.toHexadecimal(), + 'bd7133409a3ad7986fbae32a5a2990ce6bd63b20', + ); + expect(subkey.version, 4); + expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(subkey.keyStrength, 384); + + expect(user.userID, userID); + }); + + test('ECC Brainpool P-256 key', () { + const armored = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mFMEZz/50BMJKyQDAwIIAQEHAgMEnXlquLQ8KrfXtrCZ000jDP7I5nXzJQKDws4C +ZgRpECoCHUeYCdIrl3iLUJfYzb9Qq6nt/8oP+jljqiJ35W5NlLQvRGFydCBQcml2 +YWN5IEd1YXJkIDxkYXJ0cGdAb3BlbnBncC5leGFtcGxlLmNvbT6IkwQTEwgAOxYh +BM0bWxQpT4C+Zc7775lRIZ/J3pV4BQJnP/nQAhsDBQsJCAcCAiICBhUKCQgLAgQW +AgMBAh4HAheAAAoJEJlRIZ/J3pV4v3oA/R9IMDJklqQ7qUp20updBhqAbifSELId +xbd886G8hPgiAP0bpKSgkad2xFUSuO8o4b8menOFbFQphPIf/E+CWstwBLhXBGc/ ++dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGS +VO0lI/BV2JJsuQqI0Pne08Y7og4bhyv9S+D+wcU8sv0DAQgHiHgEGBMIACAWIQTN +G1sUKU+AvmXO+++ZUSGfyd6VeAUCZz/50AIbDAAKCRCZUSGfyd6VeMTKAQCIrK5W +d0O6gwPJ4rSHWo13Q0K9Lth8ZBtXySqZZ/XSBwEAj22bXtH3QkS/K70fExKX4KZ7 +k49k9YikNemTd0HPLRc= +=38Dv +-----END PGP PUBLIC KEY BLOCK----- +'''; + final publicKey = PublicKey.fromArmored(armored); + final user = publicKey.users[0]; + final subkey = publicKey.subkeys[0]; + + expect( + publicKey.fingerprint.toHexadecimal(), + 'cd1b5b14294f80be65cefbef9951219fc9de9578', + ); + expect(publicKey.version, 4); + expect(publicKey.keyAlgorithm, KeyAlgorithm.ecdsa); + expect(publicKey.keyStrength, 256); + + expect( + subkey.fingerprint.toHexadecimal(), + 'fda27358dac2b11fb5388cca65b53bd1aff05e06', + ); + expect(subkey.version, 4); + expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(subkey.keyStrength, 256); + + expect(user.userID, userID); + }); + + test('ECC Curve 25519 key', () { + const armored = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEZz/6BhYJKwYBBAHaRw8BAQdAZQAdPHBwHpMPvaCgVCkt/eMCp1ubVxnC/U3a +aMF98SC0L0RhcnQgUHJpdmFjeSBHdWFyZCA8ZGFydHBnQG9wZW5wZ3AuZXhhbXBs +ZS5jb20+iJMEExYKADsWIQQRSLKiT1gJd8J7JiI+1HXdIS0iHgUCZz/6BgIbAwUL +CQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRA+1HXdIS0iHrifAQDPuCf7ztzy +K9opPkJ2aw7SR/IFTE7dfdIuOTDud+Ph4AEA4YRvlmHv27VVAsS8XoDxT+t1tbkA +xgiwXtqXA2t/Ewy4OARnP/oGEgorBgEEAZdVAQUBAQdACcGlnxIiapTiJPzvYtjz +GgwZm0VDgRxkHRUTym4n6X8DAQgHiHgEGBYKACAWIQQRSLKiT1gJd8J7JiI+1HXd +IS0iHgUCZz/6BgIbDAAKCRA+1HXdIS0iHrDSAP0Q/+hfme0EqcVJf/Z38dQwhXIM +Dy6c8mMkLVpS/zXLOAEAp3nZkkgY1MgZAfbx4BEq+uJv801qCBNeThFeOe8/3gs= +=/O/r +-----END PGP PUBLIC KEY BLOCK----- +'''; + final publicKey = PublicKey.fromArmored(armored); + final user = publicKey.users[0]; + final subkey = publicKey.subkeys[0]; + + expect( + publicKey.fingerprint.toHexadecimal(), + '1148b2a24f580977c27b26223ed475dd212d221e', + ); + expect(publicKey.version, 4); + expect(publicKey.keyAlgorithm, KeyAlgorithm.eddsaLegacy); + expect(publicKey.keyStrength, 255); + + expect( + subkey.fingerprint.toHexadecimal(), + '12ba12e01b12582680057e8b44d20d3a674af1a7', + ); + expect(subkey.version, 4); + expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(subkey.keyStrength, 255); + + expect(user.userID, userID); + }); + + test('Rfc9580 Curve 25519 key', () { + const armored = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: PHP Privacy v2 + +xioGZ0/jphsAAAAgy19s9qAQsttkAS7gPmFeakgJwkKklQVReq/oPNJVWKrCuwYfGwgAAABcBQJn +T+OmIiEGeyUi0XrLxokVQFD7GiRV+fyaRjGK1u9QOdPm47wEpnkJEHslItF6y8aJAhsDAwsHCQQi +AQIDBRUIDAoOBRYAAQIDAh4LDScHAQkBBwIJAgcDCQMAAAAAx94QMOS423OajdIBpQ7T4MklWxyz +4SNy5d9A3+a7huNEw6qmaJ+pqVGcrNctLB9qWyD4+xjDtMLqHYOeNUsunRUUaBM8BbFCb86wljnA +1RpABwXNL0RhcnQgUHJpdmFjeSBHdWFyZCA8ZGFydHBnQG9wZW5wZ3AuZXhhbXBsZS5jb20+wpUG +EBsIAAAANgUCZ0/jpiIhBnslItF6y8aJFUBQ+xokVfn8mkYxitbvUDnT5uO8BKZ5CRB7JSLResvG +iQIZAQAAAADh1RDsF0xb6N2mB4vw9rYIrNcKdWSwPb0IBY6kNrnN84nMcGCseJ9JWpX+4le3HP/s +Ej9rf8N+GfwQHY7FqUk4m+Mmrg9FSY8rtgtQ/WF/pPBeC80qTmd1eWVuIFZhbiBOZ3V5ZW4gPG5n +dXllbm52MTk4MUBnbWFpbC5jb20+wpIGEBsIAAAAMwUCZ0/jpiIhBnslItF6y8aJFUBQ+xokVfn8 +mkYxitbvUDnT5uO8BKZ5CRB7JSLResvGiQAAAADyNxAEWM9B3Ds6IOlYh2UIJ6BFMuDamUakpZff +sKKi6VPlhwTKb80sbAMCI2QeBpKkFR6j+4hBuleDEm34RE0Qo2P9/e8eJLUt6QvNwv8XrFMHDs4q +BmdP46YZAAAAIDv21k8ovP04IZRAc+Wiq+zFU0HW4P/GA5bLvCCPYHltwpUGGBsIAAAANgUCZ0/j +piIhBnslItF6y8aJFUBQ+xokVfn8mkYxitbvUDnT5uO8BKZ5CRB7JSLResvGiQIbDAAAAAAiEhAM +bPIvl2dct0Etc792kYzRRbUC3XDl5uzoLJj59aBkhkE+e5nX8j6hCfYSjQnPq9oKmMeOjdgkjeDM +fjfQblPSXk32Zpv5RXzNhOIelkIuA9USba2Zw7ZRB4iuDfOgEJAw7VEt +-----END PGP PUBLIC KEY BLOCK----- +'''; + final publicKey = PublicKey.fromArmored(armored); + final user = publicKey.users[0]; + final subkey = publicKey.subkeys[0]; + + expect( + publicKey.fingerprint.toHexadecimal(), + '7b2522d17acbc689154050fb1a2455f9fc9a46318ad6ef5039d3e6e3bc04a679', + ); + expect(publicKey.version, 6); + expect(publicKey.keyAlgorithm, KeyAlgorithm.ed25519); + expect(publicKey.keyStrength, 255); + + expect( + subkey.fingerprint.toHexadecimal(), + 'ef330d173def1188f84a0904394ae44b89ae2e9413e33ac524602beec62a13b0', + ); + expect(subkey.version, 6); + expect(subkey.keyAlgorithm, KeyAlgorithm.x25519); + expect(subkey.keyStrength, 255); + + expect(user.userID, userID); + }); + + test('Rfc9580 Curve 448 key', () { + const armored = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xkMGZ0/jphwAAAA5a1Aan5R/DuK8jXNrY9+z7IKjQ8NHO6J1CTodHHpDslwa7V85AavFpaHoxlp0 +yn2fJX2fSnJkqdsAwsAtBh8cCAAAAFwFAmdP46ciIQaTh3wqdlbkHo45oW1AVhRmOkxVk9ozBveA +NfRmZpxgUQkQk4d8KnZW5B4CGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcBCQEHAgkCBwMJ +AwAAAABauRD3wkQRbuI3zf+UyqDl2Y1qlqCHtdViFqwgyZE4mhZZYIrmfHYguy7GJm3FCb4vDZJM +X6qSmyH2kclVUvp3VGJXpOo35lDUidSAIYffeYT7jC2KIz7IcA5WAiHIDW7+5rQ30yehH3MOBhjR +dKNWDdrezDt8A6pl9pRFMlzO+YGQfjwAzS9EYXJ0IFByaXZhY3kgR3VhcmQgPGRhcnRwZ0BvcGVu +cGdwLmV4YW1wbGUuY29tPsLABwYQHAgAAAA2BQJnT+OnIiEGk4d8KnZW5B6OOaFtQFYUZjpMVZPa +Mwb3gDX0ZmacYFEJEJOHfCp2VuQeAhkBAAAAAFvNEKq2qVYMJA+GbX7k4Qk+0pnJFW2+kEQ4qmDE +HhYIoeD9YnicGqr5+479nt9AqxQVV1IJT8mwxqQYIrBsoWxy83Z4o7A9umbkn4CmGFi61EbJ/JjZ +NhLtut2DSlWz9wntNQArseGnHhGjGrupJqiNhkyeCdhJ09grcds6mS1JI4i+EQDNKk5ndXllbiBW +YW4gTmd1eWVuIDxuZ3V5ZW5udjE5ODFAZ21haWwuY29tPsLABAYQHAgAAAAzBQJnT+OnIiEGk4d8 +KnZW5B6OOaFtQFYUZjpMVZPaMwb3gDX0ZmacYFEJEJOHfCp2VuQeAAAAAPx5EGrwf9dxyc8Zcf3T +WTH678WXGljJ5QML4S98Po7Q8vcE+1IGEE/hjOpi/cTpL9z+2lQ6XvLYVD54YuFzsEj11zhMM1gc +4pASBABfwzft/LL75Gr8NvTvc7/oD1PNnXOaoLjKfuXIlFAMel73EQqtp9MR7uQ3iDI7WIXmTG/f +mnT0KQDOQgZnT+OnGgAAADiY/+RbigV4l1adpmwFDGKZLpK7qYHnSuxjknWgOD48e9nDdZ3gkW+o +TOZaHsI546Kexs04A4ZCv8LABwYYHAgAAAA2BQJnT+OnIiEGk4d8KnZW5B6OOaFtQFYUZjpMVZPa +Mwb3gDX0ZmacYFEJEJOHfCp2VuQeAhsMAAAAAMM5EPfNxWSEvc5dMhOkM8a9a8EFgF8qOkwJj9sQ +ISt80um5TscuNDSqmwCJCTLXDhblTcwseZGrBjlrnfij7+9VPQDPRzaYUSopXQDG4l8JFKpYxvEY +4xo3/Pw117VEfs6lTc8PLF+NkIlv6Ip88od/3Op2bjJfQX57cOQwx65hc/bdCgDVEEdmNh5TAjGf +FmXTxip3kr4= +-----END PGP PUBLIC KEY BLOCK----- +'''; + final publicKey = PublicKey.fromArmored(armored); + final user = publicKey.users[0]; + final subkey = publicKey.subkeys[0]; + + expect( + publicKey.fingerprint.toHexadecimal(), + '93877c2a7656e41e8e39a16d405614663a4c5593da3306f78035f466669c6051', + ); + expect(publicKey.version, 6); + expect(publicKey.keyAlgorithm, KeyAlgorithm.ed448); + expect(publicKey.keyStrength, 448); + + expect( + subkey.fingerprint.toHexadecimal(), + '21a08e3d6074094d6978ba062176baa279dbf992577f806e13696d7252775b68', + ); + expect(subkey.version, 6); + expect(subkey.keyAlgorithm, KeyAlgorithm.x448); + expect(subkey.keyStrength, 448); + + expect(user.userID, userID); + }); + }); +} From 3dafd0c1b3bcec368a1076fb4e59bea153a37665 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 13:29:14 +0700 Subject: [PATCH 118/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/helpers.dart | 39 +++++++++++++++++----------------- lib/src/packet/public_key.dart | 22 +++++++++++++++++-- 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/lib/src/common/helpers.dart b/lib/src/common/helpers.dart index 15286245..f54ebe49 100644 --- a/lib/src/common/helpers.dart +++ b/lib/src/common/helpers.dart @@ -8,14 +8,11 @@ import 'dart:math'; import 'dart:typed_data'; import 'package:pointycastle/export.dart'; -import '../enum/ecc.dart'; -import '../enum/preset_rfc.dart'; import '../type/s2k.dart'; import '../enum/s2k_type.dart'; import '../enum/hash_algorithm.dart'; import '../enum/symmetric_algorithm.dart'; import 'argon2_s2k.dart'; -import 'config.dart'; import 'extensions.dart'; import 'generic_s2k.dart'; @@ -118,26 +115,28 @@ final class Helper { } static assertHash(final HashAlgorithm hash) { - assert(hash != HashAlgorithm.unknown && - hash != HashAlgorithm.md5 && - hash != HashAlgorithm.sha1 && - hash != HashAlgorithm.ripemd160); + switch (hash) { + case HashAlgorithm.unknown: + case HashAlgorithm.md5: + case HashAlgorithm.sha1: + case HashAlgorithm.ripemd160: + throw UnsupportedError( + 'Hash ${hash.name} is unsupported.', + ); + default: + } } static assertSymmetric(final SymmetricAlgorithm symmetric) { - assert(symmetric != SymmetricAlgorithm.plaintext && - symmetric != SymmetricAlgorithm.cast5 && - symmetric != SymmetricAlgorithm.idea && - symmetric != SymmetricAlgorithm.tripledes); - } - - static assertEcc(final Ecc ecc) { - if (Config.presetRfc == PresetRfc.rfc4880) { - assert(ecc != Ecc.brainpoolP256r1 && - ecc != Ecc.brainpoolP384r1 && - ecc != Ecc.brainpoolP512r1 && - ecc != Ecc.ed25519 && - ecc != Ecc.curve25519); + switch (symmetric) { + case SymmetricAlgorithm.plaintext: + case SymmetricAlgorithm.cast5: + case SymmetricAlgorithm.idea: + case SymmetricAlgorithm.tripledes: + throw UnsupportedError( + 'Symmetric ${symmetric.name} is unsupported.', + ); + default: } } } diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index ecaf3d2c..cb78b585 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -12,6 +12,7 @@ import '../enum/hash_algorithm.dart'; import '../enum/key_version.dart'; import '../enum/montgomery_curve.dart'; import '../enum/key_algorithm.dart'; +import '../enum/rsa_key_size.dart'; import '../type/key_material.dart'; import '../type/key_packet.dart'; import '../type/subkey_packet.dart'; @@ -50,7 +51,8 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { 'Version $keyVersion of the key packet is unsupported.', ); } - _calculateFingerprintAndKeyID(); + _assertKeyStrength(); + _calculateFingerprint(); } factory PublicKeyPacket.fromBytes(final Uint8List bytes) { @@ -138,7 +140,23 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { bool get isV6Key => keyVersion == KeyVersion.v6.value; - _calculateFingerprintAndKeyID() { + void _assertKeyStrength() { + switch (keyAlgorithm) { + case KeyAlgorithm.rsaEncryptSign: + case KeyAlgorithm.rsaEncrypt: + case KeyAlgorithm.rsaSign: + case KeyAlgorithm.dsa: + case KeyAlgorithm.elgamal: + if (keyMaterial.keyStrength < RSAKeySize.normal.bits) { + throw UnsupportedError( + 'Key strength ${keyMaterial.keyStrength} of the algorithm ${keyAlgorithm.name} is unsupported.', + ); + } + default: + } + } + + _calculateFingerprint() { if (isV6Key) { _fingerprint = Uint8List.fromList( Helper.hashDigest(signBytes, HashAlgorithm.sha256), From 4116f30c751727bfef06ed5d68e96d2637b8f971 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 14:02:18 +0700 Subject: [PATCH 119/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/private_key.dart | 13 +- test/key/key_decryption_test.dart | 409 ++++++++++++++++++++++++++++++ test/key/key_reading_test.dart | 11 + 3 files changed, 431 insertions(+), 2 deletions(-) create mode 100644 test/key/key_decryption_test.dart diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index 6e611d58..efb89238 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -60,11 +60,20 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { return PublicKey(PacketList(packets)); } + /// Reads an armored OpenPGP private key and returns a PrivateKey object + factory PrivateKey.fromArmored(final String armored) { + final armor = Armor.decode(armored); + if (armor.type != ArmorType.privateKey) { + throw ArgumentError('Armored text not of private key type'); + } + return PrivateKey(PacketList.decode(armor.data)); + } + @override String armor() => Armor.encode(ArmorType.privateKey, packetList.encode()); @override - PrivateKeyInterface encrypt( + PrivateKey encrypt( final String passphrase, [ final Iterable subkeyPassphrases = const [], ]) { @@ -113,7 +122,7 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { } @override - PrivateKeyInterface decrypt( + PrivateKey decrypt( String passphrase, [ Iterable subkeyPassphrases = const [], ]) { diff --git a/test/key/key_decryption_test.dart b/test/key/key_decryption_test.dart new file mode 100644 index 00000000..4fcd5dce --- /dev/null +++ b/test/key/key_decryption_test.dart @@ -0,0 +1,409 @@ +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/key_algorithm.dart'; +import 'package:dart_pg/src/key/private_key.dart'; +import 'package:test/test.dart'; + +void main() { + group('Key decryption', () { + const userID = 'Dart Privacy Guard '; + const passphrase = 'password'; + + test('RSA key', () { + const armored = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +=QeXb +-----END PGP PRIVATE KEY BLOCK----- +'''; + final privateKey = PrivateKey.fromArmored(armored).decrypt(passphrase); + final user = privateKey.users[0]; + final subkey = privateKey.subkeys[0]; + + expect( + privateKey.fingerprint.toHexadecimal(), + '5ccceda54f917089f8c488000532372a028f2ff5', + ); + expect(privateKey.version, 4); + expect(privateKey.keyAlgorithm, KeyAlgorithm.rsaEncryptSign); + expect(privateKey.keyStrength, 2048); + + expect( + subkey.fingerprint.toHexadecimal(), + '79fb82ac1204bdc854e87364316ef1539787254f', + ); + expect(subkey.version, 4); + expect(subkey.keyAlgorithm, KeyAlgorithm.rsaEncryptSign); + expect(subkey.keyStrength, 2048); + + expect(user.userID, userID); + }); + + test('DSA & ElGamal key', () { + const armored = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lQOBBGc/4H8RCAD3gDG7FSfKWRy4/gNe0sgYYgZ4zAFZTLWneObcLYpY+kmwufUI +rAG0iWt8kzLbiLonA1B7ss041yCqJgTHFcttH5nFG/q25MT+xq+V55TRZGzaD6Gy +65E++CuS0m3gJmLH14bX6++5Evtc/bkOTGgIfRGfEH8pL1ePc9ev1EFtmOYgZkX0 +a7oM8TiNKVNnUKwhuBi7pbC6OEOuDL1HmbsAHIE4n6mMs3HzJCNEAkvHNcQGZNdn +8SD3d3ukljOq03ko+tC2Orys1AwiZokL10EOiZYQZ55KAOFXM/HuayVQ8xR9Zgg8 +i+8HdjvAzaifH6ThOMDE2I80Yp/jllG/PBBvAQD3msVRxzpaT3WiMeopZAOM+jeF +xwy8bG0W+BOJwn9BjQgAzFLIFwHauCi4ooI0JsndyKE6TCexzA46r0nA/1BcNpRt +UlhLsEwJ0appPkCufixLL0RYKTnxgfrAbxR+mrIPla1djnNysoYj8NfDms6aIoxK +zT7gN9WWwgwaTSo7lesJ8gZ6aZLt1kBEHj+72MUb5Jgynzjd6HA8FwQvXIUaQl0H +DmYByzr/hBtVzrog5fSR/jTXX/ZbTivqgHV1JzT0NlTBrFEGuIFRj54JvEaiYtE/ +Z/lWkIRmcrAwaSCeutF4w4Xv9BhhGdRtLo2AJCQeR26sOk1kxihdes4zBrhI+e0w +rAG7G4cNOtS090O8tvIWlxlrJXyYmpZR6zHxBYbBIQf+Lk0Jou8CnR1S8oeNEdBf +euboNWTDVz93iSEBHkSIFfwt07IvHra0wKKkYwX/p8zWyw/Me2e1xtRommFSpIqW +9pWimT4bkTtPqXAwRD4WhfVOnGxzXsXaYSWIf4sr9GusPJTsAi2cdtKTDzXFWDpJ +DvJ24bPa/P4oEiECno2s2KkfQqWZbSRbOsDX8/Mdz58XzOWOv3j0A1oLLZFK+a0L +iJso31S0jG71IiMFlaXWNQD025UV6Ouplqe/P6qdArk72Qaum4QBsFfTLyKw12JQ +c9HPfV4Fst0sSbky+0QqqVfegHnvcnJBrjZ2pahL+EWULYR9lty0LinZ0v6+5Htw +DP4HAwL+XZ4svYGKjvi/exgTE75xl3LOtpURlnHAFGdOXhonNlz93sgByRG1AY2j +XOoZERU12Uz2iBwNbuuHC1wLXioU4pJ1vDyP0fdbLL6P8bC3tC9EYXJ0IFByaXZh +Y3kgR3VhcmQgPGRhcnRwZ0BvcGVucGdwLmV4YW1wbGUuY29tPoiTBBMRCAA7FiEE +moM0SjcRhkwfUCCUunJ9D2tQwoEFAmc/4H8CGwMFCwkIBwICIgIGFQoJCAsCBBYC +AwECHgcCF4AACgkQunJ9D2tQwoG5xgEAuRtdGCvWqLWUwsv+sJm6qSw0yKLdhnEx +EWDi3A5kQhUA/jsccBTV4FLnET858yKpwyqlLsu31ZMjzCY/geg8hmrYnQJrBGc/ +4H8QCACv2nUuptC3DfhXtkI3H825wX+OwZzwOP8MG/JNUut/RoxFDXnld1S/GVz/ +dDBXs+cMQY4M9Fe2KfsGbNVeOId8eBENIFE7DBH6Qigh8WkBUueV6qPeFkWVj7u7 +2TwRPjR3A6uAwLv/ZrMZJWqD52tDr2YDv8e5LlS8uzVmTYo9nU8ohn3n6KyKNarE +hpuXHqo3SHt4H7601Nr3TqdRU5ZWW1F6QEjj0Mj1st7ctPkMjj4R5IzfRZHyDQIr +JA5Vp76cZ+oXZFIgrkWwJ//HZ/txc2PGLYYf6OCgTZRFRh720ETDf4arK1FheJ2q +Vad4YX+21PvdzU8DeIn4McO8QyM7AAMFB/9RdNV1kNfAl7LPOUp46sO/97esXDT5 +DfLcBgV+bdUGuQonASsKk53phnwp1mLhfRe1TldkoWpxkOOZAHXDdr+hbSp+UNim +K9BsX/jCYcQr0Il5Agq8F8Idxrwv2ftlFVAsuDrwyzhGFHFC5mXPeloDG1aF8cyW +KXbONM3DzBwISW4pwKXXYZqqiDgmlQ/anjEExZqmSs6+sqWpl45iAsAB/jYvzuBl +06pJFxFJo7TwNAKhgTFHMGlHXjl/JCbd1xv8mG2QrL2vLSFOsqaqhdEh22uGrODH +C7iH+3aPMyghWLAkARUPkRXjGRXryHtl8w8DRvQjllGb0KUH6SqD65H0/gcDAmi6 +PTHn8B9z+Jrbr1pOM2y/vM4LdKh8lq2oozIU9Y7xMkI05K/TUrOYky5XwIkJ93FT +haX3IuRYjteH5gw6JqL6QjkHCdfzrZx7R+x00GtgIOlzyLkXRR6ZHoh4BBgRCAAg +FiEEmoM0SjcRhkwfUCCUunJ9D2tQwoEFAmc/4H8CGwwACgkQunJ9D2tQwoHyvwEA +6NmUz1wxurA5xPduxnsqHRbMoy5IZy2WCLV7bi8T8BsA/jX99W5wMiC29qByQdzI +CorNeTDAKJEwL5SJhzt+bmo+ +=7ZVq +-----END PGP PRIVATE KEY BLOCK----- +'''; + final privateKey = PrivateKey.fromArmored(armored).decrypt(passphrase); + final user = privateKey.users[0]; + final subkey = privateKey.subkeys[0]; + + expect( + privateKey.fingerprint.toHexadecimal(), + '9a83344a3711864c1f502094ba727d0f6b50c281', + ); + expect(privateKey.version, 4); + expect(privateKey.keyAlgorithm, KeyAlgorithm.dsa); + expect(privateKey.keyStrength, 2048); + + expect( + subkey.fingerprint.toHexadecimal(), + '8f1cb653b2c3a4808302b63b52c1ed36e9e22006', + ); + expect(subkey.version, 4); + expect(subkey.keyAlgorithm, KeyAlgorithm.elgamal); + expect(subkey.keyStrength, 2048); + + expect(user.userID, userID); + }); + + test('ECC NIST P-384 key', () { + const armored = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lNIEZz/5iRMFK4EEACIDAwR3cUlciuVCSwGLA4sH6SwA1axNDMU3GqnHOOmCVoM0 +qOAX9FdVeP0DqixqjRYm2qQoUpm+QBK84KA5oGfcYNRmYXwYnC64s05yLQkL836D +mcV38F5c0k3IQIwhuWVwu33+BwMCYJx1nYlH0Mj4vl/mSNGARY0WUyL0C6UJ7S65 +Ly16TGI3V490OtTlB4B8UIL0s4l0odNR1A6zF8oGXlNBBWe/Q9U+W34J/q+/zKyF +/rZ6EIUfGoOh2kRLeRVWcvjXI+m0L0RhcnQgUHJpdmFjeSBHdWFyZCA8ZGFydHBn +QG9wZW5wZ3AuZXhhbXBsZS5jb20+iLMEExMJADsWIQSjJRB+ZryrPupAdVA5bsuL +uG0ZIgUCZz/5iQIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRA5bsuL +uG0ZInjBAX9bv51DhgTNGDot6gTEKJ7c0FypBuPZPlNOKMCshtvu36+jiJyMpGBa +R/C+CB8wctkBgNT0/5Mj1e+UXqFAmflWmODnszrYpdiGSF2GJxVwOBLJeFVqPQtl +2mSnmKmhhoVy3ZzWBGc/+YkSBSuBBAAiAwME6+LPwcbdvoEdxPA2L002pKXp8Nt7 +PxIsjUegQRw2bTQXMlu5zplsUNprWJZ1W/iqLhmEH8eLONxcuzVEQMb3dCImxBzm +L3Y5HxGGti81EsU7JBIZxHKhl85RY78HdHgCAwEJCf4HAwIIaqT4lafbcPhMsDaE +o9wLpC5rY6k28ax3dHv1RLibxxcZafaTP1eKy+W0fBtOm1vDkkc/HCxsFBtIkZ/c +/rfA30/p6jibrlfk4qOVlSGYPN5QjexvmaK1Us46fWLEe4iYBBgTCQAgFiEEoyUQ +fma8qz7qQHVQOW7Li7htGSIFAmc/+YkCGwwACgkQOW7Li7htGSJT3AGAgueSdnW+ +Lvrv2K9VfqgHGB2NDYsdLdXOIHardOmkFmD3rTbjsjHkQrLCs/lCQkVhAYCxmcY1 +0PcZBwYqmzhn+ek2wQOTGM2wO3qJVZ9Z+z5kkQXH6JtyfkZATJgA/kougNs= +=rr8a +-----END PGP PRIVATE KEY BLOCK----- +'''; + final privateKey = PrivateKey.fromArmored(armored).decrypt(passphrase); + final user = privateKey.users[0]; + final subkey = privateKey.subkeys[0]; + + expect( + privateKey.fingerprint.toHexadecimal(), + 'a325107e66bcab3eea407550396ecb8bb86d1922', + ); + expect(privateKey.version, 4); + expect(privateKey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(privateKey.keyStrength, 384); + + expect( + subkey.fingerprint.toHexadecimal(), + 'bd7133409a3ad7986fbae32a5a2990ce6bd63b20', + ); + expect(subkey.version, 4); + expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(subkey.keyStrength, 384); + + expect(user.userID, userID); + }); + + test('ECC Brainpool P-256 key', () { + const armored = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lKYEZz/50BMJKyQDAwIIAQEHAgMEnXlquLQ8KrfXtrCZ000jDP7I5nXzJQKDws4C +ZgRpECoCHUeYCdIrl3iLUJfYzb9Qq6nt/8oP+jljqiJ35W5NlP4HAwI3GD01hi3V +P/hQXGRXNxRo8r4melKsvJRp2VTArNRXunWZFvxjQTqFbnF3OHkh8Fe7P6drfUlU +6vWS3K/AG+uxaN112eUP5DqktHorP8pKtC9EYXJ0IFByaXZhY3kgR3VhcmQgPGRh +cnRwZ0BvcGVucGdwLmV4YW1wbGUuY29tPoiTBBMTCAA7FiEEzRtbFClPgL5lzvvv +mVEhn8nelXgFAmc/+dACGwMFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQ +mVEhn8nelXi/egD9H0gwMmSWpDupSnbS6l0GGoBuJ9IQsh3Ft3zzobyE+CIA/Ruk +pKCRp3bEVRK47yjhvyZ6c4VsVCmE8h/8T4Jay3AEnKoEZz/50BIJKyQDAwIIAQEH +AgMEa4NEpjB8C1OE04AznUizgnUFg83qpkJM3Rz2V1JDMZJU7SUj8FXYkmy5CojQ ++d7TxjuiDhuHK/1L4P7BxTyy/QMBCAf+BwMCLM2ShOzK5gv4r+BsOIlgMyPBEsUc +/sO1PLeLyEQoCa5CcHATAtPrJfBHhD+TJbFxOA0HBnCtvQn47oGBkvnBCL4x94Q1 +McHOENbwToV6Voh4BBgTCAAgFiEEzRtbFClPgL5lzvvvmVEhn8nelXgFAmc/+dAC +GwwACgkQmVEhn8nelXjEygEAiKyuVndDuoMDyeK0h1qNd0NCvS7YfGQbV8kqmWf1 +0gcBAI9tm17R90JEvyu9HxMSl+Cme5OPZPWIpDXpk3dBzy0X +=CNK9 +-----END PGP PRIVATE KEY BLOCK----- +'''; + final privateKey = PrivateKey.fromArmored(armored).decrypt(passphrase); + final user = privateKey.users[0]; + final subkey = privateKey.subkeys[0]; + + expect( + privateKey.fingerprint.toHexadecimal(), + 'cd1b5b14294f80be65cefbef9951219fc9de9578', + ); + expect(privateKey.version, 4); + expect(privateKey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(privateKey.keyStrength, 256); + + expect( + subkey.fingerprint.toHexadecimal(), + 'fda27358dac2b11fb5388cca65b53bd1aff05e06', + ); + expect(subkey.version, 4); + expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(subkey.keyStrength, 256); + + expect(user.userID, userID); + }); + + test('ECC Curve 25519 key', () { + const armored = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lIYEZz/6BhYJKwYBBAHaRw8BAQdAZQAdPHBwHpMPvaCgVCkt/eMCp1ubVxnC/U3a +aMF98SD+BwMCG2M/g+96Esj4hyo7hP1EHf6XFy/uhDRNcoTjWNnPF+KVBLNdGn0o +UjSHZXBi0ScUcW6e/SfT4B77HNS8Abr7Ersx5PVWmYARpn2gVNFqlLQvRGFydCBQ +cml2YWN5IEd1YXJkIDxkYXJ0cGdAb3BlbnBncC5leGFtcGxlLmNvbT6IkwQTFgoA +OxYhBBFIsqJPWAl3wnsmIj7Udd0hLSIeBQJnP/oGAhsDBQsJCAcCAiICBhUKCQgL +AgQWAgMBAh4HAheAAAoJED7Udd0hLSIeuJ8BAM+4J/vO3PIr2ik+QnZrDtJH8gVM +Tt190i45MO534+HgAQDhhG+WYe/btVUCxLxegPFP63W1uQDGCLBe2pcDa38TDJyL +BGc/+gYSCisGAQQBl1UBBQEBB0AJwaWfEiJqlOIk/O9i2PMaDBmbRUOBHGQdFRPK +bifpfwMBCAf+BwMCCMCreqPjxcX45R3/KPQFMRNxZ0l8OCBHq5IByLEAAOGszlRo +yrLREP280yDxw39q0VSIkn2tmM7bleGk71aJ9UzTxcMZzdCYT2f8tmsQOYh4BBgW +CgAgFiEEEUiyok9YCXfCeyYiPtR13SEtIh4FAmc/+gYCGwwACgkQPtR13SEtIh6w +0gD9EP/oX5ntBKnFSX/2d/HUMIVyDA8unPJjJC1aUv81yzgBAKd52ZJIGNTIGQH2 +8eARKvrib/NNaggTXk4RXjnvP94L +=4mmg +-----END PGP PRIVATE KEY BLOCK----- +'''; + final privateKey = PrivateKey.fromArmored(armored).decrypt(passphrase); + final user = privateKey.users[0]; + final subkey = privateKey.subkeys[0]; + + expect( + privateKey.fingerprint.toHexadecimal(), + '1148b2a24f580977c27b26223ed475dd212d221e', + ); + expect(privateKey.version, 4); + expect(privateKey.keyAlgorithm, KeyAlgorithm.eddsaLegacy); + expect(privateKey.keyStrength, 255); + + expect( + subkey.fingerprint.toHexadecimal(), + '12ba12e01b12582680057e8b44d20d3a674af1a7', + ); + expect(subkey.version, 4); + expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(subkey.keyStrength, 255); + + expect(user.userID, userID); + }); + + test('Rfc9580 Curve 25519 key', () { + const armored = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xX0GZ0/8WhsAAAAgHDf+yTRCzA8vM6PV+ovg3URqlceQQUcH/Ekc7ZsR9Q3+HQcLAwiv+8mof0Fc +BuCm/9pWdojyrmg+x9KU0U+hzPx2wGmqtaz9WIIEW9PbTkBZc/5BCB15qC6ZLvL3oQc9h4XmHwhg +a/TtUUpYW6cvk45KzsK7Bh8bCAAAAFwFAmdP/FsiIQaWDEY7sAo6ZqJa+4tswpHYFSCDSFPzJdEJ +Yvcai0SjTAkQlgxGO7AKOmYCGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcBCQEHAgkCBwMJ +AwAAAAAqIhCiMxuCDvJ0r1dyF9hUSzRiXZqzB3uhdMrkX1YtDPV7RYzfulWDImcd8siqveYshD4w +uo6RBzsIt6aJrKUZSlzd1HR9NuZKEWaB9TYuZOo0Cc0vRGFydCBQcml2YWN5IEd1YXJkIDxkYXJ0 +cGdAb3BlbnBncC5leGFtcGxlLmNvbT7ClQYQGwgAAAA2BQJnT/xbIiEGlgxGO7AKOmaiWvuLbMKR +2BUgg0hT8yXRCWL3GotEo0wJEJYMRjuwCjpmAhkBAAAAAAYjEJdMNIo0MDDvlfOiEBlzQKdyvNhf +OwORi8ABLx96zDD6cj+0l/JMyi75OL5uuNZDdxgFrqTYQ4/2r6F3wuTXBpKGUBouZBO7Y+QgI1Ww +kJoPzSpOZ3V5ZW4gVmFuIE5ndXllbiA8bmd1eWVubnYxOTgxQGdtYWlsLmNvbT7CkgYQGwgAAAAz +BQJnT/xbIiEGlgxGO7AKOmaiWvuLbMKR2BUgg0hT8yXRCWL3GotEo0wJEJYMRjuwCjpmAAAAADKm +EOpBOnKA+bNtXdYqExEUv8KwwiiVpLdUqSwlsKONUZMhr/NaWBafrsmUhQMVnir4x3JWVMs2aHVO +3t0udVsFcBYQcm7vQcXsKBLeyCCWUzMHx30GZ0/8WxkAAAAgzmnTRHsYD3OeQDlZPC4Ujcqlkl+6 +iesJlNX9VYO4/Ar+HQcLAwhZpk+g24kZSeAivkvADh9xHx7hVPD6hl99bI30XqNdIMK2VcY/Zk3c +k949Su5SV/sELKg9EikzZdEoiY+N7rWCHJXFxRNbp9scCSiJu8KVBhgbCAAAADYFAmdP/FsiIQaW +DEY7sAo6ZqJa+4tswpHYFSCDSFPzJdEJYvcai0SjTAkQlgxGO7AKOmYCGwwAAAAA0RcQJB9YvxrM +NDm6QTmbvRO5EjvPyQf9I1gqhYkVZExIvt/x5vpX/ID29c91PagKcr4eoJrl9Xyfn8ksYFkmXI/B +C8sssUAhAtMI5sgtlfA7Ow7VHjDLcQ0doBgA9J4afJjd12/e+PYN4Kdqvt5Gbv5ltA== +-----END PGP PRIVATE KEY BLOCK----- +'''; + final privateKey = PrivateKey.fromArmored(armored).decrypt(passphrase); + final directSignature = privateKey.directSignatures[0]; + final user = privateKey.users[0]; + final subkey = privateKey.subkeys[0]; + + expect( + privateKey.fingerprint.toHexadecimal(), + '960c463bb00a3a66a25afb8b6cc291d81520834853f325d10962f71a8b44a34c', + ); + expect( + directSignature.issuerFingerprint.toHexadecimal(), + '960c463bb00a3a66a25afb8b6cc291d81520834853f325d10962f71a8b44a34c', + ); + expect(privateKey.version, 6); + expect(privateKey.keyAlgorithm, KeyAlgorithm.ed25519); + expect(privateKey.keyStrength, 255); + + expect( + subkey.fingerprint.toHexadecimal(), + 'd06da2dc36880041a63e014afa1907e04112d44fb11c0918158a5a2a6e988193', + ); + expect(subkey.version, 6); + expect(subkey.keyAlgorithm, KeyAlgorithm.x25519); + expect(subkey.keyStrength, 255); + + expect(user.userID, userID); + }); + + test('Rfc9580 Curve 448 key', () { + const armored = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xa8GZ0/8WxwAAAA5yymL3ZZ7U9OrS3LdxuhvSc/SMFFNYSwyB9f4JHMgw/DxUx4+81alHHCjGNRE +Lylg9n81LWeN0SCA/h0HCwMIBg46deIoPR3gg3z8j3H3k3h4a0NDBbT5iTOpswmrc6NdU4X1zabs +wUt1SSLtm9LPyXlOyIpQeE3CfDlYmNDzs7Gz7MIa+dbimHUmKrOWvAtlKZeIBtowletTex9cxkdv +JyrcDVZGwsAtBh8cCAAAAFwFAmdP/FsiIQZcGXS4BAC8Li+HPo5erpI7ttQLRDRvqUzZ/YMZCmIS +WAkQXBl0uAQAvC4CGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcBCQEHAgkCBwMJAwAAAABW +ARABLtqQ+hSn18x6xfVEaXx8BqT/L3ghgvGeYI+Ou7b/bDnLoTySTQslRxWGYhUFOkMiPUPvxyoo +aeH8iPj+QsS5kbPoIWicG9QAbEeBjGWzGezqMt+B0DuIiQGGiE/FAjLdbOM2ctvBBRvk2TeGAZ7Y +VTHXEv7hxSWK0koylRtNmCIAzS9EYXJ0IFByaXZhY3kgR3VhcmQgPGRhcnRwZ0BvcGVucGdwLmV4 +YW1wbGUuY29tPsLABwYQHAgAAAA2BQJnT/xbIiEGXBl0uAQAvC4vhz6OXq6SO7bUC0Q0b6lM2f2D +GQpiElgJEFwZdLgEALwuAhkBAAAAAPHqEBYl8EyZZ87R1GxCa/nbpsV64m/USnwef1z7QP+cotTK +YgQ1belLShpGWsEWlejSAffDWWjTyj4Zet+/ZD8qRArffNTJKckUiQBLo3FRST7q6/+fr05vNVXd +6qzuNRZ8qNEMn0RXWVlknx0ngB+RDY8rxenWE+aX/FJkzAlw2em/KADNKk5ndXllbiBWYW4gTmd1 +eWVuIDxuZ3V5ZW5udjE5ODFAZ21haWwuY29tPsLABAYQHAgAAAAzBQJnT/xbIiEGXBl0uAQAvC4v +hz6OXq6SO7bUC0Q0b6lM2f2DGQpiElgJEFwZdLgEALwuAAAAANCTEMNpNd0oX1itofCILqF8O8Na +MlIdTThwhdw2VNWIxj+mdL+WI6N14pybiOy1l487NZRSO3EgmRjYtCyxmfQ7dEOVSkdYy4SylQD2 +G3wBZYSq2d03Y4uaV0dcvcImPBBsPfP21Ka40VER9F90WQus0I1y77c4C3KXc4FNl0KIeZlTMADH +rQZnT/xcGgAAADhTie394+2su5pQnDF2EoeexN9C5SexeDqfMeBq6WZG7LEdkJRkrPdNZJsX+pXM +cZYaKMy6NJIfUv4dBwsDCOnMOf3LbUZK4DW7Z5VeIJQATLUbvtUju5uVCeXxx4Me1MivD4eQdCDE +ISRfcRK3oQ1d9FrHbXSV0Y2VU9/dFKcIayJaIKGLeUzG8PHUNTCD9HkSfjTf2x+kwQsu4AtyvGbZ +UL1gwsAHBhgcCAAAADYFAmdP/FwiIQZcGXS4BAC8Li+HPo5erpI7ttQLRDRvqUzZ/YMZCmISWAkQ +XBl0uAQAvC4CGwwAAAAA5wcQcWaN5Sdj4wJxMX/wB9Uhf3hICRUGVFN1iYWnhmUPeJ5b3SnQ3/rl +oQCDLwoWcOkSS4+wQExO4WXxyvUNKJ15HL4YV2nx0VbmgC7GeukcVAdKATn1OO9Nt0MevTxuRW/J +lzIMFLqFsDiKPzuwpZBhzEh+sj0Z3WtqZOueBwxVocwDANUX0mClieCAd2U0VQHW7lMIkRYMY5ZS +x6s= +-----END PGP PRIVATE KEY BLOCK----- +'''; + final privateKey = PrivateKey.fromArmored(armored).decrypt(passphrase); + final directSignature = privateKey.directSignatures[0]; + final user = privateKey.users[0]; + final subkey = privateKey.subkeys[0]; + + expect( + privateKey.fingerprint.toHexadecimal(), + '5c1974b80400bc2e2f873e8e5eae923bb6d40b44346fa94cd9fd83190a621258', + ); + expect( + directSignature.issuerFingerprint.toHexadecimal(), + '5c1974b80400bc2e2f873e8e5eae923bb6d40b44346fa94cd9fd83190a621258', + ); + expect(privateKey.version, 6); + expect(privateKey.keyAlgorithm, KeyAlgorithm.ed448); + expect(privateKey.keyStrength, 448); + + expect( + subkey.fingerprint.toHexadecimal(), + 'a831a2141587c8bace0971316b2f15a5be482ddf3b059d3b8589d989ee740693', + ); + expect(subkey.version, 6); + expect(subkey.keyAlgorithm, KeyAlgorithm.x448); + expect(subkey.keyStrength, 448); + + expect(user.userID, userID); + }); + }); +} diff --git a/test/key/key_reading_test.dart b/test/key/key_reading_test.dart index f1c8334f..77a4251e 100644 --- a/test/key/key_reading_test.dart +++ b/test/key/key_reading_test.dart @@ -6,6 +6,7 @@ import 'package:test/test.dart'; void main() { group('Key reading', () { const userID = 'Dart Privacy Guard '; + test('RSA key', () { const armored = ''' -----BEGIN PGP PUBLIC KEY BLOCK----- @@ -273,6 +274,7 @@ fjfQblPSXk32Zpv5RXzNhOIelkIuA9USba2Zw7ZRB4iuDfOgEJAw7VEt -----END PGP PUBLIC KEY BLOCK----- '''; final publicKey = PublicKey.fromArmored(armored); + final directSignature = publicKey.directSignatures[0]; final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -280,6 +282,10 @@ fjfQblPSXk32Zpv5RXzNhOIelkIuA9USba2Zw7ZRB4iuDfOgEJAw7VEt publicKey.fingerprint.toHexadecimal(), '7b2522d17acbc689154050fb1a2455f9fc9a46318ad6ef5039d3e6e3bc04a679', ); + expect( + directSignature.issuerFingerprint.toHexadecimal(), + '7b2522d17acbc689154050fb1a2455f9fc9a46318ad6ef5039d3e6e3bc04a679', + ); expect(publicKey.version, 6); expect(publicKey.keyAlgorithm, KeyAlgorithm.ed25519); expect(publicKey.keyStrength, 255); @@ -322,6 +328,7 @@ FmXTxip3kr4= -----END PGP PUBLIC KEY BLOCK----- '''; final publicKey = PublicKey.fromArmored(armored); + final directSignature = publicKey.directSignatures[0]; final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -329,6 +336,10 @@ FmXTxip3kr4= publicKey.fingerprint.toHexadecimal(), '93877c2a7656e41e8e39a16d405614663a4c5593da3306f78035f466669c6051', ); + expect( + directSignature.issuerFingerprint.toHexadecimal(), + '93877c2a7656e41e8e39a16d405614663a4c5593da3306f78035f466669c6051', + ); expect(publicKey.version, 6); expect(publicKey.keyAlgorithm, KeyAlgorithm.ed448); expect(publicKey.keyStrength, 448); From 805c694b931fdbccab9b84dfcf718aede0009d9e Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 14:05:16 +0700 Subject: [PATCH 120/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/key/key_decryption_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/key/key_decryption_test.dart b/test/key/key_decryption_test.dart index 4fcd5dce..bec6fd31 100644 --- a/test/key/key_decryption_test.dart +++ b/test/key/key_decryption_test.dart @@ -195,7 +195,7 @@ Lvrv2K9VfqgHGB2NDYsdLdXOIHardOmkFmD3rTbjsjHkQrLCs/lCQkVhAYCxmcY1 'a325107e66bcab3eea407550396ecb8bb86d1922', ); expect(privateKey.version, 4); - expect(privateKey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(privateKey.keyAlgorithm, KeyAlgorithm.ecdsa); expect(privateKey.keyStrength, 384); expect( @@ -239,7 +239,7 @@ GwwACgkQmVEhn8nelXjEygEAiKyuVndDuoMDyeK0h1qNd0NCvS7YfGQbV8kqmWf1 'cd1b5b14294f80be65cefbef9951219fc9de9578', ); expect(privateKey.version, 4); - expect(privateKey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(privateKey.keyAlgorithm, KeyAlgorithm.ecdsa); expect(privateKey.keyStrength, 256); expect( From df78d259c13343603bc24b9d79e86893f96fff0b Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 15:04:43 +0700 Subject: [PATCH 121/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/argon2_s2k.dart | 8 +-- lib/src/common/generic_s2k.dart | 6 +-- lib/src/cryptor/aead/eax.dart | 24 ++++----- lib/src/cryptor/aead/gcm.dart | 24 ++++----- lib/src/cryptor/aead/ocb.dart | 50 +++++++++---------- lib/src/cryptor/asymmetric/dsa.dart | 12 ++--- lib/src/cryptor/asymmetric/elgamal.dart | 14 +++--- lib/src/cryptor/symmetric/base_engine.dart | 4 +- lib/src/cryptor/symmetric/blowfish.dart | 8 +-- lib/src/cryptor/symmetric/camellia.dart | 8 +-- lib/src/cryptor/symmetric/cast5.dart | 10 ++-- lib/src/cryptor/symmetric/idea.dart | 8 +-- lib/src/cryptor/symmetric/twofish.dart | 6 +-- lib/src/key/base.dart | 15 +++--- lib/src/key/private_key.dart | 20 ++++---- lib/src/key/public_key.dart | 2 +- lib/src/key/subkey.dart | 26 +++++----- lib/src/key/user.dart | 11 ++-- lib/src/packet/aead_encrypted_data.dart | 4 +- lib/src/packet/base.dart | 2 +- lib/src/packet/compressed_data.dart | 2 +- lib/src/packet/key/dsa_public_material.dart | 6 +-- lib/src/packet/key/dsa_secret_material.dart | 8 +-- lib/src/packet/key/ec_public_material.dart | 4 +- lib/src/packet/key/ec_secret_material.dart | 2 +- lib/src/packet/key/ecdh_public_material.dart | 2 +- lib/src/packet/key/ecdh_secret_material.dart | 4 +- .../packet/key/ecdh_session_key_cryptor.dart | 4 +- lib/src/packet/key/ecdsa_public_material.dart | 2 +- lib/src/packet/key/ecdsa_secret_material.dart | 6 +-- .../key/eddsa_legacy_public_material.dart | 2 +- .../key/eddsa_legacy_secret_material.dart | 8 +-- lib/src/packet/key/eddsa_public_material.dart | 6 +-- lib/src/packet/key/eddsa_secret_material.dart | 8 +-- .../packet/key/elgamal_public_material.dart | 4 +- .../packet/key/elgamal_secret_material.dart | 6 +-- .../key/elgamal_session_key_cryptor.dart | 4 +- .../key/montgomery_public_material.dart | 4 +- .../key/montgomery_secret_material.dart | 6 +-- .../key/montgomery_session_key_cryptor.dart | 4 +- lib/src/packet/key/rsa_public_material.dart | 6 +-- lib/src/packet/key/rsa_secret_material.dart | 8 +-- .../packet/key/rsa_session_key_cryptor.dart | 4 +- lib/src/packet/key/session_key.dart | 6 +-- lib/src/packet/literal_data.dart | 6 +-- lib/src/packet/marker.dart | 3 +- lib/src/packet/one_pass_signature.dart | 2 +- lib/src/packet/packet_list.dart | 8 +-- lib/src/packet/padding.dart | 2 +- lib/src/packet/public_key.dart | 16 +++--- .../public_key_encrypted_session_key.dart | 2 +- lib/src/packet/public_subkey.dart | 2 +- lib/src/packet/secret_key.dart | 33 ++++++------ lib/src/packet/secret_subkey.dart | 2 +- lib/src/packet/signature.dart | 32 ++++++------ lib/src/packet/signature_subpacket.dart | 2 +- lib/src/packet/sym_encrypted_data.dart | 14 ++++-- ...ym_encrypted_integrity_protected_data.dart | 2 +- lib/src/packet/sym_encrypted_session_key.dart | 2 +- lib/src/packet/trust.dart | 2 +- lib/src/packet/user_attribute.dart | 4 +- lib/src/packet/user_id.dart | 4 +- lib/src/type/key.dart | 8 +-- 63 files changed, 260 insertions(+), 264 deletions(-) diff --git a/lib/src/common/argon2_s2k.dart b/lib/src/common/argon2_s2k.dart index 3c6c8a9d..29762bdc 100644 --- a/lib/src/common/argon2_s2k.dart +++ b/lib/src/common/argon2_s2k.dart @@ -53,7 +53,7 @@ class Argon2S2k implements S2kInterface { } @override - Uint8List produceKey(String passphrase, int length) { + produceKey(String passphrase, int length) { final gen = Argon2BytesGenerator() ..init(Argon2Parameters( Argon2Parameters.ARGON2_id, @@ -67,10 +67,10 @@ class Argon2S2k implements S2kInterface { } @override - int get length => type.length; + get length => type.length; @override - Uint8List get toBytes => Uint8List.fromList([ + get toBytes => Uint8List.fromList([ type.value, ...salt, iteration, @@ -79,5 +79,5 @@ class Argon2S2k implements S2kInterface { ]); @override - S2kType get type => S2kType.argon2; + get type => S2kType.argon2; } diff --git a/lib/src/common/generic_s2k.dart b/lib/src/common/generic_s2k.dart index 4099ac19..34314446 100644 --- a/lib/src/common/generic_s2k.dart +++ b/lib/src/common/generic_s2k.dart @@ -81,7 +81,7 @@ class GenericS2k implements S2kInterface { } @override - Uint8List produceKey(final String passphrase, final int length) { + produceKey(final String passphrase, final int length) { switch (type) { case S2kType.simple: return _hashDigest(passphrase.toBytes(), length); @@ -107,10 +107,10 @@ class GenericS2k implements S2kInterface { } @override - int get length => type.length; + get length => type.length; @override - Uint8List get toBytes { + get toBytes { final bytes = [type.value, hash.value]; switch (type) { case S2kType.simple: diff --git a/lib/src/cryptor/aead/eax.dart b/lib/src/cryptor/aead/eax.dart index 36af543a..520002bb 100644 --- a/lib/src/cryptor/aead/eax.dart +++ b/lib/src/cryptor/aead/eax.dart @@ -14,24 +14,24 @@ import '../../type/aead.dart'; /// EAX Authenticated-Encryption class /// Author Nguyen Van Nguyen class Eax implements AeadInterface { - final Uint8List _key; - final SymmetricAlgorithm _symmetric; + final Uint8List key; + final SymmetricAlgorithm symmetric; - Eax(this._key, this._symmetric); + Eax(this.key, this.symmetric); @override - Uint8List encrypt( + encrypt( final Uint8List plainText, final Uint8List nonce, final Uint8List aData, ) { final cipher = EAX( - _symmetric.cipherEngine, + symmetric.cipherEngine, )..init( true, AEADParameters( - KeyParameter(_key), - _symmetric.blockSize * 8, + KeyParameter(key), + symmetric.blockSize * 8, nonce, aData, ), @@ -41,18 +41,18 @@ class Eax implements AeadInterface { } @override - Uint8List decrypt( + decrypt( final Uint8List cipherText, final Uint8List nonce, final Uint8List aData, ) { final cipher = EAX( - _symmetric.cipherEngine, + symmetric.cipherEngine, )..init( false, AEADParameters( - KeyParameter(_key), - _symmetric.blockSize * 8, + KeyParameter(key), + symmetric.blockSize * 8, nonce, aData, ), @@ -62,7 +62,7 @@ class Eax implements AeadInterface { } @override - Uint8List getNonce( + getNonce( final Uint8List iv, final Uint8List chunkIndex, ) { diff --git a/lib/src/cryptor/aead/gcm.dart b/lib/src/cryptor/aead/gcm.dart index ca8053d2..3cc692fb 100644 --- a/lib/src/cryptor/aead/gcm.dart +++ b/lib/src/cryptor/aead/gcm.dart @@ -14,24 +14,24 @@ import '../../type/aead.dart'; /// GCM Authenticated-Encryption class /// Author Nguyen Van Nguyen class Gcm implements AeadInterface { - final Uint8List _key; - final SymmetricAlgorithm _symmetric; + final Uint8List key; + final SymmetricAlgorithm symmetric; - Gcm(this._key, this._symmetric); + Gcm(this.key, this.symmetric); @override - Uint8List encrypt( + encrypt( final Uint8List plainText, final Uint8List nonce, final Uint8List aData, ) { final cipher = GCMBlockCipher( - _symmetric.cipherEngine, + symmetric.cipherEngine, )..init( true, AEADParameters( - KeyParameter(_key), - _symmetric.blockSize * 8, + KeyParameter(key), + symmetric.blockSize * 8, nonce, aData, ), @@ -40,18 +40,18 @@ class Gcm implements AeadInterface { } @override - Uint8List decrypt( + decrypt( final Uint8List cipherText, final Uint8List nonce, final Uint8List aData, ) { final cipher = GCMBlockCipher( - _symmetric.cipherEngine, + symmetric.cipherEngine, )..init( false, AEADParameters( - KeyParameter(_key), - _symmetric.blockSize * 8, + KeyParameter(key), + symmetric.blockSize * 8, nonce, aData, ), @@ -60,7 +60,7 @@ class Gcm implements AeadInterface { } @override - Uint8List getNonce( + getNonce( final Uint8List iv, final Uint8List chunkIndex, ) { diff --git a/lib/src/cryptor/aead/ocb.dart b/lib/src/cryptor/aead/ocb.dart index af27c8a5..7b64e75d 100644 --- a/lib/src/cryptor/aead/ocb.dart +++ b/lib/src/cryptor/aead/ocb.dart @@ -14,25 +14,25 @@ import '../../type/aead.dart'; /// OCB Authenticated-Encryption class /// Author Nguyen Van Nguyen class Ocb implements AeadInterface { - final Uint8List _key; - final SymmetricAlgorithm _symmetric; + final Uint8List key; + final SymmetricAlgorithm symmetric; - Ocb(this._key, this._symmetric); + Ocb(this.key, this.symmetric); @override - Uint8List encrypt( + encrypt( final Uint8List plainText, final Uint8List nonce, final Uint8List aData, ) { final cipher = OCBCipher( - _symmetric.cipherEngine, - _symmetric.cipherEngine, + symmetric.cipherEngine, + symmetric.cipherEngine, )..init( true, AEADParameters( - KeyParameter(_key), - _symmetric.blockSize * 8, + KeyParameter(key), + symmetric.blockSize * 8, nonce, aData, ), @@ -42,19 +42,19 @@ class Ocb implements AeadInterface { } @override - Uint8List decrypt( + decrypt( final Uint8List cipherText, final Uint8List nonce, final Uint8List aData, ) { final cipher = OCBCipher( - _symmetric.cipherEngine, - _symmetric.cipherEngine, + symmetric.cipherEngine, + symmetric.cipherEngine, )..init( false, AEADParameters( - KeyParameter(_key), - _symmetric.blockSize * 8, + KeyParameter(key), + symmetric.blockSize * 8, nonce, aData, ), @@ -64,7 +64,7 @@ class Ocb implements AeadInterface { } @override - Uint8List getNonce( + getNonce( final Uint8List iv, final Uint8List chunkIndex, ) { @@ -144,10 +144,10 @@ class OCBCipher implements AEADCipher { int get macSize => _macSize; @override - String get algorithmName => '${_mainCipher.algorithmName}/OCB'; + get algorithmName => '${_mainCipher.algorithmName}/OCB'; @override - Uint8List get mac => _getMac(); + get mac => _getMac(); /// The block size int get blockSize => _blockSize; @@ -156,7 +156,7 @@ class OCBCipher implements AEADCipher { BlockCipher get underlyingCipher => _mainCipher; @override - void init( + init( final bool forEncryption, final CipherParameters params, ) { @@ -236,7 +236,7 @@ class OCBCipher implements AEADCipher { } @override - int doFinal(final Uint8List output, final int outOff) { + doFinal(final Uint8List output, final int outOff) { /// For decryption, get the tag from the end of the message Uint8List? tag; if (!_forEncryption) { @@ -316,7 +316,7 @@ class OCBCipher implements AEADCipher { } @override - int processByte( + processByte( final int input, final Uint8List output, final int outOff, @@ -330,7 +330,7 @@ class OCBCipher implements AEADCipher { } @override - int processBytes( + processBytes( final Uint8List input, final int inOff, final int len, @@ -353,7 +353,7 @@ class OCBCipher implements AEADCipher { } @override - int getUpdateOutputSize(final int len) { + getUpdateOutputSize(final int len) { var totalData = len + _mainBlockPos; if (!forEncryption) { if (totalData < macSize) { @@ -365,7 +365,7 @@ class OCBCipher implements AEADCipher { } @override - int getOutputSize(final int len) { + getOutputSize(final int len) { final totalData = len + _mainBlockPos; if (_forEncryption) { return totalData + _macSize; @@ -374,7 +374,7 @@ class OCBCipher implements AEADCipher { } @override - void processAADByte(final int input) { + processAADByte(final int input) { _hashBlock[_hashBlockPos] = input; if (++_hashBlockPos == _hashBlock.length) { _processHashBlock(); @@ -382,7 +382,7 @@ class OCBCipher implements AEADCipher { } @override - void processAADBytes( + processAADBytes( final Uint8List input, final int offset, final int len, @@ -396,7 +396,7 @@ class OCBCipher implements AEADCipher { } @override - void reset() { + reset() { _reset(true); } diff --git a/lib/src/cryptor/asymmetric/dsa.dart b/lib/src/cryptor/asymmetric/dsa.dart index 88bd545f..d2276166 100644 --- a/lib/src/cryptor/asymmetric/dsa.dart +++ b/lib/src/cryptor/asymmetric/dsa.dart @@ -24,10 +24,10 @@ class DSASigner implements Signer { DSASigner([this._digest]); @override - String get algorithmName => '${_digest?.algorithmName}/DSA'; + get algorithmName => '${_digest?.algorithmName}/DSA'; @override - void init(final bool forSigning, CipherParameters params) { + init(final bool forSigning, CipherParameters params) { _forSigning = forSigning; if (params is ParametersWithRandom) { _random = params.random; @@ -53,7 +53,7 @@ class DSASigner implements Signer { } @override - void reset() { + reset() { _digest?.reset(); } @@ -92,7 +92,7 @@ class DSASigner implements Signer { } @override - bool verifySignature( + verifySignature( final Uint8List message, covariant final DSASignature signature, ) { @@ -166,13 +166,13 @@ class DSASignature implements Signature { String toString() => '(${r.toString()},${s.toString()})'; @override - bool operator ==(other) { + operator ==(other) { if (other is! DSASignature) return false; return (other.r == r) && (other.s == s); } @override - int get hashCode { + get hashCode { return r.hashCode + s.hashCode; } } diff --git a/lib/src/cryptor/asymmetric/elgamal.dart b/lib/src/cryptor/asymmetric/elgamal.dart index 533ea0a6..6483545c 100644 --- a/lib/src/cryptor/asymmetric/elgamal.dart +++ b/lib/src/cryptor/asymmetric/elgamal.dart @@ -21,10 +21,10 @@ class ElGamalEngine implements AsymmetricBlockCipher { late int _bitSize; @override - String get algorithmName => 'ElGamal'; + get algorithmName => 'ElGamal'; @override - void init(final bool forEncryption, CipherParameters params) { + init(final bool forEncryption, CipherParameters params) { _forEncryption = forEncryption; if (params is ParametersWithRandom) { _random = params.random; @@ -52,14 +52,14 @@ class ElGamalEngine implements AsymmetricBlockCipher { /// Return the maximum size for an input block to this engine. @override - int get inputBlockSize => _forEncryption ? (_bitSize - 1) ~/ 8 : 2 * ((_bitSize + 7) >> 3); + get inputBlockSize => _forEncryption ? (_bitSize - 1) ~/ 8 : 2 * ((_bitSize + 7) >> 3); /// Return the maximum size for an output block to this engine. @override - int get outputBlockSize => _forEncryption ? 2 * ((_bitSize + 7) >> 3) : (_bitSize - 1) ~/ 8; + get outputBlockSize => _forEncryption ? 2 * ((_bitSize + 7) >> 3) : (_bitSize - 1) ~/ 8; @override - Uint8List process(final Uint8List data) { + process(final Uint8List data) { final out = Uint8List(outputBlockSize); final len = processBlock(data, 0, data.length, out, 0); return out.sublist(0, len); @@ -67,7 +67,7 @@ class ElGamalEngine implements AsymmetricBlockCipher { /// Process a single block using the basic ElGamal algorithm. @override - int processBlock( + processBlock( final Uint8List input, final int inOff, final int inLength, @@ -123,7 +123,7 @@ class ElGamalEngine implements AsymmetricBlockCipher { } @override - void reset() {} + reset() {} BigInt _calculateK(final BigInt n) { BigInt k; diff --git a/lib/src/cryptor/symmetric/base_engine.dart b/lib/src/cryptor/symmetric/base_engine.dart index 4d893f93..c8659327 100644 --- a/lib/src/cryptor/symmetric/base_engine.dart +++ b/lib/src/cryptor/symmetric/base_engine.dart @@ -11,12 +11,12 @@ import 'package:pointycastle/api.dart'; /// Author Nguyen Van Nguyen abstract class BaseEngine implements BlockCipher { @override - Uint8List process(final Uint8List input) { + process(final Uint8List input) { final output = Uint8List(blockSize); final len = processBlock(input, 0, output, 0); return output.sublist(0, len); } @override - void reset() {} + reset() {} } diff --git a/lib/src/cryptor/symmetric/blowfish.dart b/lib/src/cryptor/symmetric/blowfish.dart index 61b7d18c..8858f500 100644 --- a/lib/src/cryptor/symmetric/blowfish.dart +++ b/lib/src/cryptor/symmetric/blowfish.dart @@ -311,13 +311,13 @@ class BlowfishEngine extends BaseEngine { final _p = List.filled(_pSZ, 0); @override - String get algorithmName => 'Blowfish'; + get algorithmName => 'Blowfish'; @override - int get blockSize => _blockSize; + get blockSize => _blockSize; @override - void init(final bool forEncryption, final CipherParameters? params) { + init(final bool forEncryption, final CipherParameters? params) { if (params is KeyParameter) { _forEncryption = forEncryption; _workingKey = params.key; @@ -330,7 +330,7 @@ class BlowfishEngine extends BaseEngine { } @override - int processBlock( + processBlock( final Uint8List input, final int inOff, final Uint8List output, diff --git a/lib/src/cryptor/symmetric/camellia.dart b/lib/src/cryptor/symmetric/camellia.dart index 4f0ba0ca..0cfd3715 100644 --- a/lib/src/cryptor/symmetric/camellia.dart +++ b/lib/src/cryptor/symmetric/camellia.dart @@ -231,13 +231,13 @@ class CamelliaEngine extends BaseEngine { final _state = List.filled(4, 0); @override - String get algorithmName => 'Camellia'; + get algorithmName => 'Camellia'; @override - int get blockSize => _blockSize; + get blockSize => _blockSize; @override - void init(final bool forEncryption, final CipherParameters? params) { + init(final bool forEncryption, final CipherParameters? params) { if (params is! KeyParameter) { throw Exception('only simple KeyParameter expected.'); } @@ -248,7 +248,7 @@ class CamelliaEngine extends BaseEngine { } @override - int processBlock( + processBlock( final Uint8List input, final int inOff, final Uint8List output, diff --git a/lib/src/cryptor/symmetric/cast5.dart b/lib/src/cryptor/symmetric/cast5.dart index 72238edc..c4b4a36f 100644 --- a/lib/src/cryptor/symmetric/cast5.dart +++ b/lib/src/cryptor/symmetric/cast5.dart @@ -567,20 +567,20 @@ class CAST5Engine extends BaseEngine { int _rounds = _maxRounds; @override - String get algorithmName => 'CAST5'; + get algorithmName => 'CAST5'; @override - int get blockSize => _blockSize; + get blockSize => _blockSize; @override - void reset() { + reset() { super.reset(); _rotating.setAll(0, List.filled(17, 0)); _masking.setAll(0, List.filled(17, 0)); } @override - void init(final bool forEncryption, final CipherParameters? params) { + init(final bool forEncryption, final CipherParameters? params) { if (params is KeyParameter) { _forEncryption = forEncryption; _workingKey = params.key; @@ -593,7 +593,7 @@ class CAST5Engine extends BaseEngine { } @override - int processBlock( + processBlock( final Uint8List input, final int inOff, final Uint8List output, diff --git a/lib/src/cryptor/symmetric/idea.dart b/lib/src/cryptor/symmetric/idea.dart index e460832e..382c2944 100644 --- a/lib/src/cryptor/symmetric/idea.dart +++ b/lib/src/cryptor/symmetric/idea.dart @@ -21,13 +21,13 @@ class IDEAEngine extends BaseEngine { late List _workingKey; @override - String get algorithmName => 'IDEA'; + get algorithmName => 'IDEA'; @override - int get blockSize => _blockSize; + get blockSize => _blockSize; @override - void init(final bool forEncryption, final CipherParameters? params) { + init(final bool forEncryption, final CipherParameters? params) { if (params is KeyParameter) { _workingKey = _generateWorkingKey(forEncryption, params.key); } else { @@ -38,7 +38,7 @@ class IDEAEngine extends BaseEngine { } @override - int processBlock( + processBlock( final Uint8List input, final int inOff, final Uint8List output, diff --git a/lib/src/cryptor/symmetric/twofish.dart b/lib/src/cryptor/symmetric/twofish.dart index 75f6947a..3b350648 100644 --- a/lib/src/cryptor/symmetric/twofish.dart +++ b/lib/src/cryptor/symmetric/twofish.dart @@ -379,13 +379,13 @@ class TwofishEngine extends BaseEngine { Uint8List _workingKey = Uint8List(0); @override - String get algorithmName => 'Twofish'; + get algorithmName => 'Twofish'; @override - int get blockSize => _blockSize; + get blockSize => _blockSize; @override - void init(final bool forEncryption, final CipherParameters? params) { + init(final bool forEncryption, final CipherParameters? params) { if (params is KeyParameter) { _forEncryption = forEncryption; _workingKey = params.key; diff --git a/lib/src/key/base.dart b/lib/src/key/base.dart index 9df5c0cf..4bad136d 100644 --- a/lib/src/key/base.dart +++ b/lib/src/key/base.dart @@ -4,10 +4,7 @@ library; -import 'dart:typed_data'; - import 'package:dart_pg/src/common/helpers.dart'; -import 'package:dart_pg/src/enum/key_algorithm.dart'; import 'package:dart_pg/src/packet/packet_list.dart'; import 'package:dart_pg/src/type/key.dart'; import 'package:dart_pg/src/type/key_packet.dart'; @@ -59,22 +56,22 @@ abstract class BaseKey implements KeyInterface { } @override - Uint8List get fingerprint => keyPacket.fingerprint; + get fingerprint => keyPacket.fingerprint; @override - KeyAlgorithm get keyAlgorithm => keyPacket.keyAlgorithm; + get keyAlgorithm => keyPacket.keyAlgorithm; @override - Uint8List get keyID => keyPacket.keyID; + get keyID => keyPacket.keyID; @override - int get keyStrength => keyPacket.keyStrength; + get keyStrength => keyPacket.keyStrength; @override - int get version => keyPacket.keyVersion; + get version => keyPacket.keyVersion; @override - PacketListInterface get packetList => PacketList([ + get packetList => PacketList([ keyPacket, ...revocationSignatures, ...directSignatures, diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index efb89238..7675b965 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -8,14 +8,12 @@ import 'package:dart_pg/src/common/armor.dart'; import 'package:dart_pg/src/common/config.dart'; import 'package:dart_pg/src/enum/aead_algorithm.dart'; import 'package:dart_pg/src/enum/armor_type.dart'; -import 'package:dart_pg/src/enum/hash_algorithm.dart'; import 'package:dart_pg/src/enum/key_version.dart'; import 'package:dart_pg/src/key/base.dart'; import 'package:dart_pg/src/key/public_key.dart'; import 'package:dart_pg/src/key/subkey.dart'; import 'package:dart_pg/src/packet/base.dart'; import 'package:dart_pg/src/packet/packet_list.dart'; -import 'package:dart_pg/src/type/key.dart'; import 'package:dart_pg/src/type/packet.dart'; import 'package:dart_pg/src/type/private_key.dart'; import 'package:dart_pg/src/type/secret_key_packet.dart'; @@ -24,22 +22,22 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { PrivateKey(super.packetList); @override - SecretKeyPacketInterface get secretKeyPacket => super.keyPacket as SecretKeyPacketInterface; + get secretKeyPacket => super.keyPacket as SecretKeyPacketInterface; @override - bool get isDecrypted => secretKeyPacket.isDecrypted; + get isDecrypted => secretKeyPacket.isDecrypted; @override - bool get isEncrypted => secretKeyPacket.isEncrypted; + get isEncrypted => secretKeyPacket.isEncrypted; @override - bool get aeadProtected => secretKeyPacket.aeadProtected; + get aeadProtected => secretKeyPacket.aeadProtected; @override - HashAlgorithm get preferredHash => secretKeyPacket.preferredHash; + get preferredHash => secretKeyPacket.preferredHash; @override - KeyInterface get publicKey { + get publicKey { final packets = []; for (final packet in packetList) { switch (packet.type) { @@ -70,10 +68,10 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { } @override - String armor() => Armor.encode(ArmorType.privateKey, packetList.encode()); + armor() => Armor.encode(ArmorType.privateKey, packetList.encode()); @override - PrivateKey encrypt( + encrypt( final String passphrase, [ final Iterable subkeyPassphrases = const [], ]) { @@ -122,7 +120,7 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { } @override - PrivateKey decrypt( + decrypt( String passphrase, [ Iterable subkeyPassphrases = const [], ]) { diff --git a/lib/src/key/public_key.dart b/lib/src/key/public_key.dart index 4345e4c7..e3f019f8 100644 --- a/lib/src/key/public_key.dart +++ b/lib/src/key/public_key.dart @@ -68,5 +68,5 @@ final class PublicKey extends BaseKey { String armor() => Armor.encode(ArmorType.publicKey, packetList.encode()); @override - KeyInterface get publicKey => this; + get publicKey => this; } diff --git a/lib/src/key/subkey.dart b/lib/src/key/subkey.dart index fbbf64a7..5c2a0d8e 100644 --- a/lib/src/key/subkey.dart +++ b/lib/src/key/subkey.dart @@ -6,12 +6,10 @@ library; import 'dart:typed_data'; -import 'package:dart_pg/src/enum/key_algorithm.dart'; import 'package:dart_pg/src/key/base.dart'; import 'package:dart_pg/src/packet/packet_list.dart'; import 'package:dart_pg/src/packet/signature/key_flags.dart'; import 'package:dart_pg/src/type/key.dart'; -import 'package:dart_pg/src/type/packet_list.dart'; import 'package:dart_pg/src/type/signature_packet.dart'; import 'package:dart_pg/src/type/subkey.dart'; import 'package:dart_pg/src/type/subkey_packet.dart'; @@ -39,25 +37,25 @@ final class Subkey implements SubkeyInterface { }); @override - DateTime get creationTime => keyPacket.creationTime; + get creationTime => keyPacket.creationTime; @override - DateTime? get expirationTime => BaseKey.keyExpiration(bindingSignatures); + get expirationTime => BaseKey.keyExpiration(bindingSignatures); @override - Uint8List get fingerprint => keyPacket.fingerprint; + get fingerprint => keyPacket.fingerprint; @override - KeyAlgorithm get keyAlgorithm => keyPacket.keyAlgorithm; + get keyAlgorithm => keyPacket.keyAlgorithm; @override - Uint8List get keyID => keyPacket.keyID; + get keyID => keyPacket.keyID; @override - int get keyStrength => keyPacket.keyStrength; + get keyStrength => keyPacket.keyStrength; @override - bool get isEncryptionKey { + get isEncryptionKey { if (keyPacket.isEncryptionKey) { for (final signature in bindingSignatures) { final keyFlags = signature.getSubpacket(); @@ -70,7 +68,7 @@ final class Subkey implements SubkeyInterface { } @override - bool get isSigningKey { + get isSigningKey { if (keyPacket.isSigningKey) { for (final signature in bindingSignatures) { final keyFlags = signature.getSubpacket(); @@ -83,17 +81,17 @@ final class Subkey implements SubkeyInterface { } @override - PacketListInterface get packetList => PacketList([ + get packetList => PacketList([ keyPacket, ...revocationSignatures, ...bindingSignatures, ]); @override - int get version => keyPacket.keyVersion; + get version => keyPacket.keyVersion; @override - bool isRevoked([ + isRevoked([ final DateTime? time, ]) { for (final revocation in revocationSignatures) { @@ -112,7 +110,7 @@ final class Subkey implements SubkeyInterface { } @override - bool verify([DateTime? time]) { + verify([DateTime? time]) { for (final signature in bindingSignatures) { if (signature.verify( mainKey.keyPacket, diff --git a/lib/src/key/user.dart b/lib/src/key/user.dart index fa4fcc48..3bd3bcbc 100644 --- a/lib/src/key/user.dart +++ b/lib/src/key/user.dart @@ -9,7 +9,6 @@ import 'dart:typed_data'; import 'package:dart_pg/src/packet/base.dart'; import 'package:dart_pg/src/packet/packet_list.dart'; import 'package:dart_pg/src/type/key.dart'; -import 'package:dart_pg/src/type/packet_list.dart'; import 'package:dart_pg/src/type/signature_packet.dart'; import 'package:dart_pg/src/type/user.dart'; import 'package:dart_pg/src/type/user_id_packet.dart'; @@ -41,7 +40,7 @@ final class User implements UserInterface { }); @override - bool get isPrimary { + get isPrimary { final signatures = selfSignatures.toList(); signatures.sort( (a, b) => b.creationTime.compareTo( @@ -57,7 +56,7 @@ final class User implements UserInterface { } @override - PacketListInterface get packetList => PacketList([ + get packetList => PacketList([ userIDPacket, ...revocationSignatures, ...selfSignatures, @@ -65,10 +64,10 @@ final class User implements UserInterface { ]); @override - String get userID => (userIDPacket is UserIDPacket) ? (userIDPacket as UserIDPacket).userID : ""; + get userID => (userIDPacket is UserIDPacket) ? (userIDPacket as UserIDPacket).userID : ""; @override - bool isRevoked([DateTime? time]) { + isRevoked([DateTime? time]) { for (final revocation in revocationSignatures) { if (revocation.verify( mainKey.keyPacket, @@ -85,7 +84,7 @@ final class User implements UserInterface { } @override - bool verify([DateTime? time]) { + verify([DateTime? time]) { for (final signature in selfSignatures) { if (signature.verify( mainKey.keyPacket, diff --git a/lib/src/packet/aead_encrypted_data.dart b/lib/src/packet/aead_encrypted_data.dart index 5b268121..e41a63d1 100644 --- a/lib/src/packet/aead_encrypted_data.dart +++ b/lib/src/packet/aead_encrypted_data.dart @@ -105,7 +105,7 @@ class AeadEncryptedDataPacket extends BasePacket implements EncryptedDataPacketI } @override - Uint8List get data => Uint8List.fromList([ + get data => Uint8List.fromList([ version, symmetric.value, aead.value, @@ -115,7 +115,7 @@ class AeadEncryptedDataPacket extends BasePacket implements EncryptedDataPacketI ]); @override - AeadEncryptedDataPacket decrypt( + decrypt( final Uint8List key, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, }) { diff --git a/lib/src/packet/base.dart b/lib/src/packet/base.dart index 644da793..6c791501 100644 --- a/lib/src/packet/base.dart +++ b/lib/src/packet/base.dart @@ -44,7 +44,7 @@ abstract class BasePacket implements PacketInterface { BasePacket(this.type); @override - Uint8List encode() { + encode() { switch (type) { case PacketType.aeadEncryptedData: case PacketType.compressedData: diff --git a/lib/src/packet/compressed_data.dart b/lib/src/packet/compressed_data.dart index 2f8808ad..3c94f3c1 100644 --- a/lib/src/packet/compressed_data.dart +++ b/lib/src/packet/compressed_data.dart @@ -55,7 +55,7 @@ class CompressedDataPacket extends BasePacket { ); @override - Uint8List get data => Uint8List.fromList([ + get data => Uint8List.fromList([ algorithm.value, ...compressed, ]); diff --git a/lib/src/packet/key/dsa_public_material.dart b/lib/src/packet/key/dsa_public_material.dart index e2126987..b1f8582b 100644 --- a/lib/src/packet/key/dsa_public_material.dart +++ b/lib/src/packet/key/dsa_public_material.dart @@ -63,10 +63,10 @@ class DSAPublicMaterial implements VerificationKeyMaterial { } @override - int get keyStrength => prime.bitLength; + get keyStrength => prime.bitLength; @override - Uint8List get toBytes => Uint8List.fromList([ + get toBytes => Uint8List.fromList([ ...prime.bitLength.pack16(), ...prime.toUnsignedBytes(), ...order.bitLength.pack16(), @@ -78,7 +78,7 @@ class DSAPublicMaterial implements VerificationKeyMaterial { ]); @override - bool verify( + verify( final Uint8List message, final HashAlgorithm hash, final Uint8List signature, diff --git a/lib/src/packet/key/dsa_secret_material.dart b/lib/src/packet/key/dsa_secret_material.dart index 743381ad..42b95828 100644 --- a/lib/src/packet/key/dsa_secret_material.dart +++ b/lib/src/packet/key/dsa_secret_material.dart @@ -40,10 +40,10 @@ class DSASecretMaterial implements SigningKeyMaterialInterface { DSASecretMaterial(Helper.readMPI(bytes), publicMaterial); @override - int get keyStrength => publicMaterial.keyStrength; + get keyStrength => publicMaterial.keyStrength; @override - Uint8List sign(final Uint8List message, final HashAlgorithm hash) { + sign(final Uint8List message, final HashAlgorithm hash) { final signer = DSASigner(Digest(hash.digestName)) ..init( true, @@ -53,13 +53,13 @@ class DSASecretMaterial implements SigningKeyMaterialInterface { } @override - Uint8List get toBytes => Uint8List.fromList([ + get toBytes => Uint8List.fromList([ ...exponent.bitLength.pack16(), ...exponent.toUnsignedBytes(), ]); @override - bool get isValid { + get isValid { // Check that 1 < g < p if (publicMaterial.generator.compareTo(BigInt.one) <= 0 || publicMaterial.generator.compareTo(publicMaterial.prime) >= 0) { diff --git a/lib/src/packet/key/ec_public_material.dart b/lib/src/packet/key/ec_public_material.dart index 8a4285a9..8bc8140a 100644 --- a/lib/src/packet/key/ec_public_material.dart +++ b/lib/src/packet/key/ec_public_material.dart @@ -29,7 +29,7 @@ abstract class ECPublicMaterial implements KeyMaterialInterface { ); @override - int get keyStrength { + get keyStrength { if (curve == Ecc.ed25519 || curve == Ecc.curve25519) { return 255; } else { @@ -43,7 +43,7 @@ abstract class ECPublicMaterial implements KeyMaterialInterface { } @override - Uint8List get toBytes { + get toBytes { return Uint8List.fromList([ ...oid.encode().sublist(1), ...q.bitLength.pack16(), diff --git a/lib/src/packet/key/ec_secret_material.dart b/lib/src/packet/key/ec_secret_material.dart index 77d6a9e5..c374a5ea 100644 --- a/lib/src/packet/key/ec_secret_material.dart +++ b/lib/src/packet/key/ec_secret_material.dart @@ -19,7 +19,7 @@ abstract class ECSecretMaterial implements KeyMaterialInterface { ECSecretMaterial(this.d); @override - Uint8List get toBytes => Uint8List.fromList([ + get toBytes => Uint8List.fromList([ ...d.bitLength.pack16(), ...d.toUnsignedBytes(), ]); diff --git a/lib/src/packet/key/ecdh_public_material.dart b/lib/src/packet/key/ecdh_public_material.dart index 057481fc..a6eb69ed 100644 --- a/lib/src/packet/key/ecdh_public_material.dart +++ b/lib/src/packet/key/ecdh_public_material.dart @@ -64,7 +64,7 @@ class ECDHPublicMaterial extends ECPublicMaterial { ]); @override - Uint8List get toBytes => Uint8List.fromList([ + get toBytes => Uint8List.fromList([ ...super.toBytes, 0x3, reserved, diff --git a/lib/src/packet/key/ecdh_secret_material.dart b/lib/src/packet/key/ecdh_secret_material.dart index 1bf78e19..5e05ee43 100644 --- a/lib/src/packet/key/ecdh_secret_material.dart +++ b/lib/src/packet/key/ecdh_secret_material.dart @@ -82,10 +82,10 @@ class ECDHSecretMaterial extends ECSecretMaterial implements SecretKeyMaterialIn } @override - int get keyStrength => publicMaterial.keyStrength; + get keyStrength => publicMaterial.keyStrength; @override - bool get isValid { + get isValid { switch (publicMaterial.curve) { case Ecc.curve25519: final privateKey = nacl.PrivateKey( diff --git a/lib/src/packet/key/ecdh_session_key_cryptor.dart b/lib/src/packet/key/ecdh_session_key_cryptor.dart index 54739364..62c57d9c 100644 --- a/lib/src/packet/key/ecdh_session_key_cryptor.dart +++ b/lib/src/packet/key/ecdh_session_key_cryptor.dart @@ -127,7 +127,7 @@ class ECDHSessionKeyCryptor extends SessionKeyCryptor { } @override - Uint8List decrypt(final SecretKeyMaterialInterface key) { + decrypt(final SecretKeyMaterialInterface key) { if (key is ECDHSecretMaterial) { final Uint8List sharedKey; switch (key.publicMaterial.curve) { @@ -177,7 +177,7 @@ class ECDHSessionKeyCryptor extends SessionKeyCryptor { } @override - Uint8List encode() => Uint8List.fromList([ + encode() => Uint8List.fromList([ ...ephemeralKey.bitLength.pack16(), ...ephemeralKey.toUnsignedBytes(), wrappedKey.length, diff --git a/lib/src/packet/key/ecdsa_public_material.dart b/lib/src/packet/key/ecdsa_public_material.dart index 63e9d281..09674ff9 100644 --- a/lib/src/packet/key/ecdsa_public_material.dart +++ b/lib/src/packet/key/ecdsa_public_material.dart @@ -30,7 +30,7 @@ class ECDSAPublicMaterial extends ECPublicMaterial implements VerificationKeyMat } @override - bool verify( + verify( final Uint8List message, final HashAlgorithm hash, final Uint8List signature, diff --git a/lib/src/packet/key/ecdsa_secret_material.dart b/lib/src/packet/key/ecdsa_secret_material.dart index 9435e611..c18f3e75 100644 --- a/lib/src/packet/key/ecdsa_secret_material.dart +++ b/lib/src/packet/key/ecdsa_secret_material.dart @@ -57,17 +57,17 @@ class ECDSASecretMaterial extends ECSecretMaterial implements SigningKeyMaterial } @override - bool get isValid { + get isValid { final parameters = ECDomainParameters(publicMaterial.curve.name.toLowerCase()); final q = parameters.curve.decodePoint(publicMaterial.q.toUnsignedBytes()); return q != null && !q.isInfinity && (parameters.G * d) == q; } @override - int get keyStrength => publicMaterial.keyStrength; + get keyStrength => publicMaterial.keyStrength; @override - Uint8List sign(final Uint8List message, final HashAlgorithm hash) { + sign(final Uint8List message, final HashAlgorithm hash) { final signer = Signer('${hash.digestName}/DET-ECDSA') ..init( true, diff --git a/lib/src/packet/key/eddsa_legacy_public_material.dart b/lib/src/packet/key/eddsa_legacy_public_material.dart index bb986983..4edf9edb 100644 --- a/lib/src/packet/key/eddsa_legacy_public_material.dart +++ b/lib/src/packet/key/eddsa_legacy_public_material.dart @@ -30,7 +30,7 @@ class EdDSALegacyPublicMaterial extends ECPublicMaterial implements Verification } @override - bool verify( + verify( final Uint8List message, final HashAlgorithm hash, final Uint8List signature, diff --git a/lib/src/packet/key/eddsa_legacy_secret_material.dart b/lib/src/packet/key/eddsa_legacy_secret_material.dart index fdcf587d..5e6c2b9f 100644 --- a/lib/src/packet/key/eddsa_legacy_secret_material.dart +++ b/lib/src/packet/key/eddsa_legacy_secret_material.dart @@ -49,7 +49,7 @@ class EdDSALegacySecretMaterial implements SigningKeyMaterialInterface { } @override - bool get isValid { + get isValid { final signingKey = nacl.SigningKey.fromSeed(seed.toUnsignedBytes()); final dG = Uint8List.fromList([ 0x40, @@ -59,10 +59,10 @@ class EdDSALegacySecretMaterial implements SigningKeyMaterialInterface { } @override - int get keyStrength => publicMaterial.keyStrength; + get keyStrength => publicMaterial.keyStrength; @override - Uint8List sign(Uint8List message, HashAlgorithm hash) { + sign(Uint8List message, HashAlgorithm hash) { final signed = nacl.SigningKey.fromSeed(seed.toUnsignedBytes()).sign( Helper.hashDigest(message, hash), ); @@ -76,7 +76,7 @@ class EdDSALegacySecretMaterial implements SigningKeyMaterialInterface { } @override - Uint8List get toBytes => Uint8List.fromList([ + get toBytes => Uint8List.fromList([ ...seed.bitLength.pack16(), ...seed.toUnsignedBytes(), ]); diff --git a/lib/src/packet/key/eddsa_public_material.dart b/lib/src/packet/key/eddsa_public_material.dart index c5e309c0..aeff5ab0 100644 --- a/lib/src/packet/key/eddsa_public_material.dart +++ b/lib/src/packet/key/eddsa_public_material.dart @@ -36,13 +36,13 @@ class EdDSAPublicMaterial implements VerificationKeyMaterial { ); @override - int get keyStrength => curve.keyStrength; + get keyStrength => curve.keyStrength; @override - Uint8List get toBytes => publicKey; + get toBytes => publicKey; @override - bool verify( + verify( final Uint8List message, final HashAlgorithm hash, final Uint8List signature, diff --git a/lib/src/packet/key/eddsa_secret_material.dart b/lib/src/packet/key/eddsa_secret_material.dart index b934732b..c9da507b 100644 --- a/lib/src/packet/key/eddsa_secret_material.dart +++ b/lib/src/packet/key/eddsa_secret_material.dart @@ -57,7 +57,7 @@ class EdDSASecretMaterial implements SigningKeyMaterialInterface { } @override - bool get isValid { + get isValid { final publicKey = switch (publicMaterial.curve) { EdDSACurve.ed25519 => nacl.SigningKey.fromSeed( secretKey, @@ -71,10 +71,10 @@ class EdDSASecretMaterial implements SigningKeyMaterialInterface { } @override - int get keyStrength => publicMaterial.keyStrength; + get keyStrength => publicMaterial.keyStrength; @override - Uint8List sign( + sign( final Uint8List message, final HashAlgorithm hash, ) => @@ -94,5 +94,5 @@ class EdDSASecretMaterial implements SigningKeyMaterialInterface { }; @override - Uint8List get toBytes => secretKey; + get toBytes => secretKey; } diff --git a/lib/src/packet/key/elgamal_public_material.dart b/lib/src/packet/key/elgamal_public_material.dart index 9fc8e6bf..7f9012cf 100644 --- a/lib/src/packet/key/elgamal_public_material.dart +++ b/lib/src/packet/key/elgamal_public_material.dart @@ -40,10 +40,10 @@ class ElGamalPublicMaterial implements KeyMaterialInterface { } @override - int get keyStrength => prime.bitLength; + get keyStrength => prime.bitLength; @override - Uint8List get toBytes => Uint8List.fromList([ + get toBytes => Uint8List.fromList([ ...prime.bitLength.pack16(), ...prime.toUnsignedBytes(), ...generator.bitLength.pack16(), diff --git a/lib/src/packet/key/elgamal_secret_material.dart b/lib/src/packet/key/elgamal_secret_material.dart index 776c05e5..079737ca 100644 --- a/lib/src/packet/key/elgamal_secret_material.dart +++ b/lib/src/packet/key/elgamal_secret_material.dart @@ -38,10 +38,10 @@ class ElGamalSecretMaterial implements SecretKeyMaterialInterface { ); @override - int get keyStrength => publicMaterial.keyStrength; + get keyStrength => publicMaterial.keyStrength; @override - bool get isValid { + get isValid { // Check that 1 < g < p if (publicMaterial.generator.compareTo(BigInt.one) <= 0 || publicMaterial.generator.compareTo(publicMaterial.prime) >= 0) { @@ -81,7 +81,7 @@ class ElGamalSecretMaterial implements SecretKeyMaterialInterface { } @override - Uint8List get toBytes => Uint8List.fromList([ + get toBytes => Uint8List.fromList([ ...exponent.bitLength.pack16(), ...exponent.toUnsignedBytes(), ]); diff --git a/lib/src/packet/key/elgamal_session_key_cryptor.dart b/lib/src/packet/key/elgamal_session_key_cryptor.dart index 1205fa1c..f5042a3f 100644 --- a/lib/src/packet/key/elgamal_session_key_cryptor.dart +++ b/lib/src/packet/key/elgamal_session_key_cryptor.dart @@ -32,7 +32,7 @@ class ElGamalSessionKeyCryptor extends SessionKeyCryptor { } @override - Uint8List decrypt(final SecretKeyMaterialInterface key) { + decrypt(final SecretKeyMaterialInterface key) { if (key is ElGamalSecretMaterial) { return _pkcs1Decode(SessionKeyCryptor.processInBlocks( ElGamalEngine() @@ -51,7 +51,7 @@ class ElGamalSessionKeyCryptor extends SessionKeyCryptor { } @override - Uint8List encode() => Uint8List.fromList([ + encode() => Uint8List.fromList([ ...gamma.bitLength.pack16(), ...gamma.toUnsignedBytes(), ...phi.bitLength.pack16(), diff --git a/lib/src/packet/key/montgomery_public_material.dart b/lib/src/packet/key/montgomery_public_material.dart index 34941f4a..8d906369 100644 --- a/lib/src/packet/key/montgomery_public_material.dart +++ b/lib/src/packet/key/montgomery_public_material.dart @@ -28,8 +28,8 @@ class MontgomeryPublicMaterial implements KeyMaterialInterface { ); @override - int get keyStrength => curve.keyStrength; + get keyStrength => curve.keyStrength; @override - Uint8List get toBytes => publicKey; + get toBytes => publicKey; } diff --git a/lib/src/packet/key/montgomery_secret_material.dart b/lib/src/packet/key/montgomery_secret_material.dart index 08ecb1f5..0a241f43 100644 --- a/lib/src/packet/key/montgomery_secret_material.dart +++ b/lib/src/packet/key/montgomery_secret_material.dart @@ -50,13 +50,13 @@ class MontgomerySecretMaterial implements SecretKeyMaterialInterface { } @override - int get keyStrength => publicMaterial.keyStrength; + get keyStrength => publicMaterial.keyStrength; @override - Uint8List get toBytes => secretKey; + get toBytes => secretKey; @override - bool get isValid { + get isValid { final publicKey = switch (publicMaterial.curve) { MontgomeryCurve.x25519 => PrivateKey( secretKey, diff --git a/lib/src/packet/key/montgomery_session_key_cryptor.dart b/lib/src/packet/key/montgomery_session_key_cryptor.dart index 77aa1a71..7d1c6c37 100644 --- a/lib/src/packet/key/montgomery_session_key_cryptor.dart +++ b/lib/src/packet/key/montgomery_session_key_cryptor.dart @@ -62,7 +62,7 @@ class MontgomerySessionKeyCryptor extends SessionKeyCryptor { } @override - Uint8List decrypt(final SecretKeyMaterialInterface key) { + decrypt(final SecretKeyMaterialInterface key) { if (key is MontgomerySecretMaterial) { final keyWrapper = AesKeyWrapper( key.publicMaterial.curve.kekSize, @@ -86,7 +86,7 @@ class MontgomerySessionKeyCryptor extends SessionKeyCryptor { } @override - Uint8List encode() => Uint8List.fromList([ + encode() => Uint8List.fromList([ ...ephemeralKey, wrappedKey.length, ...wrappedKey, diff --git a/lib/src/packet/key/rsa_public_material.dart b/lib/src/packet/key/rsa_public_material.dart index a7bdb705..b9f44ea7 100644 --- a/lib/src/packet/key/rsa_public_material.dart +++ b/lib/src/packet/key/rsa_public_material.dart @@ -38,10 +38,10 @@ class RSAPublicMaterial implements VerificationKeyMaterial { } @override - int get keyStrength => modulus.bitLength; + get keyStrength => modulus.bitLength; @override - Uint8List get toBytes => Uint8List.fromList([ + get toBytes => Uint8List.fromList([ ...modulus.bitLength.pack16(), ...modulus.toUnsignedBytes(), ...exponent.bitLength.pack16(), @@ -49,7 +49,7 @@ class RSAPublicMaterial implements VerificationKeyMaterial { ]); @override - bool verify( + verify( final Uint8List message, final HashAlgorithm hash, final Uint8List signature, diff --git a/lib/src/packet/key/rsa_secret_material.dart b/lib/src/packet/key/rsa_secret_material.dart index 48ba170a..b7369d3f 100644 --- a/lib/src/packet/key/rsa_secret_material.dart +++ b/lib/src/packet/key/rsa_secret_material.dart @@ -107,7 +107,7 @@ class RSASecretMaterial implements SigningKeyMaterialInterface { } @override - bool get isValid { + get isValid { // expect pq = n if ((primeP * primeQ).compareTo(publicMaterial.modulus) != 0) { return false; @@ -124,10 +124,10 @@ class RSASecretMaterial implements SigningKeyMaterialInterface { } @override - int get keyStrength => publicMaterial.keyStrength; + get keyStrength => publicMaterial.keyStrength; @override - Uint8List sign(final Uint8List message, final HashAlgorithm hash) { + sign(final Uint8List message, final HashAlgorithm hash) { final signer = Signer('${hash.digestName}/RSA') ..init( true, @@ -141,7 +141,7 @@ class RSASecretMaterial implements SigningKeyMaterialInterface { } @override - Uint8List get toBytes => Uint8List.fromList([ + get toBytes => Uint8List.fromList([ ...exponent.bitLength.pack16(), ...exponent.toUnsignedBytes(), ...primeP.bitLength.pack16(), diff --git a/lib/src/packet/key/rsa_session_key_cryptor.dart b/lib/src/packet/key/rsa_session_key_cryptor.dart index 04f5677e..7f7faa52 100644 --- a/lib/src/packet/key/rsa_session_key_cryptor.dart +++ b/lib/src/packet/key/rsa_session_key_cryptor.dart @@ -43,7 +43,7 @@ class RSASessionKeyCryptor extends SessionKeyCryptor { } @override - Uint8List decrypt(final SecretKeyMaterialInterface key) { + decrypt(final SecretKeyMaterialInterface key) { if (key is RSASecretMaterial) { return SessionKeyCryptor.processInBlocks( AsymmetricBlockCipher('RSA/PKCS1') @@ -59,7 +59,7 @@ class RSASessionKeyCryptor extends SessionKeyCryptor { } @override - Uint8List encode() => Uint8List.fromList([ + encode() => Uint8List.fromList([ ...encrypted.bitLength.pack16(), ...encrypted.toUnsignedBytes(), ]); diff --git a/lib/src/packet/key/session_key.dart b/lib/src/packet/key/session_key.dart index 808e458d..426f3f7e 100644 --- a/lib/src/packet/key/session_key.dart +++ b/lib/src/packet/key/session_key.dart @@ -42,12 +42,12 @@ class SessionKey implements SessionKeyInterface { } @override - Uint8List encode() => Uint8List.fromList( + encode() => Uint8List.fromList( [symmetric.value, ...encryptionKey], ); @override - Uint8List computeChecksum() { + computeChecksum() { var sum = 0; for (var i = 0; i < encryptionKey.length; i++) { sum = (sum + encryptionKey[i]) & 0xffff; @@ -56,7 +56,7 @@ class SessionKey implements SessionKeyInterface { } @override - void checksum(final Uint8List checksum) { + checksum(final Uint8List checksum) { final computedChecksum = computeChecksum(); if (!((computedChecksum[0] == checksum[0]) && (computedChecksum[1] == checksum[1]))) { throw StateError('Session key checksum mismatch!'); diff --git a/lib/src/packet/literal_data.dart b/lib/src/packet/literal_data.dart index 6d6317de..2641d269 100644 --- a/lib/src/packet/literal_data.dart +++ b/lib/src/packet/literal_data.dart @@ -79,13 +79,13 @@ class LiteralDataPacket extends BasePacket implements LiteralDataInterface { ); @override - Uint8List get data => Uint8List.fromList([ + get data => Uint8List.fromList([ ...header, ...signBytes, ]); @override - Uint8List get signBytes => binary.isNotEmpty + get signBytes => binary.isNotEmpty ? binary : text .replaceAll( @@ -95,7 +95,7 @@ class LiteralDataPacket extends BasePacket implements LiteralDataInterface { .toBytes(); @override - Uint8List get header => Uint8List.fromList([ + get header => Uint8List.fromList([ format.value, filename.length, ...filename.toBytes(), diff --git a/lib/src/packet/marker.dart b/lib/src/packet/marker.dart index cfb3d316..d23fcd64 100644 --- a/lib/src/packet/marker.dart +++ b/lib/src/packet/marker.dart @@ -5,7 +5,6 @@ library; import 'dart:convert'; -import 'dart:typed_data'; import 'base.dart'; @@ -17,5 +16,5 @@ class MarkerPacket extends BasePacket { MarkerPacket() : super(PacketType.marker); @override - Uint8List get data => utf8.encoder.convert(marker); + get data => utf8.encoder.convert(marker); } diff --git a/lib/src/packet/one_pass_signature.dart b/lib/src/packet/one_pass_signature.dart index f4c4292c..29624356 100644 --- a/lib/src/packet/one_pass_signature.dart +++ b/lib/src/packet/one_pass_signature.dart @@ -119,7 +119,7 @@ class OnePassSignaturePacket extends BasePacket { } @override - Uint8List get data => Uint8List.fromList([ + get data => Uint8List.fromList([ version, signatureType.value, hashAlgorithm.value, diff --git a/lib/src/packet/packet_list.dart b/lib/src/packet/packet_list.dart index 5365abed..e84bce42 100644 --- a/lib/src/packet/packet_list.dart +++ b/lib/src/packet/packet_list.dart @@ -70,7 +70,7 @@ class PacketList extends ListBase implements PacketListInterfac } @override - Uint8List encode() => Uint8List.fromList( + encode() => Uint8List.fromList( packets .map( (packet) => packet.encode(), @@ -99,13 +99,13 @@ class PacketList extends ListBase implements PacketListInterfac } @override - int get length => packets.length; + get length => packets.length; @override - PacketInterface operator [](int index) => packets[index]; + operator [](int index) => packets[index]; @override - void operator []=(int index, PacketInterface packet) { + operator []=(int index, PacketInterface packet) { packets[index] = packet; } diff --git a/lib/src/packet/padding.dart b/lib/src/packet/padding.dart index a383369c..f5cb2a2d 100644 --- a/lib/src/packet/padding.dart +++ b/lib/src/packet/padding.dart @@ -25,5 +25,5 @@ class PaddingPacket extends BasePacket { } @override - Uint8List get data => padding; + get data => padding; } diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index cb78b585..849ecf7c 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -102,7 +102,7 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { } @override - Uint8List get data { + get data { final kmBytes = keyMaterial.toBytes; return Uint8List.fromList([ keyVersion, @@ -114,25 +114,25 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { } @override - Uint8List get fingerprint => _fingerprint; + get fingerprint => _fingerprint; @override - bool get isEncryptionKey => keyAlgorithm.forEncryption; + get isEncryptionKey => keyAlgorithm.forEncryption; @override - bool get isSigningKey => keyAlgorithm.forSigning; + get isSigningKey => keyAlgorithm.forSigning; @override - bool get isSubkey => this is SubkeyPacketInterface; + get isSubkey => this is SubkeyPacketInterface; @override - Uint8List get keyID => _keyID; + get keyID => _keyID; @override - int get keyStrength => keyMaterial.keyStrength; + get keyStrength => keyMaterial.keyStrength; @override - Uint8List get signBytes => Uint8List.fromList([ + get signBytes => Uint8List.fromList([ 0x95 + keyVersion, ...isV6Key ? data.length.pack32() : data.length.pack16(), ...data, diff --git a/lib/src/packet/public_key_encrypted_session_key.dart b/lib/src/packet/public_key_encrypted_session_key.dart index 6f68d1b5..ae7fbf8c 100644 --- a/lib/src/packet/public_key_encrypted_session_key.dart +++ b/lib/src/packet/public_key_encrypted_session_key.dart @@ -160,7 +160,7 @@ class PublicKeyEncryptedSessionKeyPacket extends BasePacket { } @override - Uint8List get data => Uint8List.fromList([ + get data => Uint8List.fromList([ version, ...version == 6 ? [keyFingerprint.length + 1] : [], ...version == 6 ? [keyVersion] : [], diff --git a/lib/src/packet/public_subkey.dart b/lib/src/packet/public_subkey.dart index b3f283b8..45e46792 100644 --- a/lib/src/packet/public_subkey.dart +++ b/lib/src/packet/public_subkey.dart @@ -14,7 +14,7 @@ import 'public_key.dart'; /// Author Nguyen Van Nguyen class PublicSubkeyPacket extends PublicKeyPacket implements SubkeyPacketInterface { @override - PacketType get type => PacketType.publicSubkey; + get type => PacketType.publicSubkey; PublicSubkeyPacket( super.keyVersion, diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index d2292570..020ac614 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -26,7 +26,6 @@ import '../enum/key_algorithm.dart'; import '../enum/s2k_type.dart'; import '../enum/s2k_usage.dart'; import '../enum/symmetric_algorithm.dart'; -import '../type/key_material.dart'; import '../type/s2k.dart'; import '../type/secret_key_material.dart'; import '../type/secret_key_packet.dart'; @@ -395,7 +394,7 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { } @override - Uint8List get data { + get data { final isV6 = publicKey.keyVersion == KeyVersion.v6.value; if (isEncrypted) { final optBytes = Uint8List.fromList([ @@ -423,49 +422,49 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { } @override - Uint8List get signBytes => publicKey.signBytes; + get signBytes => publicKey.signBytes; @override - DateTime get creationTime => publicKey.creationTime; + get creationTime => publicKey.creationTime; @override - Uint8List get fingerprint => publicKey.fingerprint; + get fingerprint => publicKey.fingerprint; @override - bool get isDecrypted => secretKeyMaterial != null; + get isDecrypted => secretKeyMaterial != null; @override - bool get isEncrypted => s2kUsage != S2kUsage.none && symmetric != null; + get isEncrypted => s2kUsage != S2kUsage.none && symmetric != null; @override - bool get isEncryptionKey => publicKey.isEncryptionKey; + get isEncryptionKey => publicKey.isEncryptionKey; @override - bool get isSigningKey => publicKey.isSigningKey; + get isSigningKey => publicKey.isSigningKey; @override - bool get isSubkey => this is SubkeyPacketInterface; + get isSubkey => this is SubkeyPacketInterface; @override - KeyAlgorithm get keyAlgorithm => publicKey.keyAlgorithm; + get keyAlgorithm => publicKey.keyAlgorithm; @override - Uint8List get keyID => publicKey.keyID; + get keyID => publicKey.keyID; @override - KeyMaterialInterface get keyMaterial => publicKey.keyMaterial; + get keyMaterial => publicKey.keyMaterial; @override - int get keyStrength => publicKey.keyStrength; + get keyStrength => publicKey.keyStrength; @override - int get keyVersion => publicKey.keyVersion; + get keyVersion => publicKey.keyVersion; @override - bool get aeadProtected => aead != null; + get aeadProtected => aead != null; @override - HashAlgorithm get preferredHash { + get preferredHash { if ((keyMaterial is ECPublicMaterial)) { final curve = Ecc.values.firstWhere( (info) => info.asn1Oid == (keyMaterial as ECPublicMaterial).oid, diff --git a/lib/src/packet/secret_subkey.dart b/lib/src/packet/secret_subkey.dart index 0c76d079..05cbd0f1 100644 --- a/lib/src/packet/secret_subkey.dart +++ b/lib/src/packet/secret_subkey.dart @@ -21,7 +21,7 @@ import 'secret_key.dart'; /// Author Nguyen Van Nguyen class SecretSubkeyPacket extends SecretKeyPacket implements SubkeyPacketInterface { @override - PacketType get type => PacketType.secretSubkey; + get type => PacketType.secretSubkey; SecretSubkeyPacket( PublicSubkeyPacket super.publicKey, diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 37cedee7..d9beaeef 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -230,7 +230,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { } @override - Uint8List get data => Uint8List.fromList([ + get data => Uint8List.fromList([ ...signatureData, ..._encodeSubpackets(unhashedSubpackets, version == 6), ...signedHashValue, @@ -240,20 +240,20 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { ]); @override - DateTime get creationTime => + get creationTime => getSubpacket()?.creationTime ?? DateTime.fromMillisecondsSinceEpoch(0); @override - DateTime? get expirationTime => getSubpacket()?.expirationTime; + get expirationTime => getSubpacket()?.expirationTime; @override - int get keyExpirationTime => getSubpacket()?.expiry ?? 0; + get keyExpirationTime => getSubpacket()?.expiry ?? 0; @override - bool get isCertRevocation => signatureType == SignatureType.certRevocation; + get isCertRevocation => signatureType == SignatureType.certRevocation; @override - bool get isCertification => switch (signatureType) { + get isCertification => switch (signatureType) { SignatureType.certGeneric || SignatureType.certPersona || SignatureType.certCasual || @@ -263,26 +263,26 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { }; @override - bool get isDirectKey => signatureType == SignatureType.directKey; + get isDirectKey => signatureType == SignatureType.directKey; @override - bool get isKeyRevocation => signatureType == SignatureType.keyRevocation; + get isKeyRevocation => signatureType == SignatureType.keyRevocation; @override - bool get isPrimaryUserID => getSubpacket()?.isPrimary ?? false; + get isPrimaryUserID => getSubpacket()?.isPrimary ?? false; @override - bool get isSubkeyBinding => signatureType == SignatureType.subkeyBinding; + get isSubkeyBinding => signatureType == SignatureType.subkeyBinding; @override - bool get isSubkeyRevocation => signatureType == SignatureType.subkeyRevocation; + get isSubkeyRevocation => signatureType == SignatureType.subkeyRevocation; @override - Uint8List get issuerFingerprint => + get issuerFingerprint => getSubpacket()?.fingerprint ?? Uint8List(version == 6 ? 32 : 20); @override - Uint8List get issuerKeyID { + get issuerKeyID { final subpacket = getSubpacket(); if (subpacket != null) { return subpacket.keyID; @@ -294,12 +294,12 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { } @override - T? getSubpacket() { + getSubpacket() { return hashedSubpackets.whereType().elementAtOrNull(0) ?? unhashedSubpackets.whereType().elementAtOrNull(0); } @override - bool isExpired([final DateTime? time]) { + isExpired([final DateTime? time]) { final timestamp = time?.millisecondsSinceEpoch ?? DateTime.now().millisecondsSinceEpoch; final creation = creationTime.millisecondsSinceEpoch; final expiration = expirationTime?.millisecondsSinceEpoch ?? DateTime.now().millisecondsSinceEpoch; @@ -307,7 +307,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { } @override - bool verify( + verify( final KeyPacketInterface verifyKey, final Uint8List dataToVerify, [ final DateTime? time, diff --git a/lib/src/packet/signature_subpacket.dart b/lib/src/packet/signature_subpacket.dart index 6768acbb..ba97a3f0 100644 --- a/lib/src/packet/signature_subpacket.dart +++ b/lib/src/packet/signature_subpacket.dart @@ -60,7 +60,7 @@ class SignatureSubpacket implements SubpacketInterface { }); @override - Uint8List encode() { + encode() { final List header; final bodyLen = data.length + 1; diff --git a/lib/src/packet/sym_encrypted_data.dart b/lib/src/packet/sym_encrypted_data.dart index a92d5d9d..8446864a 100644 --- a/lib/src/packet/sym_encrypted_data.dart +++ b/lib/src/packet/sym_encrypted_data.dart @@ -26,9 +26,15 @@ class SymEncryptedDataPacket extends BasePacket implements EncryptedDataPacketIn @override final PacketListInterface? packets; - SymEncryptedDataPacket(this.encrypted, {this.packets}) : super(PacketType.symEncryptedData); + SymEncryptedDataPacket( + this.encrypted, { + this.packets, + }) : super(PacketType.symEncryptedData); - factory SymEncryptedDataPacket.fromBytes(final Uint8List bytes) => SymEncryptedDataPacket(bytes); + factory SymEncryptedDataPacket.fromBytes( + final Uint8List bytes, + ) => + SymEncryptedDataPacket(bytes); factory SymEncryptedDataPacket.encryptPackets( final Uint8List key, @@ -59,10 +65,10 @@ class SymEncryptedDataPacket extends BasePacket implements EncryptedDataPacketIn } @override - Uint8List get data => encrypted; + get data => encrypted; @override - SymEncryptedDataPacket decrypt( + decrypt( final Uint8List key, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, }) { diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index b63b41cd..fe902b46 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -183,7 +183,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc } @override - Uint8List get data => Uint8List.fromList([ + get data => Uint8List.fromList([ version, ...symmetric != null ? [symmetric!.value] : [], ...aead != null ? [aead!.value] : [], diff --git a/lib/src/packet/sym_encrypted_session_key.dart b/lib/src/packet/sym_encrypted_session_key.dart index 61c5a007..02aca4b3 100644 --- a/lib/src/packet/sym_encrypted_session_key.dart +++ b/lib/src/packet/sym_encrypted_session_key.dart @@ -254,7 +254,7 @@ class SymEncryptedSessionKeyPacket extends BasePacket { } @override - Uint8List get data => Uint8List.fromList([ + get data => Uint8List.fromList([ version, ...version == 6 ? [3 + s2k.length + iv.length] : [], symmetric.value, diff --git a/lib/src/packet/trust.dart b/lib/src/packet/trust.dart index a53e2975..83857723 100644 --- a/lib/src/packet/trust.dart +++ b/lib/src/packet/trust.dart @@ -24,5 +24,5 @@ class TrustPacket extends BasePacket { ); @override - Uint8List get data => levelAndTrustAmount; + get data => levelAndTrustAmount; } diff --git a/lib/src/packet/user_attribute.dart b/lib/src/packet/user_attribute.dart index 68139033..6822fbe1 100644 --- a/lib/src/packet/user_attribute.dart +++ b/lib/src/packet/user_attribute.dart @@ -28,7 +28,7 @@ class UserAttributePacket extends BasePacket implements UserIDPacketInterface { ); @override - Uint8List get data => Uint8List.fromList( + get data => Uint8List.fromList( attributes .map( (attr) => attr.encode(), @@ -38,7 +38,7 @@ class UserAttributePacket extends BasePacket implements UserIDPacketInterface { ); @override - Uint8List get signBytes => Uint8List.fromList([ + get signBytes => Uint8List.fromList([ 0xd1, ...data.length.pack32(), ...data, diff --git a/lib/src/packet/user_id.dart b/lib/src/packet/user_id.dart index f605a260..21a6d10d 100644 --- a/lib/src/packet/user_id.dart +++ b/lib/src/packet/user_id.dart @@ -34,10 +34,10 @@ class UserIDPacket extends BasePacket implements UserIDPacketInterface { UserIDPacket(utf8.decode(bytes)); @override - Uint8List get data => userID.toBytes(); + get data => userID.toBytes(); @override - Uint8List get signBytes => Uint8List.fromList([ + get signBytes => Uint8List.fromList([ 0xb4, ...data.length.pack32(), ...data, diff --git a/lib/src/type/key.dart b/lib/src/type/key.dart index a9fb1387..043db727 100644 --- a/lib/src/type/key.dart +++ b/lib/src/type/key.dart @@ -46,14 +46,14 @@ abstract interface class KeyInterface implements ArmorableInterface, PacketConta int get keyStrength; /// Get revocation signatures - Iterable get revocationSignatures; + List get revocationSignatures; /// Get direct signatures - Iterable get directSignatures; + List get directSignatures; /// Get users - Iterable get users; + List get users; /// Get subkeys - Iterable get subkeys; + List get subkeys; } From a4e800d965d19cbd56fe8de9125a34b9c13018f4 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 15:10:44 +0700 Subject: [PATCH 122/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/ecdh_session_key_cryptor.dart | 2 +- lib/src/packet/key/elgamal_session_key_cryptor.dart | 2 +- lib/src/packet/key/montgomery_session_key_cryptor.dart | 2 +- lib/src/packet/key/rsa_session_key_cryptor.dart | 2 +- lib/src/packet/public_key_encrypted_session_key.dart | 2 +- lib/src/packet/signature.dart | 6 +++--- lib/src/type/session_key_cryptor.dart | 2 +- lib/src/type/signature_packet.dart | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/src/packet/key/ecdh_session_key_cryptor.dart b/lib/src/packet/key/ecdh_session_key_cryptor.dart index 62c57d9c..a8f8d668 100644 --- a/lib/src/packet/key/ecdh_session_key_cryptor.dart +++ b/lib/src/packet/key/ecdh_session_key_cryptor.dart @@ -177,7 +177,7 @@ class ECDHSessionKeyCryptor extends SessionKeyCryptor { } @override - encode() => Uint8List.fromList([ + toBytes() => Uint8List.fromList([ ...ephemeralKey.bitLength.pack16(), ...ephemeralKey.toUnsignedBytes(), wrappedKey.length, diff --git a/lib/src/packet/key/elgamal_session_key_cryptor.dart b/lib/src/packet/key/elgamal_session_key_cryptor.dart index f5042a3f..6441ae10 100644 --- a/lib/src/packet/key/elgamal_session_key_cryptor.dart +++ b/lib/src/packet/key/elgamal_session_key_cryptor.dart @@ -51,7 +51,7 @@ class ElGamalSessionKeyCryptor extends SessionKeyCryptor { } @override - encode() => Uint8List.fromList([ + toBytes() => Uint8List.fromList([ ...gamma.bitLength.pack16(), ...gamma.toUnsignedBytes(), ...phi.bitLength.pack16(), diff --git a/lib/src/packet/key/montgomery_session_key_cryptor.dart b/lib/src/packet/key/montgomery_session_key_cryptor.dart index 7d1c6c37..0d204644 100644 --- a/lib/src/packet/key/montgomery_session_key_cryptor.dart +++ b/lib/src/packet/key/montgomery_session_key_cryptor.dart @@ -86,7 +86,7 @@ class MontgomerySessionKeyCryptor extends SessionKeyCryptor { } @override - encode() => Uint8List.fromList([ + toBytes() => Uint8List.fromList([ ...ephemeralKey, wrappedKey.length, ...wrappedKey, diff --git a/lib/src/packet/key/rsa_session_key_cryptor.dart b/lib/src/packet/key/rsa_session_key_cryptor.dart index 7f7faa52..1d4a69ce 100644 --- a/lib/src/packet/key/rsa_session_key_cryptor.dart +++ b/lib/src/packet/key/rsa_session_key_cryptor.dart @@ -59,7 +59,7 @@ class RSASessionKeyCryptor extends SessionKeyCryptor { } @override - encode() => Uint8List.fromList([ + toBytes() => Uint8List.fromList([ ...encrypted.bitLength.pack16(), ...encrypted.toUnsignedBytes(), ]); diff --git a/lib/src/packet/public_key_encrypted_session_key.dart b/lib/src/packet/public_key_encrypted_session_key.dart index ae7fbf8c..baeb1c2a 100644 --- a/lib/src/packet/public_key_encrypted_session_key.dart +++ b/lib/src/packet/public_key_encrypted_session_key.dart @@ -167,7 +167,7 @@ class PublicKeyEncryptedSessionKeyPacket extends BasePacket { ...version == 6 ? keyFingerprint : [], ...version == 3 ? keyID : [], keyAlgorithm.value, - ...cryptor.encode(), + ...cryptor.toBytes(), ]); PublicKeyEncryptedSessionKeyPacket decrypt( diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index d9beaeef..200e79a7 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -46,10 +46,10 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { final Uint8List signature; @override - final Iterable hashedSubpackets; + final List hashedSubpackets; @override - final Iterable unhashedSubpackets; + final List unhashedSubpackets; @override final Uint8List signatureData; @@ -364,7 +364,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { } } - static Iterable _readSubpackets(final Uint8List bytes) { + static List _readSubpackets(final Uint8List bytes) { final subpackets = []; var offset = 0; while (offset < bytes.length) { diff --git a/lib/src/type/session_key_cryptor.dart b/lib/src/type/session_key_cryptor.dart index afb4ffbb..b52565aa 100644 --- a/lib/src/type/session_key_cryptor.dart +++ b/lib/src/type/session_key_cryptor.dart @@ -12,7 +12,7 @@ import 'secret_key_material.dart'; /// Author Nguyen Van Nguyen abstract interface class SessionKeyCryptorInterface { /// Serialize session key cryptor to bytes - Uint8List encode(); + Uint8List toBytes(); /// Decrypt session key by using secret key packet Uint8List decrypt(SecretKeyMaterialInterface key); diff --git a/lib/src/type/signature_packet.dart b/lib/src/type/signature_packet.dart index d8614af5..bda72c5e 100644 --- a/lib/src/type/signature_packet.dart +++ b/lib/src/type/signature_packet.dart @@ -29,10 +29,10 @@ abstract interface class SignaturePacketInterface implements PacketInterface { HashAlgorithm get hashAlgorithm; /// Get hashed subpackets - Iterable get hashedSubpackets; + List get hashedSubpackets; /// Get unhashed subpackets - Iterable get unhashedSubpackets; + List get unhashedSubpackets; /// Get signature data Uint8List get signatureData; From 4a9f32582f3b6b1e5d70b7368cdc8a65468901ac Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 15:14:35 +0700 Subject: [PATCH 123/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/session_key.dart | 4 ++-- lib/src/packet/key/session_key_cryptor.dart | 2 +- lib/src/packet/public_key_encrypted_session_key.dart | 4 ++-- lib/src/packet/sym_encrypted_session_key.dart | 2 +- lib/src/type/session_key.dart | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/src/packet/key/session_key.dart b/lib/src/packet/key/session_key.dart index 426f3f7e..d2a73d79 100644 --- a/lib/src/packet/key/session_key.dart +++ b/lib/src/packet/key/session_key.dart @@ -29,7 +29,7 @@ class SessionKey implements SessionKeyInterface { symmetric, ); - factory SessionKey.decode(final Uint8List data) { + factory SessionKey.fromBytes(final Uint8List data) { final sessionKeySymmetric = SymmetricAlgorithm.values.firstWhere( (algo) => algo.value == data[0], ); @@ -42,7 +42,7 @@ class SessionKey implements SessionKeyInterface { } @override - encode() => Uint8List.fromList( + toBytes() => Uint8List.fromList( [symmetric.value, ...encryptionKey], ); diff --git a/lib/src/packet/key/session_key_cryptor.dart b/lib/src/packet/key/session_key_cryptor.dart index 4f927fba..a9c90a1d 100644 --- a/lib/src/packet/key/session_key_cryptor.dart +++ b/lib/src/packet/key/session_key_cryptor.dart @@ -21,7 +21,7 @@ export 'rsa_session_key_cryptor.dart'; /// Author Nguyen Van Nguyen abstract class SessionKeyCryptor implements SessionKeyCryptorInterface { static SessionKeyInterface decodeSessionKey(final Uint8List data) { - return SessionKey.decode(data); + return SessionKey.fromBytes(data); } static Uint8List processInBlocks( diff --git a/lib/src/packet/public_key_encrypted_session_key.dart b/lib/src/packet/public_key_encrypted_session_key.dart index baeb1c2a..5f237e74 100644 --- a/lib/src/packet/public_key_encrypted_session_key.dart +++ b/lib/src/packet/public_key_encrypted_session_key.dart @@ -122,7 +122,7 @@ class PublicKeyEncryptedSessionKeyPacket extends BasePacket { KeyAlgorithm.x25519 || KeyAlgorithm.x448 => sessionKey.encryptionKey, _ => version == 3 ? Uint8List.fromList([ - ...sessionKey.encode(), + ...sessionKey.toBytes(), ...sessionKey.computeChecksum(), ]) : Uint8List.fromList([ @@ -187,7 +187,7 @@ class PublicKeyEncryptedSessionKeyPacket extends BasePacket { final keyData = cryptor.decrypt(key.secretKeyMaterial!); final SessionKeyInterface sessionKey; if (version == 3) { - sessionKey = SessionKey.decode(keyData); + sessionKey = SessionKey.fromBytes(keyData); } else { switch (keyAlgorithm) { case KeyAlgorithm.x25519: diff --git a/lib/src/packet/sym_encrypted_session_key.dart b/lib/src/packet/sym_encrypted_session_key.dart index 02aca4b3..262b0ab6 100644 --- a/lib/src/packet/sym_encrypted_session_key.dart +++ b/lib/src/packet/sym_encrypted_session_key.dart @@ -173,7 +173,7 @@ class SymEncryptedSessionKeyPacket extends BasePacket { ), ); iv = Uint8List(0); - encrypted = cipher.process(sessionKey.encode()); + encrypted = cipher.process(sessionKey.toBytes()); } } else { iv = Uint8List(0); diff --git a/lib/src/type/session_key.dart b/lib/src/type/session_key.dart index abd7c85b..4f412558 100644 --- a/lib/src/type/session_key.dart +++ b/lib/src/type/session_key.dart @@ -24,5 +24,5 @@ abstract interface class SessionKeyInterface { Uint8List computeChecksum(); /// Serialize session key to bytes - Uint8List encode(); + Uint8List toBytes(); } From bbe6b3c38ff9cf814ff8a63f6065267b75c51644 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 17:22:20 +0700 Subject: [PATCH 124/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/private_key.dart | 121 ++++++++++++++++++++-- lib/src/packet/public_key.dart | 30 ++++-- lib/src/packet/secret_key.dart | 6 +- lib/src/packet/secret_subkey.dart | 5 +- lib/src/packet/signature.dart | 160 ++++++++++++++++++++++++++++-- 5 files changed, 289 insertions(+), 33 deletions(-) diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index 7675b965..0ce07954 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -8,7 +8,11 @@ import 'package:dart_pg/src/common/armor.dart'; import 'package:dart_pg/src/common/config.dart'; import 'package:dart_pg/src/enum/aead_algorithm.dart'; import 'package:dart_pg/src/enum/armor_type.dart'; +import 'package:dart_pg/src/enum/ecc.dart'; +import 'package:dart_pg/src/enum/key_algorithm.dart'; +import 'package:dart_pg/src/enum/key_type.dart'; import 'package:dart_pg/src/enum/key_version.dart'; +import 'package:dart_pg/src/enum/rsa_key_size.dart'; import 'package:dart_pg/src/key/base.dart'; import 'package:dart_pg/src/key/public_key.dart'; import 'package:dart_pg/src/key/subkey.dart'; @@ -21,6 +25,114 @@ import 'package:dart_pg/src/type/secret_key_packet.dart'; final class PrivateKey extends BaseKey implements PrivateKeyInterface { PrivateKey(super.packetList); + /// Reads an armored OpenPGP private key and returns a PrivateKey object + factory PrivateKey.fromArmored(final String armored) { + final armor = Armor.decode(armored); + if (armor.type != ArmorType.privateKey) { + throw ArgumentError('Armored text not of private key type'); + } + return PrivateKey(PacketList.decode(armor.data)); + } + + /// Generate a new OpenPGP key pair. Support RSA, ECC, Curve25519 and Curve448 key types. + /// The generated primary key will have signing capabilities. + /// One subkey with encryption capabilities is also generated if `signOnly` is false. + factory PrivateKey.generate( + final Iterable userIDs, + final String passphrase, { + final KeyType type = KeyType.rsa, + final RSAKeySize rsaKeySize = RSAKeySize.high, + final Ecc curve = Ecc.secp521r1, + final int keyExpiry = 0, + final bool signOnly = false, + final DateTime? time, + }) { + if (userIDs.isEmpty || passphrase.isEmpty) { + throw ArgumentError( + 'UserIDs and passphrase are required for key generation', + ); + } + if (type == KeyType.ecc && (curve == Ecc.ed25519 || curve == Ecc.curve25519)) { + throw UnsupportedError( + 'Legacy curve ${curve.name} is unsupported for key generation', + ); + } + final KeyAlgorithm keyAlgorithm = switch (type) { + KeyType.rsa => KeyAlgorithm.rsaEncryptSign, + KeyType.ecc => KeyAlgorithm.ecdsa, + KeyType.curve25519 => KeyAlgorithm.ed25519, + KeyType.curve448 => KeyAlgorithm.ed448, + }; + + final secretKey = SecretKeyPacket.generate( + keyAlgorithm, + rsaKeySize: rsaKeySize, + curve: curve, + time: time, + ); + final v6Key = secretKey.keyVersion == KeyVersion.v6.value; + final aead = v6Key && Config.aeadProtect ? Config.preferredAead : null; + final packets = [ + secretKey.encrypt( + passphrase, + Config.preferredSymmetric, + aead, + ), + ]; + if (v6Key) { + /// Wrap secret key with direct key signature + packets.add(SignaturePacket.createDirectKeySignature( + secretKey, + keyExpiry: keyExpiry, + time: time, + )); + } + + /// Wrap user id with certificate signature + var index = 0; + for (final userID in userIDs) { + final packet = UserIDPacket(userID); + packets.addAll([ + packet, + SignaturePacket.createSelfCertificate( + secretKey, + packet, + isPrimaryUser: index == 0, + keyExpiry: keyExpiry, + time: time, + ), + ]); + index++; + } + + if (signOnly) { + /// Generate & Wrap secret subkey with binding signature + final KeyAlgorithm subkeyAlgorithm = switch (type) { + KeyType.rsa => KeyAlgorithm.rsaEncryptSign, + KeyType.ecc => KeyAlgorithm.ecdh, + KeyType.curve25519 => KeyAlgorithm.x25519, + KeyType.curve448 => KeyAlgorithm.x448, + }; + final secretSubkey = SecretSubkeyPacket.generate( + subkeyAlgorithm, + rsaKeySize: rsaKeySize, + curve: curve, + time: time, + ).encrypt(passphrase, Config.preferredSymmetric, aead); + packets.addAll([ + secretSubkey, + SignaturePacket.createSubkeyBinding( + secretKey, + secretSubkey, + keyExpiry: keyExpiry, + time: time, + ), + ]); + } + + return PrivateKey(PacketList(packets)); + } + @override get secretKeyPacket => super.keyPacket as SecretKeyPacketInterface; @@ -58,15 +170,6 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { return PublicKey(PacketList(packets)); } - /// Reads an armored OpenPGP private key and returns a PrivateKey object - factory PrivateKey.fromArmored(final String armored) { - final armor = Armor.decode(armored); - if (armor.type != ArmorType.privateKey) { - throw ArgumentError('Armored text not of private key type'); - } - return PrivateKey(PacketList.decode(armor.data)); - } - @override armor() => Armor.encode(ArmorType.privateKey, packetList.encode()); diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index 849ecf7c..31c9e1ce 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -7,6 +7,7 @@ library; import 'dart:typed_data'; import '../common/helpers.dart'; +import '../enum/ecc.dart'; import '../enum/eddsa_curve.dart'; import '../enum/hash_algorithm.dart'; import '../enum/key_version.dart'; @@ -46,12 +47,7 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { this.keyMaterial, { this.keyAlgorithm = KeyAlgorithm.rsaEncryptSign, }) : super(PacketType.publicKey) { - if (keyVersion != KeyVersion.v4.value && keyVersion != KeyVersion.v6.value) { - throw UnsupportedError( - 'Version $keyVersion of the key packet is unsupported.', - ); - } - _assertKeyStrength(); + _assertKey(); _calculateFingerprint(); } @@ -140,7 +136,27 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { bool get isV6Key => keyVersion == KeyVersion.v6.value; - void _assertKeyStrength() { + void _assertKey() { + if (keyVersion != KeyVersion.v4.value && keyVersion != KeyVersion.v6.value) { + throw UnsupportedError( + 'Version $keyVersion of the key packet is unsupported.', + ); + } + if (isV6Key) { + if (keyMaterial is ECPublicMaterial) { + final curve = (keyMaterial as ECPublicMaterial).curve; + if (curve == Ecc.ed25519 || curve == Ecc.curve25519) { + throw ArgumentError( + 'Legacy curve ${curve.name} cannot be used with v$keyVersion key packet.', + ); + } + } + if (keyAlgorithm == KeyAlgorithm.dsa || keyAlgorithm == KeyAlgorithm.elgamal) { + throw ArgumentError( + 'Key algorithm ${keyAlgorithm.name} cannot be used with v$keyVersion key packet.', + ); + } + } switch (keyAlgorithm) { case KeyAlgorithm.rsaEncryptSign: case KeyAlgorithm.rsaEncrypt: diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 020ac614..3679c88b 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -84,19 +84,18 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { final KeyAlgorithm algorithm, { final RSAKeySize rsaKeySize = RSAKeySize.normal, final Ecc curve = Ecc.secp521r1, - final DateTime? date, + final DateTime? time, }) { final keyMaterial = generateKeyMaterial( algorithm, rsaKeySize: rsaKeySize, curve: curve, - date: date, ); return SecretKeyPacket( PublicKeyPacket( algorithm.keyVersion, - date ?? DateTime.now(), + time ?? DateTime.now(), keyMaterial.publicMaterial, keyAlgorithm: algorithm, ), @@ -109,7 +108,6 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { final KeyAlgorithm algorithm, { final RSAKeySize rsaKeySize = RSAKeySize.normal, final Ecc curve = Ecc.secp521r1, - final DateTime? date, }) { return switch (algorithm) { KeyAlgorithm.rsaEncryptSign || diff --git a/lib/src/packet/secret_subkey.dart b/lib/src/packet/secret_subkey.dart index 05cbd0f1..6f7868d9 100644 --- a/lib/src/packet/secret_subkey.dart +++ b/lib/src/packet/secret_subkey.dart @@ -57,18 +57,17 @@ class SecretSubkeyPacket extends SecretKeyPacket implements SubkeyPacketInterfac final KeyAlgorithm algorithm, { final RSAKeySize rsaKeySize = RSAKeySize.normal, final Ecc curve = Ecc.secp521r1, - final DateTime? date, + final DateTime? time, }) { final keyMaterial = SecretKeyPacket.generateKeyMaterial( algorithm, rsaKeySize: rsaKeySize, curve: curve, - date: date, ); return SecretSubkeyPacket( PublicSubkeyPacket( algorithm.keyVersion, - date ?? DateTime.now(), + time ?? DateTime.now(), keyMaterial.publicMaterial, keyAlgorithm: algorithm, ), diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 200e79a7..39124e30 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -6,6 +6,15 @@ library; import 'dart:typed_data'; +import 'package:dart_pg/src/enum/aead_algorithm.dart'; +import 'package:dart_pg/src/enum/compression_algorithm.dart'; +import 'package:dart_pg/src/enum/key_flag.dart'; +import 'package:dart_pg/src/enum/key_version.dart'; +import 'package:dart_pg/src/enum/support_feature.dart'; +import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; +import 'package:dart_pg/src/type/subkey_packet.dart'; +import 'package:dart_pg/src/type/user_id_packet.dart'; + import '../common/helpers.dart'; import '../enum/hash_algorithm.dart'; import '../enum/key_algorithm.dart'; @@ -167,14 +176,14 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { ); } + /// Create signature factory SignaturePacket.createSignature( SecretKeyPacketInterface signKey, SignatureType signatureType, Uint8List dataToSign, { final HashAlgorithm? preferredHash, final Iterable subpackets = const [], - final int keyExpirationTime = 0, - final DateTime? date, + final DateTime? time, }) { final version = signKey.keyVersion; final keyAlgorithm = signKey.keyAlgorithm; @@ -182,7 +191,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { Helper.assertHash(hashAlgorithm); final hashedSubpackets = [ - SignatureCreationTime.fromTime(date ?? DateTime.now()), + SignatureCreationTime.fromTime(time ?? DateTime.now()), IssuerFingerprint.fromKey(signKey), IssuerKeyID(signKey.keyID), ...subpackets, @@ -190,9 +199,6 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { if (version == 4) { hashedSubpackets.add(NotationData.saltNotation(hashAlgorithm.saltSize)); } - if (keyExpirationTime > 0) { - hashedSubpackets.add(KeyExpirationTime.fromTime(keyExpirationTime)); - } final salt = version == 6 ? Helper.randomBytes( hashAlgorithm.saltSize, @@ -229,6 +235,101 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { ); } + /// Create direct key signature + factory SignaturePacket.createDirectKeySignature( + SecretKeyPacketInterface signKey, { + int keyExpiry = 0, + DateTime? time, + }) { + final subpackets = _keySubpackets(signKey.keyVersion); + if (keyExpiry > 0) { + subpackets.add(KeyExpirationTime.fromTime(keyExpiry)); + } + return SignaturePacket.createSignature( + signKey, + SignatureType.directKey, + signKey.signBytes, + subpackets: subpackets, + time: time, + ); + } + + /// Create self signature + factory SignaturePacket.createSelfCertificate( + SecretKeyPacketInterface signKey, + UserIDPacketInterface userID, { + bool isPrimaryUser = false, + int keyExpiry = 0, + DateTime? time, + }) { + final subpackets = signKey.keyVersion == KeyVersion.v4.value + ? _keySubpackets( + signKey.keyVersion, + ) + : []; + if (isPrimaryUser) { + subpackets.add(PrimaryUserID(Uint8List.fromList([1]))); + } + if (keyExpiry > 0) { + subpackets.add(KeyExpirationTime.fromTime(keyExpiry)); + } + return SignaturePacket.createSignature( + signKey, + SignatureType.certGeneric, + userID.signBytes, + subpackets: subpackets, + time: time, + ); + } + + /// Create subkey binding signature + factory SignaturePacket.createSubkeyBinding( + SecretKeyPacketInterface signKey, + SubkeyPacketInterface subkey, { + int keyExpiry = 0, + bool forSigning = false, + DateTime? time, + }) { + final subpackets = []; + if (keyExpiry > 0) { + subpackets.add(KeyExpirationTime.fromTime(keyExpiry)); + } + if (forSigning) { + subpackets.add(KeyFlags.fromFlags( + KeyFlag.signData.value, + )); + if (subkey is SecretKeyPacketInterface) { + subpackets.add(EmbeddedSignature.fromSignature( + SignaturePacket.createSignature( + subkey as SecretKeyPacketInterface, + SignatureType.keyBinding, + Uint8List.fromList([ + ...signKey.signBytes, + ...subkey.signBytes, + ]), + time: time, + ), + )); + } + } else { + subpackets.add( + KeyFlags.fromFlags( + KeyFlag.encryptCommunication.value | KeyFlag.encryptStorage.value, + ), + ); + } + return SignaturePacket.createSignature( + signKey, + SignatureType.subkeyBinding, + Uint8List.fromList([ + ...signKey.signBytes, + ...subkey.signBytes, + ]), + subpackets: subpackets, + time: time, + ); + } + @override get data => Uint8List.fromList([ ...signatureData, @@ -240,8 +341,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { ]); @override - get creationTime => - getSubpacket()?.creationTime ?? DateTime.fromMillisecondsSinceEpoch(0); + get creationTime => getSubpacket()?.creationTime ?? DateTime.fromMillisecondsSinceEpoch(0); @override get expirationTime => getSubpacket()?.expirationTime; @@ -278,8 +378,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { get isSubkeyRevocation => signatureType == SignatureType.subkeyRevocation; @override - get issuerFingerprint => - getSubpacket()?.fingerprint ?? Uint8List(version == 6 ? 32 : 20); + get issuerFingerprint => getSubpacket()?.fingerprint ?? Uint8List(version == 6 ? 32 : 20); @override get issuerKeyID { @@ -349,6 +448,47 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { } } + static List _keySubpackets(final int version) { + final symmetrics = Uint8List.fromList([ + SymmetricAlgorithm.aes128.value, + SymmetricAlgorithm.aes256.value, + ]); + final aeads = Uint8List.fromList([ + ...AeadAlgorithm.values.map((algo) => algo.value), + ]); + final subpackets = [ + KeyFlags.fromFlags(KeyFlag.certifyKeys.value | KeyFlag.signData.value), + PreferredSymmetricAlgorithms(symmetrics), + PreferredAeadAlgorithms(aeads), + PreferredHashAlgorithms(Uint8List.fromList([ + HashAlgorithm.sha256.value, + HashAlgorithm.sha3_256.value, + HashAlgorithm.sha512.value, + HashAlgorithm.sha3_512.value, + ])), + PreferredCompressionAlgorithms(Uint8List.fromList([ + CompressionAlgorithm.uncompressed.value, + CompressionAlgorithm.zip.value, + CompressionAlgorithm.zlib.value, + ])), + Features.fromFeatures( + SupportFeature.version1SEIPD.value | SupportFeature.aeadEncrypted.value | SupportFeature.version2SEIPD.value, + ), + ]; + if (version == KeyVersion.v6.value) { + subpackets.add(PreferredAeadCiphers(Uint8List.fromList(aeads + .map((aead) => [ + symmetrics[0], + aead, + symmetrics[1], + aead, + ]) + .expand((ciphers) => ciphers) + .toList()))); + } + return subpackets; + } + static Uint8List _signMessage( final SecretKeyPacketInterface key, final HashAlgorithm hash, From 5f13423897578d7b61a3d623079f3e8abf62ffc3 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 17:28:50 +0700 Subject: [PATCH 125/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/private_key.dart | 5 ++--- lib/src/packet/public_key.dart | 5 +++-- lib/src/packet/secret_key.dart | 3 +++ lib/src/type/key_packet.dart | 3 +++ 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index 0ce07954..6870d828 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -70,8 +70,7 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { curve: curve, time: time, ); - final v6Key = secretKey.keyVersion == KeyVersion.v6.value; - final aead = v6Key && Config.aeadProtect ? Config.preferredAead : null; + final aead = secretKey.isV6Key && Config.aeadProtect ? Config.preferredAead : null; final packets = [ secretKey.encrypt( passphrase, @@ -79,7 +78,7 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { aead, ), ]; - if (v6Key) { + if (secretKey.isV6Key) { /// Wrap secret key with direct key signature packets.add(SignaturePacket.createDirectKeySignature( secretKey, diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index 31c9e1ce..aabae084 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -121,6 +121,9 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { @override get isSubkey => this is SubkeyPacketInterface; + @override + bool get isV6Key => keyVersion == KeyVersion.v6.value; + @override get keyID => _keyID; @@ -134,8 +137,6 @@ class PublicKeyPacket extends BasePacket implements KeyPacketInterface { ...data, ]); - bool get isV6Key => keyVersion == KeyVersion.v6.value; - void _assertKey() { if (keyVersion != KeyVersion.v4.value && keyVersion != KeyVersion.v6.value) { throw UnsupportedError( diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 3679c88b..8fec133e 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -442,6 +442,9 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { @override get isSubkey => this is SubkeyPacketInterface; + + @override + bool get isV6Key => publicKey.isV6Key; @override get keyAlgorithm => publicKey.keyAlgorithm; diff --git a/lib/src/type/key_packet.dart b/lib/src/type/key_packet.dart index cf77da5f..fc8f6e97 100644 --- a/lib/src/type/key_packet.dart +++ b/lib/src/type/key_packet.dart @@ -40,6 +40,9 @@ abstract interface class KeyPacketInterface implements PacketInterface { /// Is encryption key bool get isEncryptionKey; + /// Is version 6 key + bool get isV6Key; + /// Get bytes for sign Uint8List get signBytes; From ef48e3206ff1f2db8e74836f3acee191e1232a14 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 20:08:45 +0700 Subject: [PATCH 126/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/private_key.dart | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index 6870d828..e208a789 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -6,12 +6,10 @@ library; import 'package:dart_pg/src/common/armor.dart'; import 'package:dart_pg/src/common/config.dart'; -import 'package:dart_pg/src/enum/aead_algorithm.dart'; import 'package:dart_pg/src/enum/armor_type.dart'; import 'package:dart_pg/src/enum/ecc.dart'; import 'package:dart_pg/src/enum/key_algorithm.dart'; import 'package:dart_pg/src/enum/key_type.dart'; -import 'package:dart_pg/src/enum/key_version.dart'; import 'package:dart_pg/src/enum/rsa_key_size.dart'; import 'package:dart_pg/src/key/base.dart'; import 'package:dart_pg/src/key/public_key.dart'; @@ -184,10 +182,7 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { throw StateError('Private key must be decrypted before encrypting.'); } - AeadAlgorithm? aead; - if (version == KeyVersion.v6.value && Config.aeadProtect) { - aead = Config.preferredAead; - } + final aead = keyPacket.isV6Key && Config.aeadProtect ? Config.preferredAead : null; final subkeys = this.subkeys.map((subkey) { final index = this.subkeys.indexOf(subkey); final subkeyPass = subkeyPassphrases.elementAtOrNull(index) ?? passphrase; From b6d9eb184c0b9a44f38e8c3904267d5281a9009d Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 20:34:44 +0700 Subject: [PATCH 127/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/signature.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 39124e30..16839e34 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -276,7 +276,10 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { return SignaturePacket.createSignature( signKey, SignatureType.certGeneric, - userID.signBytes, + Uint8List.fromList([ + ...signKey.signBytes, + ...userID.signBytes, + ]), subpackets: subpackets, time: time, ); From 3b4e2993908633ce16f396cebbb8da8a9c955cbb Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 21:03:24 +0700 Subject: [PATCH 128/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/helpers.dart | 6 ++++++ lib/src/key/base.dart | 11 +++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/src/common/helpers.dart b/lib/src/common/helpers.dart index f54ebe49..3b80ae98 100644 --- a/lib/src/common/helpers.dart +++ b/lib/src/common/helpers.dart @@ -81,6 +81,12 @@ final class Helper { return k; } + static int randomInt( + final int min, + final int max, + ) => + _random.nextInt(max - min) + min; + static S2kInterface stringToKey(final S2kType type) { assert(type != S2kType.simple); return switch (type) { diff --git a/lib/src/key/base.dart b/lib/src/key/base.dart index 4bad136d..13b13144 100644 --- a/lib/src/key/base.dart +++ b/lib/src/key/base.dart @@ -5,6 +5,7 @@ library; import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/packet/base.dart'; import 'package:dart_pg/src/packet/packet_list.dart'; import 'package:dart_pg/src/type/key.dart'; import 'package:dart_pg/src/type/key_packet.dart'; @@ -77,6 +78,16 @@ abstract class BaseKey implements KeyInterface { ...directSignatures, ...users.map((user) => user.packetList).expand((packet) => packet), ...subkeys.map((subkey) => subkey.packetList).expand((packet) => packet), + ...keyPacket.isV6Key + ? [ + PaddingPacket.createPadding( + Helper.randomInt( + PaddingPacket.paddingMin, + PaddingPacket.paddingMax, + ), + ) + ] + : [], ]); _readPacketList(final PacketListInterface packetList) { From fd033676d8c02a38371eef37e6fc4cf422aadebc Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Wed, 4 Dec 2024 21:22:46 +0700 Subject: [PATCH 129/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/private_key.dart | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index e208a789..ab6d157c 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -50,14 +50,14 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { 'UserIDs and passphrase are required for key generation', ); } - if (type == KeyType.ecc && (curve == Ecc.ed25519 || curve == Ecc.curve25519)) { + if (type == KeyType.ecc && curve == Ecc.curve25519) { throw UnsupportedError( - 'Legacy curve ${curve.name} is unsupported for key generation', + 'Ecc curve ${curve.name} is unsupported for key generation', ); } final KeyAlgorithm keyAlgorithm = switch (type) { KeyType.rsa => KeyAlgorithm.rsaEncryptSign, - KeyType.ecc => KeyAlgorithm.ecdsa, + KeyType.ecc => curve == Ecc.ed25519 ? KeyAlgorithm.eddsaLegacy : KeyAlgorithm.ecdsa, KeyType.curve25519 => KeyAlgorithm.ed25519, KeyType.curve448 => KeyAlgorithm.ed448, }; @@ -110,10 +110,11 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { KeyType.curve25519 => KeyAlgorithm.x25519, KeyType.curve448 => KeyAlgorithm.x448, }; + final subkeyCurve = keyAlgorithm == KeyAlgorithm.eddsaLegacy ? Ecc.curve25519 : curve; final secretSubkey = SecretSubkeyPacket.generate( subkeyAlgorithm, rsaKeySize: rsaKeySize, - curve: curve, + curve: subkeyCurve, time: time, ).encrypt(passphrase, Config.preferredSymmetric, aead); packets.addAll([ From 341f0390b06f4e4e40fc665fdb1d0132cc9f229d Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 09:09:41 +0700 Subject: [PATCH 130/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/private_key.dart | 2 +- test/key/key_generation_test.dart | 165 ++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 test/key/key_generation_test.dart diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index ab6d157c..a6a28148 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -102,7 +102,7 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { index++; } - if (signOnly) { + if (!signOnly) { /// Generate & Wrap secret subkey with binding signature final KeyAlgorithm subkeyAlgorithm = switch (type) { KeyType.rsa => KeyAlgorithm.rsaEncryptSign, diff --git a/test/key/key_generation_test.dart b/test/key/key_generation_test.dart new file mode 100644 index 00000000..376bcad5 --- /dev/null +++ b/test/key/key_generation_test.dart @@ -0,0 +1,165 @@ +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/ecc.dart'; +import 'package:dart_pg/src/enum/key_algorithm.dart'; +import 'package:dart_pg/src/enum/key_type.dart'; +import 'package:dart_pg/src/enum/key_version.dart'; +import 'package:dart_pg/src/enum/rsa_key_size.dart'; +import 'package:dart_pg/src/key/private_key.dart'; +import 'package:test/test.dart'; + +void main() { + group('Key generation', () { + const userID = 'Dart Privacy Guard '; + final passphrase = Helper.generatePassword(); + + test('RSA key', () { + final privateKey = PrivateKey.generate( + [userID], + passphrase, + type: KeyType.rsa, + ); + expect(privateKey.version, KeyVersion.v4.value); + expect(privateKey.keyAlgorithm, KeyAlgorithm.rsaEncryptSign); + expect(privateKey.keyStrength, RSAKeySize.high.bits); + expect(privateKey.users[0].userID, userID); + + final priKey = PrivateKey.fromArmored( + privateKey.armor(), + ).decrypt(passphrase); + expect(priKey.fingerprint, privateKey.fingerprint); + expect(priKey.users[0].userID, userID); + expect( + priKey.subkeys[0].fingerprint, + privateKey.subkeys[0].fingerprint, + ); + }); + + test('ECDSA NIST P-384 key', () { + final privateKey = PrivateKey.generate( + [userID], + passphrase, + type: KeyType.ecc, + curve: Ecc.secp384r1, + ); + final subkey = privateKey.subkeys[0]; + expect(privateKey.version, KeyVersion.v4.value); + expect(privateKey.keyAlgorithm, KeyAlgorithm.ecdsa); + expect(privateKey.keyStrength, 384); + expect(privateKey.users[0].userID, userID); + expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(subkey.keyStrength, 384); + + final priKey = PrivateKey.fromArmored( + privateKey.armor(), + ).decrypt(passphrase); + expect(priKey.fingerprint, privateKey.fingerprint); + expect(priKey.users[0].userID, userID); + expect( + priKey.subkeys[0].fingerprint, + subkey.fingerprint, + ); + }); + + test('ECDSA Brainpool P-512 key', () { + final privateKey = PrivateKey.generate( + [userID], + passphrase, + type: KeyType.ecc, + curve: Ecc.brainpoolP512r1, + ); + final subkey = privateKey.subkeys[0]; + expect(privateKey.version, KeyVersion.v4.value); + expect(privateKey.keyAlgorithm, KeyAlgorithm.ecdsa); + expect(privateKey.keyStrength, 512); + expect(privateKey.users[0].userID, userID); + expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(subkey.keyStrength, 512); + + final priKey = PrivateKey.fromArmored( + privateKey.armor(), + ).decrypt(passphrase); + expect(priKey.fingerprint, privateKey.fingerprint); + expect(priKey.users[0].userID, userID); + expect( + priKey.subkeys[0].fingerprint, + subkey.fingerprint, + ); + }); + + test('EdDSA legacy key', () { + final privateKey = PrivateKey.generate( + [userID], + passphrase, + type: KeyType.ecc, + curve: Ecc.ed25519, + ); + final subkey = privateKey.subkeys[0]; + expect(privateKey.version, KeyVersion.v4.value); + expect(privateKey.keyAlgorithm, KeyAlgorithm.eddsaLegacy); + expect(privateKey.keyStrength, 255); + expect(privateKey.users[0].userID, userID); + expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); + expect(subkey.keyStrength, 255); + + final priKey = PrivateKey.fromArmored( + privateKey.armor(), + ).decrypt(passphrase); + expect(priKey.fingerprint, privateKey.fingerprint); + expect(priKey.users[0].userID, userID); + expect( + priKey.subkeys[0].fingerprint, + subkey.fingerprint, + ); + }); + + test('RFC9580 Curve 25519 key', () { + final privateKey = PrivateKey.generate( + [userID], + passphrase, + type: KeyType.curve25519, + ); + final subkey = privateKey.subkeys[0]; + expect(privateKey.version, KeyVersion.v6.value); + expect(privateKey.keyAlgorithm, KeyAlgorithm.ed25519); + expect(privateKey.keyStrength, 255); + expect(privateKey.users[0].userID, userID); + expect(subkey.keyAlgorithm, KeyAlgorithm.x25519); + expect(subkey.keyStrength, 255); + + final priKey = PrivateKey.fromArmored( + privateKey.armor(), + ).decrypt(passphrase); + expect(priKey.fingerprint, privateKey.fingerprint); + expect(priKey.users[0].userID, userID); + expect( + priKey.subkeys[0].fingerprint, + subkey.fingerprint, + ); + }); + + test('RFC9580 Curve 448 key', () { + final privateKey = PrivateKey.generate( + [userID], + passphrase, + type: KeyType.curve448, + ); + final subkey = privateKey.subkeys[0]; + expect(privateKey.version, KeyVersion.v6.value); + expect(privateKey.keyAlgorithm, KeyAlgorithm.ed448); + expect(privateKey.keyStrength, 448); + expect(privateKey.users[0].userID, userID); + expect(subkey.keyAlgorithm, KeyAlgorithm.x448); + expect(subkey.keyStrength, 448); + + final priKey = PrivateKey.fromArmored( + privateKey.armor(), + ).decrypt(passphrase); + expect(priKey.fingerprint, privateKey.fingerprint); + expect(priKey.users[0].userID, userID); + expect( + priKey.subkeys[0].fingerprint, + subkey.fingerprint, + ); + }); + }); +} From 2a0df87135c9f39768ad9abdf64904dcc89e4e02 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 09:58:25 +0700 Subject: [PATCH 131/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base.dart | 55 ++++++++++++++++++++++++++++++++++++++++ lib/src/key/subkey.dart | 6 ++--- lib/src/key/user.dart | 32 +++++++++++++++++++++-- lib/src/type/key.dart | 18 +++++++++++++ lib/src/type/subkey.dart | 2 +- lib/src/type/user.dart | 7 +++++ 6 files changed, 113 insertions(+), 7 deletions(-) diff --git a/lib/src/key/base.dart b/lib/src/key/base.dart index 13b13144..ea48de79 100644 --- a/lib/src/key/base.dart +++ b/lib/src/key/base.dart @@ -90,6 +90,61 @@ abstract class BaseKey implements KeyInterface { : [], ]); + @override + isRevoked({ + KeyInterface? verifyKey, + SignaturePacketInterface? certificate, + DateTime? time, + }) { + if (revocationSignatures.isNotEmpty) { + final keyID = certificate?.issuerKeyID; + final keyPacket = verifyKey?.publicKey.keyPacket ?? publicKey.keyPacket; + for (final signature in revocationSignatures) { + if (keyID == null || signature.issuerKeyID.equals(keyID)) { + if (signature.verify(keyPacket, keyPacket.signBytes, time)) { + return true; + } + } + } + } + return false; + } + + @override + isCertified({ + final KeyInterface? verifyKey, + final SignaturePacketInterface? certificate, + final DateTime? time, + }) { + for (var user in users) { + if (user.isPrimary) { + return user.isCertified( + verifyKey: verifyKey, + certificate: certificate, + time: time, + ); + } + } + return false; + } + + @override + verify([final String userID = '', final DateTime? time]) { + for (final signature in directSignatures) { + if (!signature.verify(publicKey.keyPacket, keyPacket.signBytes, time)) { + return false; + } + } + for (var user in users) { + if (userID.isEmpty || user.userID == userID) { + if (!user.verify(time)) { + return false; + } + } + } + return true; + } + _readPacketList(final PacketListInterface packetList) { final keyPacketList = packetList.takeWhile( (packet) => packet is KeyPacketInterface, diff --git a/lib/src/key/subkey.dart b/lib/src/key/subkey.dart index 5c2a0d8e..c497e54b 100644 --- a/lib/src/key/subkey.dart +++ b/lib/src/key/subkey.dart @@ -91,9 +91,7 @@ final class Subkey implements SubkeyInterface { get version => keyPacket.keyVersion; @override - isRevoked([ - final DateTime? time, - ]) { + isRevoked([final DateTime? time]) { for (final revocation in revocationSignatures) { if (revocation.verify( mainKey.keyPacket, @@ -110,7 +108,7 @@ final class Subkey implements SubkeyInterface { } @override - verify([DateTime? time]) { + verify([final DateTime? time]) { for (final signature in bindingSignatures) { if (signature.verify( mainKey.keyPacket, diff --git a/lib/src/key/user.dart b/lib/src/key/user.dart index 3bd3bcbc..06355b1a 100644 --- a/lib/src/key/user.dart +++ b/lib/src/key/user.dart @@ -6,6 +6,7 @@ library; import 'dart:typed_data'; +import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/packet/base.dart'; import 'package:dart_pg/src/packet/packet_list.dart'; import 'package:dart_pg/src/type/key.dart'; @@ -67,7 +68,7 @@ final class User implements UserInterface { get userID => (userIDPacket is UserIDPacket) ? (userIDPacket as UserIDPacket).userID : ""; @override - isRevoked([DateTime? time]) { + isRevoked([final DateTime? time]) { for (final revocation in revocationSignatures) { if (revocation.verify( mainKey.keyPacket, @@ -84,7 +85,34 @@ final class User implements UserInterface { } @override - verify([DateTime? time]) { + isCertified({ + final KeyInterface? verifyKey, + final SignaturePacketInterface? certificate, + final DateTime? time, + }) { + if (otherSignatures.isNotEmpty) { + final keyID = certificate?.issuerKeyID; + final keyPacket = verifyKey?.publicKey.keyPacket ?? mainKey.keyPacket; + for (final signature in otherSignatures) { + if (keyID == null || signature.issuerKeyID.equals(keyID)) { + if (signature.verify( + keyPacket, + Uint8List.fromList([ + ...mainKey.keyPacket.signBytes, + ...userIDPacket.signBytes, + ]), + time, + )) { + return true; + } + } + } + } + return false; + } + + @override + verify([final DateTime? time]) { for (final signature in selfSignatures) { if (signature.verify( mainKey.keyPacket, diff --git a/lib/src/type/key.dart b/lib/src/type/key.dart index 043db727..4d1825e8 100644 --- a/lib/src/type/key.dart +++ b/lib/src/type/key.dart @@ -56,4 +56,22 @@ abstract interface class KeyInterface implements ArmorableInterface, PacketConta /// Get subkeys List get subkeys; + + /// Check if the key is revoked + bool isRevoked({ + final KeyInterface? verifyKey, + final SignaturePacketInterface? certificate, + final DateTime? time, + }); + + /// Check if the key is certified + bool isCertified({ + final KeyInterface? verifyKey, + final SignaturePacketInterface? certificate, + final DateTime? time, + }); + + /// Verify the key. + /// Check for existence and validity of direct & user signature. + bool verify([final String userID = '', final DateTime? time]); } diff --git a/lib/src/type/subkey.dart b/lib/src/type/subkey.dart index 549bfbd4..ea0bf70f 100644 --- a/lib/src/type/subkey.dart +++ b/lib/src/type/subkey.dart @@ -55,7 +55,7 @@ abstract interface class SubkeyInterface implements PacketContainerInterface { /// Get binding signatures List get bindingSignatures; - /// Check if a subkey is revoked + /// Check if the subkey is revoked bool isRevoked([final DateTime? time]); /// Verify subkey. diff --git a/lib/src/type/user.dart b/lib/src/type/user.dart index 2f6be9b5..ef6a6747 100644 --- a/lib/src/type/user.dart +++ b/lib/src/type/user.dart @@ -37,6 +37,13 @@ abstract interface class UserInterface implements PacketContainerInterface { /// Check if a given certificate of the user is revoked bool isRevoked([final DateTime? time]); + /// Check if the key is certified + bool isCertified({ + final KeyInterface? verifyKey, + final SignaturePacketInterface? certificate, + final DateTime? time, + }); + /// Verify user. /// Check for existence and validity of self signature. bool verify([final DateTime? time]); From 992e2416d2e3b90d47024e46bd92114276309f8f Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 10:08:12 +0700 Subject: [PATCH 132/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base.dart | 4 ++-- lib/src/key/subkey.dart | 14 +++++++------- lib/src/key/user.dart | 14 +++++++------- lib/src/packet/public_key.dart | 2 +- lib/src/packet/secret_key.dart | 5 ++--- lib/src/packet/signature.dart | 17 ++++++++--------- .../sym_encrypted_integrity_protected_data.dart | 1 - lib/src/packet/sym_encrypted_session_key.dart | 1 - 8 files changed, 27 insertions(+), 31 deletions(-) diff --git a/lib/src/key/base.dart b/lib/src/key/base.dart index ea48de79..3df6838c 100644 --- a/lib/src/key/base.dart +++ b/lib/src/key/base.dart @@ -41,10 +41,10 @@ abstract class BaseKey implements KeyInterface { } @override - DateTime get creationTime => keyPacket.creationTime; + get creationTime => keyPacket.creationTime; @override - DateTime? get expirationTime { + get expirationTime { final time = keyExpiration(directSignatures.toList()); if (time == null) { for (final user in users) { diff --git a/lib/src/key/subkey.dart b/lib/src/key/subkey.dart index c497e54b..2f387324 100644 --- a/lib/src/key/subkey.dart +++ b/lib/src/key/subkey.dart @@ -6,13 +6,13 @@ library; import 'dart:typed_data'; -import 'package:dart_pg/src/key/base.dart'; -import 'package:dart_pg/src/packet/packet_list.dart'; -import 'package:dart_pg/src/packet/signature/key_flags.dart'; -import 'package:dart_pg/src/type/key.dart'; -import 'package:dart_pg/src/type/signature_packet.dart'; -import 'package:dart_pg/src/type/subkey.dart'; -import 'package:dart_pg/src/type/subkey_packet.dart'; +import '../key/base.dart'; +import '../packet/packet_list.dart'; +import '../packet/signature/key_flags.dart'; +import '../type/key.dart'; +import '../type/signature_packet.dart'; +import '../type/subkey.dart'; +import '../type/subkey_packet.dart'; /// OpenPGP subkey class /// Author Nguyen Van Nguyen diff --git a/lib/src/key/user.dart b/lib/src/key/user.dart index 06355b1a..571a9421 100644 --- a/lib/src/key/user.dart +++ b/lib/src/key/user.dart @@ -6,13 +6,13 @@ library; import 'dart:typed_data'; -import 'package:dart_pg/src/common/helpers.dart'; -import 'package:dart_pg/src/packet/base.dart'; -import 'package:dart_pg/src/packet/packet_list.dart'; -import 'package:dart_pg/src/type/key.dart'; -import 'package:dart_pg/src/type/signature_packet.dart'; -import 'package:dart_pg/src/type/user.dart'; -import 'package:dart_pg/src/type/user_id_packet.dart'; +import '../common/helpers.dart'; +import '../packet/base.dart'; +import '../packet/packet_list.dart'; +import '../type/key.dart'; +import '../type/signature_packet.dart'; +import '../type/user.dart'; +import '../type/user_id_packet.dart'; /// OpenPGP user class /// Author Nguyen Van Nguyen diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index aabae084..ee4630bc 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -17,8 +17,8 @@ import '../enum/rsa_key_size.dart'; import '../type/key_material.dart'; import '../type/key_packet.dart'; import '../type/subkey_packet.dart'; -import 'base.dart'; import 'key/public_material.dart'; +import 'base.dart'; /// Implementation of the Public Key (PUBKEY) Packet - Type 6 /// Author Nguyen Van Nguyen diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 8fec133e..78338f32 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -7,9 +7,6 @@ library; import 'dart:typed_data'; import 'package:pointycastle/api.dart'; -import 'key/public_material.dart'; -import 'key/secret_material.dart'; - import '../common/argon2_s2k.dart'; import '../common/config.dart'; import '../common/generic_s2k.dart'; @@ -30,6 +27,8 @@ import '../type/s2k.dart'; import '../type/secret_key_material.dart'; import '../type/secret_key_packet.dart'; import '../type/subkey_packet.dart'; +import 'key/public_material.dart'; +import 'key/secret_material.dart'; import 'base.dart'; /// Implementation of the Secret Key (SECKEY) Packet - Type 5 diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 16839e34..0c85d1af 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -6,16 +6,13 @@ library; import 'dart:typed_data'; -import 'package:dart_pg/src/enum/aead_algorithm.dart'; -import 'package:dart_pg/src/enum/compression_algorithm.dart'; -import 'package:dart_pg/src/enum/key_flag.dart'; -import 'package:dart_pg/src/enum/key_version.dart'; -import 'package:dart_pg/src/enum/support_feature.dart'; -import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; -import 'package:dart_pg/src/type/subkey_packet.dart'; -import 'package:dart_pg/src/type/user_id_packet.dart'; - import '../common/helpers.dart'; +import '../enum/aead_algorithm.dart'; +import '../enum/compression_algorithm.dart'; +import '../enum/key_flag.dart'; +import '../enum/key_version.dart'; +import '../enum/support_feature.dart'; +import '../enum/symmetric_algorithm.dart'; import '../enum/hash_algorithm.dart'; import '../enum/key_algorithm.dart'; import '../enum/signature_subpacket_type.dart'; @@ -24,7 +21,9 @@ import '../type/key_packet.dart'; import '../type/secret_key_packet.dart'; import '../type/signature_packet.dart'; import '../type/signing_key_material.dart'; +import '../type/subkey_packet.dart'; import '../type/subpacket.dart'; +import '../type/user_id_packet.dart'; import '../type/verification_key_material.dart'; import 'base.dart'; import 'signature_subpacket.dart'; diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index fe902b46..5c78aa92 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -5,7 +5,6 @@ library; import 'dart:typed_data'; - import 'package:pointycastle/api.dart'; import '../common/config.dart'; diff --git a/lib/src/packet/sym_encrypted_session_key.dart b/lib/src/packet/sym_encrypted_session_key.dart index 262b0ab6..347fa4ef 100644 --- a/lib/src/packet/sym_encrypted_session_key.dart +++ b/lib/src/packet/sym_encrypted_session_key.dart @@ -5,7 +5,6 @@ library; import 'dart:typed_data'; - import 'package:pointycastle/api.dart'; import '../common/argon2_s2k.dart'; From 4682680119019f869a52c8fcd2056a02450850d5 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 10:25:18 +0700 Subject: [PATCH 133/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base.dart | 22 +++++++++++----------- lib/src/key/private_key.dart | 35 ++++++++++++++++------------------- lib/src/key/public_key.dart | 3 +-- lib/src/type/private_key.dart | 4 ---- 4 files changed, 28 insertions(+), 36 deletions(-) diff --git a/lib/src/key/base.dart b/lib/src/key/base.dart index 3df6838c..3707a410 100644 --- a/lib/src/key/base.dart +++ b/lib/src/key/base.dart @@ -4,17 +4,17 @@ library; -import 'package:dart_pg/src/common/helpers.dart'; -import 'package:dart_pg/src/packet/base.dart'; -import 'package:dart_pg/src/packet/packet_list.dart'; -import 'package:dart_pg/src/type/key.dart'; -import 'package:dart_pg/src/type/key_packet.dart'; -import 'package:dart_pg/src/type/packet_list.dart'; -import 'package:dart_pg/src/type/signature_packet.dart'; -import 'package:dart_pg/src/type/subkey.dart'; -import 'package:dart_pg/src/type/subkey_packet.dart'; -import 'package:dart_pg/src/type/user.dart'; -import 'package:dart_pg/src/type/user_id_packet.dart'; +import '../common/helpers.dart'; +import '../packet/base.dart'; +import '../packet/packet_list.dart'; +import '../type/key.dart'; +import '../type/key_packet.dart'; +import '../type/packet_list.dart'; +import '../type/signature_packet.dart'; +import '../type/subkey.dart'; +import '../type/subkey_packet.dart'; +import '../type/user.dart'; +import '../type/user_id_packet.dart'; import 'subkey.dart'; import 'user.dart'; diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index a6a28148..c6463a89 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -4,21 +4,21 @@ library; -import 'package:dart_pg/src/common/armor.dart'; -import 'package:dart_pg/src/common/config.dart'; -import 'package:dart_pg/src/enum/armor_type.dart'; -import 'package:dart_pg/src/enum/ecc.dart'; -import 'package:dart_pg/src/enum/key_algorithm.dart'; -import 'package:dart_pg/src/enum/key_type.dart'; -import 'package:dart_pg/src/enum/rsa_key_size.dart'; -import 'package:dart_pg/src/key/base.dart'; -import 'package:dart_pg/src/key/public_key.dart'; -import 'package:dart_pg/src/key/subkey.dart'; -import 'package:dart_pg/src/packet/base.dart'; -import 'package:dart_pg/src/packet/packet_list.dart'; -import 'package:dart_pg/src/type/packet.dart'; -import 'package:dart_pg/src/type/private_key.dart'; -import 'package:dart_pg/src/type/secret_key_packet.dart'; +import '../common/armor.dart'; +import '../common/config.dart'; +import '../enum/armor_type.dart'; +import '../enum/ecc.dart'; +import '../enum/key_algorithm.dart'; +import '../enum/key_type.dart'; +import '../enum/rsa_key_size.dart'; +import '../key/base.dart'; +import '../key/public_key.dart'; +import '../key/subkey.dart'; +import '../packet/base.dart'; +import '../packet/packet_list.dart'; +import '../type/packet.dart'; +import '../type/private_key.dart'; +import '../type/secret_key_packet.dart'; final class PrivateKey extends BaseKey implements PrivateKeyInterface { PrivateKey(super.packetList); @@ -143,9 +143,6 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { @override get aeadProtected => secretKeyPacket.aeadProtected; - @override - get preferredHash => secretKeyPacket.preferredHash; - @override get publicKey { final packets = []; @@ -183,7 +180,7 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { throw StateError('Private key must be decrypted before encrypting.'); } - final aead = keyPacket.isV6Key && Config.aeadProtect ? Config.preferredAead : null; + final aead = aeadProtected && Config.aeadProtect ? Config.preferredAead : null; final subkeys = this.subkeys.map((subkey) { final index = this.subkeys.indexOf(subkey); final subkeyPass = subkeyPassphrases.elementAtOrNull(index) ?? passphrase; diff --git a/lib/src/key/public_key.dart b/lib/src/key/public_key.dart index e3f019f8..a39c1e5e 100644 --- a/lib/src/key/public_key.dart +++ b/lib/src/key/public_key.dart @@ -6,10 +6,9 @@ library; import 'dart:typed_data'; -import 'package:dart_pg/src/packet/base.dart'; - import '../common/armor.dart'; import '../enum/armor_type.dart'; +import '../packet/base.dart'; import '../packet/packet_list.dart'; import '../type/key.dart'; import 'base.dart'; diff --git a/lib/src/type/private_key.dart b/lib/src/type/private_key.dart index 44e3385c..7a930a57 100644 --- a/lib/src/type/private_key.dart +++ b/lib/src/type/private_key.dart @@ -4,7 +4,6 @@ library; -import '../enum/hash_algorithm.dart'; import 'key.dart'; import 'secret_key_packet.dart'; @@ -23,9 +22,6 @@ abstract interface class PrivateKeyInterface implements KeyInterface { /// Private key is aead protected bool get aeadProtected; - /// Get preferred hash algorithm - HashAlgorithm get preferredHash; - /// Lock a private key with the given passphrase. /// This method does not change the original key. PrivateKeyInterface encrypt( From ca2bc86da2d9bbde1bca0a742a00b450e9f1eeec Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 10:50:34 +0700 Subject: [PATCH 134/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base.dart | 19 +++++++++++++++++++ lib/src/type/key.dart | 3 +++ 2 files changed, 22 insertions(+) diff --git a/lib/src/key/base.dart b/lib/src/key/base.dart index 3707a410..0eb5fd2c 100644 --- a/lib/src/key/base.dart +++ b/lib/src/key/base.dart @@ -4,6 +4,8 @@ library; +import 'dart:typed_data'; + import '../common/helpers.dart'; import '../packet/base.dart'; import '../packet/packet_list.dart'; @@ -90,6 +92,23 @@ abstract class BaseKey implements KeyInterface { : [], ]); + @override + getEncryptionKeyPacket([Uint8List? keyID]) { + subkeys.sort( + (a, b) => b.creationTime.compareTo( + a.creationTime, + ), + ); + for (final subkey in subkeys) { + if (keyID == null || subkey.keyID.equals(keyID)) { + if (subkey.isEncryptionKey) { + return subkey.keyPacket; + } + } + } + return null; + } + @override isRevoked({ KeyInterface? verifyKey, diff --git a/lib/src/type/key.dart b/lib/src/type/key.dart index 4d1825e8..436045e2 100644 --- a/lib/src/type/key.dart +++ b/lib/src/type/key.dart @@ -57,6 +57,9 @@ abstract interface class KeyInterface implements ArmorableInterface, PacketConta /// Get subkeys List get subkeys; + /// Get encryption key packet + KeyPacketInterface? getEncryptionKeyPacket([Uint8List? keyID]); + /// Check if the key is revoked bool isRevoked({ final KeyInterface? verifyKey, From a302b51e72c0bfc42ff8d07c79585452422029b2 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 11:47:13 +0700 Subject: [PATCH 135/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/signature/notation_data.dart | 10 +++- lib/src/type/cleartext_message.dart | 45 ++++++++++++++ lib/src/type/encrypted_message.dart | 28 +++++++++ lib/src/type/literal_message.dart | 65 +++++++++++++++++++++ lib/src/type/notation_data.dart | 18 ++++++ lib/src/type/signature.dart | 38 ++++++++++++ lib/src/type/signed_message.dart | 24 ++++++++ lib/src/type/verification.dart | 25 ++++++++ 8 files changed, 250 insertions(+), 3 deletions(-) create mode 100644 lib/src/type/cleartext_message.dart create mode 100644 lib/src/type/encrypted_message.dart create mode 100644 lib/src/type/literal_message.dart create mode 100644 lib/src/type/notation_data.dart create mode 100644 lib/src/type/signature.dart create mode 100644 lib/src/type/signed_message.dart create mode 100644 lib/src/type/verification.dart diff --git a/lib/src/packet/signature/notation_data.dart b/lib/src/packet/signature/notation_data.dart index 2330dfd5..b6a69e8e 100644 --- a/lib/src/packet/signature/notation_data.dart +++ b/lib/src/packet/signature/notation_data.dart @@ -10,12 +10,13 @@ import 'dart:typed_data'; import '../../common/helpers.dart'; import '../../enum/signature_subpacket_type.dart'; +import '../../type/notation_data.dart'; import '../signature_subpacket.dart'; /// This subpacket describes a "notation" on the signature that the issuer wishes to make. /// The notation has a name and a value, each of which are strings of octets. /// Author Nguyen Van Nguyen -class NotationData extends SignatureSubpacket { +class NotationData extends SignatureSubpacket implements NotationDataInterface { static const saltName = "salt@notations.dart-pg.org"; static const headerFlagLength = 4; @@ -60,12 +61,15 @@ class NotationData extends SignatureSubpacket { ); } - bool get isHumanReadable => data[0] == 0x80; - + @override String get notationName => utf8.decode(nameData); + @override String get notationValue => utf8.decode(valueData); + @override + bool get isHumanReadable => data[0] == 0x80; + Uint8List get nameData { final nameLength = (((data[headerFlagLength] & 0xff) << 8) + (data[headerFlagLength + 1] & 0xff)); final nameOffset = headerFlagLength + headerNameLength + headerValueLength; diff --git a/lib/src/type/cleartext_message.dart b/lib/src/type/cleartext_message.dart new file mode 100644 index 00000000..fe5f61ef --- /dev/null +++ b/lib/src/type/cleartext_message.dart @@ -0,0 +1,45 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'key.dart'; +import 'notation_data.dart'; +import 'private_key.dart'; +import 'signature.dart'; +import 'signed_message.dart'; +import 'verification.dart'; + +/// Cleartext message interface +/// Author Nguyen Van Nguyen +abstract interface class CleartextMessageInterface { + /// Get cleartext + String get text; + + /// Get normalized cleartext + String get normalizeText; + + /// Sign the message + SignedMessageInterface sign( + Iterable signingKeys, { + Iterable recipients = const [], + NotationDataInterface? notationData, + DateTime? time, + }); + + /// Sign the message + SignatureInterface signDetached( + Iterable signingKeys, { + Iterable recipients = const [], + NotationDataInterface? notationData, + DateTime? time, + }); + + /// Verify detached signature & return verification array + Iterable verifyDetached( + Iterable verificationKeys, + SignatureInterface signature, [ + DateTime? time, + ]); +} diff --git a/lib/src/type/encrypted_message.dart b/lib/src/type/encrypted_message.dart new file mode 100644 index 00000000..dfc76d26 --- /dev/null +++ b/lib/src/type/encrypted_message.dart @@ -0,0 +1,28 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'encrypted_data_packet.dart'; +import 'literal_message.dart'; +import 'private_key.dart'; +import 'session_key.dart'; + +/// Encrypted message interface +/// Author Nguyen Van Nguyen +abstract interface class EncryptedMessageInterface { + /// Return encrypted packet. + EncryptedDataPacketInterface get encryptedPacket; + + /// Return session key. + SessionKeyInterface? get sessionKey; + + /// Decrypt the message. + /// One of `decryptionKeys` or `passwords` must be specified. + /// Return new message with decrypted content. + LiteralMessageInterface decrypt({ + Iterable decryptionKeys = const [], + Iterable passwords = const [], + }); +} diff --git a/lib/src/type/literal_message.dart b/lib/src/type/literal_message.dart new file mode 100644 index 00000000..8dc3c86a --- /dev/null +++ b/lib/src/type/literal_message.dart @@ -0,0 +1,65 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import '../enum/compression_algorithm.dart'; +import '../enum/symmetric_algorithm.dart'; +import 'armorable.dart'; +import 'encrypted_message.dart'; +import 'key.dart'; +import 'literal_data.dart'; +import 'notation_data.dart'; +import 'packet_container.dart'; +import 'private_key.dart'; +import 'signature.dart'; +import 'verification.dart'; + +/// Literal message interface +/// Author Nguyen Van Nguyen +abstract interface class LiteralMessageInterface implements ArmorableInterface, PacketContainerInterface { + /// Get literal data + LiteralDataInterface get literalData; + + /// Sign the message + LiteralMessageInterface sign( + Iterable signingKeys, { + Iterable recipients = const [], + NotationDataInterface? notationData, + DateTime? time, + }); + + /// Sign the message + SignatureInterface signDetached( + Iterable signingKeys, { + Iterable recipients = const [], + NotationDataInterface? notationData, + DateTime? time, + }); + + /// Verify signature + Iterable verify( + Iterable verificationKeys, [ + DateTime? time, + ]); + + /// Verify detached signature & return verification array + Iterable verifyDetached( + Iterable verificationKeys, + SignatureInterface signature, [ + DateTime? time, + ]); + + /// Encrypt the message either with public keys, passwords, or both at once. + /// Return new message with encrypted content. + EncryptedMessageInterface encrypt({ + Iterable encryptionKeys = const [], + Iterable passwords = const [], + SymmetricAlgorithm? symmetric, + }); + + /// Compress the message (the literal and signature packets of the message) + /// Return new message with compressed content. + LiteralMessageInterface compress([CompressionAlgorithm? algorithm]); +} diff --git a/lib/src/type/notation_data.dart b/lib/src/type/notation_data.dart new file mode 100644 index 00000000..019453fe --- /dev/null +++ b/lib/src/type/notation_data.dart @@ -0,0 +1,18 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +/// Notation data interface +/// Author Nguyen Van Nguyen +abstract interface class NotationDataInterface { + /// Get notation name + String get notationName; + + /// Get notation value + String get notationValue; + + /// Is human readable + bool get isHumanReadable; +} diff --git a/lib/src/type/signature.dart b/lib/src/type/signature.dart new file mode 100644 index 00000000..45eda14a --- /dev/null +++ b/lib/src/type/signature.dart @@ -0,0 +1,38 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:ffi'; + +import 'cleartext_message.dart'; +import 'key.dart'; +import 'literal_data.dart'; +import 'verification.dart'; + +/// Signature interface +/// Author Nguyen Van Nguyen +abstract interface class SignatureInterface { + /// Get signing key IDs + Iterable get signingKeyIDs; + + /// Get verification errors + Iterable get verificationErrors; + + /// Verify signature with literal data + /// Return verification iterable + Iterable verify( + Iterable verificationKeys, + LiteralDataInterface literalData, [ + DateTime? time, + ]); + + /// Verify signature with cleartext + /// Return verification iterable + Iterable verifyCleartext( + Iterable verificationKeys, + CleartextMessageInterface cleartext, [ + DateTime? time, + ]); +} diff --git a/lib/src/type/signed_message.dart b/lib/src/type/signed_message.dart new file mode 100644 index 00000000..5a41d6d8 --- /dev/null +++ b/lib/src/type/signed_message.dart @@ -0,0 +1,24 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'armorable.dart'; +import 'key.dart'; +import 'signature.dart'; +import 'verification.dart'; + +/// Signed message interface +/// Author Nguyen Van Nguyen +abstract interface class SignedMessageInterface implements ArmorableInterface { + /// Get signature of signed message + SignatureInterface get signature; + + /// Verify signature of signed message + /// Return verification iterable + Iterable verify( + Iterable verificationKeys, [ + DateTime? time, + ]); +} diff --git a/lib/src/type/verification.dart b/lib/src/type/verification.dart new file mode 100644 index 00000000..c7337235 --- /dev/null +++ b/lib/src/type/verification.dart @@ -0,0 +1,25 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:ffi'; + +import 'signature_packet.dart'; + +/// Verification interface +/// Author Nguyen Van Nguyen +abstract interface class VerificationInterface { + /// Get verification key ID + Uint8 get keyID; + + /// Get signature packet + SignaturePacketInterface get signaturePacket; + + /// GGet verification error + String get verificationError; + + /// Is verified + bool get isVerified; +} From 3c4cf4d5c5210385420cae1a0f276897e513571e Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 11:53:36 +0700 Subject: [PATCH 136/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/type/literal_data.dart | 2 +- lib/src/type/signature.dart | 4 ++-- lib/src/type/user_id_packet.dart | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/type/literal_data.dart b/lib/src/type/literal_data.dart index b5851243..3b7e789d 100644 --- a/lib/src/type/literal_data.dart +++ b/lib/src/type/literal_data.dart @@ -29,6 +29,6 @@ abstract interface class LiteralDataInterface { /// Get header Uint8List get header; - /// Get sign bytes + /// Get bytes for sign Uint8List get signBytes; } diff --git a/lib/src/type/signature.dart b/lib/src/type/signature.dart index 45eda14a..33c451ad 100644 --- a/lib/src/type/signature.dart +++ b/lib/src/type/signature.dart @@ -4,7 +4,7 @@ library; -import 'dart:ffi'; +import 'dart:typed_data'; import 'cleartext_message.dart'; import 'key.dart'; @@ -15,7 +15,7 @@ import 'verification.dart'; /// Author Nguyen Van Nguyen abstract interface class SignatureInterface { /// Get signing key IDs - Iterable get signingKeyIDs; + Iterable get signingKeyIDs; /// Get verification errors Iterable get verificationErrors; diff --git a/lib/src/type/user_id_packet.dart b/lib/src/type/user_id_packet.dart index 46becea0..fab78fa9 100644 --- a/lib/src/type/user_id_packet.dart +++ b/lib/src/type/user_id_packet.dart @@ -11,6 +11,6 @@ import 'packet.dart'; /// User ID packet interface /// Author Nguyen Van Nguyen abstract interface class UserIDPacketInterface implements PacketInterface { - /// Get bytes for sign + /// Get bytes for sign Uint8List get signBytes; } From acfb44b3dbb04fd2b78c0e088bdb41d4fcc8547e Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 12:10:40 +0700 Subject: [PATCH 137/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/type/cleartext_message.dart | 22 ++++++++--------- lib/src/type/encrypted_message.dart | 4 ++-- lib/src/type/key.dart | 2 +- lib/src/type/literal_message.dart | 34 +++++++++++++-------------- lib/src/type/secret_key_packet.dart | 8 +++---- lib/src/type/session_key.dart | 2 +- lib/src/type/session_key_cryptor.dart | 2 +- lib/src/type/signature.dart | 16 +++++++------ lib/src/type/signed_message.dart | 4 ++-- 9 files changed, 48 insertions(+), 46 deletions(-) diff --git a/lib/src/type/cleartext_message.dart b/lib/src/type/cleartext_message.dart index fe5f61ef..e2166add 100644 --- a/lib/src/type/cleartext_message.dart +++ b/lib/src/type/cleartext_message.dart @@ -22,24 +22,24 @@ abstract interface class CleartextMessageInterface { /// Sign the message SignedMessageInterface sign( - Iterable signingKeys, { - Iterable recipients = const [], - NotationDataInterface? notationData, - DateTime? time, + final Iterable signingKeys, { + final Iterable recipients = const [], + final NotationDataInterface? notationData, + final DateTime? time, }); /// Sign the message SignatureInterface signDetached( - Iterable signingKeys, { - Iterable recipients = const [], - NotationDataInterface? notationData, - DateTime? time, + final Iterable signingKeys, { + final Iterable recipients = const [], + final NotationDataInterface? notationData, + final DateTime? time, }); /// Verify detached signature & return verification array Iterable verifyDetached( - Iterable verificationKeys, - SignatureInterface signature, [ - DateTime? time, + final Iterable verificationKeys, + final SignatureInterface signature, [ + final DateTime? time, ]); } diff --git a/lib/src/type/encrypted_message.dart b/lib/src/type/encrypted_message.dart index dfc76d26..863acaab 100644 --- a/lib/src/type/encrypted_message.dart +++ b/lib/src/type/encrypted_message.dart @@ -22,7 +22,7 @@ abstract interface class EncryptedMessageInterface { /// One of `decryptionKeys` or `passwords` must be specified. /// Return new message with decrypted content. LiteralMessageInterface decrypt({ - Iterable decryptionKeys = const [], - Iterable passwords = const [], + final Iterable decryptionKeys = const [], + final Iterable passwords = const [], }); } diff --git a/lib/src/type/key.dart b/lib/src/type/key.dart index 436045e2..46b4dfee 100644 --- a/lib/src/type/key.dart +++ b/lib/src/type/key.dart @@ -58,7 +58,7 @@ abstract interface class KeyInterface implements ArmorableInterface, PacketConta List get subkeys; /// Get encryption key packet - KeyPacketInterface? getEncryptionKeyPacket([Uint8List? keyID]); + KeyPacketInterface? getEncryptionKeyPacket([final Uint8List? keyID]); /// Check if the key is revoked bool isRevoked({ diff --git a/lib/src/type/literal_message.dart b/lib/src/type/literal_message.dart index 8dc3c86a..d169dfb2 100644 --- a/lib/src/type/literal_message.dart +++ b/lib/src/type/literal_message.dart @@ -24,42 +24,42 @@ abstract interface class LiteralMessageInterface implements ArmorableInterface, /// Sign the message LiteralMessageInterface sign( - Iterable signingKeys, { - Iterable recipients = const [], - NotationDataInterface? notationData, - DateTime? time, + final Iterable signingKeys, { + final Iterable recipients = const [], + final NotationDataInterface? notationData, + final DateTime? time, }); /// Sign the message SignatureInterface signDetached( - Iterable signingKeys, { - Iterable recipients = const [], - NotationDataInterface? notationData, - DateTime? time, + final Iterable signingKeys, { + final Iterable recipients = const [], + final NotationDataInterface? notationData, + final DateTime? time, }); /// Verify signature Iterable verify( - Iterable verificationKeys, [ - DateTime? time, + final Iterable verificationKeys, [ + final DateTime? time, ]); /// Verify detached signature & return verification array Iterable verifyDetached( - Iterable verificationKeys, - SignatureInterface signature, [ - DateTime? time, + final Iterable verificationKeys, + final SignatureInterface signature, [ + final DateTime? time, ]); /// Encrypt the message either with public keys, passwords, or both at once. /// Return new message with encrypted content. EncryptedMessageInterface encrypt({ - Iterable encryptionKeys = const [], - Iterable passwords = const [], - SymmetricAlgorithm? symmetric, + final Iterable encryptionKeys = const [], + final Iterable passwords = const [], + final SymmetricAlgorithm? symmetric, }); /// Compress the message (the literal and signature packets of the message) /// Return new message with compressed content. - LiteralMessageInterface compress([CompressionAlgorithm? algorithm]); + LiteralMessageInterface compress([final CompressionAlgorithm? algorithm]); } diff --git a/lib/src/type/secret_key_packet.dart b/lib/src/type/secret_key_packet.dart index 4d858166..cfb793fd 100644 --- a/lib/src/type/secret_key_packet.dart +++ b/lib/src/type/secret_key_packet.dart @@ -33,11 +33,11 @@ abstract interface class SecretKeyPacketInterface implements KeyPacketInterface /// Encrypt secret key packet with passphrase SecretKeyPacketInterface encrypt( - String passphrase, - SymmetricAlgorithm symmetric, [ - AeadAlgorithm? aead, + final String passphrase, + final SymmetricAlgorithm symmetric, [ + final AeadAlgorithm? aead, ]); /// Decrypt secret key packet with passphrase - SecretKeyPacketInterface decrypt(String passphrase); + SecretKeyPacketInterface decrypt(final String passphrase); } diff --git a/lib/src/type/session_key.dart b/lib/src/type/session_key.dart index 4f412558..bbea5977 100644 --- a/lib/src/type/session_key.dart +++ b/lib/src/type/session_key.dart @@ -18,7 +18,7 @@ abstract interface class SessionKeyInterface { SymmetricAlgorithm get symmetric; /// Checksum the encryption key - void checksum(Uint8List checksum); + void checksum(final Uint8List checksum); /// Compute checksum Uint8List computeChecksum(); diff --git a/lib/src/type/session_key_cryptor.dart b/lib/src/type/session_key_cryptor.dart index b52565aa..1396900c 100644 --- a/lib/src/type/session_key_cryptor.dart +++ b/lib/src/type/session_key_cryptor.dart @@ -15,5 +15,5 @@ abstract interface class SessionKeyCryptorInterface { Uint8List toBytes(); /// Decrypt session key by using secret key packet - Uint8List decrypt(SecretKeyMaterialInterface key); + Uint8List decrypt(final SecretKeyMaterialInterface key); } diff --git a/lib/src/type/signature.dart b/lib/src/type/signature.dart index 33c451ad..bc06a92b 100644 --- a/lib/src/type/signature.dart +++ b/lib/src/type/signature.dart @@ -6,14 +6,16 @@ library; import 'dart:typed_data'; +import 'armorable.dart'; import 'cleartext_message.dart'; import 'key.dart'; import 'literal_data.dart'; +import 'packet_container.dart'; import 'verification.dart'; /// Signature interface /// Author Nguyen Van Nguyen -abstract interface class SignatureInterface { +abstract interface class SignatureInterface implements ArmorableInterface, PacketContainerInterface { /// Get signing key IDs Iterable get signingKeyIDs; @@ -23,16 +25,16 @@ abstract interface class SignatureInterface { /// Verify signature with literal data /// Return verification iterable Iterable verify( - Iterable verificationKeys, - LiteralDataInterface literalData, [ - DateTime? time, + final Iterable verificationKeys, + final LiteralDataInterface literalData, [ + final DateTime? time, ]); /// Verify signature with cleartext /// Return verification iterable Iterable verifyCleartext( - Iterable verificationKeys, - CleartextMessageInterface cleartext, [ - DateTime? time, + final Iterable verificationKeys, + final CleartextMessageInterface cleartext, [ + final DateTime? time, ]); } diff --git a/lib/src/type/signed_message.dart b/lib/src/type/signed_message.dart index 5a41d6d8..6e86d028 100644 --- a/lib/src/type/signed_message.dart +++ b/lib/src/type/signed_message.dart @@ -18,7 +18,7 @@ abstract interface class SignedMessageInterface implements ArmorableInterface { /// Verify signature of signed message /// Return verification iterable Iterable verify( - Iterable verificationKeys, [ - DateTime? time, + final Iterable verificationKeys, [ + final DateTime? time, ]); } From 2e5fbbb3b013fd05efcc720d48346b628137a506 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 12:14:01 +0700 Subject: [PATCH 138/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base.dart | 14 +++++++++----- lib/src/key/private_key.dart | 4 ++-- lib/src/key/public_key.dart | 5 ++++- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/src/key/base.dart b/lib/src/key/base.dart index 0eb5fd2c..e7db2c5c 100644 --- a/lib/src/key/base.dart +++ b/lib/src/key/base.dart @@ -93,7 +93,7 @@ abstract class BaseKey implements KeyInterface { ]); @override - getEncryptionKeyPacket([Uint8List? keyID]) { + getEncryptionKeyPacket([final Uint8List? keyID]) { subkeys.sort( (a, b) => b.creationTime.compareTo( a.creationTime, @@ -111,9 +111,9 @@ abstract class BaseKey implements KeyInterface { @override isRevoked({ - KeyInterface? verifyKey, - SignaturePacketInterface? certificate, - DateTime? time, + final KeyInterface? verifyKey, + final SignaturePacketInterface? certificate, + final DateTime? time, }) { if (revocationSignatures.isNotEmpty) { final keyID = certificate?.issuerKeyID; @@ -150,7 +150,11 @@ abstract class BaseKey implements KeyInterface { @override verify([final String userID = '', final DateTime? time]) { for (final signature in directSignatures) { - if (!signature.verify(publicKey.keyPacket, keyPacket.signBytes, time)) { + if (!signature.verify( + publicKey.keyPacket, + keyPacket.signBytes, + time, + )) { return false; } } diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index c6463a89..9bfefa14 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -216,8 +216,8 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { @override decrypt( - String passphrase, [ - Iterable subkeyPassphrases = const [], + final String passphrase, [ + final Iterable subkeyPassphrases = const [], ]) { if (passphrase.isEmpty) { throw ArgumentError('Passphrase are required for key decryption,'); diff --git a/lib/src/key/public_key.dart b/lib/src/key/public_key.dart index a39c1e5e..54461974 100644 --- a/lib/src/key/public_key.dart +++ b/lib/src/key/public_key.dart @@ -52,7 +52,10 @@ final class PublicKey extends BaseKey { } /// Armor multiple public key. - static String armorPublicKeys(Iterable keys) => Armor.encode( + static String armorPublicKeys( + final Iterable keys, + ) => + Armor.encode( ArmorType.publicKey, Uint8List.fromList(keys .map( From db7ea8bb115286ed1242f67727005a23889117c9 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 13:18:33 +0700 Subject: [PATCH 139/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/type/signature.dart | 3 --- lib/src/type/verification.dart | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/src/type/signature.dart b/lib/src/type/signature.dart index bc06a92b..7c9c74c1 100644 --- a/lib/src/type/signature.dart +++ b/lib/src/type/signature.dart @@ -19,9 +19,6 @@ abstract interface class SignatureInterface implements ArmorableInterface, Packe /// Get signing key IDs Iterable get signingKeyIDs; - /// Get verification errors - Iterable get verificationErrors; - /// Verify signature with literal data /// Return verification iterable Iterable verify( diff --git a/lib/src/type/verification.dart b/lib/src/type/verification.dart index c7337235..6da811b3 100644 --- a/lib/src/type/verification.dart +++ b/lib/src/type/verification.dart @@ -4,7 +4,7 @@ library; -import 'dart:ffi'; +import 'dart:typed_data'; import 'signature_packet.dart'; @@ -12,7 +12,7 @@ import 'signature_packet.dart'; /// Author Nguyen Van Nguyen abstract interface class VerificationInterface { /// Get verification key ID - Uint8 get keyID; + Uint8List get keyID; /// Get signature packet SignaturePacketInterface get signaturePacket; From ef6f611f36b2fb00d5f28d9ec96fc1ba7a32d52c Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 14:14:41 +0700 Subject: [PATCH 140/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/extensions.dart | 6 ++++++ lib/src/packet/literal_data.dart | 1 + 2 files changed, 7 insertions(+) diff --git a/lib/src/common/extensions.dart b/lib/src/common/extensions.dart index 5423dc6a..6fde2162 100644 --- a/lib/src/common/extensions.dart +++ b/lib/src/common/extensions.dart @@ -144,6 +144,12 @@ extension StringExt on String { Uint8List toBytes() => utf8.encoder.convert(this); bool hasMatch(final String text) => RegExp(this).hasMatch(text); + + String removeTrailingSpaces() => LineSplitter.split(this) + .map( + (line) => line.trimRight(), + ) + .join(r'\n'); } /// Uint8List extension diff --git a/lib/src/packet/literal_data.dart b/lib/src/packet/literal_data.dart index 2641d269..e1a2050a 100644 --- a/lib/src/packet/literal_data.dart +++ b/lib/src/packet/literal_data.dart @@ -88,6 +88,7 @@ class LiteralDataPacket extends BasePacket implements LiteralDataInterface { get signBytes => binary.isNotEmpty ? binary : text + .removeTrailingSpaces() .replaceAll( RegExp(r'\r?\n', multiLine: true), '\r\n', From 73aa8e1f0b8681341c344686b1f33cafd7e6fcd6 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 15:09:03 +0700 Subject: [PATCH 141/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/signature.dart | 44 +++++++++++++++++++++ lib/src/packet/signature/notation_data.dart | 2 +- lib/src/type/cleartext_message.dart | 2 + lib/src/type/notation_data.dart | 2 +- lib/src/type/signature.dart | 4 ++ 5 files changed, 52 insertions(+), 2 deletions(-) diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 0c85d1af..06da7d14 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -11,13 +11,17 @@ import '../enum/aead_algorithm.dart'; import '../enum/compression_algorithm.dart'; import '../enum/key_flag.dart'; import '../enum/key_version.dart'; +import '../enum/literal_format.dart'; import '../enum/support_feature.dart'; import '../enum/symmetric_algorithm.dart'; import '../enum/hash_algorithm.dart'; import '../enum/key_algorithm.dart'; import '../enum/signature_subpacket_type.dart'; import '../enum/signature_type.dart'; +import '../type/key.dart'; import '../type/key_packet.dart'; +import '../type/literal_data.dart'; +import '../type/notation_data.dart'; import '../type/secret_key_packet.dart'; import '../type/signature_packet.dart'; import '../type/signing_key_material.dart'; @@ -332,6 +336,46 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { ); } + /// Create literal data signature + factory SignaturePacket.createLiteralData( + SecretKeyPacketInterface signKey, + LiteralDataInterface literalData, { + Iterable recipients = const [], + NotationDataInterface? notationData, + DateTime? time, + }) { + final signatureType = switch (literalData.format) { + LiteralFormat.text || LiteralFormat.utf8 => SignatureType.text, + _ => SignatureType.binary + }; + final subpackets = []; + if (signKey.isV6Key) { + for (final recipient in recipients) { + subpackets.add( + IntendedRecipientFingerprint.fromKey( + recipient.keyPacket, + ), + ); + } + } + if (notationData != null) { + subpackets.add( + NotationData.fromNotation( + notationData.humanReadable, + notationData.notationName, + notationData.notationValue, + ), + ); + } + return SignaturePacket.createSignature( + signKey, + signatureType, + literalData.signBytes, + subpackets: subpackets, + time: time, + ); + } + @override get data => Uint8List.fromList([ ...signatureData, diff --git a/lib/src/packet/signature/notation_data.dart b/lib/src/packet/signature/notation_data.dart index b6a69e8e..4ea1a6a2 100644 --- a/lib/src/packet/signature/notation_data.dart +++ b/lib/src/packet/signature/notation_data.dart @@ -68,7 +68,7 @@ class NotationData extends SignatureSubpacket implements NotationDataInterface { String get notationValue => utf8.decode(valueData); @override - bool get isHumanReadable => data[0] == 0x80; + bool get humanReadable => data[0] == 0x80; Uint8List get nameData { final nameLength = (((data[headerFlagLength] & 0xff) << 8) + (data[headerFlagLength + 1] & 0xff)); diff --git a/lib/src/type/cleartext_message.dart b/lib/src/type/cleartext_message.dart index e2166add..cebdd071 100644 --- a/lib/src/type/cleartext_message.dart +++ b/lib/src/type/cleartext_message.dart @@ -18,6 +18,8 @@ abstract interface class CleartextMessageInterface { String get text; /// Get normalized cleartext + /// Remove trailing whitespace and + /// normalize EOL to canonical form String get normalizeText; /// Sign the message diff --git a/lib/src/type/notation_data.dart b/lib/src/type/notation_data.dart index 019453fe..f814adfa 100644 --- a/lib/src/type/notation_data.dart +++ b/lib/src/type/notation_data.dart @@ -14,5 +14,5 @@ abstract interface class NotationDataInterface { String get notationValue; /// Is human readable - bool get isHumanReadable; + bool get humanReadable; } diff --git a/lib/src/type/signature.dart b/lib/src/type/signature.dart index 7c9c74c1..c0bd0ef7 100644 --- a/lib/src/type/signature.dart +++ b/lib/src/type/signature.dart @@ -6,6 +6,7 @@ library; import 'dart:typed_data'; +import '../enum/hash_algorithm.dart'; import 'armorable.dart'; import 'cleartext_message.dart'; import 'key.dart'; @@ -16,6 +17,9 @@ import 'verification.dart'; /// Signature interface /// Author Nguyen Van Nguyen abstract interface class SignatureInterface implements ArmorableInterface, PacketContainerInterface { + /// Get hash algorithms + Iterable get hashAlgorithms; + /// Get signing key IDs Iterable get signingKeyIDs; From c9f23f7207f551bc60c00d4a139cf0d62a3df700 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 15:19:24 +0700 Subject: [PATCH 142/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/public_key.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/key/public_key.dart b/lib/src/key/public_key.dart index 54461974..3ab35d98 100644 --- a/lib/src/key/public_key.dart +++ b/lib/src/key/public_key.dart @@ -67,7 +67,7 @@ final class PublicKey extends BaseKey { .toList())); @override - String armor() => Armor.encode(ArmorType.publicKey, packetList.encode()); + armor() => Armor.encode(ArmorType.publicKey, packetList.encode()); @override get publicKey => this; From 12726d6e879344e774119f85c004b4ecf06af709 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 15:21:46 +0700 Subject: [PATCH 143/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/{base.dart => base_key.dart} | 0 lib/src/key/private_key.dart | 2 +- lib/src/key/public_key.dart | 2 +- lib/src/key/subkey.dart | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename lib/src/key/{base.dart => base_key.dart} (100%) diff --git a/lib/src/key/base.dart b/lib/src/key/base_key.dart similarity index 100% rename from lib/src/key/base.dart rename to lib/src/key/base_key.dart diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index 9bfefa14..7dad2dba 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -11,7 +11,7 @@ import '../enum/ecc.dart'; import '../enum/key_algorithm.dart'; import '../enum/key_type.dart'; import '../enum/rsa_key_size.dart'; -import '../key/base.dart'; +import 'base_key.dart'; import '../key/public_key.dart'; import '../key/subkey.dart'; import '../packet/base.dart'; diff --git a/lib/src/key/public_key.dart b/lib/src/key/public_key.dart index 3ab35d98..e1faa90d 100644 --- a/lib/src/key/public_key.dart +++ b/lib/src/key/public_key.dart @@ -11,7 +11,7 @@ import '../enum/armor_type.dart'; import '../packet/base.dart'; import '../packet/packet_list.dart'; import '../type/key.dart'; -import 'base.dart'; +import 'base_key.dart'; /// OpenPGP public key class /// Author Nguyen Van Nguyen diff --git a/lib/src/key/subkey.dart b/lib/src/key/subkey.dart index 2f387324..29b571dd 100644 --- a/lib/src/key/subkey.dart +++ b/lib/src/key/subkey.dart @@ -6,7 +6,7 @@ library; import 'dart:typed_data'; -import '../key/base.dart'; +import 'base_key.dart'; import '../packet/packet_list.dart'; import '../packet/signature/key_flags.dart'; import '../type/key.dart'; From 27485cf3d7ab1139f9b6137bc87ef99cd8459df4 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 15:56:20 +0700 Subject: [PATCH 144/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/compressed_data.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/src/packet/compressed_data.dart b/lib/src/packet/compressed_data.dart index 3c94f3c1..38e05dc9 100644 --- a/lib/src/packet/compressed_data.dart +++ b/lib/src/packet/compressed_data.dart @@ -8,6 +8,7 @@ import 'dart:io'; import 'dart:typed_data'; import '../enum/compression_algorithm.dart'; +import '../type/packet_list.dart'; import 'base.dart'; import 'packet_list.dart'; @@ -23,7 +24,7 @@ class CompressedDataPacket extends BasePacket { final Uint8List compressed; /// Decompressed packets contained within. - final PacketList packets; + final PacketListInterface packets; CompressedDataPacket( this.compressed, @@ -45,7 +46,7 @@ class CompressedDataPacket extends BasePacket { } factory CompressedDataPacket.fromPacketList( - final PacketList packets, { + final PacketListInterface packets, { final CompressionAlgorithm algorithm = CompressionAlgorithm.uncompressed, }) => CompressedDataPacket( @@ -61,7 +62,7 @@ class CompressedDataPacket extends BasePacket { ]); static Uint8List _compress( - final PacketList packets, + final PacketListInterface packets, final CompressionAlgorithm algorithm, ) => switch (algorithm) { From ab7691bf28995cbbfd4d910b865a45134f2e9b14 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 16:38:20 +0700 Subject: [PATCH 145/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base_key.dart | 23 +++++++++++++++++++++++ lib/src/type/key.dart | 3 +++ lib/src/type/literal_data.dart | 5 +++-- lib/src/type/signature.dart | 4 ++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/src/key/base_key.dart b/lib/src/key/base_key.dart index e7db2c5c..0d774fbb 100644 --- a/lib/src/key/base_key.dart +++ b/lib/src/key/base_key.dart @@ -6,6 +6,8 @@ library; import 'dart:typed_data'; +import 'package:dart_pg/src/packet/signature/features.dart'; + import '../common/helpers.dart'; import '../packet/base.dart'; import '../packet/packet_list.dart'; @@ -92,6 +94,27 @@ abstract class BaseKey implements KeyInterface { : [], ]); + @override + bool get aeadSupported { + for (final signature in directSignatures) { + final features = signature.getSubpacket(); + if (features != null && features.supportVersion2SEIPD) { + return true; + } + } + for (final user in users) { + if (user.isPrimary) { + for (final signature in user.selfSignatures) { + final features = signature.getSubpacket(); + if (features != null && features.supportVersion2SEIPD) { + return true; + } + } + } + } + return false; + } + @override getEncryptionKeyPacket([final Uint8List? keyID]) { subkeys.sort( diff --git a/lib/src/type/key.dart b/lib/src/type/key.dart index 46b4dfee..0921c75f 100644 --- a/lib/src/type/key.dart +++ b/lib/src/type/key.dart @@ -45,6 +45,9 @@ abstract interface class KeyInterface implements ArmorableInterface, PacketConta /// Get key strength int get keyStrength; + /// Return aead supported + bool get aeadSupported; + /// Get revocation signatures List get revocationSignatures; diff --git a/lib/src/type/literal_data.dart b/lib/src/type/literal_data.dart index 3b7e789d..579b7de8 100644 --- a/lib/src/type/literal_data.dart +++ b/lib/src/type/literal_data.dart @@ -6,11 +6,12 @@ library; import 'dart:typed_data'; -import 'package:dart_pg/src/enum/literal_format.dart'; +import '../enum/literal_format.dart'; +import 'packet.dart'; /// Literal data interface /// Author Nguyen Van Nguyen -abstract interface class LiteralDataInterface { +abstract interface class LiteralDataInterface implements PacketInterface { /// Get literal format LiteralFormat get format; diff --git a/lib/src/type/signature.dart b/lib/src/type/signature.dart index c0bd0ef7..320675fa 100644 --- a/lib/src/type/signature.dart +++ b/lib/src/type/signature.dart @@ -12,6 +12,7 @@ import 'cleartext_message.dart'; import 'key.dart'; import 'literal_data.dart'; import 'packet_container.dart'; +import 'signature_packet.dart'; import 'verification.dart'; /// Signature interface @@ -23,6 +24,9 @@ abstract interface class SignatureInterface implements ArmorableInterface, Packe /// Get signing key IDs Iterable get signingKeyIDs; + /// Get signature packets + Iterable get packets; + /// Verify signature with literal data /// Return verification iterable Iterable verify( From a3056618566e52f75f2297f9209f479ad8e5c9e0 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Thu, 5 Dec 2024 17:40:17 +0700 Subject: [PATCH 146/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/private_key.dart | 5 + lib/src/message/base_message.dart | 23 +++ lib/src/message/cleartext_message.dart | 83 ++++++++++ lib/src/message/encrypted_message.dart | 89 +++++++++++ lib/src/message/literal_message.dart | 207 +++++++++++++++++++++++++ lib/src/message/signature.dart | 106 +++++++++++++ lib/src/message/signed_message.dart | 62 ++++++++ lib/src/message/verification.dart | 33 ++++ lib/src/type/private_key.dart | 5 + 9 files changed, 613 insertions(+) create mode 100644 lib/src/message/base_message.dart create mode 100644 lib/src/message/cleartext_message.dart create mode 100644 lib/src/message/encrypted_message.dart create mode 100644 lib/src/message/literal_message.dart create mode 100644 lib/src/message/signature.dart create mode 100644 lib/src/message/signed_message.dart create mode 100644 lib/src/message/verification.dart diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index 7dad2dba..da48233c 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -4,6 +4,8 @@ library; +import 'dart:typed_data'; + import '../common/armor.dart'; import '../common/config.dart'; import '../enum/armor_type.dart'; @@ -165,6 +167,9 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { return PublicKey(PacketList(packets)); } + @override + getDecryptionKeyPacket([Uint8List? keyID]) => getEncryptionKeyPacket(keyID) as SecretKeyPacketInterface; + @override armor() => Armor.encode(ArmorType.privateKey, packetList.encode()); diff --git a/lib/src/message/base_message.dart b/lib/src/message/base_message.dart new file mode 100644 index 00000000..227b786b --- /dev/null +++ b/lib/src/message/base_message.dart @@ -0,0 +1,23 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'package:dart_pg/src/common/armor.dart'; +import 'package:dart_pg/src/enum/armor_type.dart'; +import 'package:dart_pg/src/type/armorable.dart'; +import 'package:dart_pg/src/type/packet_container.dart'; +import 'package:dart_pg/src/type/packet_list.dart'; + +/// Base abstract OpenPGP message class +/// Author Nguyen Van Nguyen +abstract class BaseMessage implements ArmorableInterface, PacketContainerInterface { + @override + final PacketListInterface packetList; + + BaseMessage(this.packetList); + + @override + armor() => Armor.encode(ArmorType.message, packetList.encode()); +} diff --git a/lib/src/message/cleartext_message.dart b/lib/src/message/cleartext_message.dart new file mode 100644 index 00000000..9e4f50c8 --- /dev/null +++ b/lib/src/message/cleartext_message.dart @@ -0,0 +1,83 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/message/signature.dart'; +import 'package:dart_pg/src/message/signed_message.dart'; +import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/type/cleartext_message.dart'; +import 'package:dart_pg/src/type/key.dart'; +import 'package:dart_pg/src/type/notation_data.dart'; +import 'package:dart_pg/src/type/private_key.dart'; +import 'package:dart_pg/src/type/signature.dart'; +import 'package:dart_pg/src/type/signed_message.dart'; + +/// Cleartext message class that represents an OpenPGP cleartext message. +/// See RFC 9580, section 7. +/// Author Nguyen Van Nguyen +class CleartextMessage implements CleartextMessageInterface { + @override + final String text; + + CleartextMessage(this.text); + + @override + String get normalizeText => text.removeTrailingSpaces().replaceAll( + RegExp(r'\r?\n', multiLine: true), + '\r\n', + ); + + @override + SignedMessageInterface sign( + Iterable signingKeys, { + Iterable recipients = const [], + NotationDataInterface? notationData, + DateTime? time, + }) { + return SignedMessage( + text, + signDetached( + signingKeys, + recipients: recipients, + notationData: notationData, + time: time, + )); + } + + @override + signDetached( + Iterable signingKeys, { + Iterable recipients = const [], + NotationDataInterface? notationData, + DateTime? time, + }) { + if (signingKeys.isEmpty) { + throw ArgumentError('No signing keys provided.'); + } + return Signature(signingKeys.map((signKey) { + return SignaturePacket.createLiteralData( + signKey.secretKeyPacket, + LiteralDataPacket.fromText(text), + recipients: recipients, + notationData: notationData, + time: time, + ); + })); + } + + @override + verifyDetached( + Iterable verificationKeys, + SignatureInterface signature, [ + DateTime? time, + ]) { + return signature.verifyCleartext( + verificationKeys, + this, + time, + ); + } +} diff --git a/lib/src/message/encrypted_message.dart b/lib/src/message/encrypted_message.dart new file mode 100644 index 00000000..2ce186c4 --- /dev/null +++ b/lib/src/message/encrypted_message.dart @@ -0,0 +1,89 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/message/base_message.dart'; +import 'package:dart_pg/src/message/literal_message.dart'; +import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/type/encrypted_data_packet.dart'; +import 'package:dart_pg/src/type/encrypted_message.dart'; +import 'package:dart_pg/src/type/literal_message.dart'; +import 'package:dart_pg/src/type/private_key.dart'; +import 'package:dart_pg/src/type/session_key.dart'; + +/// OpenPGP encrypted message class +/// Author Nguyen Van Nguyen +final class EncryptedMessage extends BaseMessage implements EncryptedMessageInterface { + SessionKeyInterface? _sessionKey; + + EncryptedMessage(super.packetList); + + @override + EncryptedDataPacketInterface get encryptedPacket { + final packets = packetList.whereType(); + if (packets.isEmpty) { + throw StateError('No encrypted data in packet list.'); + } + return packets.first; + } + + @override + SessionKeyInterface? get sessionKey => _sessionKey; + + @override + LiteralMessageInterface decrypt({ + final Iterable decryptionKeys = const [], + final Iterable passwords = const [], + }) { + if (decryptionKeys.isEmpty && passwords.isEmpty) { + throw ArgumentError('No decryption keys or passwords provided.'); + } + _sessionKey = _decryptSessionKey(decryptionKeys, passwords); + + return LiteralMessage(encryptedPacket.decrypt(_sessionKey.encryptionKey).packets); + } + + SessionKeyInterface _decryptSessionKey( + final Iterable decryptionKeys, + final Iterable passwords, + ) { + final errors = []; + final sessionKeys = []; + if (passwords.isNotEmpty) { + final skeskPackets = packetList.whereType(); + for (final skesk in skeskPackets) { + for (final password in passwords) { + try { + sessionKeys.add(skesk.decrypt(password).sessionKey!); + break; + } on Error catch (error) { + errors.add(error.toString()); + } + } + } + } + if (sessionKeys.isEmpty && decryptionKeys.isNotEmpty) { + final pkeskPackets = packetList.whereType(); + for (final pkesk in pkeskPackets) { + for (final key in decryptionKeys) { + final keyPacket = key.getDecryptionKeyPacket(); + if (keyPacket != null && pkesk.keyID.equals(keyPacket.keyID)) { + try { + sessionKeys.add(pkesk.decrypt(keyPacket).sessionKey!); + } on Error catch (error) { + errors.add(error.toString()); + } + } + } + } + } + + if (sessionKeys.isEmpty) { + throw StateError('Session key decryption failed.\n${errors.join('\n')}'); + } + return sessionKeys.first; + } +} diff --git a/lib/src/message/literal_message.dart b/lib/src/message/literal_message.dart new file mode 100644 index 00000000..f0071eff --- /dev/null +++ b/lib/src/message/literal_message.dart @@ -0,0 +1,207 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'package:dart_pg/src/common/config.dart'; +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/compression_algorithm.dart'; +import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; +import 'package:dart_pg/src/message/base_message.dart'; +import 'package:dart_pg/src/message/encrypted_message.dart'; +import 'package:dart_pg/src/message/signature.dart'; +import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/key/session_key.dart'; +import 'package:dart_pg/src/packet/packet_list.dart'; +import 'package:dart_pg/src/type/key.dart'; +import 'package:dart_pg/src/type/literal_data.dart'; +import 'package:dart_pg/src/type/literal_message.dart'; +import 'package:dart_pg/src/type/notation_data.dart'; +import 'package:dart_pg/src/type/packet_list.dart'; +import 'package:dart_pg/src/type/private_key.dart'; +import 'package:dart_pg/src/type/signature.dart'; +import 'package:dart_pg/src/type/signature_packet.dart'; +import 'package:dart_pg/src/type/signed_message.dart'; +import 'package:dart_pg/src/type/verification.dart'; + +/// OpenPGP literal message class +/// Author Nguyen Van Nguyen +final class LiteralMessage extends BaseMessage implements LiteralMessageInterface, SignedMessageInterface { + LiteralMessage(super.packetList); + + @override + get literalData { + final packets = packetList.whereType(); + if (packets.isEmpty) { + throw StateError('No literal data in packet list.'); + } + return packets.first; + } + + @override + get signature => Signature( + unwrapCompressed().whereType(), + ); + + @override + compress([final CompressionAlgorithm? algorithm]) { + final algo = algorithm ?? Config.preferredCompression; + if (algo != CompressionAlgorithm.uncompressed) { + return LiteralMessage( + PacketList( + [ + CompressedDataPacket.fromPacketList( + packetList, + algorithm: algo, + ) + ], + ), + ); + } + return this; + } + + @override + encrypt({ + final Iterable encryptionKeys = const [], + final Iterable passwords = const [], + final SymmetricAlgorithm? symmetric, + }) { + if (encryptionKeys.isEmpty && passwords.isEmpty) { + throw ArgumentError('No encryption keys or passwords provided.'); + } + var addPadding = false; + var aeadSupported = Config.aeadSupported; + for (final key in encryptionKeys) { + if (!key.aeadSupported) { + aeadSupported = false; + } + if (key.keyPacket.isV6Key) { + addPadding = true; + } + } + final sessionKey = SessionKey.produceKey( + symmetric ?? Config.preferredSymmetric, + ); + + final packetList = addPadding + ? PacketList([ + ...this.packetList.packets, + PaddingPacket.createPadding( + Helper.randomInt( + PaddingPacket.paddingMin, + PaddingPacket.paddingMax, + ), + ), + ]) + : this.packetList; + + return EncryptedMessage(PacketList([ + ...encryptionKeys.map( + (key) => PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( + key.publicKey.getEncryptionKeyPacket()!, + sessionKey, + ), + ), + ...passwords.map((password) => SymEncryptedSessionKeyPacket.encryptSessionKey( + password, + sessionKey: sessionKey, + symmetric: symmetric ?? Config.preferredSymmetric, + aead: Config.preferredAead, + aeadProtect: aeadSupported && Config.aeadProtect, + )), + SymEncryptedIntegrityProtectedDataPacket.encryptPackets( + sessionKey.encryptionKey, + packetList, + symmetric: symmetric ?? Config.preferredSymmetric, + aead: Config.preferredAead, + aeadProtect: aeadSupported && Config.aeadProtect, + ), + ])); + } + + @override + sign( + final Iterable signingKeys, { + final Iterable recipients = const [], + final NotationDataInterface? notationData, + final DateTime? time, + }) { + final signaturePackets = [ + ...unwrapCompressed().whereType(), + ...signDetached( + signingKeys, + recipients: recipients, + notationData: notationData, + time: time, + ).packets, + ]; + var index = 0; + final opsPackets = signaturePackets + .map((packet) { + return OnePassSignaturePacket.fromSignature( + packet, + (0 == index++) ? 1 : 0, + ); + }) + .toList() + .reversed; // innermost OPS refers to the first signature packet + + return LiteralMessage(PacketList([ + ...opsPackets, + literalData, + ...signaturePackets, + ])); + } + + @override + signDetached( + final Iterable signingKeys, { + final Iterable recipients = const [], + final NotationDataInterface? notationData, + final DateTime? time, + }) { + if (signingKeys.isEmpty) { + throw ArgumentError('No signing keys provided.'); + } + return Signature(signingKeys.map((signKey) { + return SignaturePacket.createLiteralData( + signKey.secretKeyPacket, + literalData, + recipients: recipients, + notationData: notationData, + time: time, + ); + })); + } + + @override + Iterable verify( + final Iterable verificationKeys, [ + final DateTime? time, + ]) { + return signature.verify( + verificationKeys, + literalData, + time, + ); + } + + @override + verifyDetached( + final Iterable verificationKeys, + final SignatureInterface signature, [ + final DateTime? time, + ]) { + return signature.verify( + verificationKeys, + literalData, + time, + ); + } + + PacketListInterface unwrapCompressed() { + return packetList.whereType().firstOrNull?.packets ?? packetList; + } +} diff --git a/lib/src/message/signature.dart b/lib/src/message/signature.dart new file mode 100644 index 00000000..98cb02f4 --- /dev/null +++ b/lib/src/message/signature.dart @@ -0,0 +1,106 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'package:dart_pg/src/common/armor.dart'; +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/armor_type.dart'; +import 'package:dart_pg/src/enum/hash_algorithm.dart'; +import 'package:dart_pg/src/message/verification.dart'; +import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/packet_list.dart'; +import 'package:dart_pg/src/type/cleartext_message.dart'; +import 'package:dart_pg/src/type/key.dart'; +import 'package:dart_pg/src/type/literal_data.dart'; +import 'package:dart_pg/src/type/signature.dart'; +import 'package:dart_pg/src/type/signature_packet.dart'; +import 'package:dart_pg/src/type/verification.dart'; + +/// Signature class +/// Author Nguyen Van Nguyen +final class Signature implements SignatureInterface { + @override + final Iterable packets; + + Signature(this.packets); + + /// Read signature from armored string + factory Signature.fromArmored(final String armored) { + final armor = Armor.decode(armored); + if (armor.type != ArmorType.signature) { + throw ArgumentError('Armored text not of signature type'); + } + return Signature( + PacketList.decode(armor.data).whereType(), + ); + } + + @override + Iterable get hashAlgorithms => packets.map( + (packet) => packet.hashAlgorithm, + ); + + @override + get packetList => PacketList(packets); + + @override + get signingKeyIDs => packets.map( + (signature) => signature.issuerKeyID, + ); + + @override + armor() => Armor.encode(ArmorType.signature, packetList.encode()); + + @override + Iterable verify( + final Iterable verificationKeys, + final LiteralDataInterface literalData, [ + final DateTime? time, + ]) { + if (verificationKeys.isEmpty) { + throw ArgumentError('No verification keys provided.'); + } + final verifications = []; + for (final packet in packets) { + for (final key in verificationKeys) { + final keyPacket = key.publicKey.keyPacket; + if (packet.issuerKeyID.equals(keyPacket.keyID)) { + var isVerified = false; + var verificationError = ''; + try { + isVerified = packet.verify( + keyPacket, + literalData.signBytes, + time, + ); + } on Error catch (error) { + verificationError = error.toString(); + } + + verifications.add(Verification( + keyPacket.keyID, + packet, + isVerified, + verificationError, + )); + } + } + } + return verifications; + } + + @override + verifyCleartext( + final Iterable verificationKeys, + final CleartextMessageInterface cleartext, [ + final DateTime? time, + ]) { + return verify( + verificationKeys, + LiteralDataPacket.fromText(cleartext.text), + time, + ); + } +} diff --git a/lib/src/message/signed_message.dart b/lib/src/message/signed_message.dart new file mode 100644 index 00000000..2e718ba3 --- /dev/null +++ b/lib/src/message/signed_message.dart @@ -0,0 +1,62 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'package:dart_pg/src/common/armor.dart'; +import 'package:dart_pg/src/enum/armor_type.dart'; +import 'package:dart_pg/src/message/signature.dart'; +import 'package:dart_pg/src/packet/packet_list.dart'; +import 'package:dart_pg/src/type/key.dart'; +import 'package:dart_pg/src/type/signature.dart'; +import 'package:dart_pg/src/type/signature_packet.dart'; +import 'package:dart_pg/src/type/signed_message.dart'; + +import 'cleartext_message.dart'; + +/// Signed message class that represents an OpenPGP cleartext signed message. +/// See RFC 9580, section 7. +/// Author Nguyen Van Nguyen +final class SignedMessage extends CleartextMessage implements SignedMessageInterface { + @override + final SignatureInterface signature; + + SignedMessage(super.text, this.signature); + + /// Read signed message from armored string + factory SignedMessage.fromArmored(final String armored) { + final armor = Armor.decode(armored); + if (armor.type != ArmorType.signedMessage) { + throw ArgumentError('Armored text not of signed message type'); + } + return SignedMessage( + armor.text, + Signature( + PacketList.decode(armor.data).whereType(), + ), + ); + } + + @override + String armor() { + return Armor.encode( + ArmorType.signedMessage, + signature.packetList.encode(), + text: text, + hashAlgo: signature.hashAlgorithms.map((hash) => hash.name).join(', '), + ); + } + + @override + verify( + final Iterable verificationKeys, [ + final DateTime? time, + ]) { + return signature.verifyCleartext( + verificationKeys, + this, + time, + ); + } +} diff --git a/lib/src/message/verification.dart b/lib/src/message/verification.dart new file mode 100644 index 00000000..87744ca1 --- /dev/null +++ b/lib/src/message/verification.dart @@ -0,0 +1,33 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'dart:typed_data'; + +import 'package:dart_pg/src/type/signature_packet.dart'; +import 'package:dart_pg/src/type/verification.dart'; + +/// Verification class +/// Author Nguyen Van Nguyen +class Verification implements VerificationInterface { + @override + final Uint8List keyID; + + @override + final SignaturePacketInterface signaturePacket; + + @override + final bool isVerified; + + @override + final String verificationError; + + Verification( + this.keyID, + this.signaturePacket, + this.isVerified, + this.verificationError, + ); +} diff --git a/lib/src/type/private_key.dart b/lib/src/type/private_key.dart index 7a930a57..aa513f56 100644 --- a/lib/src/type/private_key.dart +++ b/lib/src/type/private_key.dart @@ -4,6 +4,8 @@ library; +import 'dart:typed_data'; + import 'key.dart'; import 'secret_key_packet.dart'; @@ -22,6 +24,9 @@ abstract interface class PrivateKeyInterface implements KeyInterface { /// Private key is aead protected bool get aeadProtected; + /// Get decryptionK key packet + SecretKeyPacketInterface? getDecryptionKeyPacket([final Uint8List? keyID]); + /// Lock a private key with the given passphrase. /// This method does not change the original key. PrivateKeyInterface encrypt( From 93df8fad595f76be967e65dd6a4fb2f10509362c Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 08:15:11 +0700 Subject: [PATCH 147/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/message/encrypted_message.dart | 7 ++++++- lib/src/packet/aead_encrypted_data.dart | 4 ++-- lib/src/packet/sym_encrypted_data.dart | 4 ++-- lib/src/packet/sym_encrypted_integrity_protected_data.dart | 4 ++-- lib/src/type/encrypted_data_packet.dart | 4 ++-- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/src/message/encrypted_message.dart b/lib/src/message/encrypted_message.dart index 2ce186c4..64988ec9 100644 --- a/lib/src/message/encrypted_message.dart +++ b/lib/src/message/encrypted_message.dart @@ -43,7 +43,12 @@ final class EncryptedMessage extends BaseMessage implements EncryptedMessageInte } _sessionKey = _decryptSessionKey(decryptionKeys, passwords); - return LiteralMessage(encryptedPacket.decrypt(_sessionKey.encryptionKey).packets); + return LiteralMessage(encryptedPacket + .decrypt( + _sessionKey!.encryptionKey, + _sessionKey!.symmetric, + ) + .packets!); } SessionKeyInterface _decryptSessionKey( diff --git a/lib/src/packet/aead_encrypted_data.dart b/lib/src/packet/aead_encrypted_data.dart index e41a63d1..71c817be 100644 --- a/lib/src/packet/aead_encrypted_data.dart +++ b/lib/src/packet/aead_encrypted_data.dart @@ -116,9 +116,9 @@ class AeadEncryptedDataPacket extends BasePacket implements EncryptedDataPacketI @override decrypt( - final Uint8List key, { + final Uint8List key, [ final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - }) { + ]) { final length = encrypted.length; final data = encrypted.sublist(0, length - aead.tagLength); final authTag = encrypted.sublist(length - aead.tagLength); diff --git a/lib/src/packet/sym_encrypted_data.dart b/lib/src/packet/sym_encrypted_data.dart index 8446864a..984b7f1e 100644 --- a/lib/src/packet/sym_encrypted_data.dart +++ b/lib/src/packet/sym_encrypted_data.dart @@ -69,9 +69,9 @@ class SymEncryptedDataPacket extends BasePacket implements EncryptedDataPacketIn @override decrypt( - final Uint8List key, { + final Uint8List key, [ final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - }) { + ]) { if (!Config.allowUnauthenticated) { throw StateError('Message is not authenticated.'); } diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index 5c78aa92..8887e9f9 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -193,9 +193,9 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc @override SymEncryptedIntegrityProtectedDataPacket decrypt( - final Uint8List key, { + final Uint8List key, [ final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - }) { + ]) { if (packets != null && packets!.isNotEmpty) { return this; } else { diff --git a/lib/src/type/encrypted_data_packet.dart b/lib/src/type/encrypted_data_packet.dart index 77d31ef8..d9f9c2ab 100644 --- a/lib/src/type/encrypted_data_packet.dart +++ b/lib/src/type/encrypted_data_packet.dart @@ -20,7 +20,7 @@ abstract interface class EncryptedDataPacketInterface { /// Decrypt the encrypted data contained in the packet. EncryptedDataPacketInterface decrypt( - final Uint8List key, { + final Uint8List key, [ final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - }); + ]); } From f97073c465cf6d7e4a21e5968bf8b879b889554e Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 08:18:44 +0700 Subject: [PATCH 148/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/packet/encryption_test.dart | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/packet/encryption_test.dart b/test/packet/encryption_test.dart index 49282516..afc96b58 100644 --- a/test/packet/encryption_test.dart +++ b/test/packet/encryption_test.dart @@ -139,7 +139,7 @@ void main() { final seipd = SymEncryptedIntegrityProtectedDataPacket.fromBytes(base64.decode( 'AZgYpj5gnPi7oX4MOUME6vk1FBe38okh/ibiY6UrIL+6otumcslkydOrejv0bEFN0h07OEdd8DempXiZPMU=', - )).decrypt(sessionKey.encryptionKey, symmetric: sessionKey.symmetric); + )).decrypt(sessionKey.encryptionKey, sessionKey.symmetric); final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); }); @@ -160,7 +160,7 @@ void main() { final seipd = SymEncryptedIntegrityProtectedDataPacket.fromBytes(base64.decode( 'AdJ1Sw56PRYiKZjCvHg+2bnq02s33AJJoyBexBI4QKATFRkyez2gldJldRysLVg77Mwwfgl2n/d572WciAM=', - )).decrypt(sessionKey.encryptionKey, symmetric: sessionKey.symmetric); + )).decrypt(sessionKey.encryptionKey, sessionKey.symmetric); final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); }); @@ -181,7 +181,7 @@ void main() { final seipd = SymEncryptedIntegrityProtectedDataPacket.fromBytes(base64.decode( 'AfirtbIE3SaPO19Vq7qe5dMCcqWZbNtVMHeu5vZKBetHnnx/yveQ9brJYlzhJvGskCUJma43+iur/T1sKjE=', - )).decrypt(sessionKey.encryptionKey, symmetric: sessionKey.symmetric); + )).decrypt(sessionKey.encryptionKey, sessionKey.symmetric); final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); }); @@ -209,7 +209,7 @@ void main() { expect( () => encrypt.decrypt( key, - symmetric: SymmetricAlgorithm.aes128, + SymmetricAlgorithm.aes128, ), throwsStateError, ); @@ -229,7 +229,7 @@ void main() { encrypted.data, ).decrypt( key, - symmetric: SymmetricAlgorithm.aes128, + SymmetricAlgorithm.aes128, ); final ldPacket = decrypted.packets!.elementAt(0); expect(ldPacket.data, equals(literalData.data)); @@ -276,7 +276,7 @@ void main() { final decryptSkesk = packets.whereType().first.decrypt(password); final decryptSeipd = packets.whereType().first.decrypt( decryptSkesk.sessionKey!.encryptionKey, - symmetric: decryptSkesk.sessionKey!.symmetric, + decryptSkesk.sessionKey!.symmetric, ); final literalData = decryptSeipd.packets!.whereType().first; expect(literalData.text, literalText); @@ -300,7 +300,7 @@ void main() { final decryptSkesk = packets.whereType().first.decrypt(password); final decryptSeipd = packets.whereType().first.decrypt( decryptSkesk.sessionKey!.encryptionKey, - symmetric: decryptSkesk.sessionKey!.symmetric, + decryptSkesk.sessionKey!.symmetric, ); final literalData = decryptSeipd.packets!.whereType().first; expect( @@ -337,7 +337,7 @@ void main() { final decryptSkesk = packets.whereType().first.decrypt(password); final decryptSeipd = packets.whereType().first.decrypt( decryptSkesk.sessionKey!.encryptionKey, - symmetric: decryptSkesk.sessionKey!.symmetric, + decryptSkesk.sessionKey!.symmetric, ); final literalData = decryptSeipd.packets!.whereType().first; expect(literalData.text, literalText); @@ -409,7 +409,7 @@ WUlZSqHRuD/11Hh9WQgbyikT0/HWN6MKUYaC5ozOr4w0KIsCOk2vdOU6ZCbwcZlm+ZJFfE8T1PGu final sessionKey = pkesk.sessionKey!; final seipd = packetList.whereType().first.decrypt( sessionKey.encryptionKey, - symmetric: sessionKey.symmetric, + sessionKey.symmetric, ); final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); @@ -480,7 +480,7 @@ heUWSSwsi+sdLtKcnQQfj/RDqmhO9tmfk8sRTu3Myp9tYJLnjngOxsNEMoRRgo7eBSLVjUQlQu8= final sessionKey = pkesk.sessionKey!; final seipd = packetList.whereType().first.decrypt( sessionKey.encryptionKey, - symmetric: sessionKey.symmetric, + sessionKey.symmetric, ); final comPacket = seipd.packets!.whereType().first; final literalData = comPacket.packets.whereType().first; @@ -522,7 +522,7 @@ PXVtziQ8IyqoGshWNSwpWcPZl9KW2mUBGVIpOtCg0j8BYZTkYDl0D/a62/c1QUyqjpXqgGhzXvuS final sessionKey = pkesk.sessionKey!; final seipd = packetList.whereType().first.decrypt( sessionKey.encryptionKey, - symmetric: sessionKey.symmetric, + sessionKey.symmetric, ); final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); @@ -563,7 +563,7 @@ dJb40maS final sessionKey = pkesk.sessionKey!; final seipd = packetList.whereType().first.decrypt( sessionKey.encryptionKey, - symmetric: sessionKey.symmetric, + sessionKey.symmetric, ); final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); @@ -602,7 +602,7 @@ SdJraH1GfEeeMdV9t8623Gu3xkQ4hXf+figKNUWdq+kwHGqbQQNoeai1TYYNCuY= final sessionKey = pkesk.sessionKey!; final seipd = packetList.whereType().first.decrypt( sessionKey.encryptionKey, - symmetric: sessionKey.symmetric, + sessionKey.symmetric, ); final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); @@ -642,7 +642,7 @@ UQi6XlKOsfRPH6ozJjwLn7nPdfz5pHPtR0QljShJqeM= final sessionKey = pkesk.sessionKey!; final seipd = packetList.whereType().first.decrypt( sessionKey.encryptionKey, - symmetric: sessionKey.symmetric, + sessionKey.symmetric, ); final literalData = seipd.packets!.whereType().first; expect(literalData.binary, literalText.toBytes()); @@ -689,7 +689,7 @@ ExAnXHXIb8I= final sessionKey = pkesk.sessionKey!; final seipd = packetList.whereType().first.decrypt( sessionKey.encryptionKey, - symmetric: sessionKey.symmetric, + sessionKey.symmetric, ); final literalData = seipd.packets!.whereType().first; expect(literalData.binary, 'Hello there'.toBytes()); From 85cef9c9392a82ca82f9d59b25adaacca5d4468d Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 09:25:00 +0700 Subject: [PATCH 149/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/message/cleartext_message.dart | 3 +- lib/src/message/encrypted_message.dart | 29 ++- lib/src/message/literal_message.dart | 39 +++- lib/src/openpgp.dart | 275 ++++++++++++++++++++----- test/dart_pg_test.dart | 16 -- 5 files changed, 279 insertions(+), 83 deletions(-) delete mode 100644 test/dart_pg_test.dart diff --git a/lib/src/message/cleartext_message.dart b/lib/src/message/cleartext_message.dart index 9e4f50c8..e7a11bbe 100644 --- a/lib/src/message/cleartext_message.dart +++ b/lib/src/message/cleartext_message.dart @@ -13,7 +13,6 @@ import 'package:dart_pg/src/type/key.dart'; import 'package:dart_pg/src/type/notation_data.dart'; import 'package:dart_pg/src/type/private_key.dart'; import 'package:dart_pg/src/type/signature.dart'; -import 'package:dart_pg/src/type/signed_message.dart'; /// Cleartext message class that represents an OpenPGP cleartext message. /// See RFC 9580, section 7. @@ -31,7 +30,7 @@ class CleartextMessage implements CleartextMessageInterface { ); @override - SignedMessageInterface sign( + sign( Iterable signingKeys, { Iterable recipients = const [], NotationDataInterface? notationData, diff --git a/lib/src/message/encrypted_message.dart b/lib/src/message/encrypted_message.dart index 64988ec9..dfb8c470 100644 --- a/lib/src/message/encrypted_message.dart +++ b/lib/src/message/encrypted_message.dart @@ -4,13 +4,15 @@ library; +import 'package:dart_pg/src/common/armor.dart'; import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/armor_type.dart'; import 'package:dart_pg/src/message/base_message.dart'; import 'package:dart_pg/src/message/literal_message.dart'; import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/packet_list.dart'; import 'package:dart_pg/src/type/encrypted_data_packet.dart'; import 'package:dart_pg/src/type/encrypted_message.dart'; -import 'package:dart_pg/src/type/literal_message.dart'; import 'package:dart_pg/src/type/private_key.dart'; import 'package:dart_pg/src/type/session_key.dart'; @@ -19,22 +21,29 @@ import 'package:dart_pg/src/type/session_key.dart'; final class EncryptedMessage extends BaseMessage implements EncryptedMessageInterface { SessionKeyInterface? _sessionKey; - EncryptedMessage(super.packetList); - - @override - EncryptedDataPacketInterface get encryptedPacket { - final packets = packetList.whereType(); - if (packets.isEmpty) { + EncryptedMessage(super.packetList) { + if (packetList.whereType().isEmpty) { throw StateError('No encrypted data in packet list.'); } - return packets.first; } + /// Read Literal message from armored string + factory EncryptedMessage.fromArmored(final String armored) { + final armor = Armor.decode(armored); + if (armor.type != ArmorType.message) { + throw ArgumentError('Armored text not of message type'); + } + return EncryptedMessage(PacketList.decode(armor.data)); + } + + @override + get encryptedPacket => packetList.whereType().first; + @override - SessionKeyInterface? get sessionKey => _sessionKey; + get sessionKey => _sessionKey; @override - LiteralMessageInterface decrypt({ + decrypt({ final Iterable decryptionKeys = const [], final Iterable passwords = const [], }) { diff --git a/lib/src/message/literal_message.dart b/lib/src/message/literal_message.dart index f0071eff..372bf499 100644 --- a/lib/src/message/literal_message.dart +++ b/lib/src/message/literal_message.dart @@ -4,8 +4,12 @@ library; +import 'dart:typed_data'; + +import 'package:dart_pg/src/common/armor.dart'; import 'package:dart_pg/src/common/config.dart'; import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/armor_type.dart'; import 'package:dart_pg/src/enum/compression_algorithm.dart'; import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; import 'package:dart_pg/src/message/base_message.dart'; @@ -28,17 +32,38 @@ import 'package:dart_pg/src/type/verification.dart'; /// OpenPGP literal message class /// Author Nguyen Van Nguyen final class LiteralMessage extends BaseMessage implements LiteralMessageInterface, SignedMessageInterface { - LiteralMessage(super.packetList); - - @override - get literalData { - final packets = packetList.whereType(); - if (packets.isEmpty) { + LiteralMessage(super.packetList) { + if (packetList.whereType().isEmpty) { throw StateError('No literal data in packet list.'); } - return packets.first; } + /// Read Literal message from armored string + factory LiteralMessage.fromArmored(final String armored) { + final armor = Armor.decode(armored); + if (armor.type != ArmorType.message) { + throw ArgumentError('Armored text not of message type'); + } + return LiteralMessage(PacketList.decode(armor.data)); + } + + factory LiteralMessage.fromLiteralData( + final Uint8List literalData, { + final String filename = '', + final DateTime? time, + }) { + return LiteralMessage(PacketList([ + LiteralDataPacket( + literalData, + filename: filename, + time: time, + ) + ])); + } + + @override + get literalData => packetList.whereType().first; + @override get signature => Signature( unwrapCompressed().whereType(), diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index de8990d0..0aeaa40e 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -4,116 +4,295 @@ library; +import 'dart:typed_data'; + +import 'enum/compression_algorithm.dart'; import 'enum/ecc.dart'; import 'enum/key_type.dart'; import 'enum/rsa_key_size.dart'; +import 'enum/symmetric_algorithm.dart'; +import 'key/private_key.dart'; +import 'key/public_key.dart'; +import 'message/cleartext_message.dart'; +import 'message/encrypted_message.dart'; +import 'message/literal_message.dart'; +import 'message/signature.dart'; +import 'message/signed_message.dart'; +import 'type/cleartext_message.dart'; +import 'type/encrypted_message.dart'; +import 'type/key.dart'; +import 'type/literal_message.dart'; +import 'type/notation_data.dart'; +import 'type/private_key.dart'; +import 'type/signature.dart'; +import 'type/signed_message.dart'; +import 'type/verification.dart'; /// Export high level API for developers. /// Author Nguyen Van Nguyen final class OpenPGP { - bool get isAwesome => true; - /// Generate a new OpenPGP key pair. Support RSA, ECC, Curve25519 and Curve448 key types. /// The generated primary key will have signing capabilities. /// One subkey with encryption capabilities is also generated if `signOnly` is false. - generateKey( - Iterator userIDs, - String passphrase, { - KeyType type = KeyType.rsa, - RSAKeySize rsaKeySize = RSAKeySize.normal, - Ecc curve = Ecc.secp521r1, - int keyExpiry = 0, - DateTime? time, - }) {} + static PrivateKeyInterface generateKey( + final Iterable userIDs, + final String passphrase, { + final KeyType type = KeyType.rsa, + final RSAKeySize rsaKeySize = RSAKeySize.high, + final Ecc curve = Ecc.secp521r1, + final int keyExpiry = 0, + final DateTime? time, + }) { + return PrivateKey.generate( + userIDs, + passphrase, + type: type, + rsaKeySize: rsaKeySize, + curve: curve, + keyExpiry: keyExpiry, + time: time, + ); + } /// Read OpenPGP public key from armored string. /// Return a public key object. - readPublicKey(String keyData) {} + static KeyInterface readPublicKey(final String armored) { + return PublicKey.fromArmored(armored); + } /// Read OpenPGP public key list from armored string. /// Return iterator of public key objects. - readPublicKeys(String keyData) {} + static Iterable readPublicKeys(final String armored) { + return PublicKey.readPublicKeys(armored); + } /// Armor multiple public key. - armorPublicKeys() {} + static String armorPublicKeys(final Iterable keys) { + return PublicKey.armorPublicKeys(keys); + } /// Read OpenPGP private key from armored string. /// Return a private key object. - readPrivateKey(String keyData) {} + static PrivateKeyInterface readPrivateKey(final String armored) { + return PrivateKey.fromArmored(armored); + } /// Lock a private key with the given passphrase. /// The private key must be decrypted. - encryptPrivateKey() {} + static PrivateKeyInterface encryptPrivateKey( + final PrivateKeyInterface privateKey, + final String passphrase, [ + final Iterable subkeyPassphrases = const [], + ]) { + return privateKey.encrypt(passphrase, subkeyPassphrases); + } /// Read & unlock OpenPGP private key with the given passphrase. - decryptPrivateKey() {} - - /// Certify an OpenPGP key by a private key. - /// Return clone of the key object with the new certification added. - certifyKey() {} - - /// Revoke an OpenPGP key by a private key. - /// Return clone of the key object with the new revocation signature added. - revokeKey() {} + static PrivateKeyInterface decryptPrivateKey( + final String armored, + final String passphrase, [ + final Iterable subkeyPassphrases = const [], + ]) { + return PrivateKey.fromArmored(armored).decrypt( + passphrase, + subkeyPassphrases, + ); + } /// Read OpenPGP signature from armored string. /// Return a signature object. - readSignature(String signatureData) {} + static SignatureInterface readSignature(final String armored) { + return Signature.fromArmored(armored); + } /// Read OpenPGP signed message from armored string. /// Return a signed message object. - readSignedMessage(String messageData) {} - - /// Read OpenPGP encrypted message from armored string. - /// Return an encrypted message object. - readEncryptedMessage(String messageData) {} + static SignedMessageInterface readSignedMessage(final String armored) { + return SignedMessage.fromArmored(armored); + } /// Read OpenPGP literal message from armored string. /// Return a literal message object. - readLiteralMessage(String messageData) {} + static LiteralMessageInterface readLiteralMessage(final String armored) { + return LiteralMessage.fromArmored(armored); + } + + /// Read OpenPGP encrypted message from armored string. + /// Return an encrypted message object. + static EncryptedMessageInterface readEncryptedMessage(final String armored) { + return EncryptedMessage.fromArmored(armored); + } /// Create new cleartext message object from text. - createCleartextMessage(String text) {} + static CleartextMessageInterface createCleartextMessage(final String text) { + return CleartextMessage(text); + } /// Create new literal message object from literal data. - createLiteralMessage( - String literalData, { - String filename = '', - DateTime? time, - }) {} + static LiteralMessageInterface createLiteralMessage( + final Uint8List literalData, { + final String filename = '', + final DateTime? time, + }) { + return LiteralMessage.fromLiteralData( + literalData, + filename: filename, + time: time, + ); + } /// Sign a cleartext message. /// Return a signed message object. - signCleartext() {} + static SignedMessageInterface signCleartext( + final String text, + final Iterable signingKeys, { + final Iterable recipients = const [], + final NotationDataInterface? notationData, + final DateTime? time, + }) { + return CleartextMessage(text).sign( + signingKeys, + recipients: recipients, + notationData: notationData, + time: time, + ); + } /// Sign a cleartext message & return detached signature. - signDetachedCleartext() {} + static SignatureInterface signDetachedCleartext( + final String text, + final Iterable signingKeys, { + final Iterable recipients = const [], + final NotationDataInterface? notationData, + final DateTime? time, + }) { + return CleartextMessage(text).signDetached( + signingKeys, + recipients: recipients, + notationData: notationData, + time: time, + ); + } /// Sign a message & return signed literal message. - sign() {} + static LiteralMessageInterface sign( + final LiteralMessageInterface message, + final Iterable signingKeys, { + final Iterable recipients = const [], + final NotationDataInterface? notationData, + final DateTime? time, + }) { + return message.sign( + signingKeys, + recipients: recipients, + notationData: notationData, + time: time, + ); + } /// Sign a message & return detached signature. - signDetached() {} + static SignatureInterface signDetached( + final LiteralMessageInterface message, + final Iterable signingKeys, { + final Iterable recipients = const [], + final NotationDataInterface? notationData, + final DateTime? time, + }) { + return message.signDetached( + signingKeys, + recipients: recipients, + notationData: notationData, + time: time, + ); + } /// Verify signatures of cleartext signed message. /// Return verification array. - verify() {} + static Iterable verify( + final String armored, + final Iterable verificationKeys, [ + final DateTime? time, + ]) { + return readSignedMessage(armored).verify( + verificationKeys, + time, + ); + } /// Verify detached signatures of cleartext message. /// Return verification array. - verifyDetached() {} + static Iterable verifyDetached( + final String text, + final String signature, + final Iterable verificationKeys, [ + final DateTime? time, + ]) { + return createCleartextMessage(text).verifyDetached( + verificationKeys, + readSignature(signature), + time, + ); + } /// Encrypt a message using public keys, passwords or both at once. /// At least one of `encryptionKeys`, `passwords`must be specified. /// If signing keys are specified, those will be used to sign the message. - encrypt() {} + static EncryptedMessageInterface encrypt( + LiteralMessageInterface message, { + final Iterable encryptionKeys = const [], + final Iterable passwords = const [], + final Iterable signingKeys = const [], + final SymmetricAlgorithm? symmetric, + final CompressionAlgorithm? compression, + final NotationDataInterface? notationData, + final DateTime? time, + }) { + return signingKeys.isEmpty + ? message.compress(compression).encrypt( + encryptionKeys: encryptionKeys, + passwords: passwords, + symmetric: symmetric, + ) + : message + .sign( + signingKeys, + recipients: encryptionKeys, + notationData: notationData, + time: time, + ) + .compress(compression) + .encrypt( + encryptionKeys: encryptionKeys, + passwords: passwords, + symmetric: symmetric, + ); + } /// Decrypt a message with the user's private keys, or passwords. /// One of `decryptionKeys` or `passwords` must be specified. - decrypt() {} + static LiteralMessageInterface decrypt( + EncryptedMessageInterface message, { + final Iterable decryptionKeys = const [], + final Iterable passwords = const [], + }) { + return message.decrypt( + decryptionKeys: decryptionKeys, + passwords: passwords, + ); + } - /// Decrypt a armored/binary encrypted string with + /// Decrypt a armored encrypted string with /// the user's private keys, or passwords. /// One of `decryptionKeys` or `passwords` must be specified. - decryptMessage() {} + static LiteralMessageInterface decryptMessage( + String message, { + final Iterable decryptionKeys = const [], + final Iterable passwords = const [], + }) { + return decrypt( + readEncryptedMessage(message), + decryptionKeys: decryptionKeys, + passwords: passwords, + ); + } } diff --git a/test/dart_pg_test.dart b/test/dart_pg_test.dart deleted file mode 100644 index 153be31c..00000000 --- a/test/dart_pg_test.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:dart_pg/dart_pg.dart'; -import 'package:test/test.dart'; - -void main() { - group('A group of tests', () { - final awesome = OpenPGP(); - - setUp(() { - // Additional setup goes here. - }); - - test('First Test', () { - expect(awesome.isAwesome, isTrue); - }); - }); -} From 7500bb4e61e859bab03ba2df79591c36dea5bfd4 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 09:33:17 +0700 Subject: [PATCH 150/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/key/key_decryption_test.dart | 16 ++++++++-------- test/key/key_generation_test.dart | 26 +++++++++++++------------- test/key/key_reading_test.dart | 16 ++++++++-------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/test/key/key_decryption_test.dart b/test/key/key_decryption_test.dart index bec6fd31..f6493011 100644 --- a/test/key/key_decryption_test.dart +++ b/test/key/key_decryption_test.dart @@ -1,6 +1,6 @@ import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/enum/key_algorithm.dart'; -import 'package:dart_pg/src/key/private_key.dart'; +import 'package:dart_pg/src/openpgp.dart'; import 'package:test/test.dart'; void main() { @@ -70,7 +70,7 @@ GI+GuY8P5tMHtY7TbkZVr0qX2+zzusB4RVe0RcmI3GVM/wEZJHPPVrIpD1jhRxjZ =QeXb -----END PGP PRIVATE KEY BLOCK----- '''; - final privateKey = PrivateKey.fromArmored(armored).decrypt(passphrase); + final privateKey = OpenPGP.decryptPrivateKey(armored, passphrase); final user = privateKey.users[0]; final subkey = privateKey.subkeys[0]; @@ -139,7 +139,7 @@ CorNeTDAKJEwL5SJhzt+bmo+ =7ZVq -----END PGP PRIVATE KEY BLOCK----- '''; - final privateKey = PrivateKey.fromArmored(armored).decrypt(passphrase); + final privateKey = OpenPGP.decryptPrivateKey(armored, passphrase); final user = privateKey.users[0]; final subkey = privateKey.subkeys[0]; @@ -186,7 +186,7 @@ Lvrv2K9VfqgHGB2NDYsdLdXOIHardOmkFmD3rTbjsjHkQrLCs/lCQkVhAYCxmcY1 =rr8a -----END PGP PRIVATE KEY BLOCK----- '''; - final privateKey = PrivateKey.fromArmored(armored).decrypt(passphrase); + final privateKey = OpenPGP.decryptPrivateKey(armored, passphrase); final user = privateKey.users[0]; final subkey = privateKey.subkeys[0]; @@ -230,7 +230,7 @@ GwwACgkQmVEhn8nelXjEygEAiKyuVndDuoMDyeK0h1qNd0NCvS7YfGQbV8kqmWf1 =CNK9 -----END PGP PRIVATE KEY BLOCK----- '''; - final privateKey = PrivateKey.fromArmored(armored).decrypt(passphrase); + final privateKey = OpenPGP.decryptPrivateKey(armored, passphrase); final user = privateKey.users[0]; final subkey = privateKey.subkeys[0]; @@ -273,7 +273,7 @@ CgAgFiEEEUiyok9YCXfCeyYiPtR13SEtIh4FAmc/+gYCGwwACgkQPtR13SEtIh6w =4mmg -----END PGP PRIVATE KEY BLOCK----- '''; - final privateKey = PrivateKey.fromArmored(armored).decrypt(passphrase); + final privateKey = OpenPGP.decryptPrivateKey(armored, passphrase); final user = privateKey.users[0]; final subkey = privateKey.subkeys[0]; @@ -320,7 +320,7 @@ NDm6QTmbvRO5EjvPyQf9I1gqhYkVZExIvt/x5vpX/ID29c91PagKcr4eoJrl9Xyfn8ksYFkmXI/B C8sssUAhAtMI5sgtlfA7Ow7VHjDLcQ0doBgA9J4afJjd12/e+PYN4Kdqvt5Gbv5ltA== -----END PGP PRIVATE KEY BLOCK----- '''; - final privateKey = PrivateKey.fromArmored(armored).decrypt(passphrase); + final privateKey = OpenPGP.decryptPrivateKey(armored, passphrase); final directSignature = privateKey.directSignatures[0]; final user = privateKey.users[0]; final subkey = privateKey.subkeys[0]; @@ -378,7 +378,7 @@ lzIMFLqFsDiKPzuwpZBhzEh+sj0Z3WtqZOueBwxVocwDANUX0mClieCAd2U0VQHW7lMIkRYMY5ZS x6s= -----END PGP PRIVATE KEY BLOCK----- '''; - final privateKey = PrivateKey.fromArmored(armored).decrypt(passphrase); + final privateKey = OpenPGP.decryptPrivateKey(armored, passphrase); final directSignature = privateKey.directSignatures[0]; final user = privateKey.users[0]; final subkey = privateKey.subkeys[0]; diff --git a/test/key/key_generation_test.dart b/test/key/key_generation_test.dart index 376bcad5..a019855a 100644 --- a/test/key/key_generation_test.dart +++ b/test/key/key_generation_test.dart @@ -4,7 +4,7 @@ import 'package:dart_pg/src/enum/key_algorithm.dart'; import 'package:dart_pg/src/enum/key_type.dart'; import 'package:dart_pg/src/enum/key_version.dart'; import 'package:dart_pg/src/enum/rsa_key_size.dart'; -import 'package:dart_pg/src/key/private_key.dart'; +import 'package:dart_pg/src/openpgp.dart'; import 'package:test/test.dart'; void main() { @@ -13,7 +13,7 @@ void main() { final passphrase = Helper.generatePassword(); test('RSA key', () { - final privateKey = PrivateKey.generate( + final privateKey = OpenPGP.generateKey( [userID], passphrase, type: KeyType.rsa, @@ -23,7 +23,7 @@ void main() { expect(privateKey.keyStrength, RSAKeySize.high.bits); expect(privateKey.users[0].userID, userID); - final priKey = PrivateKey.fromArmored( + final priKey = OpenPGP.readPrivateKey( privateKey.armor(), ).decrypt(passphrase); expect(priKey.fingerprint, privateKey.fingerprint); @@ -35,7 +35,7 @@ void main() { }); test('ECDSA NIST P-384 key', () { - final privateKey = PrivateKey.generate( + final privateKey = OpenPGP.generateKey( [userID], passphrase, type: KeyType.ecc, @@ -49,7 +49,7 @@ void main() { expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); expect(subkey.keyStrength, 384); - final priKey = PrivateKey.fromArmored( + final priKey = OpenPGP.readPrivateKey( privateKey.armor(), ).decrypt(passphrase); expect(priKey.fingerprint, privateKey.fingerprint); @@ -61,7 +61,7 @@ void main() { }); test('ECDSA Brainpool P-512 key', () { - final privateKey = PrivateKey.generate( + final privateKey = OpenPGP.generateKey( [userID], passphrase, type: KeyType.ecc, @@ -75,7 +75,7 @@ void main() { expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); expect(subkey.keyStrength, 512); - final priKey = PrivateKey.fromArmored( + final priKey = OpenPGP.readPrivateKey( privateKey.armor(), ).decrypt(passphrase); expect(priKey.fingerprint, privateKey.fingerprint); @@ -87,7 +87,7 @@ void main() { }); test('EdDSA legacy key', () { - final privateKey = PrivateKey.generate( + final privateKey = OpenPGP.generateKey( [userID], passphrase, type: KeyType.ecc, @@ -101,7 +101,7 @@ void main() { expect(subkey.keyAlgorithm, KeyAlgorithm.ecdh); expect(subkey.keyStrength, 255); - final priKey = PrivateKey.fromArmored( + final priKey = OpenPGP.readPrivateKey( privateKey.armor(), ).decrypt(passphrase); expect(priKey.fingerprint, privateKey.fingerprint); @@ -113,7 +113,7 @@ void main() { }); test('RFC9580 Curve 25519 key', () { - final privateKey = PrivateKey.generate( + final privateKey = OpenPGP.generateKey( [userID], passphrase, type: KeyType.curve25519, @@ -126,7 +126,7 @@ void main() { expect(subkey.keyAlgorithm, KeyAlgorithm.x25519); expect(subkey.keyStrength, 255); - final priKey = PrivateKey.fromArmored( + final priKey = OpenPGP.readPrivateKey( privateKey.armor(), ).decrypt(passphrase); expect(priKey.fingerprint, privateKey.fingerprint); @@ -138,7 +138,7 @@ void main() { }); test('RFC9580 Curve 448 key', () { - final privateKey = PrivateKey.generate( + final privateKey = OpenPGP.generateKey( [userID], passphrase, type: KeyType.curve448, @@ -151,7 +151,7 @@ void main() { expect(subkey.keyAlgorithm, KeyAlgorithm.x448); expect(subkey.keyStrength, 448); - final priKey = PrivateKey.fromArmored( + final priKey = OpenPGP.readPrivateKey( privateKey.armor(), ).decrypt(passphrase); expect(priKey.fingerprint, privateKey.fingerprint); diff --git a/test/key/key_reading_test.dart b/test/key/key_reading_test.dart index 77a4251e..473c22c6 100644 --- a/test/key/key_reading_test.dart +++ b/test/key/key_reading_test.dart @@ -1,6 +1,6 @@ import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/enum/key_algorithm.dart'; -import 'package:dart_pg/src/key/public_key.dart'; +import 'package:dart_pg/src/openpgp.dart'; import 'package:test/test.dart'; void main() { @@ -40,7 +40,7 @@ hrmPD+bTB7WO025GVa9Kl9vs87rAeEVXtEXJiNxlTP8BGSRzz1ayKQ9Y4UcY2Q== =S9XV -----END PGP PUBLIC KEY BLOCK----- '''; - final publicKey = PublicKey.fromArmored(armored); + final publicKey = OpenPGP.readPublicKey(armored); final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -105,7 +105,7 @@ CgkQunJ9D2tQwoHyvwEA6NmUz1wxurA5xPduxnsqHRbMoy5IZy2WCLV7bi8T8BsA =3nnd -----END PGP PUBLIC KEY BLOCK----- '''; - final publicKey = PublicKey.fromArmored(armored); + final publicKey = OpenPGP.readPublicKey(armored); final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -148,7 +148,7 @@ mzhn+ek2wQOTGM2wO3qJVZ9Z+z5kkQXH6JtyfkZATJgA/kougNs= =K1/q -----END PGP PUBLIC KEY BLOCK----- '''; - final publicKey = PublicKey.fromArmored(armored); + final publicKey = OpenPGP.readPublicKey(armored); final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -189,7 +189,7 @@ k49k9YikNemTd0HPLRc= =38Dv -----END PGP PUBLIC KEY BLOCK----- '''; - final publicKey = PublicKey.fromArmored(armored); + final publicKey = OpenPGP.readPublicKey(armored); final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -228,7 +228,7 @@ Dy6c8mMkLVpS/zXLOAEAp3nZkkgY1MgZAfbx4BEq+uJv801qCBNeThFeOe8/3gs= =/O/r -----END PGP PUBLIC KEY BLOCK----- '''; - final publicKey = PublicKey.fromArmored(armored); + final publicKey = OpenPGP.readPublicKey(armored); final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -273,7 +273,7 @@ bPIvl2dct0Etc792kYzRRbUC3XDl5uzoLJj59aBkhkE+e5nX8j6hCfYSjQnPq9oKmMeOjdgkjeDM fjfQblPSXk32Zpv5RXzNhOIelkIuA9USba2Zw7ZRB4iuDfOgEJAw7VEt -----END PGP PUBLIC KEY BLOCK----- '''; - final publicKey = PublicKey.fromArmored(armored); + final publicKey = OpenPGP.readPublicKey(armored); final directSignature = publicKey.directSignatures[0]; final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -327,7 +327,7 @@ ISt80um5TscuNDSqmwCJCTLXDhblTcwseZGrBjlrnfij7+9VPQDPRzaYUSopXQDG4l8JFKpYxvEY FmXTxip3kr4= -----END PGP PUBLIC KEY BLOCK----- '''; - final publicKey = PublicKey.fromArmored(armored); + final publicKey = OpenPGP.readPublicKey(armored); final directSignature = publicKey.directSignatures[0]; final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; From d53a5b23dc7539e8dd9d68bd235271221ce55754 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 10:04:17 +0700 Subject: [PATCH 151/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/public_key.dart | 26 +++++--- test/key/key_reading_test.dart | 115 +++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 9 deletions(-) diff --git a/lib/src/key/public_key.dart b/lib/src/key/public_key.dart index e1faa90d..9ba3bcee 100644 --- a/lib/src/key/public_key.dart +++ b/lib/src/key/public_key.dart @@ -46,6 +46,14 @@ final class PublicKey extends BaseKey { ), ), ); + } else { + publicKeys.add( + PublicKey( + PacketList( + packetList.packets.sublist(indexes[i]), + ), + ), + ); } } return publicKeys; @@ -56,15 +64,15 @@ final class PublicKey extends BaseKey { final Iterable keys, ) => Armor.encode( - ArmorType.publicKey, - Uint8List.fromList(keys - .map( - (key) => key.publicKey.packetList, - ) - .expand( - (packet) => packet.encode(), - ) - .toList())); + ArmorType.publicKey, + Uint8List.fromList(keys + .map( + (key) => key.publicKey.packetList, + ) + .expand( + (packet) => packet.encode(), + ) + .toList())); @override armor() => Armor.encode(ArmorType.publicKey, packetList.encode()); diff --git a/test/key/key_reading_test.dart b/test/key/key_reading_test.dart index 473c22c6..d92b9fb7 100644 --- a/test/key/key_reading_test.dart +++ b/test/key/key_reading_test.dart @@ -1,4 +1,6 @@ +import 'package:dart_pg/src/common/armor.dart'; import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/armor_type.dart'; import 'package:dart_pg/src/enum/key_algorithm.dart'; import 'package:dart_pg/src/openpgp.dart'; import 'package:test/test.dart'; @@ -354,5 +356,118 @@ FmXTxip3kr4= expect(user.userID, userID); }); + + test('Multiple public keys', () { + const armored = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +7z/eCw== +-----END PGP PUBLIC KEY BLOCK----- +'''; + + final publicKeys = OpenPGP.readPublicKeys(armored).toList(); + expect(publicKeys.length, 5); + expect( + publicKeys[0].fingerprint.toHexadecimal(), + '5ccceda54f917089f8c488000532372a028f2ff5', + ); + expect( + publicKeys[1].fingerprint.toHexadecimal(), + '9a83344a3711864c1f502094ba727d0f6b50c281', + ); + expect( + publicKeys[2].fingerprint.toHexadecimal(), + 'a325107e66bcab3eea407550396ecb8bb86d1922', + ); + expect( + publicKeys[3].fingerprint.toHexadecimal(), + 'cd1b5b14294f80be65cefbef9951219fc9de9578', + ); + expect( + publicKeys[4].fingerprint.toHexadecimal(), + '1148b2a24f580977c27b26223ed475dd212d221e', + ); + + final armor = Armor.decode(OpenPGP.armorPublicKeys(publicKeys)); + expect(armor.type, ArmorType.publicKey); + }); }); } From 07a8a01af835c57732bcc4f81fb93b7e03b6f433 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 10:37:01 +0700 Subject: [PATCH 152/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/key/key_decryption_test.dart | 63 +++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/test/key/key_decryption_test.dart b/test/key/key_decryption_test.dart index f6493011..09abd9b8 100644 --- a/test/key/key_decryption_test.dart +++ b/test/key/key_decryption_test.dart @@ -91,6 +91,15 @@ GI+GuY8P5tMHtY7TbkZVr0qX2+zzusB4RVe0RcmI3GVM/wEZJHPPVrIpD1jhRxjZ expect(subkey.keyStrength, 2048); expect(user.userID, userID); + + final encrypted = OpenPGP.encryptPrivateKey( + privateKey, + Helper.generatePassword(), + ); + expect( + privateKey.fingerprint.toHexadecimal(), + encrypted.fingerprint.toHexadecimal(), + ); }); test('DSA & ElGamal key', () { @@ -160,6 +169,15 @@ CorNeTDAKJEwL5SJhzt+bmo+ expect(subkey.keyStrength, 2048); expect(user.userID, userID); + + final encrypted = OpenPGP.encryptPrivateKey( + privateKey, + Helper.generatePassword(), + ); + expect( + privateKey.fingerprint.toHexadecimal(), + encrypted.fingerprint.toHexadecimal(), + ); }); test('ECC NIST P-384 key', () { @@ -207,6 +225,15 @@ Lvrv2K9VfqgHGB2NDYsdLdXOIHardOmkFmD3rTbjsjHkQrLCs/lCQkVhAYCxmcY1 expect(subkey.keyStrength, 384); expect(user.userID, userID); + + final encrypted = OpenPGP.encryptPrivateKey( + privateKey, + Helper.generatePassword(), + ); + expect( + privateKey.fingerprint.toHexadecimal(), + encrypted.fingerprint.toHexadecimal(), + ); }); test('ECC Brainpool P-256 key', () { @@ -251,6 +278,15 @@ GwwACgkQmVEhn8nelXjEygEAiKyuVndDuoMDyeK0h1qNd0NCvS7YfGQbV8kqmWf1 expect(subkey.keyStrength, 256); expect(user.userID, userID); + + final encrypted = OpenPGP.encryptPrivateKey( + privateKey, + Helper.generatePassword(), + ); + expect( + privateKey.fingerprint.toHexadecimal(), + encrypted.fingerprint.toHexadecimal(), + ); }); test('ECC Curve 25519 key', () { @@ -294,6 +330,15 @@ CgAgFiEEEUiyok9YCXfCeyYiPtR13SEtIh4FAmc/+gYCGwwACgkQPtR13SEtIh6w expect(subkey.keyStrength, 255); expect(user.userID, userID); + + final encrypted = OpenPGP.encryptPrivateKey( + privateKey, + Helper.generatePassword(), + ); + expect( + privateKey.fingerprint.toHexadecimal(), + encrypted.fingerprint.toHexadecimal(), + ); }); test('Rfc9580 Curve 25519 key', () { @@ -346,6 +391,15 @@ C8sssUAhAtMI5sgtlfA7Ow7VHjDLcQ0doBgA9J4afJjd12/e+PYN4Kdqvt5Gbv5ltA== expect(subkey.keyStrength, 255); expect(user.userID, userID); + + final encrypted = OpenPGP.encryptPrivateKey( + privateKey, + Helper.generatePassword(), + ); + expect( + privateKey.fingerprint.toHexadecimal(), + encrypted.fingerprint.toHexadecimal(), + ); }); test('Rfc9580 Curve 448 key', () { @@ -404,6 +458,15 @@ x6s= expect(subkey.keyStrength, 448); expect(user.userID, userID); + + final encrypted = OpenPGP.encryptPrivateKey( + privateKey, + Helper.generatePassword(), + ); + expect( + privateKey.fingerprint.toHexadecimal(), + encrypted.fingerprint.toHexadecimal(), + ); }); }); } From 3432ee725422d6fc26336f294bf0139ca3c627ca Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 11:49:11 +0700 Subject: [PATCH 153/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/openpgp.dart | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index 0aeaa40e..d23de9cd 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -234,6 +234,19 @@ final class OpenPGP { ); } + /// Verify signatures of signed literal message. + /// Return verification array. + static Iterable verifyInline( + final String armored, + final Iterable verificationKeys, [ + final DateTime? time, + ]) { + return readLiteralMessage(armored).verify( + verificationKeys, + time, + ); + } + /// Encrypt a message using public keys, passwords or both at once. /// At least one of `encryptionKeys`, `passwords`must be specified. /// If signing keys are specified, those will be used to sign the message. From dea7f859b28f584193623b08119231d7b4b16564 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 12:29:30 +0700 Subject: [PATCH 154/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/message/cleartext_signing_test.dart | 7 + test/message/literal_data_test.dart | 7 + test/message/verification_test.dart | 481 +++++++++++++++++++++++ 3 files changed, 495 insertions(+) create mode 100644 test/message/cleartext_signing_test.dart create mode 100644 test/message/literal_data_test.dart create mode 100644 test/message/verification_test.dart diff --git a/test/message/cleartext_signing_test.dart b/test/message/cleartext_signing_test.dart new file mode 100644 index 00000000..d37a90ba --- /dev/null +++ b/test/message/cleartext_signing_test.dart @@ -0,0 +1,7 @@ +import 'package:test/test.dart'; + +void main() { + group('Sign cleartext', () {}); + + group('Verify signed message', () {}); +} diff --git a/test/message/literal_data_test.dart b/test/message/literal_data_test.dart new file mode 100644 index 00000000..3f05442e --- /dev/null +++ b/test/message/literal_data_test.dart @@ -0,0 +1,7 @@ +import 'package:test/test.dart'; + +void main() { + group('Sign & encrypt', () {}); + + group('Decrypt & verify', () {}); +} diff --git a/test/message/verification_test.dart b/test/message/verification_test.dart new file mode 100644 index 00000000..2109f871 --- /dev/null +++ b/test/message/verification_test.dart @@ -0,0 +1,481 @@ +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/openpgp.dart'; +import 'package:test/test.dart'; + +void main() { + group('Verify detached', () { + const literalText = 'Hello World :)'; + + test('with key Bob', () { + const publickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Bob's OpenPGP certificate + +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 +NEJd3XZRzaXZE2aAMQ== +=NXei +-----END PGP PUBLIC KEY BLOCK----- +'''; + const signature = ''' +-----BEGIN PGP SIGNATURE----- + +wsEpBAABCABdBYJnSvMCCZD7/MgqAV5zMDUUAAAAAAAcABBzYWx0QG5vdGF0aW9u +cy5vcGVucGdwanMub3Jncfoti0U5ghCOKmx5M7vbKBYhBNGmbhojsYLJmA94jPv8 +yCoBXnMwAABkYgv/dmXdeyHyGswzYiqG0u9Tf/zIAupHT3XByvFO4G4XqbOczc7r +bhWcku/bMjbqEr+16PqUrjknn/ZEhrCGZDJROxdVT+3FUnLkiTXOw+0Cb2tR29Rl +2qOWa7iKj1vE2qBEqQt1BCqM9OOFRCpawQp1aE/zDgjdR3pbNLGinK+tq2oj0GwT +8PA4CSnOnCVR+JNS+i8K8yKLo2m/GpBiHnMQ+ZlzPtY9lqNpQrIJXrN8q14oRZzO +KE6+DJJPmngh34LbqpZRxSfrFhZo/qdJGPwH2hi43dn/1PrS4w6Kpv/tGcHzhd7o +of+B9CPbeYhsB0MIVV2JX5FwTXwolEtHXRMpKv13MiRZaSeW49VkutzCJTweWLOS +6f2bx9xLnwwQU9u2n0obPTtQFd/CG2pc1uteX2DUyfmhcyTKEk9bJbdelK2YXteh +lvVdIiEZ2ukMebLI1UZqRofBRTIawPlDSHYe40x6PaeiZTMyMUHpcJeCrmwIs1Tx +zulYLxB5IjCH4MHD +=7AKp +-----END PGP SIGNATURE----- +'''; + final verifications = OpenPGP.verifyDetached( + literalText, + signature, + [OpenPGP.readPublicKey(publickey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'fbfcc82a015e7330'); + } + }); + + test('with key Carol', () { + const publickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=pa/S +-----END PGP PUBLIC KEY BLOCK----- +'''; + const signature = ''' +-----BEGIN PGP SIGNATURE----- + +wr0EABEKAG8FgmdK8yIJEJunidx21oSaRxQAAAAAAB4AIHNhbHRAbm90YXRpb25z +LnNlcXVvaWEtcGdwLm9yZxoGn2+mAvpD6F4t/5Mv8G+bAYvqtBMfZnt7DgK9w5hj +FiEEcf/aAEQJ5d2ww+jxm6eJ3HbWhJoAAMgfAP9K3yH+1UbwF1rHkIZWPmXcNp9H +VfWNEL0YDVD/chX5FwD/WlWByOZFFlhObRyo/0NVyUa3ZJX2ZJD2ro2kDOWQryg= +=7T8U +-----END PGP SIGNATURE----- +'''; + final verifications = OpenPGP.verifyDetached( + literalText, + signature, + [OpenPGP.readPublicKey(publickey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), '9ba789dc76d6849a'); + } + }); + + test('with key Alice', () { + const publickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Alice's OpenPGP certificate + +mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE +ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy +MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO +dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 +OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s +E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb +DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn +0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= +=iIGO +-----END PGP PUBLIC KEY BLOCK----- +'''; + const signature = ''' +-----BEGIN PGP SIGNATURE----- + +wrsEABYKAG0FgmdK8nwJkPIxVQxPR+OORRQAAAAAABwAIHNhbHRAbm90YXRp +b25zLm9wZW5wZ3Bqcy5vcmcQY0f4qE+jcGriU6TzdxVcZSYLXnJc7+8wxER4 +dhvc/RYhBOuFu1+jOnXhXpROY/IxVQxPR+OOAABSKgD/Ru+U4YtagCKdx6wy +AixSwExUcmhM0KL3yJcE8oUd5agBALC4yA7lz2Ri489Zs0whRcfXZjTIM8Ga +P7gvxbUc0GsL +=BBPw +-----END PGP SIGNATURE----- +'''; + final verifications = OpenPGP.verifyDetached( + literalText, + signature, + [OpenPGP.readPublicKey(publickey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'f231550c4f47e38e'); + } + }); + + test('with key from RFC9580', () { + const publickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf +GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy +KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw +gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE +QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn ++eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh +BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 +j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 +I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== +-----END PGP PUBLIC KEY BLOCK----- +'''; + const signature = ''' +-----BEGIN PGP SIGNATURE----- + +wpgGABsKAAAAKQWCZ0ryciKhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul +7Ce62azJAAAAAHlcIJnq4Ut3hpyC3QLKpEiPNbKIu9jLN6v+v9OQtsay2sJ1 +i6UgQbwhFpsEXmXEZePugZCqC26UmoFor/Ju80rseo9mXnSkmPJj1BuE+5wG +3WpEmDMxR0D/51a8cC+2BBn4Cw== +-----END PGP SIGNATURE----- +'''; + final verifications = OpenPGP.verifyDetached( + literalText, + signature, + [OpenPGP.readPublicKey(publickey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'cb186c4f0609a697'); + } + }); + }); + + group('Verify inline', () { + test('with key Bob', () { + const publickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Bob's OpenPGP certificate + +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 +NEJd3XZRzaXZE2aAMQ== +=NXei +-----END PGP PUBLIC KEY BLOCK----- +'''; + const message = ''' +-----BEGIN PGP MESSAGE----- + +xA0DAAoB+/zIKgFeczAByxRiAGdK8nNIZWxsbyBXb3JsZCA6KcLBOQQAAQoA +bQWCZ0rycwmQ+/zIKgFeczBFFAAAAAAAHAAgc2FsdEBub3RhdGlvbnMub3Bl +bnBncGpzLm9yZ6vzZTkkQwaMoqVBpRFpUhVfjvBXVDZ7JoJaoRQMTB0/FiEE +0aZuGiOxgsmYD3iM+/zIKgFeczAAADFdC/9M8de86hvnyOB5Q5SIpUs3NIis +XDHCgeEgAAuXyIqNuWsZYfcbd6GXIWfhJunSCzXMmC+K3YkSkIpm9L3bPzxf +xlxC9oHCRKgIb5CZp8nlKis17Pg8S9jBPP7rk5/Q+pYCoGEbLpsg5ryJ7Clg +x3uVxnChqRiWTy/MG0k8LWBss/BQo6WVk8j3ME4+Yz7M1hTlCRgCbA52ICtk +yajvo5ZYIa4WSX9bgy79Tv5VtBhplfmCepBmdJcGF1POu2ZOkp+FIEySwcH+ +ykmM9a/7gQdOiFrS/PAZlGV3uOv2V6eoaBxwnsU2vOfkWX8s6RltEZOwJ5TJ +Etshm3Rxcnv2fXLO/xMs8zu896qYZMueZ4jXGk+Iut5QtVtZZJxxW8Q+/oWR +FYXusxNdYrb12viqsjpdJ4m/oH2q6556e3zmvO7rlL/RnlWCdtOnFhfL8hyF +XtCxLFlvTgqqMZFQGT0mZcVmcP2prcBvppxqyjKaEccO1GS4mJ/BTJkFWfA3 +AgYWB/E= +=Fi+1 +-----END PGP MESSAGE----- +'''; + final verifications = OpenPGP.verifyInline( + message, + [OpenPGP.readPublicKey(publickey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'fbfcc82a015e7330'); + } + }); + + test('with key Carol', () { + const publickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=pa/S +-----END PGP PUBLIC KEY BLOCK----- +'''; + const message = ''' +-----BEGIN PGP MESSAGE----- + +xA0DAAoRm6eJ3HbWhJoByxRiAAAAAABIZWxsbyBXb3JsZCA6KcK9BAARCgBvBYJn +SvLnCRCbp4ncdtaEmkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBn +cC5vcme39jeR4Dm6DkXxp9TVRLYmydOKTSeGdGLcXgv78QnCLBYhBHH/2gBECeXd +sMPo8Zunidx21oSaAAAmhgEAvVOllWNmbX/9v/3MDbwSolVZoOhHHj8KEbVv0iRT +WnEBAMAO5xOUJ/DjT/dPn+D95gk7PleCYBBPX7kdfOWVXJjt +=0h5J +-----END PGP MESSAGE----- +'''; + final verifications = OpenPGP.verifyInline( + message, + [OpenPGP.readPublicKey(publickey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), '9ba789dc76d6849a'); + } + }); + + test('with key Alice', () { + const publickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Alice's OpenPGP certificate + +mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE +ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy +MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO +dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 +OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s +E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb +DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn +0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= +=iIGO +-----END PGP PUBLIC KEY BLOCK----- +'''; + const message = ''' +-----BEGIN PGP MESSAGE----- + +xA0DAAoW8jFVDE9H444ByxRiAGdK8rVIZWxsbyBXb3JsZCA6KcK7BAAWCgBt +BYJnSvK1CZDyMVUMT0fjjkUUAAAAAAAcACBzYWx0QG5vdGF0aW9ucy5vcGVu +cGdwanMub3JnfPNT4vSssXxZkCUaXVOjNZ2Xfmk+zuEbBLV6VH3jPDgWIQTr +hbtfozp14V6UTmPyMVUMT0fjjgAAAVIBAJmGEARKx44At+Q+3yB03g1K6YXC +SjsuuqTYd5OV3P08AP4gjbsJVlPLXf8Kk0E0daoVBCx9Ypa8DhVAAl1HsIbQ +Bg== +=aG5M +-----END PGP MESSAGE----- +'''; + final verifications = OpenPGP.verifyInline( + message, + [OpenPGP.readPublicKey(publickey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'f231550c4f47e38e'); + } + }); + + test('with key from RFC9580', () { + const publickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf +GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy +KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw +gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE +QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn ++eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh +BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 +j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 +I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== +-----END PGP PUBLIC KEY BLOCK----- +'''; + const message = ''' +-----BEGIN PGP MESSAGE----- + +xEYGAAobIPGbvDQPJYi9/qkn2Wcrl0z39nMJVJTDUxFmyNPmvC9PyxhsTwYJ +ppfk1S36bHIrDB8eJ8GKVnCPZSXsJ7rZrMkByxRiAGdK8mlIZWxsbyBXb3Js +ZCA6KcKYBgAbCgAAACkFgmdK8mkioQbLGGxPBgmml+TVLfpscisMHx4nwYpW +cI9lJewnutmsyQAAAACuKCDxm7w0DyWIvf6pJ9lnK5dM9/ZzCVSUw1MRZsjT +5rwvT5z78KFKbCRryWKPwitzIEWXNzbS1DmZNigFFzBJ81sW2iV12iaHtQbD +Keo3KDD1pX3QV6YKK+dWuA34xgJ5bAw= +-----END PGP MESSAGE----- +'''; + final verifications = OpenPGP.verifyInline( + message, + [OpenPGP.readPublicKey(publickey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'cb186c4f0609a697'); + } + }); + }); +} From 734c2eded38edf5509aeb976e81f5f50aa4e11ba Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 13:31:37 +0700 Subject: [PATCH 155/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/armor.dart | 10 +++++++--- lib/src/common/extensions.dart | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/src/common/armor.dart b/lib/src/common/armor.dart index 2384c679..c2276497 100644 --- a/lib/src/common/armor.dart +++ b/lib/src/common/armor.dart @@ -72,7 +72,7 @@ class Armor { final textLines = []; final dataLines = []; - final lines = LineSplitter.split(armored); + final lines = armored.split('\n'); for (final line in lines) { if (type == null && splitPattern.hasMatch(line)) { type = ArmorType.fromBegin(line); @@ -81,9 +81,13 @@ class Armor { headers.add(line); } else if (!textDone && type == ArmorType.signedMessage) { if (!splitPattern.hasMatch(line)) { - textLines.add(line.replaceAll(RegExp(r'^- '), '')); + textLines.add(line.trimRight().replaceAll(RegExp(r'^- '), '')); } else { textDone = true; + if (textLines.isNotEmpty) { + /// Remove first empty line (not included in the message digest) + textLines.removeAt(0); + } } } else if (!splitPattern.hasMatch(line)) { if (emptyLinePattern.hasMatch(line)) { @@ -98,7 +102,7 @@ class Armor { } } - final text = textLines.join('\r\n').trim(); + final text = textLines.join('\r\n'); final data = base64.decode(dataLines.join().trim()); if ((checksum != _crc24Checksum(data)) && (checksum.isNotEmpty || Config.checksumRequired)) { diff --git a/lib/src/common/extensions.dart b/lib/src/common/extensions.dart index 6fde2162..2b6e7027 100644 --- a/lib/src/common/extensions.dart +++ b/lib/src/common/extensions.dart @@ -145,11 +145,11 @@ extension StringExt on String { bool hasMatch(final String text) => RegExp(this).hasMatch(text); - String removeTrailingSpaces() => LineSplitter.split(this) + String removeTrailingSpaces() => split('\n') .map( (line) => line.trimRight(), ) - .join(r'\n'); + .join('\r\n'); } /// Uint8List extension From 60f5e7c3b64678fff355f19dd225787cd96f9977 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 14:08:45 +0700 Subject: [PATCH 156/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/message/signed_message.dart | 4 ++-- lib/src/openpgp.dart | 3 ++- lib/src/type/cleartext_message.dart | 4 ++-- lib/src/type/signed_cleartext_message.dart | 12 ++++++++++++ 4 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 lib/src/type/signed_cleartext_message.dart diff --git a/lib/src/message/signed_message.dart b/lib/src/message/signed_message.dart index 2e718ba3..814653b8 100644 --- a/lib/src/message/signed_message.dart +++ b/lib/src/message/signed_message.dart @@ -11,14 +11,14 @@ import 'package:dart_pg/src/packet/packet_list.dart'; import 'package:dart_pg/src/type/key.dart'; import 'package:dart_pg/src/type/signature.dart'; import 'package:dart_pg/src/type/signature_packet.dart'; -import 'package:dart_pg/src/type/signed_message.dart'; +import 'package:dart_pg/src/type/signed_cleartext_message.dart'; import 'cleartext_message.dart'; /// Signed message class that represents an OpenPGP cleartext signed message. /// See RFC 9580, section 7. /// Author Nguyen Van Nguyen -final class SignedMessage extends CleartextMessage implements SignedMessageInterface { +final class SignedMessage extends CleartextMessage implements SignedCleartextMessageInterface { @override final SignatureInterface signature; diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index d23de9cd..bbc960e7 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -25,6 +25,7 @@ import 'type/literal_message.dart'; import 'type/notation_data.dart'; import 'type/private_key.dart'; import 'type/signature.dart'; +import 'type/signed_cleartext_message.dart'; import 'type/signed_message.dart'; import 'type/verification.dart'; @@ -143,7 +144,7 @@ final class OpenPGP { /// Sign a cleartext message. /// Return a signed message object. - static SignedMessageInterface signCleartext( + static SignedCleartextMessageInterface signCleartext( final String text, final Iterable signingKeys, { final Iterable recipients = const [], diff --git a/lib/src/type/cleartext_message.dart b/lib/src/type/cleartext_message.dart index cebdd071..b9e324d7 100644 --- a/lib/src/type/cleartext_message.dart +++ b/lib/src/type/cleartext_message.dart @@ -8,7 +8,7 @@ import 'key.dart'; import 'notation_data.dart'; import 'private_key.dart'; import 'signature.dart'; -import 'signed_message.dart'; +import 'signed_cleartext_message.dart'; import 'verification.dart'; /// Cleartext message interface @@ -23,7 +23,7 @@ abstract interface class CleartextMessageInterface { String get normalizeText; /// Sign the message - SignedMessageInterface sign( + SignedCleartextMessageInterface sign( final Iterable signingKeys, { final Iterable recipients = const [], final NotationDataInterface? notationData, diff --git a/lib/src/type/signed_cleartext_message.dart b/lib/src/type/signed_cleartext_message.dart new file mode 100644 index 00000000..52585270 --- /dev/null +++ b/lib/src/type/signed_cleartext_message.dart @@ -0,0 +1,12 @@ +/// Copyright 2024-present by Dart Privacy Guard project. All rights reserved. +/// For the full copyright and license information, please view the LICENSE +/// file that was distributed with this source code. + +library; + +import 'cleartext_message.dart'; +import 'signed_message.dart'; + +/// Signed cleartext message interface +/// Author Nguyen Van Nguyen +abstract interface class SignedCleartextMessageInterface implements CleartextMessageInterface, SignedMessageInterface {} From 108183bc0f5b11617b72126824f9965581e8d0b6 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 14:15:39 +0700 Subject: [PATCH 157/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/message/cleartext_signing_test.dart | 417 ++++++++++++++++++++++- 1 file changed, 415 insertions(+), 2 deletions(-) diff --git a/test/message/cleartext_signing_test.dart b/test/message/cleartext_signing_test.dart index d37a90ba..5d3f5bda 100644 --- a/test/message/cleartext_signing_test.dart +++ b/test/message/cleartext_signing_test.dart @@ -1,7 +1,420 @@ +import 'package:dart_pg/dart_pg.dart'; +import 'package:dart_pg/src/common/helpers.dart'; import 'package:test/test.dart'; void main() { - group('Sign cleartext', () {}); + group('Sign cleartext', () { + const cleartext = ''' +What we need from the grocery store: - group('Verify signed message', () {}); +- tofu +- vegetables +- noodles + +'''; + + test('with key Bob', () { + const privateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: Bob's OpenPGP Transferable Secret Key + +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 +=miES +-----END PGP PRIVATE KEY BLOCK----- +'''; + const publickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Bob's OpenPGP certificate + +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 +NEJd3XZRzaXZE2aAMQ== +=NXei +-----END PGP PUBLIC KEY BLOCK----- +'''; + + final signedMessage = OpenPGP.signCleartext(cleartext, [OpenPGP.readPrivateKey(privateKey)]); + expect(signedMessage.text, cleartext); + + final verifications = OpenPGP.verify( + signedMessage.armor(), + [OpenPGP.readPublicKey(publickey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'fbfcc82a015e7330'); + } + }); + + test('with key Alice', () { + const privateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: Alice's OpenPGP Transferable Secret Key + +lFgEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u10AAP9XBeW6lzGOLx7zHH9AsUDUTb2pggYGMzd0P3ulJ2AfvQ4RtCZBbGlj +ZSBMb3ZlbGFjZSA8YWxpY2VAb3BlbnBncC5leGFtcGxlPoiQBBMWCAA4AhsDBQsJ +CAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE64W7X6M6deFelE5j8jFVDE9H444FAl2l +nzoACgkQ8jFVDE9H447pKwD6A5xwUqIDprBzrHfahrImaYEZzncqb25vkLV2arYf +a78A/R3AwtLQvjxwLDuzk4dUtUwvUYibL2sAHwj2kGaHnfICnF0EXEcE6RIKKwYB +BAGXVQEFAQEHQEL/BiGtq0k84Km1wqQw2DIikVYrQrMttN8d7BPfnr4iAwEIBwAA +/3/xFPG6U17rhTuq+07gmEvaFYKfxRB6sgAYiW6TMTpQEK6IeAQYFggAIBYhBOuF +u1+jOnXhXpROY/IxVQxPR+OOBQJcRwTpAhsMAAoJEPIxVQxPR+OOWdABAMUdSzpM +hzGs1O0RkWNQWbUzQ8nUOeD9wNbjE3zR+yfRAQDbYqvtWQKN4AQLTxVJN5X5AWyb +Pnn+We1aTBhaGa86AQ== +=n8OM +-----END PGP PRIVATE KEY BLOCK----- +'''; + const publickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Alice's OpenPGP certificate + +mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE +ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy +MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO +dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 +OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s +E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb +DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn +0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= +=iIGO +-----END PGP PUBLIC KEY BLOCK----- +'''; + + final signedMessage = OpenPGP.signCleartext(cleartext, [OpenPGP.readPrivateKey(privateKey)]); + expect(signedMessage.text, cleartext); + + final verifications = OpenPGP.verify( + signedMessage.armor(), + [OpenPGP.readPublicKey(publickey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'f231550c4f47e38e'); + } + }); + + test('with key from RFC9580', () { + const privateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB +exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ +BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh +RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe +7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ +LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG +GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE +M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr +k0mXubZvyl4GBg== +-----END PGP PRIVATE KEY BLOCK----- +'''; + const publickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf +GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy +KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw +gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE +QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn ++eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh +BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 +j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 +I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== +-----END PGP PUBLIC KEY BLOCK----- +'''; + + final signedMessage = OpenPGP.signCleartext(cleartext, [OpenPGP.readPrivateKey(privateKey)]); + expect(signedMessage.text, cleartext); + + final verifications = OpenPGP.verify( + signedMessage.armor(), + [OpenPGP.readPublicKey(publickey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'cb186c4f0609a697'); + } + }); + }); + + group('Verify signed message', () { + test('with key Bob', () { + const publickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Bob's OpenPGP certificate + +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 +NEJd3XZRzaXZE2aAMQ== +=NXei +-----END PGP PUBLIC KEY BLOCK----- +'''; + const message = ''' +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +What we need from the grocery store: + +- - tofu +- - vegetables +- - noodles + +-----BEGIN PGP SIGNATURE----- + +wsEhBAEBCABVBQJnUpraFiEE0aZuGiOxgsmYD3iM+/zIKgFeczAJEPv8yCoBXnMwLRQAAAAAABQA +EHNhbHRAcGhwLW9wZW5wZ3Aub3JnR92gUpKLIpx83AT82JIFkgAA73sMAFlDhrz3kOFgHu8Gde5J +nSdnjX4gePuqo9ftVOY5SpSqZoF5/msBiBDg0cxmhNcgt9YrYQghXXeW6/UVRN9EAdmgoc3IJH4+ +0Zgx6t5FG5B3WlEkNhwaKqusyHPlqExUuYtNffFZr/Tq5fX6EYFm+TE4Wawv2iZ8QgqNMmLI2Yoo +DeE5lSWLugfs3N1u3sgUz2PmcRd400dIcUhVsxcC6ww1UV3busacRQ6SeitgaBKtr7SUnglSg4Wt +PT3WadAHhMHO7fN03uYHoFu782CQAzBxcYemF97muNKsM67krSgvUdOOusD0hl9LIyu+bQHTl8M6 +FuK0heVyuKubvl7jZYjCIiPIeOMVhY/SFdUZsdS8PGCVWVeUi9fawg8GTnbZblDIOc6Lac388F5O +6Ud0juKDzzrQbcCXOl9eW5amAluyF2CusmcmoZIlATXvx/cU3Kj7+9UoayBJoZHLiwuiua9jiAS+ +tzE+e1XVG0yeiFszkQzusP22sjen49qoDUPz6w== +-----END PGP SIGNATURE----- +'''; + final verifications = OpenPGP.verify( + message, + [OpenPGP.readPublicKey(publickey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'fbfcc82a015e7330'); + } + }); + + test('with key Alice', () { + const publickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Alice's OpenPGP certificate + +mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE +ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy +MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO +dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 +OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s +E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb +DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn +0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= +=iIGO +-----END PGP PUBLIC KEY BLOCK----- +'''; + const message = ''' +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +What we need from the grocery store: + +- - tofu +- - vegetables +- - noodles + +-----BEGIN PGP SIGNATURE----- + +wrMEARYKAGUFAmdSnJcWIQTrhbtfozp14V6UTmPyMVUMT0fjjgkQ8jFVDE9H4449FAAAAAAAFAAg +c2FsdEBwaHAtb3BlbnBncC5vcmd/HQUGqEXlSHw2e9ZyRojwCQQvt4Tzux4khKM9/8EulwAAd48B +AEwCfFc0o5a/pwjhd8VYv0VpSx6T6QoWesosqbOHZD+6AQA0w7TNC3TK+Ei+9+V83rC+9pMQlHbQ +IFe5XsSXLN9vBQ== +-----END PGP SIGNATURE----- +'''; + final verifications = OpenPGP.verify( + message, + [OpenPGP.readPublicKey(publickey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'f231550c4f47e38e'); + } + }); + + test('with key from RFC9580', () { + const publickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf +GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy +KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw +gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE +QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn ++eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh +BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 +j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 +I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== +-----END PGP PUBLIC KEY BLOCK----- +'''; + const message = ''' +-----BEGIN PGP SIGNED MESSAGE----- + +What we need from the grocery store: + +- - tofu +- - vegetables +- - noodles + +-----BEGIN PGP SIGNATURE----- + +wpgGARsKAAAAKQWCY5ijYyIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAGk2IHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usJ9BvuAqo +/FvLFuGWMbKAdA+epq7V4HOtAPlBWmU8QOd6aud+aSunHQaaEJ+iTFjP2OMW0KBr +NK2ay45cX1IVAQ== +-----END PGP SIGNATURE----- +'''; + final verifications = OpenPGP.verify( + message, + [OpenPGP.readPublicKey(publickey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'cb186c4f0609a697'); + } + }); + }); } From 5156b6074b5cf306dd5fc680accc822f6d0da987 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 14:27:34 +0700 Subject: [PATCH 158/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/message/signed_message.dart | 6 +++++- test/message/cleartext_signing_test.dart | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/src/message/signed_message.dart b/lib/src/message/signed_message.dart index 814653b8..c675b691 100644 --- a/lib/src/message/signed_message.dart +++ b/lib/src/message/signed_message.dart @@ -44,7 +44,11 @@ final class SignedMessage extends CleartextMessage implements SignedCleartextMes ArmorType.signedMessage, signature.packetList.encode(), text: text, - hashAlgo: signature.hashAlgorithms.map((hash) => hash.name).join(', '), + hashAlgo: signature.hashAlgorithms + .map( + (hash) => hash.name.toUpperCase(), + ) + .join(', '), ); } diff --git a/test/message/cleartext_signing_test.dart b/test/message/cleartext_signing_test.dart index 5d3f5bda..c6a160f7 100644 --- a/test/message/cleartext_signing_test.dart +++ b/test/message/cleartext_signing_test.dart @@ -10,7 +10,6 @@ What we need from the grocery store: - tofu - vegetables - noodles - '''; test('with key Bob', () { From c2b5eb2ff113d485a69165a4716e52c3f61af659 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 15:15:34 +0700 Subject: [PATCH 159/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/message/literal_data_test.dart | 702 +++++++++++++++++++++++++++- 1 file changed, 701 insertions(+), 1 deletion(-) diff --git a/test/message/literal_data_test.dart b/test/message/literal_data_test.dart index 3f05442e..1e89c857 100644 --- a/test/message/literal_data_test.dart +++ b/test/message/literal_data_test.dart @@ -1,7 +1,707 @@ +import 'dart:convert'; + +import 'package:dart_pg/dart_pg.dart'; +import 'package:dart_pg/src/common/helpers.dart'; import 'package:test/test.dart'; void main() { + group('Sign', () {}); + group('Sign & encrypt', () {}); - group('Decrypt & verify', () {}); + group('Decrypt & verify', () { + const literalText = 'Hello World :)'; + test('with key Bob', () { + const message = ''' +-----BEGIN PGP MESSAGE----- + +wcDMA3wvqk35PDeyAQv+M1BxrFVRm72/QLb+LGb4+OcxJ3NEJl+UASFzcbpA +OBdxcbU+lOKyN2sGWBh8D/ES/iv7dhmG71NG1dhklxbReM0VPQaaVM/l+s1S +YjxFa9JIl9IUbMFknIJFRT+PTpe3xhHWtsKy/3oiJgbksqOnQJXCH4ZtPuo9 +GqEcCtrm3pCMJiQFamB6mhn251t+9SiYb4Hw5Vo6nTJsenb2zosZh66wKj3Y +HmagIDS9LdMSW2rFwI72Nnj1wdyUXMqiUbilDOhpoTuSJFg6JCoMNCwIuGOF +W+BWmm1zuaV6qkQ40nmapBj6NTRm7bFqj5vxTrCLii2z7gZRzXn6HGx9qXw3 +UhLnuzNJoXFuzp9pul2bBVkkuusWJ+k7QWoqBSdJuZeOgJdK4qlC7AKE51e7 +o+y09JtVH8EdShGwDI8OL7Dbxkcwv5zOlntnD7d14OnMRIjkw1EwmSsaDUkH +v8z2qcO866ddqbA5x4H90XxKh1VxDTS0h1qvg/gY2QfVCPPFRdFL0sGKAW3H +LmmtucpQDkMRZJpqwDKJCugYeBtGKaoSlQ1f+pxC5qlZu37SdCr7OP1/P9Sc +p0g3phRw4QbPHJ2xu/0oXpucKwEGbPhAnZwHwRNNr+1dJJSPJ0lqvYPHwDWn +L5Jivev8hQ5MnkBKRg/ITyGxbsln42VLuhu3C+o7dm1J2fw4S9NKh0l4GLBJ +nfk0eUJ8qH8SKta85TYoMOpOFSJzSMKDssq5RfWQ+q7MWu0UxpOxBrbRfAm5 +D3IdtEm9DmyAhAD5HG6bo1TTMQHk/QuKF8VOtK/lMnPDEPbFjIRbD7NYKg0S +ay7sHWVOslk+KEpytmUT4uBrjFVPPdWjL3B28/0zaf/KI6TWsQ7v5diqT59S +/PkWPbNISiumTlUwnSwzEBE3lxNMpHzePSZwzJRHh+Dq0DhnmZYsglxgHsRj +gd3eqxVtzM6fbG0vG3QHM3canOpmYMZ+rHfBouNeb78BKLgHLnCHCc/wgK+1 +jzVzBZyzb3Su1t8MP349WyKDk3m/Z/Yy9tJTMCKI0VHgL74xhzrrUT5Iwsxr +XJ0ehIL1yIZH6+794dI13in7cxZataf80Apd2e8taXortOTLFP+z/0zQT8Hf +cSjtDCQZckGmNw4GiEXWpE2XSrDktiHSlLxOSTtUQsLzJfBftqFlC/ieLonO +ww9zG0LKI4f+mXSsBkTaQBwVsdHYfhAC2R1gGWTbjvNDScaTsbaNlo8LYJ47 +lFiOpCG/SDaD4bl7BYYf9o6c6Gs9vKf9mShmWUO0tg2NDlFAzdQTBOzaxw== +=BADY +-----END PGP MESSAGE----- +'''; + const privateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: Bob's OpenPGP Transferable Secret Key + +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 +=miES +-----END PGP PRIVATE KEY BLOCK----- +'''; + const publicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Bob's OpenPGP certificate + +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 +NEJd3XZRzaXZE2aAMQ== +=NXei +-----END PGP PUBLIC KEY BLOCK----- +'''; + + final literalMessage = OpenPGP.decryptMessage( + message, + decryptionKeys: [OpenPGP.readPrivateKey(privateKey)], + ); + expect(utf8.decode(literalMessage.literalData.binary), literalText); + + final verifications = literalMessage.verify( + [OpenPGP.readPublicKey(publicKey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'fbfcc82a015e7330'); + } + }); + + test('with key Carol', () { + const message = ''' +-----BEGIN PGP MESSAGE----- + +wcDMA3wvqk35PDeyAQv+P3jUz8+V8lGGZNr9BtbhbQJqzq29wa6U5dH4H/Qdqp60 +0AZ+dojl4G83PaBWxxNNWMvmdck15SCELb9e6JJoacMRn6cL9uuBz5v4Orjn3O1N +a735zZyS04bGiTKU0NUjUlxUYiLXfAepPThZtthMnOfUUW3a7nBKUuuKzqlu7nhw +BKCf58rsZZFXiXDYUxrGR/muoglxfb8KXixd/ZlhKgoYTgZYJ6vpYh3jZMSni/wr +49UOQx+H2Z6QEKaYoYu2FQoHbD1iNGxdI7a9xLXffnu6VO63Vq/ohN0/uXj6OBGU +r5GCspM3LPjAWevp+AxGdRa5RxQFbD6yODeI2coYZwUnqE1DejOEf9gRY2dDG9e2 +1poMUWBFT6rTGjisEOPZwZnANMSBfh8dHeMsE5cYO/BV14TFqwl7oc7LTH4VVG6I +llknMul0jDM71hBtbZcGVTz8xrdD165EmxxMS/FEBKZg15thiwkZwE2nMSQAtOhd +h+46EmEhJnW8GorsP4nm0sBNAXGupU8fjiE2iDjlGwe2ZU30G4jFOCTsojRC71m8 +g9S2yV9Bj3Dp+RGUgMiFOpdel00prWgsGGPAUTHYK/YeVYgiQkGu2bQbrImmI4nd +atMkRpD4VvoxVp4zKijUaBY9rYFOfytbLUtYqMjG6szoMUHCWYCLQgs4DKd794dS +waprJuJ73JBJYhZiYqm252qg0kZE1nlTxsTJpz1Ld0ZvF6S48JJyQz/YutMjlhrL +N9rHEwCTBV1djiA4b8oqdamwFTkU2ge0OorUMbi/bEvvYQkLO35G/hc4jRZVaiJO +wqP2oIPIsEeuMG40kub7RpJWnidJMnOoG7ZScRqbwkECF89DN4sYYREJkaaHFr8= +=JDtL +-----END PGP MESSAGE----- +'''; + const privateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: Bob's OpenPGP Transferable Secret Key + +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 +=miES +-----END PGP PRIVATE KEY BLOCK----- +'''; + const publicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=pa/S +-----END PGP PUBLIC KEY BLOCK----- +'''; + + final literalMessage = OpenPGP.decryptMessage( + message, + decryptionKeys: [OpenPGP.readPrivateKey(privateKey)], + ); + expect(utf8.decode(literalMessage.literalData.binary), literalText); + + final verifications = literalMessage.verify( + [OpenPGP.readPublicKey(publicKey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), '9ba789dc76d6849a'); + } + }); + + test('with key Alice', () { + const message = ''' +-----BEGIN PGP MESSAGE----- + +wcDMA3wvqk35PDeyAQv+N2n5QLKCt+0mZZXMXTJEABvg0VlIkigYesz6FNy/ +m9eLBlevQqtCjXZGMVDATHTkmKvzMTYfs+66qT37YRFiIGOd1NbwcDGXz2sQ +/lD1hwFlpWCmrqrZnG9abaa/OLRWFiXdCXFgaHNOZXnYM+/Dwdgt4bNCbW2w +Vkd/cPWv+ZcHCOyDl5fw09syOxvYzUCTB+KOZVsXI2StZpPCrRoF8zbLoy6p +q3pLEQEYi65iHbQ07DSgXS/1XJZ8ktKDvPRYysR7htwFU9KUaqDUUoDd7/sM +iz6NoV43yubfTAVVg3puLOyy9O1g/iQtCy7+Wa98YnyF5i9V7rFfIgERbcOz +xktONm6srYLq7SVIe2nWIcAFtbKjpwTXUyfKxkHR9zUHg+I/FuNtZbPtBwja +hKpvFo8Y6zotJgJA7Q6uWhWTYCmWqzc0VO4M5ZoZmA+mG3/QLnmtkBLTr7mb +tTwD42xn4np0uTqCdO5ypbEgoYEsksvpYSxq9t4pAzY9NdxXLOjX0sBLAcH9 +PHyPS3SQOHbqRFjaauY2lpATSLKspX1uvxiQU9P70ezSnMb3T4j0fVjRO7Wc +s/QdUMWuRFJtVZfld81Z/d7vPxwdCgnxmY+6UC/B7ilBfhaiFFhfVhRGg4wP +CNtkYfJ/8p0BPpysdSBiunk+Pbkm6p5zTyszFznC/RXprnEDOYLerAvuFxnC +6LjoOXOUhAsOaRM8HRqyeD4ilAotXR+FPzAHk8aGJ3SiDG9GUHve/JaIbZWm +GVnPCFp8hpLt6mAmH4VonnmYVhrkKReT2yfGgHhd5AL8bIa/bRbwcUatUPDG +xBfiBCSwAsSjvat9sW5YOIcqo6hUMnY804L61RklAJn93ohVOt0N +=Bbti +-----END PGP MESSAGE----- +'''; + const privateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: Bob's OpenPGP Transferable Secret Key + +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 +=miES +-----END PGP PRIVATE KEY BLOCK----- +'''; + const publicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Alice's OpenPGP certificate + +mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE +ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy +MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO +dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 +OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s +E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb +DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn +0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= +=iIGO +-----END PGP PUBLIC KEY BLOCK----- +'''; + + final literalMessage = OpenPGP.decryptMessage( + message, + decryptionKeys: [OpenPGP.readPrivateKey(privateKey)], + ); + expect(utf8.decode(literalMessage.literalData.binary), literalText); + + final verifications = literalMessage.verify( + [OpenPGP.readPublicKey(publicKey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'f231550c4f47e38e'); + } + }); + + test('with key Alice and Bob', () { + const message = ''' +-----BEGIN PGP MESSAGE----- + +wcDMA3wvqk35PDeyAQwAiXlhEhgJ6ptfYt3A/kkMMUFZIqDsXwQJ5xHLQLKU +uRXiIv7LcsezCNNUOAQaEP2fiM90qCEVNGpTDLohoH9eyINSFUSleFEbesaA ++gjOyxavxgcOiJbBZXvAeE/YJXwOpLj4otAU/GMPYQIY1CRXbgL29cEGWcuA +Bs4Ioc0FoqDUmBbDinHPo7JlllEVXaIkchW/52GwidEMJMNOpkyJ1mFzemb5 +o+hO3+8PaeWtnRyXe911GhCKslYfrUHgyimPBZiCF6tUxsZ0YhGWkgohQFWD +bgBa1hSeHt2ERvN2aU5qmRLoSkOT4gTKClKEfMQ47+MlINvtpdAsiwiC5q6n +uDpK+qQKaD8GLWCBn90nxCnkzTJanJzit9Ce4q0porpOVlUXMHq2KUOi91cz +QygoHlJvOTcqCR2KyQ1L25UuUUmzbklJ/R2UkX3uQG8wMlb10lWbEhxBytnM +wCe0LDVUzrT7BVs0QalEyH/PlJXLyMJ07Y1waegSQq3INw4gSh+70sJWAUvN +KN9wWFfbX1pOzZGJyhb1lQsCFcj7Yp4o6UPG1c97TEI9RL+SockgKQk+AGKo +YmHzM8xgtETw7Taps7wLQb52oeIVNbkhYZy+7HEPo8KzTUaf95LBkQeL/TfL +nGbnTFY6wWGoOcdTnRmt+anvAmvcvZ2nmK7JlX3M1Kv/vui283NliUAsptJx +YinNvAfhe8hxpObr/iFD0dcFGu8muXgqTohtJMt0izxjTqdaAMuI9rPXt+Mb +DfJN8VlJ0ghPbpbqMUDz3exckvcCXIevfLq+9kmKzKi83Dt5cQNNHoZ5gUs6 ++JGhpkx2TISnfIDXS2SDIiU+PwQDTpJTvfqsvV5+EmnLdB7X6ZapxB2eVacq +LRps9j6dhiAlHQpPZ36dth89EIsXfus4iWMA74URmOfa5Rq6sqyX2LAv0trQ +MajDkzGv5FKfBkgPoqfC3rJm88RLnIszdkWe6XsMYl+9lUnKXcMENGZSTsoe +hW79Xuc93ZfYcsO+r+RalM9colA5cvvPk6cTdtlGzBqBNTlOtEOdJeK487cV +GWrXeqU0sT901yr0O+JameTUYwPeHNiDigeVOr/vb/ogozwyPxq8JZZgdfQc +fQ8MNbFJze7r447BRM9WvyDGnVZgaj1IPz1+v+h97c4qSaDIfe5+QwdiNa4K +Lho0u9lfRTIRGL1efK8A+eVU4lfPZmZkzxbe67rmMUevGKo4kRypcXyknLSv +MSZVSMKpxAI6cJKCrC/i/j7mX5txLIrG/ufZpbR38XisPwE8asqi2eFwVynn +CstQEiZNOIjd0d+W9cN37TGsWdAyWwv2LTawjzwAhRYvs24/FgGYJmlqFy7O +wkzWnB5x/XqdMqWC1lYexLIPTqx9wkYGqAx+QI4vh8xysmBb9CJpvQkkXwkp +IARIvbdAzc++tARNhXUPPxjeUybMxmSH+SWGU/RqMxo8jaDp7YKhoNPv4B6v +lGTgNJurYorPkcWEPFRrJqtNvWxM9tqQhljCXQUvNniSdkwLZJqaxxWrwuPe +vMq9yq4ffP8QAc1kwjaK9aJ/J45fPw== +=M/i3 +-----END PGP MESSAGE----- +'''; + const privateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: Bob's OpenPGP Transferable Secret Key + +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 +=miES +-----END PGP PRIVATE KEY BLOCK----- +'''; + const alicePublicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Alice's OpenPGP certificate + +mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE +ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy +MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO +dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 +OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s +E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb +DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn +0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= +=iIGO +-----END PGP PUBLIC KEY BLOCK----- +'''; + const bobPublicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Bob's OpenPGP certificate + +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 +NEJd3XZRzaXZE2aAMQ== +=NXei +-----END PGP PUBLIC KEY BLOCK----- +'''; + + final literalMessage = OpenPGP.decryptMessage( + message, + decryptionKeys: [OpenPGP.readPrivateKey(privateKey)], + ); + + expect(utf8.decode(literalMessage.literalData.binary), literalText); + + final verifications = literalMessage.verify( + [OpenPGP.readPublicKey(alicePublicKey), OpenPGP.readPublicKey(bobPublicKey)], + ).toList(); + expect(verifications.length, 2); + expect(verifications[0].isVerified, isTrue); + expect(verifications[1].isVerified, isTrue); + expect(verifications[0].keyID.toHexadecimal(), 'f231550c4f47e38e'); + expect(verifications[1].keyID.toHexadecimal(), 'fbfcc82a015e7330'); + }); + }); } From 486abc39fb172e0d59186756ed80ea9b6100d8a7 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 15:41:02 +0700 Subject: [PATCH 160/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/message/base_message.dart | 10 +++--- lib/src/message/cleartext_message.dart | 18 +++++------ lib/src/message/encrypted_message.dart | 22 ++++++------- lib/src/message/literal_message.dart | 44 +++++++++++++------------- lib/src/message/signature.dart | 26 +++++++-------- lib/src/message/signed_message.dart | 16 +++++----- lib/src/message/verification.dart | 4 +-- 7 files changed, 70 insertions(+), 70 deletions(-) diff --git a/lib/src/message/base_message.dart b/lib/src/message/base_message.dart index 227b786b..e7376f4b 100644 --- a/lib/src/message/base_message.dart +++ b/lib/src/message/base_message.dart @@ -4,11 +4,11 @@ library; -import 'package:dart_pg/src/common/armor.dart'; -import 'package:dart_pg/src/enum/armor_type.dart'; -import 'package:dart_pg/src/type/armorable.dart'; -import 'package:dart_pg/src/type/packet_container.dart'; -import 'package:dart_pg/src/type/packet_list.dart'; +import '../common/armor.dart'; +import '../enum/armor_type.dart'; +import '../type/armorable.dart'; +import '../type/packet_container.dart'; +import '../type/packet_list.dart'; /// Base abstract OpenPGP message class /// Author Nguyen Van Nguyen diff --git a/lib/src/message/cleartext_message.dart b/lib/src/message/cleartext_message.dart index e7a11bbe..18d194f6 100644 --- a/lib/src/message/cleartext_message.dart +++ b/lib/src/message/cleartext_message.dart @@ -4,15 +4,15 @@ library; -import 'package:dart_pg/src/common/helpers.dart'; -import 'package:dart_pg/src/message/signature.dart'; -import 'package:dart_pg/src/message/signed_message.dart'; -import 'package:dart_pg/src/packet/base.dart'; -import 'package:dart_pg/src/type/cleartext_message.dart'; -import 'package:dart_pg/src/type/key.dart'; -import 'package:dart_pg/src/type/notation_data.dart'; -import 'package:dart_pg/src/type/private_key.dart'; -import 'package:dart_pg/src/type/signature.dart'; +import '../common/helpers.dart'; +import '../message/signature.dart'; +import '../message/signed_message.dart'; +import '../packet/base.dart'; +import '../type/cleartext_message.dart'; +import '../type/key.dart'; +import '../type/notation_data.dart'; +import '../type/private_key.dart'; +import '../type/signature.dart'; /// Cleartext message class that represents an OpenPGP cleartext message. /// See RFC 9580, section 7. diff --git a/lib/src/message/encrypted_message.dart b/lib/src/message/encrypted_message.dart index dfb8c470..babd42b7 100644 --- a/lib/src/message/encrypted_message.dart +++ b/lib/src/message/encrypted_message.dart @@ -4,17 +4,17 @@ library; -import 'package:dart_pg/src/common/armor.dart'; -import 'package:dart_pg/src/common/helpers.dart'; -import 'package:dart_pg/src/enum/armor_type.dart'; -import 'package:dart_pg/src/message/base_message.dart'; -import 'package:dart_pg/src/message/literal_message.dart'; -import 'package:dart_pg/src/packet/base.dart'; -import 'package:dart_pg/src/packet/packet_list.dart'; -import 'package:dart_pg/src/type/encrypted_data_packet.dart'; -import 'package:dart_pg/src/type/encrypted_message.dart'; -import 'package:dart_pg/src/type/private_key.dart'; -import 'package:dart_pg/src/type/session_key.dart'; +import '../common/armor.dart'; +import '../common/helpers.dart'; +import '../enum/armor_type.dart'; +import '../message/base_message.dart'; +import '../message/literal_message.dart'; +import '../packet/base.dart'; +import '../packet/packet_list.dart'; +import '../type/encrypted_data_packet.dart'; +import '../type/encrypted_message.dart'; +import '../type/private_key.dart'; +import '../type/session_key.dart'; /// OpenPGP encrypted message class /// Author Nguyen Van Nguyen diff --git a/lib/src/message/literal_message.dart b/lib/src/message/literal_message.dart index 372bf499..8658aba4 100644 --- a/lib/src/message/literal_message.dart +++ b/lib/src/message/literal_message.dart @@ -6,28 +6,28 @@ library; import 'dart:typed_data'; -import 'package:dart_pg/src/common/armor.dart'; -import 'package:dart_pg/src/common/config.dart'; -import 'package:dart_pg/src/common/helpers.dart'; -import 'package:dart_pg/src/enum/armor_type.dart'; -import 'package:dart_pg/src/enum/compression_algorithm.dart'; -import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; -import 'package:dart_pg/src/message/base_message.dart'; -import 'package:dart_pg/src/message/encrypted_message.dart'; -import 'package:dart_pg/src/message/signature.dart'; -import 'package:dart_pg/src/packet/base.dart'; -import 'package:dart_pg/src/packet/key/session_key.dart'; -import 'package:dart_pg/src/packet/packet_list.dart'; -import 'package:dart_pg/src/type/key.dart'; -import 'package:dart_pg/src/type/literal_data.dart'; -import 'package:dart_pg/src/type/literal_message.dart'; -import 'package:dart_pg/src/type/notation_data.dart'; -import 'package:dart_pg/src/type/packet_list.dart'; -import 'package:dart_pg/src/type/private_key.dart'; -import 'package:dart_pg/src/type/signature.dart'; -import 'package:dart_pg/src/type/signature_packet.dart'; -import 'package:dart_pg/src/type/signed_message.dart'; -import 'package:dart_pg/src/type/verification.dart'; +import '../common/armor.dart'; +import '../common/config.dart'; +import '../common/helpers.dart'; +import '../enum/armor_type.dart'; +import '../enum/compression_algorithm.dart'; +import '../enum/symmetric_algorithm.dart'; +import '../message/base_message.dart'; +import '../message/encrypted_message.dart'; +import '../message/signature.dart'; +import '../packet/base.dart'; +import '../packet/key/session_key.dart'; +import '../packet/packet_list.dart'; +import '../type/key.dart'; +import '../type/literal_data.dart'; +import '../type/literal_message.dart'; +import '../type/notation_data.dart'; +import '../type/packet_list.dart'; +import '../type/private_key.dart'; +import '../type/signature.dart'; +import '../type/signature_packet.dart'; +import '../type/signed_message.dart'; +import '../type/verification.dart'; /// OpenPGP literal message class /// Author Nguyen Van Nguyen diff --git a/lib/src/message/signature.dart b/lib/src/message/signature.dart index 98cb02f4..0276eaa1 100644 --- a/lib/src/message/signature.dart +++ b/lib/src/message/signature.dart @@ -4,19 +4,19 @@ library; -import 'package:dart_pg/src/common/armor.dart'; -import 'package:dart_pg/src/common/helpers.dart'; -import 'package:dart_pg/src/enum/armor_type.dart'; -import 'package:dart_pg/src/enum/hash_algorithm.dart'; -import 'package:dart_pg/src/message/verification.dart'; -import 'package:dart_pg/src/packet/base.dart'; -import 'package:dart_pg/src/packet/packet_list.dart'; -import 'package:dart_pg/src/type/cleartext_message.dart'; -import 'package:dart_pg/src/type/key.dart'; -import 'package:dart_pg/src/type/literal_data.dart'; -import 'package:dart_pg/src/type/signature.dart'; -import 'package:dart_pg/src/type/signature_packet.dart'; -import 'package:dart_pg/src/type/verification.dart'; +import '../common/armor.dart'; +import '../common/helpers.dart'; +import '../enum/armor_type.dart'; +import '../enum/hash_algorithm.dart'; +import '../message/verification.dart'; +import '../packet/base.dart'; +import '../packet/packet_list.dart'; +import '../type/cleartext_message.dart'; +import '../type/key.dart'; +import '../type/literal_data.dart'; +import '../type/signature.dart'; +import '../type/signature_packet.dart'; +import '../type/verification.dart'; /// Signature class /// Author Nguyen Van Nguyen diff --git a/lib/src/message/signed_message.dart b/lib/src/message/signed_message.dart index c675b691..ea7da877 100644 --- a/lib/src/message/signed_message.dart +++ b/lib/src/message/signed_message.dart @@ -4,14 +4,14 @@ library; -import 'package:dart_pg/src/common/armor.dart'; -import 'package:dart_pg/src/enum/armor_type.dart'; -import 'package:dart_pg/src/message/signature.dart'; -import 'package:dart_pg/src/packet/packet_list.dart'; -import 'package:dart_pg/src/type/key.dart'; -import 'package:dart_pg/src/type/signature.dart'; -import 'package:dart_pg/src/type/signature_packet.dart'; -import 'package:dart_pg/src/type/signed_cleartext_message.dart'; +import '../common/armor.dart'; +import '../enum/armor_type.dart'; +import '../message/signature.dart'; +import '../packet/packet_list.dart'; +import '../type/key.dart'; +import '../type/signature.dart'; +import '../type/signature_packet.dart'; +import '../type/signed_cleartext_message.dart'; import 'cleartext_message.dart'; diff --git a/lib/src/message/verification.dart b/lib/src/message/verification.dart index 87744ca1..1e11da8d 100644 --- a/lib/src/message/verification.dart +++ b/lib/src/message/verification.dart @@ -6,8 +6,8 @@ library; import 'dart:typed_data'; -import 'package:dart_pg/src/type/signature_packet.dart'; -import 'package:dart_pg/src/type/verification.dart'; +import '../type/signature_packet.dart'; +import '../type/verification.dart'; /// Verification class /// Author Nguyen Van Nguyen From 93561c274a78b0266d401d4ec0552dfd4d5d0875 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 15:44:11 +0700 Subject: [PATCH 161/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/message/cleartext_signing_test.dart | 2 +- test/message/literal_data_test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/message/cleartext_signing_test.dart b/test/message/cleartext_signing_test.dart index c6a160f7..3950ce62 100644 --- a/test/message/cleartext_signing_test.dart +++ b/test/message/cleartext_signing_test.dart @@ -1,5 +1,5 @@ -import 'package:dart_pg/dart_pg.dart'; import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/openpgp.dart'; import 'package:test/test.dart'; void main() { diff --git a/test/message/literal_data_test.dart b/test/message/literal_data_test.dart index 1e89c857..39941a1b 100644 --- a/test/message/literal_data_test.dart +++ b/test/message/literal_data_test.dart @@ -1,7 +1,7 @@ import 'dart:convert'; -import 'package:dart_pg/dart_pg.dart'; import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/openpgp.dart'; import 'package:test/test.dart'; void main() { From 5a1bd4b00bdaf2db8bae4a9eb2ff7c37d3d78b13 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 16:22:43 +0700 Subject: [PATCH 162/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/message/literal_data_test.dart | 518 +++++++++++++++++++++++++++- 1 file changed, 516 insertions(+), 2 deletions(-) diff --git a/test/message/literal_data_test.dart b/test/message/literal_data_test.dart index 39941a1b..16e41e61 100644 --- a/test/message/literal_data_test.dart +++ b/test/message/literal_data_test.dart @@ -5,12 +5,526 @@ import 'package:dart_pg/src/openpgp.dart'; import 'package:test/test.dart'; void main() { - group('Sign', () {}); + const literalText = 'Hello World :)'; + + group('Sign', () { + test('with key Bob', () { + const privateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: Bob's OpenPGP Transferable Secret Key + +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 +=miES +-----END PGP PRIVATE KEY BLOCK----- +'''; + const publicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Bob's OpenPGP certificate + +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 +NEJd3XZRzaXZE2aAMQ== +=NXei +-----END PGP PUBLIC KEY BLOCK----- +'''; + + final literalMessage = OpenPGP.sign( + OpenPGP.createLiteralMessage(literalText.toBytes()), + [OpenPGP.readPrivateKey(privateKey)], + ); + expect(utf8.decode(literalMessage.literalData.binary), literalText); + + final verifications = literalMessage.verify( + [OpenPGP.readPublicKey(publicKey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'fbfcc82a015e7330'); + } + }); + + test('detached with key Bob', () { + const privateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: Bob's OpenPGP Transferable Secret Key + +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 +=miES +-----END PGP PRIVATE KEY BLOCK----- +'''; + const publicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Bob's OpenPGP certificate + +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 +NEJd3XZRzaXZE2aAMQ== +=NXei +-----END PGP PUBLIC KEY BLOCK----- +'''; + + final signature = OpenPGP.signDetached( + OpenPGP.createLiteralMessage(literalText.toBytes()), + [OpenPGP.readPrivateKey(privateKey)], + ); + + var verifications = signature.verify( + [OpenPGP.readPublicKey(publicKey)], + OpenPGP.createLiteralMessage(literalText.toBytes()).literalData, + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'fbfcc82a015e7330'); + } + + verifications = signature.verifyCleartext( + [OpenPGP.readPublicKey(publicKey)], + OpenPGP.createCleartextMessage(literalText), + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'fbfcc82a015e7330'); + } + }); + + test('with key Alice', () { + const privateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: Alice's OpenPGP Transferable Secret Key + +lFgEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u10AAP9XBeW6lzGOLx7zHH9AsUDUTb2pggYGMzd0P3ulJ2AfvQ4RtCZBbGlj +ZSBMb3ZlbGFjZSA8YWxpY2VAb3BlbnBncC5leGFtcGxlPoiQBBMWCAA4AhsDBQsJ +CAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE64W7X6M6deFelE5j8jFVDE9H444FAl2l +nzoACgkQ8jFVDE9H447pKwD6A5xwUqIDprBzrHfahrImaYEZzncqb25vkLV2arYf +a78A/R3AwtLQvjxwLDuzk4dUtUwvUYibL2sAHwj2kGaHnfICnF0EXEcE6RIKKwYB +BAGXVQEFAQEHQEL/BiGtq0k84Km1wqQw2DIikVYrQrMttN8d7BPfnr4iAwEIBwAA +/3/xFPG6U17rhTuq+07gmEvaFYKfxRB6sgAYiW6TMTpQEK6IeAQYFggAIBYhBOuF +u1+jOnXhXpROY/IxVQxPR+OOBQJcRwTpAhsMAAoJEPIxVQxPR+OOWdABAMUdSzpM +hzGs1O0RkWNQWbUzQ8nUOeD9wNbjE3zR+yfRAQDbYqvtWQKN4AQLTxVJN5X5AWyb +Pnn+We1aTBhaGa86AQ== +=n8OM +-----END PGP PRIVATE KEY BLOCK----- +'''; + const publicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Alice's OpenPGP certificate + +mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE +ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy +MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO +dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 +OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s +E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb +DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn +0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= +=iIGO +-----END PGP PUBLIC KEY BLOCK----- +'''; + + final literalMessage = OpenPGP.sign( + OpenPGP.createLiteralMessage(literalText.toBytes()), + [OpenPGP.readPrivateKey(privateKey)], + ); + expect(utf8.decode(literalMessage.literalData.binary), literalText); + + final verifications = literalMessage.verify( + [OpenPGP.readPublicKey(publicKey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'f231550c4f47e38e'); + } + }); + + test('detached with key Alice', () { + const privateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: Alice's OpenPGP Transferable Secret Key + +lFgEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u10AAP9XBeW6lzGOLx7zHH9AsUDUTb2pggYGMzd0P3ulJ2AfvQ4RtCZBbGlj +ZSBMb3ZlbGFjZSA8YWxpY2VAb3BlbnBncC5leGFtcGxlPoiQBBMWCAA4AhsDBQsJ +CAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE64W7X6M6deFelE5j8jFVDE9H444FAl2l +nzoACgkQ8jFVDE9H447pKwD6A5xwUqIDprBzrHfahrImaYEZzncqb25vkLV2arYf +a78A/R3AwtLQvjxwLDuzk4dUtUwvUYibL2sAHwj2kGaHnfICnF0EXEcE6RIKKwYB +BAGXVQEFAQEHQEL/BiGtq0k84Km1wqQw2DIikVYrQrMttN8d7BPfnr4iAwEIBwAA +/3/xFPG6U17rhTuq+07gmEvaFYKfxRB6sgAYiW6TMTpQEK6IeAQYFggAIBYhBOuF +u1+jOnXhXpROY/IxVQxPR+OOBQJcRwTpAhsMAAoJEPIxVQxPR+OOWdABAMUdSzpM +hzGs1O0RkWNQWbUzQ8nUOeD9wNbjE3zR+yfRAQDbYqvtWQKN4AQLTxVJN5X5AWyb +Pnn+We1aTBhaGa86AQ== +=n8OM +-----END PGP PRIVATE KEY BLOCK----- +'''; + const publicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: Alice's OpenPGP certificate + +mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE +ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy +MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO +dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 +OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s +E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb +DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn +0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= +=iIGO +-----END PGP PUBLIC KEY BLOCK----- +'''; + + final signature = OpenPGP.signDetached( + OpenPGP.createLiteralMessage(literalText.toBytes()), + [OpenPGP.readPrivateKey(privateKey)], + ); + + var verifications = signature.verify( + [OpenPGP.readPublicKey(publicKey)], + OpenPGP.createLiteralMessage(literalText.toBytes()).literalData, + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'f231550c4f47e38e'); + } + + verifications = signature.verifyCleartext( + [OpenPGP.readPublicKey(publicKey)], + OpenPGP.createCleartextMessage(literalText), + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'f231550c4f47e38e'); + } + }); + + test('with key from RFC9580', () { + const privateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB +exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ +BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh +RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe +7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ +LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG +GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE +M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr +k0mXubZvyl4GBg== +-----END PGP PRIVATE KEY BLOCK----- +'''; + const publicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf +GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy +KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw +gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE +QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn ++eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh +BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 +j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 +I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== +-----END PGP PUBLIC KEY BLOCK----- +'''; + + final literalMessage = OpenPGP.sign( + OpenPGP.createLiteralMessage(literalText.toBytes()), + [OpenPGP.readPrivateKey(privateKey)], + ); + expect(utf8.decode(literalMessage.literalData.binary), literalText); + + final verifications = literalMessage.verify( + [OpenPGP.readPublicKey(publicKey)], + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'cb186c4f0609a697'); + } + }); + + test('detached with key from RFC9580', () { + const privateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB +exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ +BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh +RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe +7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ +LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG +GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE +M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr +k0mXubZvyl4GBg== +-----END PGP PRIVATE KEY BLOCK----- +'''; + const publicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf +GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy +KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw +gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE +QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn ++eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh +BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 +j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 +I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== +-----END PGP PUBLIC KEY BLOCK----- +'''; + + final signature = OpenPGP.signDetached( + OpenPGP.createLiteralMessage(literalText.toBytes()), + [OpenPGP.readPrivateKey(privateKey)], + ); + + var verifications = signature.verify( + [OpenPGP.readPublicKey(publicKey)], + OpenPGP.createLiteralMessage(literalText.toBytes()).literalData, + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'cb186c4f0609a697'); + } + + verifications = signature.verifyCleartext( + [OpenPGP.readPublicKey(publicKey)], + OpenPGP.createCleartextMessage(literalText), + ); + for (final verification in verifications) { + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'cb186c4f0609a697'); + } + }); + }); group('Sign & encrypt', () {}); group('Decrypt & verify', () { - const literalText = 'Hello World :)'; test('with key Bob', () { const message = ''' -----BEGIN PGP MESSAGE----- From 15299310de19d5c8dfc101883ae21fbfed3d13f4 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 16:47:07 +0700 Subject: [PATCH 163/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/openpgp.dart | 55 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index bbc960e7..c40e5c31 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -6,6 +6,7 @@ library; import 'dart:typed_data'; +import 'common/extensions.dart'; import 'enum/compression_algorithm.dart'; import 'enum/ecc.dart'; import 'enum/key_type.dart'; @@ -252,7 +253,7 @@ final class OpenPGP { /// At least one of `encryptionKeys`, `passwords`must be specified. /// If signing keys are specified, those will be used to sign the message. static EncryptedMessageInterface encrypt( - LiteralMessageInterface message, { + final LiteralMessageInterface message, { final Iterable encryptionKeys = const [], final Iterable passwords = const [], final Iterable signingKeys = const [], @@ -282,10 +283,58 @@ final class OpenPGP { ); } + /// Encrypt literal data using public keys, passwords or both at once. + /// At least one of `encryptionKeys`, `passwords`must be specified. + /// If signing keys are specified, those will be used to sign the message. + static encryptLiteralData( + final Uint8List literalData, { + final Iterable encryptionKeys = const [], + final Iterable passwords = const [], + final Iterable signingKeys = const [], + final SymmetricAlgorithm? symmetric, + final CompressionAlgorithm? compression, + final NotationDataInterface? notationData, + final DateTime? time, + }) { + return encrypt( + createLiteralMessage(literalData), + encryptionKeys: encryptionKeys, + passwords: passwords, + signingKeys: signingKeys, + symmetric: symmetric, + notationData: notationData, + time: time, + ); + } + + /// Encrypt cleartext using public keys, passwords or both at once. + /// At least one of `encryptionKeys`, `passwords`must be specified. + /// If signing keys are specified, those will be used to sign the message. + static EncryptedMessageInterface encryptCleartext( + final String cleartext, { + final Iterable encryptionKeys = const [], + final Iterable passwords = const [], + final Iterable signingKeys = const [], + final SymmetricAlgorithm? symmetric, + final CompressionAlgorithm? compression, + final NotationDataInterface? notationData, + final DateTime? time, + }) { + return encrypt( + createLiteralMessage(cleartext.toBytes()), + encryptionKeys: encryptionKeys, + passwords: passwords, + signingKeys: signingKeys, + symmetric: symmetric, + notationData: notationData, + time: time, + ); + } + /// Decrypt a message with the user's private keys, or passwords. /// One of `decryptionKeys` or `passwords` must be specified. static LiteralMessageInterface decrypt( - EncryptedMessageInterface message, { + final EncryptedMessageInterface message, { final Iterable decryptionKeys = const [], final Iterable passwords = const [], }) { @@ -299,7 +348,7 @@ final class OpenPGP { /// the user's private keys, or passwords. /// One of `decryptionKeys` or `passwords` must be specified. static LiteralMessageInterface decryptMessage( - String message, { + final String message, { final Iterable decryptionKeys = const [], final Iterable passwords = const [], }) { From c2845c5aa82f593ce79524f944cbff2b7528388a Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 16:50:10 +0700 Subject: [PATCH 164/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/openpgp.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index c40e5c31..37096d86 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -283,11 +283,11 @@ final class OpenPGP { ); } - /// Encrypt literal data using public keys, passwords or both at once. + /// Encrypt binary data using public keys, passwords or both at once. /// At least one of `encryptionKeys`, `passwords`must be specified. /// If signing keys are specified, those will be used to sign the message. - static encryptLiteralData( - final Uint8List literalData, { + static EncryptedMessageInterface encryptBinaryData( + final Uint8List bytes, { final Iterable encryptionKeys = const [], final Iterable passwords = const [], final Iterable signingKeys = const [], @@ -297,7 +297,7 @@ final class OpenPGP { final DateTime? time, }) { return encrypt( - createLiteralMessage(literalData), + createLiteralMessage(bytes), encryptionKeys: encryptionKeys, passwords: passwords, signingKeys: signingKeys, From 7a68479444cdc7104ff88f335ea122e579258c95 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Fri, 6 Dec 2024 17:07:20 +0700 Subject: [PATCH 165/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/message/literal_data_test.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/message/literal_data_test.dart b/test/message/literal_data_test.dart index 16e41e61..9bf19cb3 100644 --- a/test/message/literal_data_test.dart +++ b/test/message/literal_data_test.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/openpgp.dart'; +import 'package:dart_pg/src/packet/base.dart'; import 'package:test/test.dart'; void main() { @@ -290,7 +291,7 @@ NEJd3XZRzaXZE2aAMQ== var verifications = signature.verify( [OpenPGP.readPublicKey(publicKey)], - OpenPGP.createLiteralMessage(literalText.toBytes()).literalData, + LiteralDataPacket.fromText(literalText), ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -401,7 +402,7 @@ DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn var verifications = signature.verify( [OpenPGP.readPublicKey(publicKey)], - OpenPGP.createLiteralMessage(literalText.toBytes()).literalData, + LiteralDataPacket.fromText(literalText), ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -504,7 +505,7 @@ I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== var verifications = signature.verify( [OpenPGP.readPublicKey(publicKey)], - OpenPGP.createLiteralMessage(literalText.toBytes()).literalData, + LiteralDataPacket.fromText(literalText), ); for (final verification in verifications) { expect(verification.isVerified, isTrue); From 2039338fcb02a501aa7c96e2cd91c8191948e8c8 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sat, 7 Dec 2024 17:20:09 +0700 Subject: [PATCH 166/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/data/key_data.dart | 823 ++++++++++++++++++++++++++++++ test/key/key_decryption_test.dart | 257 ++-------- test/key/key_reading_test.dart | 273 +--------- 3 files changed, 863 insertions(+), 490 deletions(-) create mode 100644 test/data/key_data.dart diff --git a/test/data/key_data.dart b/test/data/key_data.dart new file mode 100644 index 00000000..ed65a779 --- /dev/null +++ b/test/data/key_data.dart @@ -0,0 +1,823 @@ +const rfc9580PublicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf +GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy +KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw +gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE +QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn ++eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh +BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 +j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 +I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== +-----END PGP PUBLIC KEY BLOCK----- +'''; + +const rfc9580PrivateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB +exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ +BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh +RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe +7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ +LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG +GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 +2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE +M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr +k0mXubZvyl4GBg== +-----END PGP PRIVATE KEY BLOCK----- +'''; + +const alicePublicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: EB85 BB5F A33A 75E1 5E94 4E63 F231 550C 4F47 E38E +Comment: Alice Lovelace + +xjMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u13NJkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+wpAE +ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy +MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO +dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gLO +OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s +E9+eviIDAQgHwngEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb +DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn +0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= +=QX3Q +-----END PGP PUBLIC KEY BLOCK----- +'''; + +const alicePrivateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: EB85 BB5F A33A 75E1 5E94 4E63 F231 550C 4F47 E38E +Comment: Alice Lovelace + +xVgEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u10AAP9XBeW6lzGOLx7zHH9AsUDUTb2pggYGMzd0P3ulJ2AfvQ4RzSZBbGlj +ZSBMb3ZlbGFjZSA8YWxpY2VAb3BlbnBncC5leGFtcGxlPsKQBBMWCAA4AhsDBQsJ +CAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE64W7X6M6deFelE5j8jFVDE9H444FAl2l +nzoACgkQ8jFVDE9H447pKwD6A5xwUqIDprBzrHfahrImaYEZzncqb25vkLV2arYf +a78A/R3AwtLQvjxwLDuzk4dUtUwvUYibL2sAHwj2kGaHnfICx10EXEcE6RIKKwYB +BAGXVQEFAQEHQEL/BiGtq0k84Km1wqQw2DIikVYrQrMttN8d7BPfnr4iAwEIBwAA +/3/xFPG6U17rhTuq+07gmEvaFYKfxRB6sgAYiW6TMTpQEK7CeAQYFggAIBYhBOuF +u1+jOnXhXpROY/IxVQxPR+OOBQJcRwTpAhsMAAoJEPIxVQxPR+OOWdABAMUdSzpM +hzGs1O0RkWNQWbUzQ8nUOeD9wNbjE3zR+yfRAQDbYqvtWQKN4AQLTxVJN5X5AWyb +Pnn+We1aTBhaGa86AQ== +=3GfK +-----END PGP PRIVATE KEY BLOCK----- +'''; + +const bobPublicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- +Comment: D1A6 6E1A 23B1 82C9 980F 788C FBFC C82A 015E 7330 +Comment: Bob Babbage + +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 +NEJd3XZRzaXZE2aAMQ== +=F9yX +-----END PGP PUBLIC KEY BLOCK----- +'''; + +const bobPrivateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: D1A6 6E1A 23B1 82C9 980F 788C FBFC C82A 015E 7330 +Comment: Bob Babbage + +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 +=FAzO +-----END PGP PRIVATE KEY BLOCK----- +'''; + +const carolPublicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=pa/S +-----END PGP PUBLIC KEY BLOCK----- +'''; + +const carolPrivateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +1FkOSekLi8WNMdUx3XMyvP8nJ65P2Q== +=Xj8h +-----END PGP PRIVATE KEY BLOCK----- +'''; + +const rsaPublickey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBGc/4FEBCACZXe5kmfKWChtAGdoYRxRkEcfm0Ne431ovB4QS3zIOoclfyA7j +gyq1UAhKbsYhZmTKxx0FO5/HKOO2ojJU3AeKbLP+xhH8yVdEK5JiySvX/16Tz27d ++r5WGLkjZMYZAxWW9hdfurajYp8vcdACvErKMXShXUR9npCKH5O0QBagxMg6vczf +JfNG1qWJ9aeFO7yffUt9n+39ru3Cc67EAp7m29Wowto15xmeXCPkYEgE+BM+86AH +GqihOERjPCzWeFVLRuCwj2YjmZvtMh9A2gx5K6dMQ5viMDsTSoB0DEZzvY+Q7gr7 +/qiWCpSfmy3gvmReOvMzk8uI7+Nt6vdJia4vABEBAAG0L0RhcnQgUHJpdmFjeSBH +dWFyZCA8ZGFydHBnQG9wZW5wZ3AuZXhhbXBsZS5jb20+iQFRBBMBCAA7FiEEXMzt +pU+RcIn4xIgABTI3KgKPL/UFAmc/4FECGwMFCwkIBwICIgIGFQoJCAsCBBYCAwEC +HgcCF4AACgkQBTI3KgKPL/UGsgf+MZ4jtjY6DLCSirAJkV31cvzawO3KgSBXGuBf +bnZxWniWwfzJF1n38HBMIpfx9wOpks7b6js5TuBaoguQ3xc8URrtywO4HavM3fd2 +kzWeKeePQQVsdmbBJS497MXHo+WiztAl+x7wTtAO08goSKefDuhst1G14AiUxeaZ +YPZpq8FdGe+LI6tTuc/FA/0P78g21P82WoWti23OpPKjDh7TS2T47DJ/dUEEavwV +6mT7rdLoRqrZtDj52rNzPOf4vyfBQe8X5ZQljSUcbUBqLE8LeD6z8AYpw/0Iisc+ +xg4TRq38ccCG6AOWCIAExWo2OgIZkU7tKURKstPXRAa2eQWEg7kBDQRnP+BRAQgA +lK7KSXI6cbgCihf9oD1iaIJE7pPmGvgVA44DyNwqYfAD5BX1XLfjTcEltcQ+gYP5 +Rkh9IXnV1Ew1cgiF3dTyPLVhIweNWkbtoVSfdPFAKOylZkk98zUkQpTSTzdH3+yl +euyuitz3Fotgs1efDWUgStpGakKc0EQhR6vDWKOjUJye8oWMR93X1uxdIVcDM3IM +PEorTUVtLzZSQyUHQG73h4fMN7mUPrRALO0LGVXVY9ie0RTjgA9SdzZZxMRN5nGs +MD4tiiyr/JRsztIyzT4hJyg+uC2zRPEA6/q9m0egseP3BCeal7h01BEm5T8JJD5V +t8L9rQ3UakGSshiziCntCwARAQABiQE2BBgBCAAgFiEEXMztpU+RcIn4xIgABTI3 +KgKPL/UFAmc/4FECGwwACgkQBTI3KgKPL/Uzpwf/f9uukiyNC4QNF7cfgSzoZMRQ +oOeg/LqKrk/Ig036EI65xwTHm8DG0fvejIpVPrmPM0nKVhBacTTU5M6nunN5D5RJ +FlO7GPjgZt0PmBEJ/VWiiwV9tww9hegW9pFRrVswM8lrocQ87XGcIxCrQi48UpHp +5Us0Q50tqWlrMVw2TehgPDLJtmpdta4OBG8DdfwugLIrCjD6yxvPYOvX0mQDlY5G +2v9w+m3t60KSt9okDs3LouipniCA6brtpcQ9XPw7w+Is3hSSk0g8Zbm2lVVZVRiP +hrmPD+bTB7WO025GVa9Kl9vs87rAeEVXtEXJiNxlTP8BGSRzz1ayKQ9Y4UcY2Q== +=S9XV +-----END PGP PUBLIC KEY BLOCK----- +'''; + +const lockedRsaPrivateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +=QeXb +-----END PGP PRIVATE KEY BLOCK----- +'''; + +const dsaElGamalPublicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=3nnd +-----END PGP PUBLIC KEY BLOCK----- +'''; + +const lockDsaElGamalPrivateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +CorNeTDAKJEwL5SJhzt+bmo+ +=7ZVq +-----END PGP PRIVATE KEY BLOCK----- +'''; + +const eccNistP384PublicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mG8EZz/5iRMFK4EEACIDAwR3cUlciuVCSwGLA4sH6SwA1axNDMU3GqnHOOmCVoM0 +qOAX9FdVeP0DqixqjRYm2qQoUpm+QBK84KA5oGfcYNRmYXwYnC64s05yLQkL836D +mcV38F5c0k3IQIwhuWVwu320L0RhcnQgUHJpdmFjeSBHdWFyZCA8ZGFydHBnQG9w +ZW5wZ3AuZXhhbXBsZS5jb20+iLMEExMJADsWIQSjJRB+ZryrPupAdVA5bsuLuG0Z +IgUCZz/5iQIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRA5bsuLuG0Z +InjBAX9bv51DhgTNGDot6gTEKJ7c0FypBuPZPlNOKMCshtvu36+jiJyMpGBaR/C+ +CB8wctkBgNT0/5Mj1e+UXqFAmflWmODnszrYpdiGSF2GJxVwOBLJeFVqPQtl2mSn +mKmhhoVy3bhzBGc/+YkSBSuBBAAiAwME6+LPwcbdvoEdxPA2L002pKXp8Nt7PxIs +jUegQRw2bTQXMlu5zplsUNprWJZ1W/iqLhmEH8eLONxcuzVEQMb3dCImxBzmL3Y5 +HxGGti81EsU7JBIZxHKhl85RY78HdHgCAwEJCYiYBBgTCQAgFiEEoyUQfma8qz7q +QHVQOW7Li7htGSIFAmc/+YkCGwwACgkQOW7Li7htGSJT3AGAgueSdnW+Lvrv2K9V +fqgHGB2NDYsdLdXOIHardOmkFmD3rTbjsjHkQrLCs/lCQkVhAYCxmcY10PcZBwYq +mzhn+ek2wQOTGM2wO3qJVZ9Z+z5kkQXH6JtyfkZATJgA/kougNs= +=K1/q +-----END PGP PUBLIC KEY BLOCK----- +'''; + +const lockedEccNistP384PrivateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lNIEZz/5iRMFK4EEACIDAwR3cUlciuVCSwGLA4sH6SwA1axNDMU3GqnHOOmCVoM0 +qOAX9FdVeP0DqixqjRYm2qQoUpm+QBK84KA5oGfcYNRmYXwYnC64s05yLQkL836D +mcV38F5c0k3IQIwhuWVwu33+BwMCYJx1nYlH0Mj4vl/mSNGARY0WUyL0C6UJ7S65 +Ly16TGI3V490OtTlB4B8UIL0s4l0odNR1A6zF8oGXlNBBWe/Q9U+W34J/q+/zKyF +/rZ6EIUfGoOh2kRLeRVWcvjXI+m0L0RhcnQgUHJpdmFjeSBHdWFyZCA8ZGFydHBn +QG9wZW5wZ3AuZXhhbXBsZS5jb20+iLMEExMJADsWIQSjJRB+ZryrPupAdVA5bsuL +uG0ZIgUCZz/5iQIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRA5bsuL +uG0ZInjBAX9bv51DhgTNGDot6gTEKJ7c0FypBuPZPlNOKMCshtvu36+jiJyMpGBa +R/C+CB8wctkBgNT0/5Mj1e+UXqFAmflWmODnszrYpdiGSF2GJxVwOBLJeFVqPQtl +2mSnmKmhhoVy3ZzWBGc/+YkSBSuBBAAiAwME6+LPwcbdvoEdxPA2L002pKXp8Nt7 +PxIsjUegQRw2bTQXMlu5zplsUNprWJZ1W/iqLhmEH8eLONxcuzVEQMb3dCImxBzm +L3Y5HxGGti81EsU7JBIZxHKhl85RY78HdHgCAwEJCf4HAwIIaqT4lafbcPhMsDaE +o9wLpC5rY6k28ax3dHv1RLibxxcZafaTP1eKy+W0fBtOm1vDkkc/HCxsFBtIkZ/c +/rfA30/p6jibrlfk4qOVlSGYPN5QjexvmaK1Us46fWLEe4iYBBgTCQAgFiEEoyUQ +fma8qz7qQHVQOW7Li7htGSIFAmc/+YkCGwwACgkQOW7Li7htGSJT3AGAgueSdnW+ +Lvrv2K9VfqgHGB2NDYsdLdXOIHardOmkFmD3rTbjsjHkQrLCs/lCQkVhAYCxmcY1 +0PcZBwYqmzhn+ek2wQOTGM2wO3qJVZ9Z+z5kkQXH6JtyfkZATJgA/kougNs= +=rr8a +-----END PGP PRIVATE KEY BLOCK----- +'''; + +const eccBrainpoolP256PublicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mFMEZz/50BMJKyQDAwIIAQEHAgMEnXlquLQ8KrfXtrCZ000jDP7I5nXzJQKDws4C +ZgRpECoCHUeYCdIrl3iLUJfYzb9Qq6nt/8oP+jljqiJ35W5NlLQvRGFydCBQcml2 +YWN5IEd1YXJkIDxkYXJ0cGdAb3BlbnBncC5leGFtcGxlLmNvbT6IkwQTEwgAOxYh +BM0bWxQpT4C+Zc7775lRIZ/J3pV4BQJnP/nQAhsDBQsJCAcCAiICBhUKCQgLAgQW +AgMBAh4HAheAAAoJEJlRIZ/J3pV4v3oA/R9IMDJklqQ7qUp20updBhqAbifSELId +xbd886G8hPgiAP0bpKSgkad2xFUSuO8o4b8menOFbFQphPIf/E+CWstwBLhXBGc/ ++dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGS +VO0lI/BV2JJsuQqI0Pne08Y7og4bhyv9S+D+wcU8sv0DAQgHiHgEGBMIACAWIQTN +G1sUKU+AvmXO+++ZUSGfyd6VeAUCZz/50AIbDAAKCRCZUSGfyd6VeMTKAQCIrK5W +d0O6gwPJ4rSHWo13Q0K9Lth8ZBtXySqZZ/XSBwEAj22bXtH3QkS/K70fExKX4KZ7 +k49k9YikNemTd0HPLRc= +=38Dv +-----END PGP PUBLIC KEY BLOCK----- +'''; + +const lockedEccBrainpoolP256PrivateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lKYEZz/50BMJKyQDAwIIAQEHAgMEnXlquLQ8KrfXtrCZ000jDP7I5nXzJQKDws4C +ZgRpECoCHUeYCdIrl3iLUJfYzb9Qq6nt/8oP+jljqiJ35W5NlP4HAwI3GD01hi3V +P/hQXGRXNxRo8r4melKsvJRp2VTArNRXunWZFvxjQTqFbnF3OHkh8Fe7P6drfUlU +6vWS3K/AG+uxaN112eUP5DqktHorP8pKtC9EYXJ0IFByaXZhY3kgR3VhcmQgPGRh +cnRwZ0BvcGVucGdwLmV4YW1wbGUuY29tPoiTBBMTCAA7FiEEzRtbFClPgL5lzvvv +mVEhn8nelXgFAmc/+dACGwMFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQ +mVEhn8nelXi/egD9H0gwMmSWpDupSnbS6l0GGoBuJ9IQsh3Ft3zzobyE+CIA/Ruk +pKCRp3bEVRK47yjhvyZ6c4VsVCmE8h/8T4Jay3AEnKoEZz/50BIJKyQDAwIIAQEH +AgMEa4NEpjB8C1OE04AznUizgnUFg83qpkJM3Rz2V1JDMZJU7SUj8FXYkmy5CojQ ++d7TxjuiDhuHK/1L4P7BxTyy/QMBCAf+BwMCLM2ShOzK5gv4r+BsOIlgMyPBEsUc +/sO1PLeLyEQoCa5CcHATAtPrJfBHhD+TJbFxOA0HBnCtvQn47oGBkvnBCL4x94Q1 +McHOENbwToV6Voh4BBgTCAAgFiEEzRtbFClPgL5lzvvvmVEhn8nelXgFAmc/+dAC +GwwACgkQmVEhn8nelXjEygEAiKyuVndDuoMDyeK0h1qNd0NCvS7YfGQbV8kqmWf1 +0gcBAI9tm17R90JEvyu9HxMSl+Cme5OPZPWIpDXpk3dBzy0X +=CNK9 +-----END PGP PRIVATE KEY BLOCK----- +'''; + +const eccCurve25519PublicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEZz/6BhYJKwYBBAHaRw8BAQdAZQAdPHBwHpMPvaCgVCkt/eMCp1ubVxnC/U3a +aMF98SC0L0RhcnQgUHJpdmFjeSBHdWFyZCA8ZGFydHBnQG9wZW5wZ3AuZXhhbXBs +ZS5jb20+iJMEExYKADsWIQQRSLKiT1gJd8J7JiI+1HXdIS0iHgUCZz/6BgIbAwUL +CQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRA+1HXdIS0iHrifAQDPuCf7ztzy +K9opPkJ2aw7SR/IFTE7dfdIuOTDud+Ph4AEA4YRvlmHv27VVAsS8XoDxT+t1tbkA +xgiwXtqXA2t/Ewy4OARnP/oGEgorBgEEAZdVAQUBAQdACcGlnxIiapTiJPzvYtjz +GgwZm0VDgRxkHRUTym4n6X8DAQgHiHgEGBYKACAWIQQRSLKiT1gJd8J7JiI+1HXd +IS0iHgUCZz/6BgIbDAAKCRA+1HXdIS0iHrDSAP0Q/+hfme0EqcVJf/Z38dQwhXIM +Dy6c8mMkLVpS/zXLOAEAp3nZkkgY1MgZAfbx4BEq+uJv801qCBNeThFeOe8/3gs= +=/O/r +-----END PGP PUBLIC KEY BLOCK----- +'''; + +const lockedEccCurve25519PrivateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lIYEZz/6BhYJKwYBBAHaRw8BAQdAZQAdPHBwHpMPvaCgVCkt/eMCp1ubVxnC/U3a +aMF98SD+BwMCG2M/g+96Esj4hyo7hP1EHf6XFy/uhDRNcoTjWNnPF+KVBLNdGn0o +UjSHZXBi0ScUcW6e/SfT4B77HNS8Abr7Ersx5PVWmYARpn2gVNFqlLQvRGFydCBQ +cml2YWN5IEd1YXJkIDxkYXJ0cGdAb3BlbnBncC5leGFtcGxlLmNvbT6IkwQTFgoA +OxYhBBFIsqJPWAl3wnsmIj7Udd0hLSIeBQJnP/oGAhsDBQsJCAcCAiICBhUKCQgL +AgQWAgMBAh4HAheAAAoJED7Udd0hLSIeuJ8BAM+4J/vO3PIr2ik+QnZrDtJH8gVM +Tt190i45MO534+HgAQDhhG+WYe/btVUCxLxegPFP63W1uQDGCLBe2pcDa38TDJyL +BGc/+gYSCisGAQQBl1UBBQEBB0AJwaWfEiJqlOIk/O9i2PMaDBmbRUOBHGQdFRPK +bifpfwMBCAf+BwMCCMCreqPjxcX45R3/KPQFMRNxZ0l8OCBHq5IByLEAAOGszlRo +yrLREP280yDxw39q0VSIkn2tmM7bleGk71aJ9UzTxcMZzdCYT2f8tmsQOYh4BBgW +CgAgFiEEEUiyok9YCXfCeyYiPtR13SEtIh4FAmc/+gYCGwwACgkQPtR13SEtIh6w +0gD9EP/oX5ntBKnFSX/2d/HUMIVyDA8unPJjJC1aUv81yzgBAKd52ZJIGNTIGQH2 +8eARKvrib/NNaggTXk4RXjnvP94L +=4mmg +-----END PGP PRIVATE KEY BLOCK----- +'''; + +const rfc9580Curve25519PublicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xioGZ0/jphsAAAAgy19s9qAQsttkAS7gPmFeakgJwkKklQVReq/oPNJVWKrCuwYfGwgAAABcBQJn +T+OmIiEGeyUi0XrLxokVQFD7GiRV+fyaRjGK1u9QOdPm47wEpnkJEHslItF6y8aJAhsDAwsHCQQi +AQIDBRUIDAoOBRYAAQIDAh4LDScHAQkBBwIJAgcDCQMAAAAAx94QMOS423OajdIBpQ7T4MklWxyz +4SNy5d9A3+a7huNEw6qmaJ+pqVGcrNctLB9qWyD4+xjDtMLqHYOeNUsunRUUaBM8BbFCb86wljnA +1RpABwXNL0RhcnQgUHJpdmFjeSBHdWFyZCA8ZGFydHBnQG9wZW5wZ3AuZXhhbXBsZS5jb20+wpUG +EBsIAAAANgUCZ0/jpiIhBnslItF6y8aJFUBQ+xokVfn8mkYxitbvUDnT5uO8BKZ5CRB7JSLResvG +iQIZAQAAAADh1RDsF0xb6N2mB4vw9rYIrNcKdWSwPb0IBY6kNrnN84nMcGCseJ9JWpX+4le3HP/s +Ej9rf8N+GfwQHY7FqUk4m+Mmrg9FSY8rtgtQ/WF/pPBeC80qTmd1eWVuIFZhbiBOZ3V5ZW4gPG5n +dXllbm52MTk4MUBnbWFpbC5jb20+wpIGEBsIAAAAMwUCZ0/jpiIhBnslItF6y8aJFUBQ+xokVfn8 +mkYxitbvUDnT5uO8BKZ5CRB7JSLResvGiQAAAADyNxAEWM9B3Ds6IOlYh2UIJ6BFMuDamUakpZff +sKKi6VPlhwTKb80sbAMCI2QeBpKkFR6j+4hBuleDEm34RE0Qo2P9/e8eJLUt6QvNwv8XrFMHDs4q +BmdP46YZAAAAIDv21k8ovP04IZRAc+Wiq+zFU0HW4P/GA5bLvCCPYHltwpUGGBsIAAAANgUCZ0/j +piIhBnslItF6y8aJFUBQ+xokVfn8mkYxitbvUDnT5uO8BKZ5CRB7JSLResvGiQIbDAAAAAAiEhAM +bPIvl2dct0Etc792kYzRRbUC3XDl5uzoLJj59aBkhkE+e5nX8j6hCfYSjQnPq9oKmMeOjdgkjeDM +fjfQblPSXk32Zpv5RXzNhOIelkIuA9USba2Zw7ZRB4iuDfOgEJAw7VEt +-----END PGP PUBLIC KEY BLOCK----- +'''; + +const lockRfc9580Curve25519PrivateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xX0GZ0/8WhsAAAAgHDf+yTRCzA8vM6PV+ovg3URqlceQQUcH/Ekc7ZsR9Q3+HQcLAwiv+8mof0Fc +BuCm/9pWdojyrmg+x9KU0U+hzPx2wGmqtaz9WIIEW9PbTkBZc/5BCB15qC6ZLvL3oQc9h4XmHwhg +a/TtUUpYW6cvk45KzsK7Bh8bCAAAAFwFAmdP/FsiIQaWDEY7sAo6ZqJa+4tswpHYFSCDSFPzJdEJ +Yvcai0SjTAkQlgxGO7AKOmYCGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcBCQEHAgkCBwMJ +AwAAAAAqIhCiMxuCDvJ0r1dyF9hUSzRiXZqzB3uhdMrkX1YtDPV7RYzfulWDImcd8siqveYshD4w +uo6RBzsIt6aJrKUZSlzd1HR9NuZKEWaB9TYuZOo0Cc0vRGFydCBQcml2YWN5IEd1YXJkIDxkYXJ0 +cGdAb3BlbnBncC5leGFtcGxlLmNvbT7ClQYQGwgAAAA2BQJnT/xbIiEGlgxGO7AKOmaiWvuLbMKR +2BUgg0hT8yXRCWL3GotEo0wJEJYMRjuwCjpmAhkBAAAAAAYjEJdMNIo0MDDvlfOiEBlzQKdyvNhf +OwORi8ABLx96zDD6cj+0l/JMyi75OL5uuNZDdxgFrqTYQ4/2r6F3wuTXBpKGUBouZBO7Y+QgI1Ww +kJoPzSpOZ3V5ZW4gVmFuIE5ndXllbiA8bmd1eWVubnYxOTgxQGdtYWlsLmNvbT7CkgYQGwgAAAAz +BQJnT/xbIiEGlgxGO7AKOmaiWvuLbMKR2BUgg0hT8yXRCWL3GotEo0wJEJYMRjuwCjpmAAAAADKm +EOpBOnKA+bNtXdYqExEUv8KwwiiVpLdUqSwlsKONUZMhr/NaWBafrsmUhQMVnir4x3JWVMs2aHVO +3t0udVsFcBYQcm7vQcXsKBLeyCCWUzMHx30GZ0/8WxkAAAAgzmnTRHsYD3OeQDlZPC4Ujcqlkl+6 +iesJlNX9VYO4/Ar+HQcLAwhZpk+g24kZSeAivkvADh9xHx7hVPD6hl99bI30XqNdIMK2VcY/Zk3c +k949Su5SV/sELKg9EikzZdEoiY+N7rWCHJXFxRNbp9scCSiJu8KVBhgbCAAAADYFAmdP/FsiIQaW +DEY7sAo6ZqJa+4tswpHYFSCDSFPzJdEJYvcai0SjTAkQlgxGO7AKOmYCGwwAAAAA0RcQJB9YvxrM +NDm6QTmbvRO5EjvPyQf9I1gqhYkVZExIvt/x5vpX/ID29c91PagKcr4eoJrl9Xyfn8ksYFkmXI/B +C8sssUAhAtMI5sgtlfA7Ow7VHjDLcQ0doBgA9J4afJjd12/e+PYN4Kdqvt5Gbv5ltA== +-----END PGP PRIVATE KEY BLOCK----- +'''; + +const rfc9580Curve448PublicKey = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xkMGZ0/jphwAAAA5a1Aan5R/DuK8jXNrY9+z7IKjQ8NHO6J1CTodHHpDslwa7V85AavFpaHoxlp0 +yn2fJX2fSnJkqdsAwsAtBh8cCAAAAFwFAmdP46ciIQaTh3wqdlbkHo45oW1AVhRmOkxVk9ozBveA +NfRmZpxgUQkQk4d8KnZW5B4CGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcBCQEHAgkCBwMJ +AwAAAABauRD3wkQRbuI3zf+UyqDl2Y1qlqCHtdViFqwgyZE4mhZZYIrmfHYguy7GJm3FCb4vDZJM +X6qSmyH2kclVUvp3VGJXpOo35lDUidSAIYffeYT7jC2KIz7IcA5WAiHIDW7+5rQ30yehH3MOBhjR +dKNWDdrezDt8A6pl9pRFMlzO+YGQfjwAzS9EYXJ0IFByaXZhY3kgR3VhcmQgPGRhcnRwZ0BvcGVu +cGdwLmV4YW1wbGUuY29tPsLABwYQHAgAAAA2BQJnT+OnIiEGk4d8KnZW5B6OOaFtQFYUZjpMVZPa +Mwb3gDX0ZmacYFEJEJOHfCp2VuQeAhkBAAAAAFvNEKq2qVYMJA+GbX7k4Qk+0pnJFW2+kEQ4qmDE +HhYIoeD9YnicGqr5+479nt9AqxQVV1IJT8mwxqQYIrBsoWxy83Z4o7A9umbkn4CmGFi61EbJ/JjZ +NhLtut2DSlWz9wntNQArseGnHhGjGrupJqiNhkyeCdhJ09grcds6mS1JI4i+EQDNKk5ndXllbiBW +YW4gTmd1eWVuIDxuZ3V5ZW5udjE5ODFAZ21haWwuY29tPsLABAYQHAgAAAAzBQJnT+OnIiEGk4d8 +KnZW5B6OOaFtQFYUZjpMVZPaMwb3gDX0ZmacYFEJEJOHfCp2VuQeAAAAAPx5EGrwf9dxyc8Zcf3T +WTH678WXGljJ5QML4S98Po7Q8vcE+1IGEE/hjOpi/cTpL9z+2lQ6XvLYVD54YuFzsEj11zhMM1gc +4pASBABfwzft/LL75Gr8NvTvc7/oD1PNnXOaoLjKfuXIlFAMel73EQqtp9MR7uQ3iDI7WIXmTG/f +mnT0KQDOQgZnT+OnGgAAADiY/+RbigV4l1adpmwFDGKZLpK7qYHnSuxjknWgOD48e9nDdZ3gkW+o +TOZaHsI546Kexs04A4ZCv8LABwYYHAgAAAA2BQJnT+OnIiEGk4d8KnZW5B6OOaFtQFYUZjpMVZPa +Mwb3gDX0ZmacYFEJEJOHfCp2VuQeAhsMAAAAAMM5EPfNxWSEvc5dMhOkM8a9a8EFgF8qOkwJj9sQ +ISt80um5TscuNDSqmwCJCTLXDhblTcwseZGrBjlrnfij7+9VPQDPRzaYUSopXQDG4l8JFKpYxvEY +4xo3/Pw117VEfs6lTc8PLF+NkIlv6Ip88od/3Op2bjJfQX57cOQwx65hc/bdCgDVEEdmNh5TAjGf +FmXTxip3kr4= +-----END PGP PUBLIC KEY BLOCK----- +'''; + +const lockedRfc9580Curve448PrivateKey = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xa8GZ0/8WxwAAAA5yymL3ZZ7U9OrS3LdxuhvSc/SMFFNYSwyB9f4JHMgw/DxUx4+81alHHCjGNRE +Lylg9n81LWeN0SCA/h0HCwMIBg46deIoPR3gg3z8j3H3k3h4a0NDBbT5iTOpswmrc6NdU4X1zabs +wUt1SSLtm9LPyXlOyIpQeE3CfDlYmNDzs7Gz7MIa+dbimHUmKrOWvAtlKZeIBtowletTex9cxkdv +JyrcDVZGwsAtBh8cCAAAAFwFAmdP/FsiIQZcGXS4BAC8Li+HPo5erpI7ttQLRDRvqUzZ/YMZCmIS +WAkQXBl0uAQAvC4CGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcBCQEHAgkCBwMJAwAAAABW +ARABLtqQ+hSn18x6xfVEaXx8BqT/L3ghgvGeYI+Ou7b/bDnLoTySTQslRxWGYhUFOkMiPUPvxyoo +aeH8iPj+QsS5kbPoIWicG9QAbEeBjGWzGezqMt+B0DuIiQGGiE/FAjLdbOM2ctvBBRvk2TeGAZ7Y +VTHXEv7hxSWK0koylRtNmCIAzS9EYXJ0IFByaXZhY3kgR3VhcmQgPGRhcnRwZ0BvcGVucGdwLmV4 +YW1wbGUuY29tPsLABwYQHAgAAAA2BQJnT/xbIiEGXBl0uAQAvC4vhz6OXq6SO7bUC0Q0b6lM2f2D +GQpiElgJEFwZdLgEALwuAhkBAAAAAPHqEBYl8EyZZ87R1GxCa/nbpsV64m/USnwef1z7QP+cotTK +YgQ1belLShpGWsEWlejSAffDWWjTyj4Zet+/ZD8qRArffNTJKckUiQBLo3FRST7q6/+fr05vNVXd +6qzuNRZ8qNEMn0RXWVlknx0ngB+RDY8rxenWE+aX/FJkzAlw2em/KADNKk5ndXllbiBWYW4gTmd1 +eWVuIDxuZ3V5ZW5udjE5ODFAZ21haWwuY29tPsLABAYQHAgAAAAzBQJnT/xbIiEGXBl0uAQAvC4v +hz6OXq6SO7bUC0Q0b6lM2f2DGQpiElgJEFwZdLgEALwuAAAAANCTEMNpNd0oX1itofCILqF8O8Na +MlIdTThwhdw2VNWIxj+mdL+WI6N14pybiOy1l487NZRSO3EgmRjYtCyxmfQ7dEOVSkdYy4SylQD2 +G3wBZYSq2d03Y4uaV0dcvcImPBBsPfP21Ka40VER9F90WQus0I1y77c4C3KXc4FNl0KIeZlTMADH +rQZnT/xcGgAAADhTie394+2su5pQnDF2EoeexN9C5SexeDqfMeBq6WZG7LEdkJRkrPdNZJsX+pXM +cZYaKMy6NJIfUv4dBwsDCOnMOf3LbUZK4DW7Z5VeIJQATLUbvtUju5uVCeXxx4Me1MivD4eQdCDE +ISRfcRK3oQ1d9FrHbXSV0Y2VU9/dFKcIayJaIKGLeUzG8PHUNTCD9HkSfjTf2x+kwQsu4AtyvGbZ +UL1gwsAHBhgcCAAAADYFAmdP/FwiIQZcGXS4BAC8Li+HPo5erpI7ttQLRDRvqUzZ/YMZCmISWAkQ +XBl0uAQAvC4CGwwAAAAA5wcQcWaN5Sdj4wJxMX/wB9Uhf3hICRUGVFN1iYWnhmUPeJ5b3SnQ3/rl +oQCDLwoWcOkSS4+wQExO4WXxyvUNKJ15HL4YV2nx0VbmgC7GeukcVAdKATn1OO9Nt0MevTxuRW/J +lzIMFLqFsDiKPzuwpZBhzEh+sj0Z3WtqZOueBwxVocwDANUX0mClieCAd2U0VQHW7lMIkRYMY5ZS +x6s= +-----END PGP PRIVATE KEY BLOCK----- +'''; + +const multiplePublicKeys = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +7z/eCw== +-----END PGP PUBLIC KEY BLOCK----- +'''; diff --git a/test/key/key_decryption_test.dart b/test/key/key_decryption_test.dart index 09abd9b8..e1e50bab 100644 --- a/test/key/key_decryption_test.dart +++ b/test/key/key_decryption_test.dart @@ -3,74 +3,18 @@ import 'package:dart_pg/src/enum/key_algorithm.dart'; import 'package:dart_pg/src/openpgp.dart'; import 'package:test/test.dart'; +import '../data/key_data.dart'; + void main() { group('Key decryption', () { const userID = 'Dart Privacy Guard '; const passphrase = 'password'; test('RSA key', () { - const armored = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- - -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 -=QeXb ------END PGP PRIVATE KEY BLOCK----- -'''; - final privateKey = OpenPGP.decryptPrivateKey(armored, passphrase); + final privateKey = OpenPGP.decryptPrivateKey( + lockedRsaPrivateKey, + passphrase, + ); final user = privateKey.users[0]; final subkey = privateKey.subkeys[0]; @@ -103,52 +47,10 @@ GI+GuY8P5tMHtY7TbkZVr0qX2+zzusB4RVe0RcmI3GVM/wEZJHPPVrIpD1jhRxjZ }); test('DSA & ElGamal key', () { - const armored = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- - -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 -CorNeTDAKJEwL5SJhzt+bmo+ -=7ZVq ------END PGP PRIVATE KEY BLOCK----- -'''; - final privateKey = OpenPGP.decryptPrivateKey(armored, passphrase); + final privateKey = OpenPGP.decryptPrivateKey( + lockDsaElGamalPrivateKey, + passphrase, + ); final user = privateKey.users[0]; final subkey = privateKey.subkeys[0]; @@ -181,30 +83,10 @@ CorNeTDAKJEwL5SJhzt+bmo+ }); test('ECC NIST P-384 key', () { - const armored = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- - -lNIEZz/5iRMFK4EEACIDAwR3cUlciuVCSwGLA4sH6SwA1axNDMU3GqnHOOmCVoM0 -qOAX9FdVeP0DqixqjRYm2qQoUpm+QBK84KA5oGfcYNRmYXwYnC64s05yLQkL836D -mcV38F5c0k3IQIwhuWVwu33+BwMCYJx1nYlH0Mj4vl/mSNGARY0WUyL0C6UJ7S65 -Ly16TGI3V490OtTlB4B8UIL0s4l0odNR1A6zF8oGXlNBBWe/Q9U+W34J/q+/zKyF -/rZ6EIUfGoOh2kRLeRVWcvjXI+m0L0RhcnQgUHJpdmFjeSBHdWFyZCA8ZGFydHBn -QG9wZW5wZ3AuZXhhbXBsZS5jb20+iLMEExMJADsWIQSjJRB+ZryrPupAdVA5bsuL -uG0ZIgUCZz/5iQIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRA5bsuL -uG0ZInjBAX9bv51DhgTNGDot6gTEKJ7c0FypBuPZPlNOKMCshtvu36+jiJyMpGBa -R/C+CB8wctkBgNT0/5Mj1e+UXqFAmflWmODnszrYpdiGSF2GJxVwOBLJeFVqPQtl -2mSnmKmhhoVy3ZzWBGc/+YkSBSuBBAAiAwME6+LPwcbdvoEdxPA2L002pKXp8Nt7 -PxIsjUegQRw2bTQXMlu5zplsUNprWJZ1W/iqLhmEH8eLONxcuzVEQMb3dCImxBzm -L3Y5HxGGti81EsU7JBIZxHKhl85RY78HdHgCAwEJCf4HAwIIaqT4lafbcPhMsDaE -o9wLpC5rY6k28ax3dHv1RLibxxcZafaTP1eKy+W0fBtOm1vDkkc/HCxsFBtIkZ/c -/rfA30/p6jibrlfk4qOVlSGYPN5QjexvmaK1Us46fWLEe4iYBBgTCQAgFiEEoyUQ -fma8qz7qQHVQOW7Li7htGSIFAmc/+YkCGwwACgkQOW7Li7htGSJT3AGAgueSdnW+ -Lvrv2K9VfqgHGB2NDYsdLdXOIHardOmkFmD3rTbjsjHkQrLCs/lCQkVhAYCxmcY1 -0PcZBwYqmzhn+ek2wQOTGM2wO3qJVZ9Z+z5kkQXH6JtyfkZATJgA/kougNs= -=rr8a ------END PGP PRIVATE KEY BLOCK----- -'''; - final privateKey = OpenPGP.decryptPrivateKey(armored, passphrase); + final privateKey = OpenPGP.decryptPrivateKey( + lockedEccNistP384PrivateKey, + passphrase, + ); final user = privateKey.users[0]; final subkey = privateKey.subkeys[0]; @@ -237,27 +119,10 @@ Lvrv2K9VfqgHGB2NDYsdLdXOIHardOmkFmD3rTbjsjHkQrLCs/lCQkVhAYCxmcY1 }); test('ECC Brainpool P-256 key', () { - const armored = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- - -lKYEZz/50BMJKyQDAwIIAQEHAgMEnXlquLQ8KrfXtrCZ000jDP7I5nXzJQKDws4C -ZgRpECoCHUeYCdIrl3iLUJfYzb9Qq6nt/8oP+jljqiJ35W5NlP4HAwI3GD01hi3V -P/hQXGRXNxRo8r4melKsvJRp2VTArNRXunWZFvxjQTqFbnF3OHkh8Fe7P6drfUlU -6vWS3K/AG+uxaN112eUP5DqktHorP8pKtC9EYXJ0IFByaXZhY3kgR3VhcmQgPGRh -cnRwZ0BvcGVucGdwLmV4YW1wbGUuY29tPoiTBBMTCAA7FiEEzRtbFClPgL5lzvvv -mVEhn8nelXgFAmc/+dACGwMFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQ -mVEhn8nelXi/egD9H0gwMmSWpDupSnbS6l0GGoBuJ9IQsh3Ft3zzobyE+CIA/Ruk -pKCRp3bEVRK47yjhvyZ6c4VsVCmE8h/8T4Jay3AEnKoEZz/50BIJKyQDAwIIAQEH -AgMEa4NEpjB8C1OE04AznUizgnUFg83qpkJM3Rz2V1JDMZJU7SUj8FXYkmy5CojQ -+d7TxjuiDhuHK/1L4P7BxTyy/QMBCAf+BwMCLM2ShOzK5gv4r+BsOIlgMyPBEsUc -/sO1PLeLyEQoCa5CcHATAtPrJfBHhD+TJbFxOA0HBnCtvQn47oGBkvnBCL4x94Q1 -McHOENbwToV6Voh4BBgTCAAgFiEEzRtbFClPgL5lzvvvmVEhn8nelXgFAmc/+dAC -GwwACgkQmVEhn8nelXjEygEAiKyuVndDuoMDyeK0h1qNd0NCvS7YfGQbV8kqmWf1 -0gcBAI9tm17R90JEvyu9HxMSl+Cme5OPZPWIpDXpk3dBzy0X -=CNK9 ------END PGP PRIVATE KEY BLOCK----- -'''; - final privateKey = OpenPGP.decryptPrivateKey(armored, passphrase); + final privateKey = OpenPGP.decryptPrivateKey( + lockedEccBrainpoolP256PrivateKey, + passphrase, + ); final user = privateKey.users[0]; final subkey = privateKey.subkeys[0]; @@ -290,26 +155,10 @@ GwwACgkQmVEhn8nelXjEygEAiKyuVndDuoMDyeK0h1qNd0NCvS7YfGQbV8kqmWf1 }); test('ECC Curve 25519 key', () { - const armored = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- - -lIYEZz/6BhYJKwYBBAHaRw8BAQdAZQAdPHBwHpMPvaCgVCkt/eMCp1ubVxnC/U3a -aMF98SD+BwMCG2M/g+96Esj4hyo7hP1EHf6XFy/uhDRNcoTjWNnPF+KVBLNdGn0o -UjSHZXBi0ScUcW6e/SfT4B77HNS8Abr7Ersx5PVWmYARpn2gVNFqlLQvRGFydCBQ -cml2YWN5IEd1YXJkIDxkYXJ0cGdAb3BlbnBncC5leGFtcGxlLmNvbT6IkwQTFgoA -OxYhBBFIsqJPWAl3wnsmIj7Udd0hLSIeBQJnP/oGAhsDBQsJCAcCAiICBhUKCQgL -AgQWAgMBAh4HAheAAAoJED7Udd0hLSIeuJ8BAM+4J/vO3PIr2ik+QnZrDtJH8gVM -Tt190i45MO534+HgAQDhhG+WYe/btVUCxLxegPFP63W1uQDGCLBe2pcDa38TDJyL -BGc/+gYSCisGAQQBl1UBBQEBB0AJwaWfEiJqlOIk/O9i2PMaDBmbRUOBHGQdFRPK -bifpfwMBCAf+BwMCCMCreqPjxcX45R3/KPQFMRNxZ0l8OCBHq5IByLEAAOGszlRo -yrLREP280yDxw39q0VSIkn2tmM7bleGk71aJ9UzTxcMZzdCYT2f8tmsQOYh4BBgW -CgAgFiEEEUiyok9YCXfCeyYiPtR13SEtIh4FAmc/+gYCGwwACgkQPtR13SEtIh6w -0gD9EP/oX5ntBKnFSX/2d/HUMIVyDA8unPJjJC1aUv81yzgBAKd52ZJIGNTIGQH2 -8eARKvrib/NNaggTXk4RXjnvP94L -=4mmg ------END PGP PRIVATE KEY BLOCK----- -'''; - final privateKey = OpenPGP.decryptPrivateKey(armored, passphrase); + final privateKey = OpenPGP.decryptPrivateKey( + lockedEccCurve25519PrivateKey, + passphrase, + ); final user = privateKey.users[0]; final subkey = privateKey.subkeys[0]; @@ -342,30 +191,10 @@ CgAgFiEEEUiyok9YCXfCeyYiPtR13SEtIh4FAmc/+gYCGwwACgkQPtR13SEtIh6w }); test('Rfc9580 Curve 25519 key', () { - const armored = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- - -xX0GZ0/8WhsAAAAgHDf+yTRCzA8vM6PV+ovg3URqlceQQUcH/Ekc7ZsR9Q3+HQcLAwiv+8mof0Fc -BuCm/9pWdojyrmg+x9KU0U+hzPx2wGmqtaz9WIIEW9PbTkBZc/5BCB15qC6ZLvL3oQc9h4XmHwhg -a/TtUUpYW6cvk45KzsK7Bh8bCAAAAFwFAmdP/FsiIQaWDEY7sAo6ZqJa+4tswpHYFSCDSFPzJdEJ -Yvcai0SjTAkQlgxGO7AKOmYCGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcBCQEHAgkCBwMJ -AwAAAAAqIhCiMxuCDvJ0r1dyF9hUSzRiXZqzB3uhdMrkX1YtDPV7RYzfulWDImcd8siqveYshD4w -uo6RBzsIt6aJrKUZSlzd1HR9NuZKEWaB9TYuZOo0Cc0vRGFydCBQcml2YWN5IEd1YXJkIDxkYXJ0 -cGdAb3BlbnBncC5leGFtcGxlLmNvbT7ClQYQGwgAAAA2BQJnT/xbIiEGlgxGO7AKOmaiWvuLbMKR -2BUgg0hT8yXRCWL3GotEo0wJEJYMRjuwCjpmAhkBAAAAAAYjEJdMNIo0MDDvlfOiEBlzQKdyvNhf -OwORi8ABLx96zDD6cj+0l/JMyi75OL5uuNZDdxgFrqTYQ4/2r6F3wuTXBpKGUBouZBO7Y+QgI1Ww -kJoPzSpOZ3V5ZW4gVmFuIE5ndXllbiA8bmd1eWVubnYxOTgxQGdtYWlsLmNvbT7CkgYQGwgAAAAz -BQJnT/xbIiEGlgxGO7AKOmaiWvuLbMKR2BUgg0hT8yXRCWL3GotEo0wJEJYMRjuwCjpmAAAAADKm -EOpBOnKA+bNtXdYqExEUv8KwwiiVpLdUqSwlsKONUZMhr/NaWBafrsmUhQMVnir4x3JWVMs2aHVO -3t0udVsFcBYQcm7vQcXsKBLeyCCWUzMHx30GZ0/8WxkAAAAgzmnTRHsYD3OeQDlZPC4Ujcqlkl+6 -iesJlNX9VYO4/Ar+HQcLAwhZpk+g24kZSeAivkvADh9xHx7hVPD6hl99bI30XqNdIMK2VcY/Zk3c -k949Su5SV/sELKg9EikzZdEoiY+N7rWCHJXFxRNbp9scCSiJu8KVBhgbCAAAADYFAmdP/FsiIQaW -DEY7sAo6ZqJa+4tswpHYFSCDSFPzJdEJYvcai0SjTAkQlgxGO7AKOmYCGwwAAAAA0RcQJB9YvxrM -NDm6QTmbvRO5EjvPyQf9I1gqhYkVZExIvt/x5vpX/ID29c91PagKcr4eoJrl9Xyfn8ksYFkmXI/B -C8sssUAhAtMI5sgtlfA7Ow7VHjDLcQ0doBgA9J4afJjd12/e+PYN4Kdqvt5Gbv5ltA== ------END PGP PRIVATE KEY BLOCK----- -'''; - final privateKey = OpenPGP.decryptPrivateKey(armored, passphrase); + final privateKey = OpenPGP.decryptPrivateKey( + lockRfc9580Curve25519PrivateKey, + passphrase, + ); final directSignature = privateKey.directSignatures[0]; final user = privateKey.users[0]; final subkey = privateKey.subkeys[0]; @@ -403,36 +232,10 @@ C8sssUAhAtMI5sgtlfA7Ow7VHjDLcQ0doBgA9J4afJjd12/e+PYN4Kdqvt5Gbv5ltA== }); test('Rfc9580 Curve 448 key', () { - const armored = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- - -xa8GZ0/8WxwAAAA5yymL3ZZ7U9OrS3LdxuhvSc/SMFFNYSwyB9f4JHMgw/DxUx4+81alHHCjGNRE -Lylg9n81LWeN0SCA/h0HCwMIBg46deIoPR3gg3z8j3H3k3h4a0NDBbT5iTOpswmrc6NdU4X1zabs -wUt1SSLtm9LPyXlOyIpQeE3CfDlYmNDzs7Gz7MIa+dbimHUmKrOWvAtlKZeIBtowletTex9cxkdv -JyrcDVZGwsAtBh8cCAAAAFwFAmdP/FsiIQZcGXS4BAC8Li+HPo5erpI7ttQLRDRvqUzZ/YMZCmIS -WAkQXBl0uAQAvC4CGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcBCQEHAgkCBwMJAwAAAABW -ARABLtqQ+hSn18x6xfVEaXx8BqT/L3ghgvGeYI+Ou7b/bDnLoTySTQslRxWGYhUFOkMiPUPvxyoo -aeH8iPj+QsS5kbPoIWicG9QAbEeBjGWzGezqMt+B0DuIiQGGiE/FAjLdbOM2ctvBBRvk2TeGAZ7Y -VTHXEv7hxSWK0koylRtNmCIAzS9EYXJ0IFByaXZhY3kgR3VhcmQgPGRhcnRwZ0BvcGVucGdwLmV4 -YW1wbGUuY29tPsLABwYQHAgAAAA2BQJnT/xbIiEGXBl0uAQAvC4vhz6OXq6SO7bUC0Q0b6lM2f2D -GQpiElgJEFwZdLgEALwuAhkBAAAAAPHqEBYl8EyZZ87R1GxCa/nbpsV64m/USnwef1z7QP+cotTK -YgQ1belLShpGWsEWlejSAffDWWjTyj4Zet+/ZD8qRArffNTJKckUiQBLo3FRST7q6/+fr05vNVXd -6qzuNRZ8qNEMn0RXWVlknx0ngB+RDY8rxenWE+aX/FJkzAlw2em/KADNKk5ndXllbiBWYW4gTmd1 -eWVuIDxuZ3V5ZW5udjE5ODFAZ21haWwuY29tPsLABAYQHAgAAAAzBQJnT/xbIiEGXBl0uAQAvC4v -hz6OXq6SO7bUC0Q0b6lM2f2DGQpiElgJEFwZdLgEALwuAAAAANCTEMNpNd0oX1itofCILqF8O8Na -MlIdTThwhdw2VNWIxj+mdL+WI6N14pybiOy1l487NZRSO3EgmRjYtCyxmfQ7dEOVSkdYy4SylQD2 -G3wBZYSq2d03Y4uaV0dcvcImPBBsPfP21Ka40VER9F90WQus0I1y77c4C3KXc4FNl0KIeZlTMADH -rQZnT/xcGgAAADhTie394+2su5pQnDF2EoeexN9C5SexeDqfMeBq6WZG7LEdkJRkrPdNZJsX+pXM -cZYaKMy6NJIfUv4dBwsDCOnMOf3LbUZK4DW7Z5VeIJQATLUbvtUju5uVCeXxx4Me1MivD4eQdCDE -ISRfcRK3oQ1d9FrHbXSV0Y2VU9/dFKcIayJaIKGLeUzG8PHUNTCD9HkSfjTf2x+kwQsu4AtyvGbZ -UL1gwsAHBhgcCAAAADYFAmdP/FwiIQZcGXS4BAC8Li+HPo5erpI7ttQLRDRvqUzZ/YMZCmISWAkQ -XBl0uAQAvC4CGwwAAAAA5wcQcWaN5Sdj4wJxMX/wB9Uhf3hICRUGVFN1iYWnhmUPeJ5b3SnQ3/rl -oQCDLwoWcOkSS4+wQExO4WXxyvUNKJ15HL4YV2nx0VbmgC7GeukcVAdKATn1OO9Nt0MevTxuRW/J -lzIMFLqFsDiKPzuwpZBhzEh+sj0Z3WtqZOueBwxVocwDANUX0mClieCAd2U0VQHW7lMIkRYMY5ZS -x6s= ------END PGP PRIVATE KEY BLOCK----- -'''; - final privateKey = OpenPGP.decryptPrivateKey(armored, passphrase); + final privateKey = OpenPGP.decryptPrivateKey( + lockedRfc9580Curve448PrivateKey, + passphrase, + ); final directSignature = privateKey.directSignatures[0]; final user = privateKey.users[0]; final subkey = privateKey.subkeys[0]; diff --git a/test/key/key_reading_test.dart b/test/key/key_reading_test.dart index d92b9fb7..bbb58f2a 100644 --- a/test/key/key_reading_test.dart +++ b/test/key/key_reading_test.dart @@ -5,44 +5,14 @@ import 'package:dart_pg/src/enum/key_algorithm.dart'; import 'package:dart_pg/src/openpgp.dart'; import 'package:test/test.dart'; +import '../data/key_data.dart'; + void main() { group('Key reading', () { const userID = 'Dart Privacy Guard '; test('RSA key', () { - const armored = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -mQENBGc/4FEBCACZXe5kmfKWChtAGdoYRxRkEcfm0Ne431ovB4QS3zIOoclfyA7j -gyq1UAhKbsYhZmTKxx0FO5/HKOO2ojJU3AeKbLP+xhH8yVdEK5JiySvX/16Tz27d -+r5WGLkjZMYZAxWW9hdfurajYp8vcdACvErKMXShXUR9npCKH5O0QBagxMg6vczf -JfNG1qWJ9aeFO7yffUt9n+39ru3Cc67EAp7m29Wowto15xmeXCPkYEgE+BM+86AH -GqihOERjPCzWeFVLRuCwj2YjmZvtMh9A2gx5K6dMQ5viMDsTSoB0DEZzvY+Q7gr7 -/qiWCpSfmy3gvmReOvMzk8uI7+Nt6vdJia4vABEBAAG0L0RhcnQgUHJpdmFjeSBH -dWFyZCA8ZGFydHBnQG9wZW5wZ3AuZXhhbXBsZS5jb20+iQFRBBMBCAA7FiEEXMzt -pU+RcIn4xIgABTI3KgKPL/UFAmc/4FECGwMFCwkIBwICIgIGFQoJCAsCBBYCAwEC -HgcCF4AACgkQBTI3KgKPL/UGsgf+MZ4jtjY6DLCSirAJkV31cvzawO3KgSBXGuBf -bnZxWniWwfzJF1n38HBMIpfx9wOpks7b6js5TuBaoguQ3xc8URrtywO4HavM3fd2 -kzWeKeePQQVsdmbBJS497MXHo+WiztAl+x7wTtAO08goSKefDuhst1G14AiUxeaZ -YPZpq8FdGe+LI6tTuc/FA/0P78g21P82WoWti23OpPKjDh7TS2T47DJ/dUEEavwV -6mT7rdLoRqrZtDj52rNzPOf4vyfBQe8X5ZQljSUcbUBqLE8LeD6z8AYpw/0Iisc+ -xg4TRq38ccCG6AOWCIAExWo2OgIZkU7tKURKstPXRAa2eQWEg7kBDQRnP+BRAQgA -lK7KSXI6cbgCihf9oD1iaIJE7pPmGvgVA44DyNwqYfAD5BX1XLfjTcEltcQ+gYP5 -Rkh9IXnV1Ew1cgiF3dTyPLVhIweNWkbtoVSfdPFAKOylZkk98zUkQpTSTzdH3+yl -euyuitz3Fotgs1efDWUgStpGakKc0EQhR6vDWKOjUJye8oWMR93X1uxdIVcDM3IM -PEorTUVtLzZSQyUHQG73h4fMN7mUPrRALO0LGVXVY9ie0RTjgA9SdzZZxMRN5nGs -MD4tiiyr/JRsztIyzT4hJyg+uC2zRPEA6/q9m0egseP3BCeal7h01BEm5T8JJD5V -t8L9rQ3UakGSshiziCntCwARAQABiQE2BBgBCAAgFiEEXMztpU+RcIn4xIgABTI3 -KgKPL/UFAmc/4FECGwwACgkQBTI3KgKPL/Uzpwf/f9uukiyNC4QNF7cfgSzoZMRQ -oOeg/LqKrk/Ig036EI65xwTHm8DG0fvejIpVPrmPM0nKVhBacTTU5M6nunN5D5RJ -FlO7GPjgZt0PmBEJ/VWiiwV9tww9hegW9pFRrVswM8lrocQ87XGcIxCrQi48UpHp -5Us0Q50tqWlrMVw2TehgPDLJtmpdta4OBG8DdfwugLIrCjD6yxvPYOvX0mQDlY5G -2v9w+m3t60KSt9okDs3LouipniCA6brtpcQ9XPw7w+Is3hSSk0g8Zbm2lVVZVRiP -hrmPD+bTB7WO025GVa9Kl9vs87rAeEVXtEXJiNxlTP8BGSRzz1ayKQ9Y4UcY2Q== -=S9XV ------END PGP PUBLIC KEY BLOCK----- -'''; - final publicKey = OpenPGP.readPublicKey(armored); + final publicKey = OpenPGP.readPublicKey(rsaPublickey); final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -66,48 +36,7 @@ hrmPD+bTB7WO025GVa9Kl9vs87rAeEVXtEXJiNxlTP8BGSRzz1ayKQ9Y4UcY2Q== }); test('DSA & ElGamal key', () { - const armored = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -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 -=3nnd ------END PGP PUBLIC KEY BLOCK----- -'''; - final publicKey = OpenPGP.readPublicKey(armored); + final publicKey = OpenPGP.readPublicKey(dsaElGamalPublicKey); final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -131,26 +60,7 @@ CgkQunJ9D2tQwoHyvwEA6NmUz1wxurA5xPduxnsqHRbMoy5IZy2WCLV7bi8T8BsA }); test('ECC NIST P-384 key', () { - const armored = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -mG8EZz/5iRMFK4EEACIDAwR3cUlciuVCSwGLA4sH6SwA1axNDMU3GqnHOOmCVoM0 -qOAX9FdVeP0DqixqjRYm2qQoUpm+QBK84KA5oGfcYNRmYXwYnC64s05yLQkL836D -mcV38F5c0k3IQIwhuWVwu320L0RhcnQgUHJpdmFjeSBHdWFyZCA8ZGFydHBnQG9w -ZW5wZ3AuZXhhbXBsZS5jb20+iLMEExMJADsWIQSjJRB+ZryrPupAdVA5bsuLuG0Z -IgUCZz/5iQIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRA5bsuLuG0Z -InjBAX9bv51DhgTNGDot6gTEKJ7c0FypBuPZPlNOKMCshtvu36+jiJyMpGBaR/C+ -CB8wctkBgNT0/5Mj1e+UXqFAmflWmODnszrYpdiGSF2GJxVwOBLJeFVqPQtl2mSn -mKmhhoVy3bhzBGc/+YkSBSuBBAAiAwME6+LPwcbdvoEdxPA2L002pKXp8Nt7PxIs -jUegQRw2bTQXMlu5zplsUNprWJZ1W/iqLhmEH8eLONxcuzVEQMb3dCImxBzmL3Y5 -HxGGti81EsU7JBIZxHKhl85RY78HdHgCAwEJCYiYBBgTCQAgFiEEoyUQfma8qz7q -QHVQOW7Li7htGSIFAmc/+YkCGwwACgkQOW7Li7htGSJT3AGAgueSdnW+Lvrv2K9V -fqgHGB2NDYsdLdXOIHardOmkFmD3rTbjsjHkQrLCs/lCQkVhAYCxmcY10PcZBwYq -mzhn+ek2wQOTGM2wO3qJVZ9Z+z5kkQXH6JtyfkZATJgA/kougNs= -=K1/q ------END PGP PUBLIC KEY BLOCK----- -'''; - final publicKey = OpenPGP.readPublicKey(armored); + final publicKey = OpenPGP.readPublicKey(eccNistP384PublicKey); final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -174,24 +84,7 @@ mzhn+ek2wQOTGM2wO3qJVZ9Z+z5kkQXH6JtyfkZATJgA/kougNs= }); test('ECC Brainpool P-256 key', () { - const armored = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -mFMEZz/50BMJKyQDAwIIAQEHAgMEnXlquLQ8KrfXtrCZ000jDP7I5nXzJQKDws4C -ZgRpECoCHUeYCdIrl3iLUJfYzb9Qq6nt/8oP+jljqiJ35W5NlLQvRGFydCBQcml2 -YWN5IEd1YXJkIDxkYXJ0cGdAb3BlbnBncC5leGFtcGxlLmNvbT6IkwQTEwgAOxYh -BM0bWxQpT4C+Zc7775lRIZ/J3pV4BQJnP/nQAhsDBQsJCAcCAiICBhUKCQgLAgQW -AgMBAh4HAheAAAoJEJlRIZ/J3pV4v3oA/R9IMDJklqQ7qUp20updBhqAbifSELId -xbd886G8hPgiAP0bpKSgkad2xFUSuO8o4b8menOFbFQphPIf/E+CWstwBLhXBGc/ -+dASCSskAwMCCAEBBwIDBGuDRKYwfAtThNOAM51Is4J1BYPN6qZCTN0c9ldSQzGS -VO0lI/BV2JJsuQqI0Pne08Y7og4bhyv9S+D+wcU8sv0DAQgHiHgEGBMIACAWIQTN -G1sUKU+AvmXO+++ZUSGfyd6VeAUCZz/50AIbDAAKCRCZUSGfyd6VeMTKAQCIrK5W -d0O6gwPJ4rSHWo13Q0K9Lth8ZBtXySqZZ/XSBwEAj22bXtH3QkS/K70fExKX4KZ7 -k49k9YikNemTd0HPLRc= -=38Dv ------END PGP PUBLIC KEY BLOCK----- -'''; - final publicKey = OpenPGP.readPublicKey(armored); + final publicKey = OpenPGP.readPublicKey(eccBrainpoolP256PublicKey); final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -215,22 +108,7 @@ k49k9YikNemTd0HPLRc= }); test('ECC Curve 25519 key', () { - const armored = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -mDMEZz/6BhYJKwYBBAHaRw8BAQdAZQAdPHBwHpMPvaCgVCkt/eMCp1ubVxnC/U3a -aMF98SC0L0RhcnQgUHJpdmFjeSBHdWFyZCA8ZGFydHBnQG9wZW5wZ3AuZXhhbXBs -ZS5jb20+iJMEExYKADsWIQQRSLKiT1gJd8J7JiI+1HXdIS0iHgUCZz/6BgIbAwUL -CQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAKCRA+1HXdIS0iHrifAQDPuCf7ztzy -K9opPkJ2aw7SR/IFTE7dfdIuOTDud+Ph4AEA4YRvlmHv27VVAsS8XoDxT+t1tbkA -xgiwXtqXA2t/Ewy4OARnP/oGEgorBgEEAZdVAQUBAQdACcGlnxIiapTiJPzvYtjz -GgwZm0VDgRxkHRUTym4n6X8DAQgHiHgEGBYKACAWIQQRSLKiT1gJd8J7JiI+1HXd -IS0iHgUCZz/6BgIbDAAKCRA+1HXdIS0iHrDSAP0Q/+hfme0EqcVJf/Z38dQwhXIM -Dy6c8mMkLVpS/zXLOAEAp3nZkkgY1MgZAfbx4BEq+uJv801qCBNeThFeOe8/3gs= -=/O/r ------END PGP PUBLIC KEY BLOCK----- -'''; - final publicKey = OpenPGP.readPublicKey(armored); + final publicKey = OpenPGP.readPublicKey(eccCurve25519PublicKey); final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -254,28 +132,7 @@ Dy6c8mMkLVpS/zXLOAEAp3nZkkgY1MgZAfbx4BEq+uJv801qCBNeThFeOe8/3gs= }); test('Rfc9580 Curve 25519 key', () { - const armored = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Version: PHP Privacy v2 - -xioGZ0/jphsAAAAgy19s9qAQsttkAS7gPmFeakgJwkKklQVReq/oPNJVWKrCuwYfGwgAAABcBQJn -T+OmIiEGeyUi0XrLxokVQFD7GiRV+fyaRjGK1u9QOdPm47wEpnkJEHslItF6y8aJAhsDAwsHCQQi -AQIDBRUIDAoOBRYAAQIDAh4LDScHAQkBBwIJAgcDCQMAAAAAx94QMOS423OajdIBpQ7T4MklWxyz -4SNy5d9A3+a7huNEw6qmaJ+pqVGcrNctLB9qWyD4+xjDtMLqHYOeNUsunRUUaBM8BbFCb86wljnA -1RpABwXNL0RhcnQgUHJpdmFjeSBHdWFyZCA8ZGFydHBnQG9wZW5wZ3AuZXhhbXBsZS5jb20+wpUG -EBsIAAAANgUCZ0/jpiIhBnslItF6y8aJFUBQ+xokVfn8mkYxitbvUDnT5uO8BKZ5CRB7JSLResvG -iQIZAQAAAADh1RDsF0xb6N2mB4vw9rYIrNcKdWSwPb0IBY6kNrnN84nMcGCseJ9JWpX+4le3HP/s -Ej9rf8N+GfwQHY7FqUk4m+Mmrg9FSY8rtgtQ/WF/pPBeC80qTmd1eWVuIFZhbiBOZ3V5ZW4gPG5n -dXllbm52MTk4MUBnbWFpbC5jb20+wpIGEBsIAAAAMwUCZ0/jpiIhBnslItF6y8aJFUBQ+xokVfn8 -mkYxitbvUDnT5uO8BKZ5CRB7JSLResvGiQAAAADyNxAEWM9B3Ds6IOlYh2UIJ6BFMuDamUakpZff -sKKi6VPlhwTKb80sbAMCI2QeBpKkFR6j+4hBuleDEm34RE0Qo2P9/e8eJLUt6QvNwv8XrFMHDs4q -BmdP46YZAAAAIDv21k8ovP04IZRAc+Wiq+zFU0HW4P/GA5bLvCCPYHltwpUGGBsIAAAANgUCZ0/j -piIhBnslItF6y8aJFUBQ+xokVfn8mkYxitbvUDnT5uO8BKZ5CRB7JSLResvGiQIbDAAAAAAiEhAM -bPIvl2dct0Etc792kYzRRbUC3XDl5uzoLJj59aBkhkE+e5nX8j6hCfYSjQnPq9oKmMeOjdgkjeDM -fjfQblPSXk32Zpv5RXzNhOIelkIuA9USba2Zw7ZRB4iuDfOgEJAw7VEt ------END PGP PUBLIC KEY BLOCK----- -'''; - final publicKey = OpenPGP.readPublicKey(armored); + final publicKey = OpenPGP.readPublicKey(rfc9580Curve25519PublicKey); final directSignature = publicKey.directSignatures[0]; final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -304,32 +161,7 @@ fjfQblPSXk32Zpv5RXzNhOIelkIuA9USba2Zw7ZRB4iuDfOgEJAw7VEt }); test('Rfc9580 Curve 448 key', () { - const armored = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -xkMGZ0/jphwAAAA5a1Aan5R/DuK8jXNrY9+z7IKjQ8NHO6J1CTodHHpDslwa7V85AavFpaHoxlp0 -yn2fJX2fSnJkqdsAwsAtBh8cCAAAAFwFAmdP46ciIQaTh3wqdlbkHo45oW1AVhRmOkxVk9ozBveA -NfRmZpxgUQkQk4d8KnZW5B4CGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcBCQEHAgkCBwMJ -AwAAAABauRD3wkQRbuI3zf+UyqDl2Y1qlqCHtdViFqwgyZE4mhZZYIrmfHYguy7GJm3FCb4vDZJM -X6qSmyH2kclVUvp3VGJXpOo35lDUidSAIYffeYT7jC2KIz7IcA5WAiHIDW7+5rQ30yehH3MOBhjR -dKNWDdrezDt8A6pl9pRFMlzO+YGQfjwAzS9EYXJ0IFByaXZhY3kgR3VhcmQgPGRhcnRwZ0BvcGVu -cGdwLmV4YW1wbGUuY29tPsLABwYQHAgAAAA2BQJnT+OnIiEGk4d8KnZW5B6OOaFtQFYUZjpMVZPa -Mwb3gDX0ZmacYFEJEJOHfCp2VuQeAhkBAAAAAFvNEKq2qVYMJA+GbX7k4Qk+0pnJFW2+kEQ4qmDE -HhYIoeD9YnicGqr5+479nt9AqxQVV1IJT8mwxqQYIrBsoWxy83Z4o7A9umbkn4CmGFi61EbJ/JjZ -NhLtut2DSlWz9wntNQArseGnHhGjGrupJqiNhkyeCdhJ09grcds6mS1JI4i+EQDNKk5ndXllbiBW -YW4gTmd1eWVuIDxuZ3V5ZW5udjE5ODFAZ21haWwuY29tPsLABAYQHAgAAAAzBQJnT+OnIiEGk4d8 -KnZW5B6OOaFtQFYUZjpMVZPaMwb3gDX0ZmacYFEJEJOHfCp2VuQeAAAAAPx5EGrwf9dxyc8Zcf3T -WTH678WXGljJ5QML4S98Po7Q8vcE+1IGEE/hjOpi/cTpL9z+2lQ6XvLYVD54YuFzsEj11zhMM1gc -4pASBABfwzft/LL75Gr8NvTvc7/oD1PNnXOaoLjKfuXIlFAMel73EQqtp9MR7uQ3iDI7WIXmTG/f -mnT0KQDOQgZnT+OnGgAAADiY/+RbigV4l1adpmwFDGKZLpK7qYHnSuxjknWgOD48e9nDdZ3gkW+o -TOZaHsI546Kexs04A4ZCv8LABwYYHAgAAAA2BQJnT+OnIiEGk4d8KnZW5B6OOaFtQFYUZjpMVZPa -Mwb3gDX0ZmacYFEJEJOHfCp2VuQeAhsMAAAAAMM5EPfNxWSEvc5dMhOkM8a9a8EFgF8qOkwJj9sQ -ISt80um5TscuNDSqmwCJCTLXDhblTcwseZGrBjlrnfij7+9VPQDPRzaYUSopXQDG4l8JFKpYxvEY -4xo3/Pw117VEfs6lTc8PLF+NkIlv6Ip88od/3Op2bjJfQX57cOQwx65hc/bdCgDVEEdmNh5TAjGf -FmXTxip3kr4= ------END PGP PUBLIC KEY BLOCK----- -'''; - final publicKey = OpenPGP.readPublicKey(armored); + final publicKey = OpenPGP.readPublicKey(rfc9580Curve448PublicKey); final directSignature = publicKey.directSignatures[0]; final user = publicKey.users[0]; final subkey = publicKey.subkeys[0]; @@ -358,92 +190,7 @@ FmXTxip3kr4= }); test('Multiple public keys', () { - const armored = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -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 -7z/eCw== ------END PGP PUBLIC KEY BLOCK----- -'''; - - final publicKeys = OpenPGP.readPublicKeys(armored).toList(); + final publicKeys = OpenPGP.readPublicKeys(multiplePublicKeys).toList(); expect(publicKeys.length, 5); expect( publicKeys[0].fingerprint.toHexadecimal(), From d27afd19fa8e4d05114763f07383f538b7cfa3b2 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sat, 7 Dec 2024 17:31:03 +0700 Subject: [PATCH 167/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/data/key_data.dart | 46 ++-- test/message/cleartext_signing_test.dart | 298 ++--------------------- 2 files changed, 41 insertions(+), 303 deletions(-) diff --git a/test/data/key_data.dart b/test/data/key_data.dart index ed65a779..bb9230e9 100644 --- a/test/data/key_data.dart +++ b/test/data/key_data.dart @@ -32,25 +32,23 @@ k0mXubZvyl4GBg== const alicePublicKey = ''' -----BEGIN PGP PUBLIC KEY BLOCK----- -Comment: EB85 BB5F A33A 75E1 5E94 4E63 F231 550C 4F47 E38E -Comment: Alice Lovelace +Comment: Alice's OpenPGP certificate -xjMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U -b7O1u13NJkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+wpAE +mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U +b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO -dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gLO +dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s -E9+eviIDAQgHwngEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb +E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn 0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= -=QX3Q +=iIGO -----END PGP PUBLIC KEY BLOCK----- '''; const alicePrivateKey = ''' -----BEGIN PGP PRIVATE KEY BLOCK----- -Comment: EB85 BB5F A33A 75E1 5E94 4E63 F231 550C 4F47 E38E Comment: Alice Lovelace xVgEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U @@ -70,10 +68,9 @@ Pnn+We1aTBhaGa86AQ== const bobPublicKey = ''' -----BEGIN PGP PUBLIC KEY BLOCK----- -Comment: D1A6 6E1A 23B1 82C9 980F 788C FBFC C82A 015E 7330 -Comment: Bob Babbage +Comment: Bob's OpenPGP certificate -xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv +mQGNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv /seOXpgecTdOcVttfzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz /56fW2O0F23qIRd8UUJp5IIlN4RDdRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/ 5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0kIzDxrEqW+7Ba8nocQlecMF3 @@ -81,8 +78,8 @@ X5KMN5kp2zraLv9dlBBpWW43XktjcCZgMy20SouraVma8Je/ECwUWYUiAZxLIlMv 9CurEOtxUw6N3RdOtLmYZS9uEnn5y1UkF88o8Nku890uk6BrewFzJyLAx5wRZ4F0 qV/yq36UWQ0JB/AUGhHVPdFf6pl6eaxBwT5GXvbBUibtf8YI2og5RsgTWtXfU7eb SGXrl5ZMpbA6mbfhd0R8aPxWfmDWiIOhBufhMCvUHh1sApMKVZnvIff9/0Dca3wb -vLIwa3T4CyshfT0AEQEAAc0hQm9iIEJhYmJhZ2UgPGJvYkBvcGVucGdwLmV4YW1w -bGU+wsEOBBMBCgA4AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE0aZuGiOx +vLIwa3T4CyshfT0AEQEAAbQhQm9iIEJhYmJhZ2UgPGJvYkBvcGVucGdwLmV4YW1w +bGU+iQHOBBMBCgA4AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE0aZuGiOx gsmYD3iM+/zIKgFeczAFAl2lnvoACgkQ+/zIKgFeczBvbAv/VNk90a6hG8Od9xTz XxH5YRFUSGfIA1yjPIVOnKqhMwps2U+sWE3urL+MvjyQRlyRV8oY9IOhQ5Esm6DO ZYrTnE7qVETm1ajIAP2OFChEc55uH88x/anpPOXOJY7S8jbn3naC9qad75BrZ+3g @@ -91,7 +88,7 @@ DcCZCi+qEbafmTQzkAqyzS3nCh3IAqq6Y0kBuaKLm2tSNUOlZbD+OHYQNZ5Jix7c ZUzs6Xh4+I55NRWl5smrLq66yOQoFPy9jot/Qxikx/wP3MsAzeGaZSEPc0fHp5G1 6rlGbxQ3vl8/usUV7W+TMEMljgwd5x8POR6HC8EaCDfVnUBCPi/Gv+egLjsIbPJZ ZEroiE40e6/UoCiQtlpQB5exPJYSd1Q1txCwueih99PHepsDhmUQKiACszNU+RRo -zAYau2VdHqnRJ7QYdxHDiH49jPK4NTMyb/tJh2TiIwcmsIpGzsDNBF2lnPIBDADW +zAYau2VdHqnRJ7QYdxHDiH49jPK4NTMyb/tJh2TiIwcmsIpGuQGNBF2lnPIBDADW ML9cbGMrp12CtF9b2P6z9TTT74S8iyBOzaSvdGDQY/sUtZXRg21HWamXnn9sSXvI DEINOQ6A9QxdxoqWdCHrOuW3ofneYXoG+zeKc4dC86wa1TR2q9vW+RMXSO4uImA+ Uzula/6k1DogDf28qhCxMwG/i/m9g1c/0aApuDyKdQ1PXsHHNlgd/Dn6rrd5y2AO @@ -100,7 +97,7 @@ baifV7wIhEJnvqgFXDN2RXGjLeCOHV4Q2WTYPg/S4k1nMXVDwZXrvIsA0YwIMgIT 827KVZW4lXvqsge+wtnWlszcselGATyzqOK9LdHPdZGzROZYI2e8c+paLNDdVPL6 vdRBUnkCaEkOtl1mr2JpQi5nTU+gTX4IeInC7E+1a9UDF/Y85ybUz8XV8rUnR76U qVC7KidNepdHbZjjXCt8/Zo+Tec9JNbYNQB/e9ExmDntmlHEsSEQzFwzj8sxH48A -EQEAAcLA9gQYAQoAIBYhBNGmbhojsYLJmA94jPv8yCoBXnMwBQJdpZzyAhsMAAoJ +EQEAAYkBtgQYAQoAIBYhBNGmbhojsYLJmA94jPv8yCoBXnMwBQJdpZzyAhsMAAoJ EPv8yCoBXnMw6f8L/26C34dkjBffTzMj5Bdzm8MtF67OYneJ4TQMw7+41IL4rVcS KhIhk/3Ud5knaRtP2ef1+5F66h9/RPQOJ5+tvBwhBAcUWSupKnUrdVaZQanYmtSx cVV2PL9+QEiNN3tzluhaWO//rACxJ+K/ZXQlIzwQVTpNhfGzAaMVV9zpf3u0k14i @@ -110,16 +107,15 @@ qMJxfpa1lHvJLobzOP9fvrswsr92MA2+k901WeISR7qEzcI0Fdg8AyFAExaEK6Vy jP7SXGLwvfisw34OxuZr3qmx1Sufu4toH3XrB7QJN8XyqqbsGxUCBqWif9RSK4xj zRTe56iPeiSJJOIciMP9i2ldI+KgLycyeDvGoBj0HCLO3gVaBe4ubVrj5KjhX2PV NEJd3XZRzaXZE2aAMQ== -=F9yX +=NXei -----END PGP PUBLIC KEY BLOCK----- '''; const bobPrivateKey = ''' -----BEGIN PGP PRIVATE KEY BLOCK----- -Comment: D1A6 6E1A 23B1 82C9 980F 788C FBFC C82A 015E 7330 -Comment: Bob Babbage +Comment: Bob's OpenPGP Transferable Secret Key -xcSYBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv +lQVYBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv /seOXpgecTdOcVttfzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz /56fW2O0F23qIRd8UUJp5IIlN4RDdRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/ 5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0kIzDxrEqW+7Ba8nocQlecMF3 @@ -147,8 +143,8 @@ YnstRQyb/gCSKahveSEjo07CiXMr88UGALwzEr3npFAsPW3osGaFLj49y1oRe11E he9gCHFm+fuzbXrWmdPjYU5/ZdqdojzDqfu4ThfnipknpVUM1o6MQqkjM896FHm8 zbKVFSMhEP6DPHSCexMFrrSgN03PdwHTO6iBaIBBFqmGY01tmJ03SxvSpiBPON9P NVvy/6UZFedTq8A07OUAxO62YUSNtT5pmK2vzs3SAZJmbFbMh+NN204TRI72GlqT -t5hcfkuv8hrmwPS/ZR6q312mKQ6w/1pqO9qizSFCb2IgQmFiYmFnZSA8Ym9iQG9w -ZW5wZ3AuZXhhbXBsZT7CwQ4EEwEKADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgEC +t5hcfkuv8hrmwPS/ZR6q312mKQ6w/1pqO9qitCFCb2IgQmFiYmFnZSA8Ym9iQG9w +ZW5wZ3AuZXhhbXBsZT6JAc4EEwEKADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgEC F4AWIQTRpm4aI7GCyZgPeIz7/MgqAV5zMAUCXaWe+gAKCRD7/MgqAV5zMG9sC/9U 2T3RrqEbw533FPNfEflhEVRIZ8gDXKM8hU6cqqEzCmzZT6xYTe6sv4y+PJBGXJFX yhj0g6FDkSyboM5litOcTupURObVqMgA/Y4UKERznm4fzzH9qek85c4ljtLyNufe @@ -157,8 +153,8 @@ BiV7jZuDyWmZ1cUNwJkKL6oRtp+ZNDOQCrLNLecKHcgCqrpjSQG5oouba1I1Q6Vl sP44dhA1nkmLHtxlTOzpeHj4jnk1FaXmyasurrrI5CgU/L2Oi39DGKTH/A/cywDN 4ZplIQ9zR8enkbXquUZvFDe+Xz+6xRXtb5MwQyWODB3nHw85HocLwRoIN9WdQEI+ L8a/56AuOwhs8llkSuiITjR7r9SgKJC2WlAHl7E8lhJ3VDW3ELC56KH308d6mwOG -ZRAqIAKzM1T5FGjMBhq7ZV0eqdEntBh3EcOIfj2M8rg1MzJv+0mHZOIjByawikbH -xJgEXaWc8gEMANYwv1xsYyunXYK0X1vY/rP1NNPvhLyLIE7NpK90YNBj+xS1ldGD +ZRAqIAKzM1T5FGjMBhq7ZV0eqdEntBh3EcOIfj2M8rg1MzJv+0mHZOIjByawikad +BVgEXaWc8gEMANYwv1xsYyunXYK0X1vY/rP1NNPvhLyLIE7NpK90YNBj+xS1ldGD bUdZqZeef2xJe8gMQg05DoD1DF3GipZ0Ies65beh+d5hegb7N4pzh0LzrBrVNHar 29b5ExdI7i4iYD5TO6Vr/qTUOiAN/byqELEzAb+L+b2DVz/RoCm4PIp1DU9ewcc2 WB38Ofqut3nLYA5tqJ9XvAiEQme+qAVcM3ZFcaMt4I4dXhDZZNg+D9LiTWcxdUPB @@ -186,7 +182,7 @@ SA/je/RXaT8m5VxLYMxwqQXKApzD87fv0tLPlVIEvjEsaf992tFEFSNPcG1l/jpd E3Gt4QJrD9z/bICESw4b4z2DbgD/Xz9IXsA/r9cKiM1h5QMtXvuhyfVeM01enhxM GbOH3gjqqGNKysx0UODGEwr6AV9hAd8RWXMchJLaExK9J5SRawSg671ObAU24SdY vMQ9Z4kAQ2+1ReUZzf3ogSMRZtMT+d18gT6L90/y+APZIaoArLPhebIAGq39HLmJ -26x3z0WAgrpA1kNsjXEXkoiZGPLKIGoe3hrCwPYEGAEKACAWIQTRpm4aI7GCyZgP +26x3z0WAgrpA1kNsjXEXkoiZGPLKIGoe3hqJAbYEGAEKACAWIQTRpm4aI7GCyZgP eIz7/MgqAV5zMAUCXaWc8gIbDAAKCRD7/MgqAV5zMOn/C/9ugt+HZIwX308zI+QX c5vDLReuzmJ3ieE0DMO/uNSC+K1XEioSIZP91HeZJ2kbT9nn9fuReuoff0T0Dief rbwcIQQHFFkrqSp1K3VWmUGp2JrUsXFVdjy/fkBIjTd7c5boWljv/6wAsSfiv2V0 @@ -196,7 +192,7 @@ s9HXglXXqPsBRZJYfP+VStm9L5P/sKjCcX6WtZR7yS6G8zj/X767MLK/djANvpPd NVniEke6hM3CNBXYPAMhQBMWhCulcoz+0lxi8L34rMN+Dsbma96psdUrn7uLaB91 6we0CTfF8qqm7BsVAgalon/UUiuMY80U3ueoj3okiSTiHIjD/YtpXSPioC8nMng7 xqAY9Bwizt4FWgXuLm1a4+So4V9j1TRCXd12Uc2l2RNmgDE= -=FAzO +=miES -----END PGP PRIVATE KEY BLOCK----- '''; diff --git a/test/message/cleartext_signing_test.dart b/test/message/cleartext_signing_test.dart index 3950ce62..e4ff3614 100644 --- a/test/message/cleartext_signing_test.dart +++ b/test/message/cleartext_signing_test.dart @@ -2,6 +2,8 @@ import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/openpgp.dart'; import 'package:test/test.dart'; +import '../data/key_data.dart'; + void main() { group('Sign cleartext', () { const cleartext = ''' @@ -13,141 +15,15 @@ What we need from the grocery store: '''; test('with key Bob', () { - const privateKey = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- -Comment: Bob's OpenPGP Transferable Secret Key - -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 -=miES ------END PGP PRIVATE KEY BLOCK----- -'''; - const publickey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Bob's OpenPGP certificate - -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 -NEJd3XZRzaXZE2aAMQ== -=NXei ------END PGP PUBLIC KEY BLOCK----- -'''; - - final signedMessage = OpenPGP.signCleartext(cleartext, [OpenPGP.readPrivateKey(privateKey)]); + final signedMessage = OpenPGP.signCleartext( + cleartext, + [OpenPGP.readPrivateKey(bobPrivateKey)], + ); expect(signedMessage.text, cleartext); final verifications = OpenPGP.verify( signedMessage.armor(), - [OpenPGP.readPublicKey(publickey)], + [OpenPGP.readPublicKey(bobPublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -156,47 +32,15 @@ NEJd3XZRzaXZE2aAMQ== }); test('with key Alice', () { - const privateKey = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- -Comment: Alice's OpenPGP Transferable Secret Key - -lFgEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U -b7O1u10AAP9XBeW6lzGOLx7zHH9AsUDUTb2pggYGMzd0P3ulJ2AfvQ4RtCZBbGlj -ZSBMb3ZlbGFjZSA8YWxpY2VAb3BlbnBncC5leGFtcGxlPoiQBBMWCAA4AhsDBQsJ -CAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE64W7X6M6deFelE5j8jFVDE9H444FAl2l -nzoACgkQ8jFVDE9H447pKwD6A5xwUqIDprBzrHfahrImaYEZzncqb25vkLV2arYf -a78A/R3AwtLQvjxwLDuzk4dUtUwvUYibL2sAHwj2kGaHnfICnF0EXEcE6RIKKwYB -BAGXVQEFAQEHQEL/BiGtq0k84Km1wqQw2DIikVYrQrMttN8d7BPfnr4iAwEIBwAA -/3/xFPG6U17rhTuq+07gmEvaFYKfxRB6sgAYiW6TMTpQEK6IeAQYFggAIBYhBOuF -u1+jOnXhXpROY/IxVQxPR+OOBQJcRwTpAhsMAAoJEPIxVQxPR+OOWdABAMUdSzpM -hzGs1O0RkWNQWbUzQ8nUOeD9wNbjE3zR+yfRAQDbYqvtWQKN4AQLTxVJN5X5AWyb -Pnn+We1aTBhaGa86AQ== -=n8OM ------END PGP PRIVATE KEY BLOCK----- -'''; - const publickey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Alice's OpenPGP certificate - -mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U -b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE -ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy -MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO -dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 -OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s -E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb -DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn -0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= -=iIGO ------END PGP PUBLIC KEY BLOCK----- -'''; - - final signedMessage = OpenPGP.signCleartext(cleartext, [OpenPGP.readPrivateKey(privateKey)]); + final signedMessage = OpenPGP.signCleartext( + cleartext, + [OpenPGP.readPrivateKey(alicePrivateKey)], + ); expect(signedMessage.text, cleartext); final verifications = OpenPGP.verify( signedMessage.armor(), - [OpenPGP.readPublicKey(publickey)], + [OpenPGP.readPublicKey(alicePublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -205,43 +49,15 @@ DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn }); test('with key from RFC9580', () { - const privateKey = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- - -xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB -exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ -BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 -2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh -RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe -7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ -LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG -GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 -2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE -M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr -k0mXubZvyl4GBg== ------END PGP PRIVATE KEY BLOCK----- -'''; - const publickey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf -GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy -KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw -gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE -QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn -+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh -BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 -j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 -I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== ------END PGP PUBLIC KEY BLOCK----- -'''; - - final signedMessage = OpenPGP.signCleartext(cleartext, [OpenPGP.readPrivateKey(privateKey)]); + final signedMessage = OpenPGP.signCleartext( + cleartext, + [OpenPGP.readPrivateKey(rfc9580PrivateKey)], + ); expect(signedMessage.text, cleartext); final verifications = OpenPGP.verify( signedMessage.armor(), - [OpenPGP.readPublicKey(publickey)], + [OpenPGP.readPublicKey(rfc9580PublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -252,50 +68,6 @@ I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== group('Verify signed message', () { test('with key Bob', () { - const publickey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Bob's OpenPGP certificate - -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 -NEJd3XZRzaXZE2aAMQ== -=NXei ------END PGP PUBLIC KEY BLOCK----- -'''; const message = ''' -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 @@ -321,7 +93,7 @@ tzE+e1XVG0yeiFszkQzusP22sjen49qoDUPz6w== '''; final verifications = OpenPGP.verify( message, - [OpenPGP.readPublicKey(publickey)], + [OpenPGP.readPublicKey(bobPublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -330,22 +102,6 @@ tzE+e1XVG0yeiFszkQzusP22sjen49qoDUPz6w== }); test('with key Alice', () { - const publickey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Alice's OpenPGP certificate - -mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U -b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE -ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy -MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO -dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 -OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s -E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb -DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn -0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= -=iIGO ------END PGP PUBLIC KEY BLOCK----- -'''; const message = ''' -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 @@ -366,7 +122,7 @@ IFe5XsSXLN9vBQ== '''; final verifications = OpenPGP.verify( message, - [OpenPGP.readPublicKey(publickey)], + [OpenPGP.readPublicKey(alicePublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -375,20 +131,6 @@ IFe5XsSXLN9vBQ== }); test('with key from RFC9580', () { - const publickey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf -GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy -KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw -gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE -QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn -+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh -BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 -j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 -I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== ------END PGP PUBLIC KEY BLOCK----- -'''; const message = ''' -----BEGIN PGP SIGNED MESSAGE----- @@ -408,7 +150,7 @@ NK2ay45cX1IVAQ== '''; final verifications = OpenPGP.verify( message, - [OpenPGP.readPublicKey(publickey)], + [OpenPGP.readPublicKey(rfc9580PublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); From 77842b9f85a7b5a800e587705ffbcb3ac3b2425b Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sat, 7 Dec 2024 17:35:28 +0700 Subject: [PATCH 168/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/message/verification_test.dart | 296 +--------------------------- 1 file changed, 10 insertions(+), 286 deletions(-) diff --git a/test/message/verification_test.dart b/test/message/verification_test.dart index 2109f871..c152ffba 100644 --- a/test/message/verification_test.dart +++ b/test/message/verification_test.dart @@ -2,55 +2,13 @@ import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/openpgp.dart'; import 'package:test/test.dart'; +import '../data/key_data.dart'; + void main() { group('Verify detached', () { const literalText = 'Hello World :)'; test('with key Bob', () { - const publickey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Bob's OpenPGP certificate - -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 -NEJd3XZRzaXZE2aAMQ== -=NXei ------END PGP PUBLIC KEY BLOCK----- -'''; const signature = ''' -----BEGIN PGP SIGNATURE----- @@ -71,7 +29,7 @@ zulYLxB5IjCH4MHD final verifications = OpenPGP.verifyDetached( literalText, signature, - [OpenPGP.readPublicKey(publickey)], + [OpenPGP.readPublicKey(bobPublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -80,71 +38,6 @@ zulYLxB5IjCH4MHD }); test('with key Carol', () { - const publickey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -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 -=pa/S ------END PGP PUBLIC KEY BLOCK----- -'''; const signature = ''' -----BEGIN PGP SIGNATURE----- @@ -158,7 +51,7 @@ VfWNEL0YDVD/chX5FwD/WlWByOZFFlhObRyo/0NVyUa3ZJX2ZJD2ro2kDOWQryg= final verifications = OpenPGP.verifyDetached( literalText, signature, - [OpenPGP.readPublicKey(publickey)], + [OpenPGP.readPublicKey(carolPublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -167,22 +60,6 @@ VfWNEL0YDVD/chX5FwD/WlWByOZFFlhObRyo/0NVyUa3ZJX2ZJD2ro2kDOWQryg= }); test('with key Alice', () { - const publickey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Alice's OpenPGP certificate - -mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U -b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE -ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy -MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO -dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 -OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s -E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb -DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn -0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= -=iIGO ------END PGP PUBLIC KEY BLOCK----- -'''; const signature = ''' -----BEGIN PGP SIGNATURE----- @@ -197,7 +74,7 @@ P7gvxbUc0GsL final verifications = OpenPGP.verifyDetached( literalText, signature, - [OpenPGP.readPublicKey(publickey)], + [OpenPGP.readPublicKey(alicePublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -206,20 +83,6 @@ P7gvxbUc0GsL }); test('with key from RFC9580', () { - const publickey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf -GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy -KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw -gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE -QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn -+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh -BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 -j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 -I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== ------END PGP PUBLIC KEY BLOCK----- -'''; const signature = ''' -----BEGIN PGP SIGNATURE----- @@ -232,7 +95,7 @@ i6UgQbwhFpsEXmXEZePugZCqC26UmoFor/Ju80rseo9mXnSkmPJj1BuE+5wG final verifications = OpenPGP.verifyDetached( literalText, signature, - [OpenPGP.readPublicKey(publickey)], + [OpenPGP.readPublicKey(rfc9580PublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -243,50 +106,6 @@ i6UgQbwhFpsEXmXEZePugZCqC26UmoFor/Ju80rseo9mXnSkmPJj1BuE+5wG group('Verify inline', () { test('with key Bob', () { - const publickey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Bob's OpenPGP certificate - -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 -NEJd3XZRzaXZE2aAMQ== -=NXei ------END PGP PUBLIC KEY BLOCK----- -'''; const message = ''' -----BEGIN PGP MESSAGE----- @@ -308,7 +127,7 @@ AgYWB/E= '''; final verifications = OpenPGP.verifyInline( message, - [OpenPGP.readPublicKey(publickey)], + [OpenPGP.readPublicKey(bobPublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -317,71 +136,6 @@ AgYWB/E= }); test('with key Carol', () { - const publickey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -xsPuBF3+CmgRDADZhdKTM3ms3XpXnQke83FgaIBtP1g1qhqpCfg50WiPS0kjiMC0 -OJz2vh59nusbBLzgI//Y1VMhKfIWYbqMcIY+lWbseHjl52rqW6AaJ0TH4NgVt7vh -yVeJt0k/NnxvNhMd0587KXmfpDxrwBqc/l5cVB+p0rL8vs8kxojHXAi5V3koM0Uj -REWs5Jpj/XU9LhEoyXZkeJC/pes1u6UKoFYn7dFIP49Kkd1kb+1bNfdPYtA0JpcG -zYgeMNOvdWJwn43dNhxoeuXfmAEhA8LdzT0C0O+7akXOKWrfhXJ8MTBqvPgWZYx7 -MNuQx/ejIMZHl+Iaf7hG976ILH+NCGiKkhidd9GIuA/WteHiQbXLyfiQ4n8P12q9 -+4dq6ybUM65tnozRyyN+1m3rU2a/+Ly3JCh4TeO27w+cxMWkaeHyTQaJVMbMbDpX -duVd32MA33UVNH5/KXMVczVi5asVjuKDSojJDV1QwX8izZNl1t+AI0L3balCabV0 -SFhlfnBEUj1my1sBAMOSO/I67BvBS3IPHZWXHjgclhs26mPzRlZLryAUWR2DDACH -5fx+yUAdZ8Vu/2zWTHxwWJ/X6gGTLqa9CmfDq5UDqYFFzuWwN4HJ+ryOuak1CGwS -KJUBSA75HExbv0naWg+suy+pEDvF0VALPU9VUkSQtHyR10YO2FWOe3AEtpbYDRwp -dr1ZwEbb3L6IGQ5i/4CNHbJ2u3yUeXsDNAvrpVSEcIjA01RPCOKmf58SDZp4yDdP -xGhM8w6a18+fdQr22f2cJ0xgfPlbzFbO+FUsEgKvn6QTLhbaYw4zs7rdQDejWHV8 -2hP4K+rb9FwknYdV9uo4m77MgGlU+4yvJnGEYaL3jwjI3bH9aooNOl6XbvVAzNzo -mYmaTO7mp6xFAu43yuGyd9K+1E4k7CQTROxTZ+RdtQjV95hSsEmMg792nQvDSBW4 -xwfOQ7pf3kC7r9fm8u9nBlEN12HsbQ8Yvux/ld5q5RaIlD19jzfVR6+hJzbj2ZnU -yQs4ksAfIHTzTdLttRxS9lTRTkVx2vbUnoSBy6TYF1mf6nRPpSm1riZxnkR4+BQL -/0rUAxwegTNIG/5M612s2a45QvYK1turZ7spI1RGitJUIjBXUuR76jIsyqagIhBl -5nEsQ4HLv8OQ3EgJ5T9gldLFpHNczLxBQnnNwfPoD2e0kC/iy0rfiNX8HWpTgQpb -zAosLj5/E0iNlildynIhuqBosyRWFqGva0O6qioL90srlzlfKCloe9R9w3HizjCb -f59yEspuJt9iHVNOPOW2Wj5ub0KTiJPp9vBmrFaB79/IlgojpQoYvQ77Hx5A9CJq -paMCHGOW6Uz9euN1ozzETEkIPtL8XAxcogfpe2JKE1uS7ugxsKEGEDfxOQFKAGV0 -XFtIx50vFCr2vQro0WB858CGN47dCxChhNUxNtGc11JNEkNv/X7hKtRf/5VCmnaz -GWwNK47cqZ7GJfEBnElD7s/tQvTC5Qp7lg9gEt47TUX0bjzUTCxNvLosuKL9+J1W -ln1myRpff/5ZOAnZTPHR+AbX4bRB4sK5zijQe4139Dn2oRYK+EIYoBAxFxSOzehP -IcKKBB8RCAA8BQJd/gppAwsJCgkQm6eJ3HbWhJoEFQoJCAIWAQIXgAIbAwIeARYh -BHH/2gBECeXdsMPo8Zunidx21oSaAABihQD/VWnF1HbBhP+kLwWsqxuYjEslEsM2 -UQPeKGK9an8HZ78BAJPaiL3OpuOmsIoCfOghhMZOKXjIV+Z57LwaMw7FQfPgzSZD -YXJvbCBPbGRzdHlsZSA8Y2Fyb2xAb3BlbnBncC5leGFtcGxlPsKKBBMRCAA8BQJd -/gppAwsJCgkQm6eJ3HbWhJoEFQoJCAIWAQIXgAIbAwIeARYhBHH/2gBECeXdsMPo -8Zunidx21oSaAABQTAD/ZMXAvSbKaMJJpAfwp1C7KAj6K2k2CAz5jwUXyGf1+jUA -/2iAMiX1XcLy3n0L8ytzge8/UAFHafBl4rn4DmUugfhjzsPMBF3+CmgQDADZhdKT -M3ms3XpXnQke83FgaIBtP1g1qhqpCfg50WiPS0kjiMC0OJz2vh59nusbBLzgI//Y -1VMhKfIWYbqMcIY+lWbseHjl52rqW6AaJ0TH4NgVt7vhyVeJt0k/NnxvNhMd0587 -KXmfpDxrwBqc/l5cVB+p0rL8vs8kxojHXAi5V3koM0UjREWs5Jpj/XU9LhEoyXZk -eJC/pes1u6UKoFYn7dFIP49Kkd1kb+1bNfdPYtA0JpcGzYgeMNOvdWJwn43dNhxo -euXfmAEhA8LdzT0C0O+7akXOKWrfhXJ8MTBqvPgWZYx7MNuQx/ejIMZHl+Iaf7hG -976ILH+NCGiKkhidd9GIuA/WteHiQbXLyfiQ4n8P12q9+4dq6ybUM65tnozRyyN+ -1m3rU2a/+Ly3JCh4TeO27w+cxMWkaeHyTQaJVMbMbDpXduVd32MA33UVNH5/KXMV -czVi5asVjuKDSojJDV1QwX8izZNl1t+AI0L3balCabV0SFhlfnBEUj1my1sMAIfl -/H7JQB1nxW7/bNZMfHBYn9fqAZMupr0KZ8OrlQOpgUXO5bA3gcn6vI65qTUIbBIo -lQFIDvkcTFu/SdpaD6y7L6kQO8XRUAs9T1VSRJC0fJHXRg7YVY57cAS2ltgNHCl2 -vVnARtvcvogZDmL/gI0dsna7fJR5ewM0C+ulVIRwiMDTVE8I4qZ/nxINmnjIN0/E -aEzzDprXz591CvbZ/ZwnTGB8+VvMVs74VSwSAq+fpBMuFtpjDjOzut1AN6NYdXza -E/gr6tv0XCSdh1X26jibvsyAaVT7jK8mcYRhovePCMjdsf1qig06Xpdu9UDM3OiZ -iZpM7uanrEUC7jfK4bJ30r7UTiTsJBNE7FNn5F21CNX3mFKwSYyDv3adC8NIFbjH -B85Dul/eQLuv1+by72cGUQ3XYextDxi+7H+V3mrlFoiUPX2PN9VHr6EnNuPZmdTJ -CziSwB8gdPNN0u21HFL2VNFORXHa9tSehIHLpNgXWZ/qdE+lKbWuJnGeRHj4FAv+ -MQaafW0uHF+N8MDm8UWPvf4Vd0UJ0UpIjRWl2hTV+BHkNfvZlBRhhQIphNiKRe/W -ap0f/lW2Gm2uS0KgByjjNXEzTiwrte2GX65M6F6Lz8N31kt1Iig1xGOuv+6HmxTN -R8gL2K5PdJeJn8PTJWrRS7+BY8Hdkgb+wVpzE5cCvpFiG/P0yqfBdLWxVPlPI7dc -hDkmx4iAhHJX9J/gX/hC6L3AzPNJqNPAKy20wYp/ruTbbwBolW/4ikWij460JrvB -sm6Sp81A3ebaiN9XkJygLOyhGyhMieGulCYz6AahAFcECtPXGTcordV1mJth8yjF -4gZfDQyg0nMW4Yr49yeFXcRMUw1yzN3Q9v2zzqDuFi2lGYTXYmVqLYzM9KbLO2Wx -E/21xnBjLsl09l/FdA/bhdZq3t4/apbFOeQQ/j/AphvzWbsJnhG9Q7+d3VoDlz0g -FiSduCYIAAq8dUOJNjrUTkZsL1pOIjhYjCMi2uiKS6RQkT6nvuumPF/D/VTnUGeZ -wooEGBEIADwFAl3+CmkDCwkKCRCbp4ncdtaEmgQVCgkIAhYBAheAAhsMAh4BFiEE -cf/aAEQJ5d2ww+jxm6eJ3HbWhJoAAEEpAP91hFqmcb2ZqVcaRDMSVmhkEcFIRmpH -vDoQtVn8sArWqwEAi8HwbMhL+YwRItRZDknpC4vFjTHVMd1zMrz/JyeuT9k= -=pa/S ------END PGP PUBLIC KEY BLOCK----- -'''; const message = ''' -----BEGIN PGP MESSAGE----- @@ -395,7 +149,7 @@ WnEBAMAO5xOUJ/DjT/dPn+D95gk7PleCYBBPX7kdfOWVXJjt '''; final verifications = OpenPGP.verifyInline( message, - [OpenPGP.readPublicKey(publickey)], + [OpenPGP.readPublicKey(carolPublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -404,22 +158,6 @@ WnEBAMAO5xOUJ/DjT/dPn+D95gk7PleCYBBPX7kdfOWVXJjt }); test('with key Alice', () { - const publickey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Alice's OpenPGP certificate - -mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U -b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE -ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy -MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO -dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 -OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s -E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb -DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn -0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= -=iIGO ------END PGP PUBLIC KEY BLOCK----- -'''; const message = ''' -----BEGIN PGP MESSAGE----- @@ -434,7 +172,7 @@ Bg== '''; final verifications = OpenPGP.verifyInline( message, - [OpenPGP.readPublicKey(publickey)], + [OpenPGP.readPublicKey(alicePublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -443,20 +181,6 @@ Bg== }); test('with key from RFC9580', () { - const publickey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf -GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy -KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw -gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE -QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn -+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh -BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 -j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 -I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== ------END PGP PUBLIC KEY BLOCK----- -'''; const message = ''' -----BEGIN PGP MESSAGE----- @@ -470,7 +194,7 @@ Keo3KDD1pX3QV6YKK+dWuA34xgJ5bAw= '''; final verifications = OpenPGP.verifyInline( message, - [OpenPGP.readPublicKey(publickey)], + [OpenPGP.readPublicKey(rfc9580PublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); From 5f994ada23837bb473bdd9d7f113536f1a1f2d33 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sat, 7 Dec 2024 17:46:16 +0700 Subject: [PATCH 169/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/message/literal_data_test.dart | 688 ++-------------------------- 1 file changed, 29 insertions(+), 659 deletions(-) diff --git a/test/message/literal_data_test.dart b/test/message/literal_data_test.dart index 9bf19cb3..f01a3bac 100644 --- a/test/message/literal_data_test.dart +++ b/test/message/literal_data_test.dart @@ -5,148 +5,21 @@ import 'package:dart_pg/src/openpgp.dart'; import 'package:dart_pg/src/packet/base.dart'; import 'package:test/test.dart'; +import '../data/key_data.dart'; + void main() { const literalText = 'Hello World :)'; group('Sign', () { test('with key Bob', () { - const privateKey = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- -Comment: Bob's OpenPGP Transferable Secret Key - -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 -=miES ------END PGP PRIVATE KEY BLOCK----- -'''; - const publicKey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Bob's OpenPGP certificate - -mQGNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv -/seOXpgecTdOcVttfzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz -/56fW2O0F23qIRd8UUJp5IIlN4RDdRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/ -5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0kIzDxrEqW+7Ba8nocQlecMF3 -X5KMN5kp2zraLv9dlBBpWW43XktjcCZgMy20SouraVma8Je/ECwUWYUiAZxLIlMv -9CurEOtxUw6N3RdOtLmYZS9uEnn5y1UkF88o8Nku890uk6BrewFzJyLAx5wRZ4F0 -qV/yq36UWQ0JB/AUGhHVPdFf6pl6eaxBwT5GXvbBUibtf8YI2og5RsgTWtXfU7eb -SGXrl5ZMpbA6mbfhd0R8aPxWfmDWiIOhBufhMCvUHh1sApMKVZnvIff9/0Dca3wb -vLIwa3T4CyshfT0AEQEAAbQhQm9iIEJhYmJhZ2UgPGJvYkBvcGVucGdwLmV4YW1w -bGU+iQHOBBMBCgA4AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE0aZuGiOx -gsmYD3iM+/zIKgFeczAFAl2lnvoACgkQ+/zIKgFeczBvbAv/VNk90a6hG8Od9xTz -XxH5YRFUSGfIA1yjPIVOnKqhMwps2U+sWE3urL+MvjyQRlyRV8oY9IOhQ5Esm6DO -ZYrTnE7qVETm1ajIAP2OFChEc55uH88x/anpPOXOJY7S8jbn3naC9qad75BrZ+3g -9EBUWiy5p8TykP05WSnSxNRt7vFKLfEB4nGkehpwHXOVF0CRNwYle42bg8lpmdXF -DcCZCi+qEbafmTQzkAqyzS3nCh3IAqq6Y0kBuaKLm2tSNUOlZbD+OHYQNZ5Jix7c -ZUzs6Xh4+I55NRWl5smrLq66yOQoFPy9jot/Qxikx/wP3MsAzeGaZSEPc0fHp5G1 -6rlGbxQ3vl8/usUV7W+TMEMljgwd5x8POR6HC8EaCDfVnUBCPi/Gv+egLjsIbPJZ -ZEroiE40e6/UoCiQtlpQB5exPJYSd1Q1txCwueih99PHepsDhmUQKiACszNU+RRo -zAYau2VdHqnRJ7QYdxHDiH49jPK4NTMyb/tJh2TiIwcmsIpGuQGNBF2lnPIBDADW -ML9cbGMrp12CtF9b2P6z9TTT74S8iyBOzaSvdGDQY/sUtZXRg21HWamXnn9sSXvI -DEINOQ6A9QxdxoqWdCHrOuW3ofneYXoG+zeKc4dC86wa1TR2q9vW+RMXSO4uImA+ -Uzula/6k1DogDf28qhCxMwG/i/m9g1c/0aApuDyKdQ1PXsHHNlgd/Dn6rrd5y2AO -baifV7wIhEJnvqgFXDN2RXGjLeCOHV4Q2WTYPg/S4k1nMXVDwZXrvIsA0YwIMgIT -86Rafp1qKlgPNbiIlC1g9RY/iFaGN2b4Ir6GDohBQSfZW2+LXoPZuVE/wGlQ01rh -827KVZW4lXvqsge+wtnWlszcselGATyzqOK9LdHPdZGzROZYI2e8c+paLNDdVPL6 -vdRBUnkCaEkOtl1mr2JpQi5nTU+gTX4IeInC7E+1a9UDF/Y85ybUz8XV8rUnR76U -qVC7KidNepdHbZjjXCt8/Zo+Tec9JNbYNQB/e9ExmDntmlHEsSEQzFwzj8sxH48A -EQEAAYkBtgQYAQoAIBYhBNGmbhojsYLJmA94jPv8yCoBXnMwBQJdpZzyAhsMAAoJ -EPv8yCoBXnMw6f8L/26C34dkjBffTzMj5Bdzm8MtF67OYneJ4TQMw7+41IL4rVcS -KhIhk/3Ud5knaRtP2ef1+5F66h9/RPQOJ5+tvBwhBAcUWSupKnUrdVaZQanYmtSx -cVV2PL9+QEiNN3tzluhaWO//rACxJ+K/ZXQlIzwQVTpNhfGzAaMVV9zpf3u0k14i -tcv6alKY8+rLZvO1wIIeRZLmU0tZDD5HtWDvUV7rIFI1WuoLb+KZgbYn3OWjCPHV -dTrdZ2CqnZbG3SXw6awH9bzRLV9EXkbhIMez0deCVdeo+wFFklh8/5VK2b0vk/+w -qMJxfpa1lHvJLobzOP9fvrswsr92MA2+k901WeISR7qEzcI0Fdg8AyFAExaEK6Vy -jP7SXGLwvfisw34OxuZr3qmx1Sufu4toH3XrB7QJN8XyqqbsGxUCBqWif9RSK4xj -zRTe56iPeiSJJOIciMP9i2ldI+KgLycyeDvGoBj0HCLO3gVaBe4ubVrj5KjhX2PV -NEJd3XZRzaXZE2aAMQ== -=NXei ------END PGP PUBLIC KEY BLOCK----- -'''; - final literalMessage = OpenPGP.sign( OpenPGP.createLiteralMessage(literalText.toBytes()), - [OpenPGP.readPrivateKey(privateKey)], + [OpenPGP.readPrivateKey(bobPrivateKey)], ); expect(utf8.decode(literalMessage.literalData.binary), literalText); final verifications = literalMessage.verify( - [OpenPGP.readPublicKey(publicKey)], + [OpenPGP.readPublicKey(bobPublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -155,142 +28,13 @@ NEJd3XZRzaXZE2aAMQ== }); test('detached with key Bob', () { - const privateKey = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- -Comment: Bob's OpenPGP Transferable Secret Key - -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 -=miES ------END PGP PRIVATE KEY BLOCK----- -'''; - const publicKey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Bob's OpenPGP certificate - -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 -NEJd3XZRzaXZE2aAMQ== -=NXei ------END PGP PUBLIC KEY BLOCK----- -'''; - final signature = OpenPGP.signDetached( OpenPGP.createLiteralMessage(literalText.toBytes()), - [OpenPGP.readPrivateKey(privateKey)], + [OpenPGP.readPrivateKey(bobPrivateKey)], ); var verifications = signature.verify( - [OpenPGP.readPublicKey(publicKey)], + [OpenPGP.readPublicKey(bobPublicKey)], LiteralDataPacket.fromText(literalText), ); for (final verification in verifications) { @@ -299,7 +43,7 @@ NEJd3XZRzaXZE2aAMQ== } verifications = signature.verifyCleartext( - [OpenPGP.readPublicKey(publicKey)], + [OpenPGP.readPublicKey(bobPublicKey)], OpenPGP.createCleartextMessage(literalText), ); for (final verification in verifications) { @@ -309,99 +53,29 @@ NEJd3XZRzaXZE2aAMQ== }); test('with key Alice', () { - const privateKey = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- -Comment: Alice's OpenPGP Transferable Secret Key - -lFgEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U -b7O1u10AAP9XBeW6lzGOLx7zHH9AsUDUTb2pggYGMzd0P3ulJ2AfvQ4RtCZBbGlj -ZSBMb3ZlbGFjZSA8YWxpY2VAb3BlbnBncC5leGFtcGxlPoiQBBMWCAA4AhsDBQsJ -CAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE64W7X6M6deFelE5j8jFVDE9H444FAl2l -nzoACgkQ8jFVDE9H447pKwD6A5xwUqIDprBzrHfahrImaYEZzncqb25vkLV2arYf -a78A/R3AwtLQvjxwLDuzk4dUtUwvUYibL2sAHwj2kGaHnfICnF0EXEcE6RIKKwYB -BAGXVQEFAQEHQEL/BiGtq0k84Km1wqQw2DIikVYrQrMttN8d7BPfnr4iAwEIBwAA -/3/xFPG6U17rhTuq+07gmEvaFYKfxRB6sgAYiW6TMTpQEK6IeAQYFggAIBYhBOuF -u1+jOnXhXpROY/IxVQxPR+OOBQJcRwTpAhsMAAoJEPIxVQxPR+OOWdABAMUdSzpM -hzGs1O0RkWNQWbUzQ8nUOeD9wNbjE3zR+yfRAQDbYqvtWQKN4AQLTxVJN5X5AWyb -Pnn+We1aTBhaGa86AQ== -=n8OM ------END PGP PRIVATE KEY BLOCK----- -'''; - const publicKey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Alice's OpenPGP certificate - -mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U -b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE -ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy -MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO -dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 -OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s -E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb -DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn -0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= -=iIGO ------END PGP PUBLIC KEY BLOCK----- -'''; - final literalMessage = OpenPGP.sign( OpenPGP.createLiteralMessage(literalText.toBytes()), - [OpenPGP.readPrivateKey(privateKey)], + [OpenPGP.readPrivateKey(alicePrivateKey)], ); expect(utf8.decode(literalMessage.literalData.binary), literalText); final verifications = literalMessage.verify( - [OpenPGP.readPublicKey(publicKey)], + [OpenPGP.readPublicKey(alicePublicKey)], ); for (final verification in verifications) { - expect(verification.isVerified, isTrue); - expect(verification.keyID.toHexadecimal(), 'f231550c4f47e38e'); - } - }); - - test('detached with key Alice', () { - const privateKey = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- -Comment: Alice's OpenPGP Transferable Secret Key - -lFgEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U -b7O1u10AAP9XBeW6lzGOLx7zHH9AsUDUTb2pggYGMzd0P3ulJ2AfvQ4RtCZBbGlj -ZSBMb3ZlbGFjZSA8YWxpY2VAb3BlbnBncC5leGFtcGxlPoiQBBMWCAA4AhsDBQsJ -CAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE64W7X6M6deFelE5j8jFVDE9H444FAl2l -nzoACgkQ8jFVDE9H447pKwD6A5xwUqIDprBzrHfahrImaYEZzncqb25vkLV2arYf -a78A/R3AwtLQvjxwLDuzk4dUtUwvUYibL2sAHwj2kGaHnfICnF0EXEcE6RIKKwYB -BAGXVQEFAQEHQEL/BiGtq0k84Km1wqQw2DIikVYrQrMttN8d7BPfnr4iAwEIBwAA -/3/xFPG6U17rhTuq+07gmEvaFYKfxRB6sgAYiW6TMTpQEK6IeAQYFggAIBYhBOuF -u1+jOnXhXpROY/IxVQxPR+OOBQJcRwTpAhsMAAoJEPIxVQxPR+OOWdABAMUdSzpM -hzGs1O0RkWNQWbUzQ8nUOeD9wNbjE3zR+yfRAQDbYqvtWQKN4AQLTxVJN5X5AWyb -Pnn+We1aTBhaGa86AQ== -=n8OM ------END PGP PRIVATE KEY BLOCK----- -'''; - const publicKey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Alice's OpenPGP certificate - -mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U -b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE -ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy -MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO -dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 -OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s -E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb -DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn -0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= -=iIGO ------END PGP PUBLIC KEY BLOCK----- -'''; + expect(verification.isVerified, isTrue); + expect(verification.keyID.toHexadecimal(), 'f231550c4f47e38e'); + } + }); + test('detached with key Alice', () { final signature = OpenPGP.signDetached( OpenPGP.createLiteralMessage(literalText.toBytes()), - [OpenPGP.readPrivateKey(privateKey)], + [OpenPGP.readPrivateKey(alicePrivateKey)], ); var verifications = signature.verify( - [OpenPGP.readPublicKey(publicKey)], + [OpenPGP.readPublicKey(alicePublicKey)], LiteralDataPacket.fromText(literalText), ); for (final verification in verifications) { @@ -410,7 +84,7 @@ DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn } verifications = signature.verifyCleartext( - [OpenPGP.readPublicKey(publicKey)], + [OpenPGP.readPublicKey(alicePublicKey)], OpenPGP.createCleartextMessage(literalText), ); for (final verification in verifications) { @@ -420,45 +94,14 @@ DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn }); test('with key from RFC9580', () { - const privateKey = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- - -xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB -exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ -BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 -2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh -RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe -7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ -LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG -GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 -2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE -M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr -k0mXubZvyl4GBg== ------END PGP PRIVATE KEY BLOCK----- -'''; - const publicKey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf -GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy -KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw -gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE -QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn -+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh -BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 -j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 -I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== ------END PGP PUBLIC KEY BLOCK----- -'''; - final literalMessage = OpenPGP.sign( OpenPGP.createLiteralMessage(literalText.toBytes()), - [OpenPGP.readPrivateKey(privateKey)], + [OpenPGP.readPrivateKey(rfc9580PrivateKey)], ); expect(utf8.decode(literalMessage.literalData.binary), literalText); final verifications = literalMessage.verify( - [OpenPGP.readPublicKey(publicKey)], + [OpenPGP.readPublicKey(rfc9580PublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -467,44 +110,13 @@ I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== }); test('detached with key from RFC9580', () { - const privateKey = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- - -xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB -exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ -BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 -2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh -RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe -7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/ -LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG -GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6 -2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE -M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr -k0mXubZvyl4GBg== ------END PGP PRIVATE KEY BLOCK----- -'''; - const publicKey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf -GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy -KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw -gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE -QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn -+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh -BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8 -j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805 -I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== ------END PGP PUBLIC KEY BLOCK----- -'''; - final signature = OpenPGP.signDetached( OpenPGP.createLiteralMessage(literalText.toBytes()), - [OpenPGP.readPrivateKey(privateKey)], + [OpenPGP.readPrivateKey(rfc9580PrivateKey)], ); var verifications = signature.verify( - [OpenPGP.readPublicKey(publicKey)], + [OpenPGP.readPublicKey(rfc9580PublicKey)], LiteralDataPacket.fromText(literalText), ); for (final verification in verifications) { @@ -513,7 +125,7 @@ I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg== } verifications = signature.verifyCleartext( - [OpenPGP.readPublicKey(publicKey)], + [OpenPGP.readPublicKey(rfc9580PublicKey)], OpenPGP.createCleartextMessage(literalText), ); for (final verification in verifications) { @@ -907,115 +519,14 @@ xBfiBCSwAsSjvat9sW5YOIcqo6hUMnY804L61RklAJn93ohVOt0N =Bbti -----END PGP MESSAGE----- '''; - const privateKey = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- -Comment: Bob's OpenPGP Transferable Secret Key - -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 -=miES ------END PGP PRIVATE KEY BLOCK----- -'''; - const publicKey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Alice's OpenPGP certificate - -mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U -b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE -ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy -MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO -dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 -OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s -E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb -DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn -0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= -=iIGO ------END PGP PUBLIC KEY BLOCK----- -'''; - final literalMessage = OpenPGP.decryptMessage( message, - decryptionKeys: [OpenPGP.readPrivateKey(privateKey)], + decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); expect(utf8.decode(literalMessage.literalData.binary), literalText); final verifications = literalMessage.verify( - [OpenPGP.readPublicKey(publicKey)], + [OpenPGP.readPublicKey(alicePublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -1056,161 +567,20 @@ lGTgNJurYorPkcWEPFRrJqtNvWxM9tqQhljCXQUvNniSdkwLZJqaxxWrwuPe vMq9yq4ffP8QAc1kwjaK9aJ/J45fPw== =M/i3 -----END PGP MESSAGE----- -'''; - const privateKey = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- -Comment: Bob's OpenPGP Transferable Secret Key - -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 -=miES ------END PGP PRIVATE KEY BLOCK----- -'''; - const alicePublicKey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Alice's OpenPGP certificate - -mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U -b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE -ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy -MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO -dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4 -OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s -E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb -DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn -0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE= -=iIGO ------END PGP PUBLIC KEY BLOCK----- -'''; - const bobPublicKey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Bob's OpenPGP certificate - -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 -NEJd3XZRzaXZE2aAMQ== -=NXei ------END PGP PUBLIC KEY BLOCK----- '''; final literalMessage = OpenPGP.decryptMessage( message, - decryptionKeys: [OpenPGP.readPrivateKey(privateKey)], + decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); expect(utf8.decode(literalMessage.literalData.binary), literalText); final verifications = literalMessage.verify( - [OpenPGP.readPublicKey(alicePublicKey), OpenPGP.readPublicKey(bobPublicKey)], + [ + OpenPGP.readPublicKey(alicePublicKey), + OpenPGP.readPublicKey(bobPublicKey), + ], ).toList(); expect(verifications.length, 2); expect(verifications[0].isVerified, isTrue); From 011895ea45f4b75d247302d4ac52eca920ac5756 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sun, 8 Dec 2024 10:45:33 +0700 Subject: [PATCH 170/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/type/encrypted_message.dart | 4 +- test/message/literal_data_test.dart | 366 +++++++--------------------- 2 files changed, 85 insertions(+), 285 deletions(-) diff --git a/lib/src/type/encrypted_message.dart b/lib/src/type/encrypted_message.dart index 863acaab..2d6196ba 100644 --- a/lib/src/type/encrypted_message.dart +++ b/lib/src/type/encrypted_message.dart @@ -4,14 +4,16 @@ library; +import 'armorable.dart'; import 'encrypted_data_packet.dart'; import 'literal_message.dart'; +import 'packet_container.dart'; import 'private_key.dart'; import 'session_key.dart'; /// Encrypted message interface /// Author Nguyen Van Nguyen -abstract interface class EncryptedMessageInterface { +abstract interface class EncryptedMessageInterface implements ArmorableInterface, PacketContainerInterface { /// Return encrypted packet. EncryptedDataPacketInterface get encryptedPacket; diff --git a/test/message/literal_data_test.dart b/test/message/literal_data_test.dart index f01a3bac..e6b7de89 100644 --- a/test/message/literal_data_test.dart +++ b/test/message/literal_data_test.dart @@ -135,7 +135,84 @@ void main() { }); }); - group('Sign & encrypt', () {}); + group('Sign & encrypt', () { + final literalData = Helper.randomBytes(1024); + test('from Bob to Alice', () { + final encrytpedMessage = OpenPGP.encryptBinaryData( + literalData, + encryptionKeys: [OpenPGP.readPublicKey(bobPublicKey)], + signingKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], + ); + final literalMessage = OpenPGP.decryptMessage( + encrytpedMessage.armor(), + decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], + ); + expect(literalMessage.literalData.binary, equals(literalData)); + + final verifications = literalMessage.verify( + [OpenPGP.readPublicKey(bobPublicKey)], + ); + expect(verifications.first.isVerified, isTrue); + expect(verifications.first.keyID.toHexadecimal(), 'f231550c4f47e38e'); + }); + + test('from Alice to Bob', () { + final encrytpedMessage = OpenPGP.encryptBinaryData( + literalData, + encryptionKeys: [OpenPGP.readPublicKey(alicePublicKey)], + signingKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], + ); + final literalMessage = OpenPGP.decryptMessage( + encrytpedMessage.armor(), + decryptionKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], + ); + expect(literalMessage.literalData.binary, equals(literalData)); + + final verifications = literalMessage.verify( + [OpenPGP.readPublicKey(bobPublicKey)], + ); + expect(verifications.first.isVerified, isTrue); + expect(verifications.first.keyID.toHexadecimal(), 'fbfcc82a015e7330'); + }); + + test('from Alice to rfc9580', () { + final encrytpedMessage = OpenPGP.encryptBinaryData( + literalData, + encryptionKeys: [OpenPGP.readPublicKey(rfc9580PublicKey)], + signingKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], + ); + final literalMessage = OpenPGP.decryptMessage( + encrytpedMessage.armor(), + decryptionKeys: [OpenPGP.readPrivateKey(rfc9580PrivateKey)], + ); + expect(literalMessage.literalData.binary, equals(literalData)); + + final verifications = literalMessage.verify( + [OpenPGP.readPublicKey(alicePublicKey)], + ); + expect(verifications.first.isVerified, isTrue); + expect(verifications.first.keyID.toHexadecimal(), 'f231550c4f47e38e'); + }); + + test('from rfc9580 to Alice', () { + final encrytpedMessage = OpenPGP.encryptBinaryData( + literalData, + encryptionKeys: [OpenPGP.readPublicKey(alicePublicKey)], + signingKeys: [OpenPGP.readPrivateKey(rfc9580PrivateKey)], + ); + final literalMessage = OpenPGP.decryptMessage( + encrytpedMessage.armor(), + decryptionKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], + ); + expect(literalMessage.literalData.binary, equals(literalData)); + + final verifications = literalMessage.verify( + [OpenPGP.readPublicKey(rfc9580PublicKey)], + ); + expect(verifications.first.isVerified, isTrue); + expect(verifications.first.keyID.toHexadecimal(), 'cb186c4f0609a697'); + }); + }); group('Decrypt & verify', () { test('with key Bob', () { @@ -167,143 +244,14 @@ lFiOpCG/SDaD4bl7BYYf9o6c6Gs9vKf9mShmWUO0tg2NDlFAzdQTBOzaxw== =BADY -----END PGP MESSAGE----- '''; - const privateKey = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- -Comment: Bob's OpenPGP Transferable Secret Key - -lQVYBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv -/seOXpgecTdOcVttfzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz -/56fW2O0F23qIRd8UUJp5IIlN4RDdRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/ -5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0kIzDxrEqW+7Ba8nocQlecMF3 -X5KMN5kp2zraLv9dlBBpWW43XktjcCZgMy20SouraVma8Je/ECwUWYUiAZxLIlMv -9CurEOtxUw6N3RdOtLmYZS9uEnn5y1UkF88o8Nku890uk6BrewFzJyLAx5wRZ4F0 -qV/yq36UWQ0JB/AUGhHVPdFf6pl6eaxBwT5GXvbBUibtf8YI2og5RsgTWtXfU7eb -SGXrl5ZMpbA6mbfhd0R8aPxWfmDWiIOhBufhMCvUHh1sApMKVZnvIff9/0Dca3wb -vLIwa3T4CyshfT0AEQEAAQAL/RZqbJW2IqQDCnJi4Ozm++gPqBPiX1RhTWSjwxfM -cJKUZfzLj414rMKm6Jh1cwwGY9jekROhB9WmwaaKT8HtcIgrZNAlYzANGRCM4TLK -3VskxfSwKKna8l+s+mZglqbAjUg3wmFuf9Tj2xcUZYmyRm1DEmcN2ZzpvRtHgX7z -Wn1mAKUlSDJZSQks0zjuMNbupcpyJokdlkUg2+wBznBOTKzgMxVNC9b2g5/tMPUs -hGGWmF1UH+7AHMTaS6dlmr2ZBIyogdnfUqdNg5sZwsxSNrbglKP4sqe7X61uEAIQ -bD7rT3LonLbhkrj3I8wilUD8usIwt5IecoHhd9HziqZjRCc1BUBkboUEoyedbDV4 -i4qfsFZ6CEWoLuD5pW7dEp0M+WeuHXO164Rc+LnH6i1VQrpb1Okl4qO6ejIpIjBI -1t3GshtUu/mwGBBxs60KBX5g77mFQ9lLCRj8lSYqOsHRKBhUp4qM869VA+fD0BRP -fqPT0I9IH4Oa/A3jYJcg622GwQYA1LhnP208Waf6PkQSJ6kyr8ymY1yVh9VBE/g6 -fRDYA+pkqKnw9wfH2Qho3ysAA+OmVOX8Hldg+Pc0Zs0e5pCavb0En8iFLvTA0Q2E -LR5rLue9uD7aFuKFU/VdcddY9Ww/vo4k5p/tVGp7F8RYCFn9rSjIWbfvvZi1q5Tx -+akoZbga+4qQ4WYzB/obdX6SCmi6BndcQ1QdjCCQU6gpYx0MddVERbIp9+2SXDyL -hpxjSyz+RGsZi/9UAshT4txP4+MZBgDfK3ZqtW+h2/eMRxkANqOJpxSjMyLO/FXN -WxzTDYeWtHNYiAlOwlQZEPOydZFty9IVzzNFQCIUCGjQ/nNyhw7adSgUk3+BXEx/ -MyJPYY0BYuhLxLYcrfQ9nrhaVKxRJj25SVHj2ASsiwGJRZW4CC3uw40OYxfKEvNC -mer/VxM3kg8qqGf9KUzJ1dVdAvjyx2Hz6jY2qWCyRQ6IMjWHyd43C4r3jxooYKUC -YnstRQyb/gCSKahveSEjo07CiXMr88UGALwzEr3npFAsPW3osGaFLj49y1oRe11E -he9gCHFm+fuzbXrWmdPjYU5/ZdqdojzDqfu4ThfnipknpVUM1o6MQqkjM896FHm8 -zbKVFSMhEP6DPHSCexMFrrSgN03PdwHTO6iBaIBBFqmGY01tmJ03SxvSpiBPON9P -NVvy/6UZFedTq8A07OUAxO62YUSNtT5pmK2vzs3SAZJmbFbMh+NN204TRI72GlqT -t5hcfkuv8hrmwPS/ZR6q312mKQ6w/1pqO9qitCFCb2IgQmFiYmFnZSA8Ym9iQG9w -ZW5wZ3AuZXhhbXBsZT6JAc4EEwEKADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgEC -F4AWIQTRpm4aI7GCyZgPeIz7/MgqAV5zMAUCXaWe+gAKCRD7/MgqAV5zMG9sC/9U -2T3RrqEbw533FPNfEflhEVRIZ8gDXKM8hU6cqqEzCmzZT6xYTe6sv4y+PJBGXJFX -yhj0g6FDkSyboM5litOcTupURObVqMgA/Y4UKERznm4fzzH9qek85c4ljtLyNufe -doL2pp3vkGtn7eD0QFRaLLmnxPKQ/TlZKdLE1G3u8Uot8QHicaR6GnAdc5UXQJE3 -BiV7jZuDyWmZ1cUNwJkKL6oRtp+ZNDOQCrLNLecKHcgCqrpjSQG5oouba1I1Q6Vl -sP44dhA1nkmLHtxlTOzpeHj4jnk1FaXmyasurrrI5CgU/L2Oi39DGKTH/A/cywDN -4ZplIQ9zR8enkbXquUZvFDe+Xz+6xRXtb5MwQyWODB3nHw85HocLwRoIN9WdQEI+ -L8a/56AuOwhs8llkSuiITjR7r9SgKJC2WlAHl7E8lhJ3VDW3ELC56KH308d6mwOG -ZRAqIAKzM1T5FGjMBhq7ZV0eqdEntBh3EcOIfj2M8rg1MzJv+0mHZOIjByawikad -BVgEXaWc8gEMANYwv1xsYyunXYK0X1vY/rP1NNPvhLyLIE7NpK90YNBj+xS1ldGD -bUdZqZeef2xJe8gMQg05DoD1DF3GipZ0Ies65beh+d5hegb7N4pzh0LzrBrVNHar -29b5ExdI7i4iYD5TO6Vr/qTUOiAN/byqELEzAb+L+b2DVz/RoCm4PIp1DU9ewcc2 -WB38Ofqut3nLYA5tqJ9XvAiEQme+qAVcM3ZFcaMt4I4dXhDZZNg+D9LiTWcxdUPB -leu8iwDRjAgyAhPzpFp+nWoqWA81uIiULWD1Fj+IVoY3ZvgivoYOiEFBJ9lbb4te -g9m5UT/AaVDTWuHzbspVlbiVe+qyB77C2daWzNyx6UYBPLOo4r0t0c91kbNE5lgj -Z7xz6los0N1U8vq91EFSeQJoSQ62XWavYmlCLmdNT6BNfgh4icLsT7Vr1QMX9jzn -JtTPxdXytSdHvpSpULsqJ016l0dtmONcK3z9mj5N5z0k1tg1AH970TGYOe2aUcSx -IRDMXDOPyzEfjwARAQABAAv9F2CwsjS+Sjh1M1vegJbZjei4gF1HHpEM0K0PSXsp -SfVvpR4AoSJ4He6CXSMWg0ot8XKtDuZoV9jnJaES5UL9pMAD7JwIOqZm/DYVJM5h -OASCh1c356/wSbFbzRHPtUdZO9Q30WFNJM5pHbCJPjtNoRmRGkf71RxtvHBzy7np -Ga+W6U/NVKHw0i0CYwMI0YlKDakYW3Pm+QL+gHZFvngGweTod0f9l2VLLAmeQR/c -+EZs7lNumhuZ8mXcwhUc9JQIhOkpO+wreDysEFkAcsKbkQP3UDUsA1gFx9pbMzT0 -tr1oZq2a4QBtxShHzP/ph7KLpN+6qtjks3xB/yjTgaGmtrwM8tSe0wD1RwXS+/1o -BHpXTnQ7TfeOGUAu4KCoOQLv6ELpKWbRBLWuiPwMdbGpvVFALO8+kvKAg9/r+/ny -zM2GQHY+J3Jh5JxPiJnHfXNZjIKLbFbIPdSKNyJBuazXW8xIa//mEHMI5OcvsZBK -clAIp7LXzjEjKXIwHwDcTn9pBgDpdOKTHOtJ3JUKx0rWVsDH6wq6iKV/FTVSY5jl -zN+puOEsskF1Lfxn9JsJihAVO3yNsp6RvkKtyNlFazaCVKtDAmkjoh60XNxcNRqr -gCnwdpbgdHP6v/hvZY54ZaJjz6L2e8unNEkYLxDt8cmAyGPgH2XgL7giHIp9jrsQ -aS381gnYwNX6wE1aEikgtY91nqJjwPlibF9avSyYQoMtEqM/1UjTjB2KdD/MitK5 -fP0VpvuXpNYZedmyq4UOMwdkiNMGAOrfmOeT0olgLrTMT5H97Cn3Yxbk13uXHNu/ -ZUZZNe8s+QtuLfUlKAJtLEUutN33TlWQY522FV0m17S+b80xJib3yZVJteVurrh5 -HSWHAM+zghQAvCesg5CLXa2dNMkTCmZKgCBvfDLZuZbjFwnwCI6u/NhOY9egKuUf -SA/je/RXaT8m5VxLYMxwqQXKApzD87fv0tLPlVIEvjEsaf992tFEFSNPcG1l/jpd -5AVXw6kKuf85UkJtYR1x2MkQDrqY1QX/XMw00kt8y9kMZUre19aCArcmor+hDhRJ -E3Gt4QJrD9z/bICESw4b4z2DbgD/Xz9IXsA/r9cKiM1h5QMtXvuhyfVeM01enhxM -GbOH3gjqqGNKysx0UODGEwr6AV9hAd8RWXMchJLaExK9J5SRawSg671ObAU24SdY -vMQ9Z4kAQ2+1ReUZzf3ogSMRZtMT+d18gT6L90/y+APZIaoArLPhebIAGq39HLmJ -26x3z0WAgrpA1kNsjXEXkoiZGPLKIGoe3hqJAbYEGAEKACAWIQTRpm4aI7GCyZgP -eIz7/MgqAV5zMAUCXaWc8gIbDAAKCRD7/MgqAV5zMOn/C/9ugt+HZIwX308zI+QX -c5vDLReuzmJ3ieE0DMO/uNSC+K1XEioSIZP91HeZJ2kbT9nn9fuReuoff0T0Dief -rbwcIQQHFFkrqSp1K3VWmUGp2JrUsXFVdjy/fkBIjTd7c5boWljv/6wAsSfiv2V0 -JSM8EFU6TYXxswGjFVfc6X97tJNeIrXL+mpSmPPqy2bztcCCHkWS5lNLWQw+R7Vg -71Fe6yBSNVrqC2/imYG2J9zlowjx1XU63Wdgqp2Wxt0l8OmsB/W80S1fRF5G4SDH -s9HXglXXqPsBRZJYfP+VStm9L5P/sKjCcX6WtZR7yS6G8zj/X767MLK/djANvpPd -NVniEke6hM3CNBXYPAMhQBMWhCulcoz+0lxi8L34rMN+Dsbma96psdUrn7uLaB91 -6we0CTfF8qqm7BsVAgalon/UUiuMY80U3ueoj3okiSTiHIjD/YtpXSPioC8nMng7 -xqAY9Bwizt4FWgXuLm1a4+So4V9j1TRCXd12Uc2l2RNmgDE= -=miES ------END PGP PRIVATE KEY BLOCK----- -'''; - const publicKey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- -Comment: Bob's OpenPGP certificate - -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 -NEJd3XZRzaXZE2aAMQ== -=NXei ------END PGP PUBLIC KEY BLOCK----- -'''; - final literalMessage = OpenPGP.decryptMessage( message, - decryptionKeys: [OpenPGP.readPrivateKey(privateKey)], + decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); expect(utf8.decode(literalMessage.literalData.binary), literalText); final verifications = literalMessage.verify( - [OpenPGP.readPublicKey(publicKey)], + [OpenPGP.readPublicKey(bobPublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); @@ -332,164 +280,14 @@ wqP2oIPIsEeuMG40kub7RpJWnidJMnOoG7ZScRqbwkECF89DN4sYYREJkaaHFr8= =JDtL -----END PGP MESSAGE----- '''; - const privateKey = ''' ------BEGIN PGP PRIVATE KEY BLOCK----- -Comment: Bob's OpenPGP Transferable Secret Key - -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 -=miES ------END PGP PRIVATE KEY BLOCK----- -'''; - const publicKey = ''' ------BEGIN PGP PUBLIC KEY BLOCK----- - -xsPuBF3+CmgRDADZhdKTM3ms3XpXnQke83FgaIBtP1g1qhqpCfg50WiPS0kjiMC0 -OJz2vh59nusbBLzgI//Y1VMhKfIWYbqMcIY+lWbseHjl52rqW6AaJ0TH4NgVt7vh -yVeJt0k/NnxvNhMd0587KXmfpDxrwBqc/l5cVB+p0rL8vs8kxojHXAi5V3koM0Uj -REWs5Jpj/XU9LhEoyXZkeJC/pes1u6UKoFYn7dFIP49Kkd1kb+1bNfdPYtA0JpcG -zYgeMNOvdWJwn43dNhxoeuXfmAEhA8LdzT0C0O+7akXOKWrfhXJ8MTBqvPgWZYx7 -MNuQx/ejIMZHl+Iaf7hG976ILH+NCGiKkhidd9GIuA/WteHiQbXLyfiQ4n8P12q9 -+4dq6ybUM65tnozRyyN+1m3rU2a/+Ly3JCh4TeO27w+cxMWkaeHyTQaJVMbMbDpX -duVd32MA33UVNH5/KXMVczVi5asVjuKDSojJDV1QwX8izZNl1t+AI0L3balCabV0 -SFhlfnBEUj1my1sBAMOSO/I67BvBS3IPHZWXHjgclhs26mPzRlZLryAUWR2DDACH -5fx+yUAdZ8Vu/2zWTHxwWJ/X6gGTLqa9CmfDq5UDqYFFzuWwN4HJ+ryOuak1CGwS -KJUBSA75HExbv0naWg+suy+pEDvF0VALPU9VUkSQtHyR10YO2FWOe3AEtpbYDRwp -dr1ZwEbb3L6IGQ5i/4CNHbJ2u3yUeXsDNAvrpVSEcIjA01RPCOKmf58SDZp4yDdP -xGhM8w6a18+fdQr22f2cJ0xgfPlbzFbO+FUsEgKvn6QTLhbaYw4zs7rdQDejWHV8 -2hP4K+rb9FwknYdV9uo4m77MgGlU+4yvJnGEYaL3jwjI3bH9aooNOl6XbvVAzNzo -mYmaTO7mp6xFAu43yuGyd9K+1E4k7CQTROxTZ+RdtQjV95hSsEmMg792nQvDSBW4 -xwfOQ7pf3kC7r9fm8u9nBlEN12HsbQ8Yvux/ld5q5RaIlD19jzfVR6+hJzbj2ZnU -yQs4ksAfIHTzTdLttRxS9lTRTkVx2vbUnoSBy6TYF1mf6nRPpSm1riZxnkR4+BQL -/0rUAxwegTNIG/5M612s2a45QvYK1turZ7spI1RGitJUIjBXUuR76jIsyqagIhBl -5nEsQ4HLv8OQ3EgJ5T9gldLFpHNczLxBQnnNwfPoD2e0kC/iy0rfiNX8HWpTgQpb -zAosLj5/E0iNlildynIhuqBosyRWFqGva0O6qioL90srlzlfKCloe9R9w3HizjCb -f59yEspuJt9iHVNOPOW2Wj5ub0KTiJPp9vBmrFaB79/IlgojpQoYvQ77Hx5A9CJq -paMCHGOW6Uz9euN1ozzETEkIPtL8XAxcogfpe2JKE1uS7ugxsKEGEDfxOQFKAGV0 -XFtIx50vFCr2vQro0WB858CGN47dCxChhNUxNtGc11JNEkNv/X7hKtRf/5VCmnaz -GWwNK47cqZ7GJfEBnElD7s/tQvTC5Qp7lg9gEt47TUX0bjzUTCxNvLosuKL9+J1W -ln1myRpff/5ZOAnZTPHR+AbX4bRB4sK5zijQe4139Dn2oRYK+EIYoBAxFxSOzehP -IcKKBB8RCAA8BQJd/gppAwsJCgkQm6eJ3HbWhJoEFQoJCAIWAQIXgAIbAwIeARYh -BHH/2gBECeXdsMPo8Zunidx21oSaAABihQD/VWnF1HbBhP+kLwWsqxuYjEslEsM2 -UQPeKGK9an8HZ78BAJPaiL3OpuOmsIoCfOghhMZOKXjIV+Z57LwaMw7FQfPgzSZD -YXJvbCBPbGRzdHlsZSA8Y2Fyb2xAb3BlbnBncC5leGFtcGxlPsKKBBMRCAA8BQJd -/gppAwsJCgkQm6eJ3HbWhJoEFQoJCAIWAQIXgAIbAwIeARYhBHH/2gBECeXdsMPo -8Zunidx21oSaAABQTAD/ZMXAvSbKaMJJpAfwp1C7KAj6K2k2CAz5jwUXyGf1+jUA -/2iAMiX1XcLy3n0L8ytzge8/UAFHafBl4rn4DmUugfhjzsPMBF3+CmgQDADZhdKT -M3ms3XpXnQke83FgaIBtP1g1qhqpCfg50WiPS0kjiMC0OJz2vh59nusbBLzgI//Y -1VMhKfIWYbqMcIY+lWbseHjl52rqW6AaJ0TH4NgVt7vhyVeJt0k/NnxvNhMd0587 -KXmfpDxrwBqc/l5cVB+p0rL8vs8kxojHXAi5V3koM0UjREWs5Jpj/XU9LhEoyXZk -eJC/pes1u6UKoFYn7dFIP49Kkd1kb+1bNfdPYtA0JpcGzYgeMNOvdWJwn43dNhxo -euXfmAEhA8LdzT0C0O+7akXOKWrfhXJ8MTBqvPgWZYx7MNuQx/ejIMZHl+Iaf7hG -976ILH+NCGiKkhidd9GIuA/WteHiQbXLyfiQ4n8P12q9+4dq6ybUM65tnozRyyN+ -1m3rU2a/+Ly3JCh4TeO27w+cxMWkaeHyTQaJVMbMbDpXduVd32MA33UVNH5/KXMV -czVi5asVjuKDSojJDV1QwX8izZNl1t+AI0L3balCabV0SFhlfnBEUj1my1sMAIfl -/H7JQB1nxW7/bNZMfHBYn9fqAZMupr0KZ8OrlQOpgUXO5bA3gcn6vI65qTUIbBIo -lQFIDvkcTFu/SdpaD6y7L6kQO8XRUAs9T1VSRJC0fJHXRg7YVY57cAS2ltgNHCl2 -vVnARtvcvogZDmL/gI0dsna7fJR5ewM0C+ulVIRwiMDTVE8I4qZ/nxINmnjIN0/E -aEzzDprXz591CvbZ/ZwnTGB8+VvMVs74VSwSAq+fpBMuFtpjDjOzut1AN6NYdXza -E/gr6tv0XCSdh1X26jibvsyAaVT7jK8mcYRhovePCMjdsf1qig06Xpdu9UDM3OiZ -iZpM7uanrEUC7jfK4bJ30r7UTiTsJBNE7FNn5F21CNX3mFKwSYyDv3adC8NIFbjH -B85Dul/eQLuv1+by72cGUQ3XYextDxi+7H+V3mrlFoiUPX2PN9VHr6EnNuPZmdTJ -CziSwB8gdPNN0u21HFL2VNFORXHa9tSehIHLpNgXWZ/qdE+lKbWuJnGeRHj4FAv+ -MQaafW0uHF+N8MDm8UWPvf4Vd0UJ0UpIjRWl2hTV+BHkNfvZlBRhhQIphNiKRe/W -ap0f/lW2Gm2uS0KgByjjNXEzTiwrte2GX65M6F6Lz8N31kt1Iig1xGOuv+6HmxTN -R8gL2K5PdJeJn8PTJWrRS7+BY8Hdkgb+wVpzE5cCvpFiG/P0yqfBdLWxVPlPI7dc -hDkmx4iAhHJX9J/gX/hC6L3AzPNJqNPAKy20wYp/ruTbbwBolW/4ikWij460JrvB -sm6Sp81A3ebaiN9XkJygLOyhGyhMieGulCYz6AahAFcECtPXGTcordV1mJth8yjF -4gZfDQyg0nMW4Yr49yeFXcRMUw1yzN3Q9v2zzqDuFi2lGYTXYmVqLYzM9KbLO2Wx -E/21xnBjLsl09l/FdA/bhdZq3t4/apbFOeQQ/j/AphvzWbsJnhG9Q7+d3VoDlz0g -FiSduCYIAAq8dUOJNjrUTkZsL1pOIjhYjCMi2uiKS6RQkT6nvuumPF/D/VTnUGeZ -wooEGBEIADwFAl3+CmkDCwkKCRCbp4ncdtaEmgQVCgkIAhYBAheAAhsMAh4BFiEE -cf/aAEQJ5d2ww+jxm6eJ3HbWhJoAAEEpAP91hFqmcb2ZqVcaRDMSVmhkEcFIRmpH -vDoQtVn8sArWqwEAi8HwbMhL+YwRItRZDknpC4vFjTHVMd1zMrz/JyeuT9k= -=pa/S ------END PGP PUBLIC KEY BLOCK----- -'''; - final literalMessage = OpenPGP.decryptMessage( message, - decryptionKeys: [OpenPGP.readPrivateKey(privateKey)], + decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); expect(utf8.decode(literalMessage.literalData.binary), literalText); final verifications = literalMessage.verify( - [OpenPGP.readPublicKey(publicKey)], + [OpenPGP.readPublicKey(carolPublicKey)], ); for (final verification in verifications) { expect(verification.isVerified, isTrue); From be0a0691cff91815ddfc207d571ac263a0bda163 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sun, 8 Dec 2024 10:52:21 +0700 Subject: [PATCH 171/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/message/literal_data_test.dart | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/message/literal_data_test.dart b/test/message/literal_data_test.dart index e6b7de89..9886229a 100644 --- a/test/message/literal_data_test.dart +++ b/test/message/literal_data_test.dart @@ -140,12 +140,12 @@ void main() { test('from Bob to Alice', () { final encrytpedMessage = OpenPGP.encryptBinaryData( literalData, - encryptionKeys: [OpenPGP.readPublicKey(bobPublicKey)], - signingKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], + encryptionKeys: [OpenPGP.readPublicKey(alicePublicKey)], + signingKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); final literalMessage = OpenPGP.decryptMessage( encrytpedMessage.armor(), - decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], + decryptionKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], ); expect(literalMessage.literalData.binary, equals(literalData)); @@ -153,26 +153,26 @@ void main() { [OpenPGP.readPublicKey(bobPublicKey)], ); expect(verifications.first.isVerified, isTrue); - expect(verifications.first.keyID.toHexadecimal(), 'f231550c4f47e38e'); + expect(verifications.first.keyID.toHexadecimal(), 'fbfcc82a015e7330'); }); test('from Alice to Bob', () { final encrytpedMessage = OpenPGP.encryptBinaryData( literalData, - encryptionKeys: [OpenPGP.readPublicKey(alicePublicKey)], - signingKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], + encryptionKeys: [OpenPGP.readPublicKey(bobPublicKey)], + signingKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], ); final literalMessage = OpenPGP.decryptMessage( encrytpedMessage.armor(), - decryptionKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], + decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); expect(literalMessage.literalData.binary, equals(literalData)); final verifications = literalMessage.verify( - [OpenPGP.readPublicKey(bobPublicKey)], + [OpenPGP.readPublicKey(alicePublicKey)], ); expect(verifications.first.isVerified, isTrue); - expect(verifications.first.keyID.toHexadecimal(), 'fbfcc82a015e7330'); + expect(verifications.first.keyID.toHexadecimal(), 'f231550c4f47e38e'); }); test('from Alice to rfc9580', () { From 6aa866f1ff6b94429a89f84143db85bb4d625ec0 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sun, 8 Dec 2024 17:33:31 +0700 Subject: [PATCH 172/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/message/literal_message.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/message/literal_message.dart b/lib/src/message/literal_message.dart index 8658aba4..cddf0566 100644 --- a/lib/src/message/literal_message.dart +++ b/lib/src/message/literal_message.dart @@ -33,7 +33,7 @@ import '../type/verification.dart'; /// Author Nguyen Van Nguyen final class LiteralMessage extends BaseMessage implements LiteralMessageInterface, SignedMessageInterface { LiteralMessage(super.packetList) { - if (packetList.whereType().isEmpty) { + if (_unwrapCompressed().whereType().isEmpty) { throw StateError('No literal data in packet list.'); } } @@ -62,11 +62,11 @@ final class LiteralMessage extends BaseMessage implements LiteralMessageInterfac } @override - get literalData => packetList.whereType().first; + get literalData => _unwrapCompressed().whereType().first; @override get signature => Signature( - unwrapCompressed().whereType(), + _unwrapCompressed().whereType(), ); @override @@ -77,7 +77,7 @@ final class LiteralMessage extends BaseMessage implements LiteralMessageInterfac PacketList( [ CompressedDataPacket.fromPacketList( - packetList, + _unwrapCompressed(), algorithm: algo, ) ], @@ -154,7 +154,7 @@ final class LiteralMessage extends BaseMessage implements LiteralMessageInterfac final DateTime? time, }) { final signaturePackets = [ - ...unwrapCompressed().whereType(), + ..._unwrapCompressed().whereType(), ...signDetached( signingKeys, recipients: recipients, @@ -226,7 +226,7 @@ final class LiteralMessage extends BaseMessage implements LiteralMessageInterfac ); } - PacketListInterface unwrapCompressed() { + PacketListInterface _unwrapCompressed() { return packetList.whereType().firstOrNull?.packets ?? packetList; } } From 9ed3f5bfacd3b67ac32617ad29a619491ab545fe Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Sun, 8 Dec 2024 17:49:26 +0700 Subject: [PATCH 173/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/message/literal_message.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/src/message/literal_message.dart b/lib/src/message/literal_message.dart index cddf0566..b134ffdb 100644 --- a/lib/src/message/literal_message.dart +++ b/lib/src/message/literal_message.dart @@ -11,6 +11,7 @@ import '../common/config.dart'; import '../common/helpers.dart'; import '../enum/armor_type.dart'; import '../enum/compression_algorithm.dart'; +import '../enum/preset_rfc.dart'; import '../enum/symmetric_algorithm.dart'; import '../message/base_message.dart'; import '../message/encrypted_message.dart'; @@ -96,14 +97,14 @@ final class LiteralMessage extends BaseMessage implements LiteralMessageInterfac if (encryptionKeys.isEmpty && passwords.isEmpty) { throw ArgumentError('No encryption keys or passwords provided.'); } - var addPadding = false; + var addPadding = Config.presetRfc == PresetRfc.rfc9580; var aeadSupported = Config.aeadSupported; for (final key in encryptionKeys) { if (!key.aeadSupported) { aeadSupported = false; } - if (key.keyPacket.isV6Key) { - addPadding = true; + if (!key.keyPacket.isV6Key) { + addPadding = false; } } final sessionKey = SessionKey.produceKey( From be90166f6a9131141d899e966548d1626e6901f6 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 08:26:20 +0700 Subject: [PATCH 174/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/message/literal_data_test.dart | 81 ++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/test/message/literal_data_test.dart b/test/message/literal_data_test.dart index 9886229a..9d899913 100644 --- a/test/message/literal_data_test.dart +++ b/test/message/literal_data_test.dart @@ -1,6 +1,8 @@ import 'dart:convert'; +import 'package:dart_pg/src/common/config.dart'; import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/preset_rfc.dart'; import 'package:dart_pg/src/openpgp.dart'; import 'package:dart_pg/src/packet/base.dart'; import 'package:test/test.dart'; @@ -9,6 +11,7 @@ import '../data/key_data.dart'; void main() { const literalText = 'Hello World :)'; + final literalData = Helper.randomBytes(1024); group('Sign', () { test('with key Bob', () { @@ -135,8 +138,84 @@ void main() { }); }); + group('Encrypt', () { + final password = Helper.generatePassword(); + + test('with password only', () { + final encrytpedMessage = OpenPGP.encryptBinaryData( + literalData, + passwords: [password], + ); + final literalMessage = OpenPGP.decryptMessage( + encrytpedMessage.armor(), + passwords: [password], + ); + expect(literalMessage.literalData.binary, equals(literalData)); + }); + + test('with aead protect', () { + Config.presetRfc = PresetRfc.rfc9580; + Config.aeadProtect = true; + + final encrytpedMessage = OpenPGP.encryptBinaryData( + literalData, + passwords: [password], + ); + + final encryptedPacket = encrytpedMessage.encryptedPacket as SymEncryptedIntegrityProtectedDataPacket; + expect(encryptedPacket.aead, Config.preferredAead); + + final literalMessage = OpenPGP.decryptMessage( + encrytpedMessage.armor(), + passwords: [password], + ); + expect(literalMessage.literalData.binary, equals(literalData)); + + Config.aeadProtect = false; + Config.presetRfc = PresetRfc.rfc4880; + }); + + test('to Alice', () { + final encrytpedMessage = OpenPGP.encryptBinaryData( + literalData, + encryptionKeys: [OpenPGP.readPublicKey(alicePublicKey)], + passwords: [password], + ); + final literalMessage = OpenPGP.decryptMessage( + encrytpedMessage.armor(), + decryptionKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], + ); + expect(literalMessage.literalData.binary, equals(literalData)); + }); + + test('to Bob', () { + final encrytpedMessage = OpenPGP.encryptBinaryData( + literalData, + encryptionKeys: [OpenPGP.readPublicKey(bobPublicKey)], + passwords: [password], + ); + final literalMessage = OpenPGP.decryptMessage( + encrytpedMessage.armor(), + decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], + ); + expect(literalMessage.literalData.binary, equals(literalData)); + }); + + test('to rfc9580', () { + final encrytpedMessage = OpenPGP.encryptBinaryData( + literalData, + encryptionKeys: [OpenPGP.readPublicKey(rfc9580PublicKey)], + passwords: [password], + ); + final literalMessage = OpenPGP.decryptMessage( + encrytpedMessage.armor(), + decryptionKeys: [OpenPGP.readPrivateKey(rfc9580PrivateKey)], + ); + expect(literalMessage.literalData.binary, equals(literalData)); + }); + }); + group('Sign & encrypt', () { - final literalData = Helper.randomBytes(1024); test('from Bob to Alice', () { final encrytpedMessage = OpenPGP.encryptBinaryData( literalData, From 4b7aabc4203181a2fc173e417295499a4a895e73 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 08:35:25 +0700 Subject: [PATCH 175/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/armor.dart | 8 ++++++++ lib/src/key/private_key.dart | 5 +---- lib/src/key/public_key.dart | 5 +---- lib/src/message/encrypted_message.dart | 5 +---- lib/src/message/literal_message.dart | 5 +---- lib/src/message/signature.dart | 5 +---- lib/src/message/signed_message.dart | 5 +---- 7 files changed, 14 insertions(+), 24 deletions(-) diff --git a/lib/src/common/armor.dart b/lib/src/common/armor.dart index c2276497..24cc346d 100644 --- a/lib/src/common/armor.dart +++ b/lib/src/common/armor.dart @@ -117,6 +117,14 @@ class Armor { ); } + /// Assert armor type + Armor assertType(ArmorType type) { + if (type != this.type) { + throw ArgumentError('Armored text not of ${type.name} type.'); + } + return this; + } + /// Armor an OpenPGP binary packet block static String encode( final ArmorType type, diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index da48233c..c1622221 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -27,10 +27,7 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { /// Reads an armored OpenPGP private key and returns a PrivateKey object factory PrivateKey.fromArmored(final String armored) { - final armor = Armor.decode(armored); - if (armor.type != ArmorType.privateKey) { - throw ArgumentError('Armored text not of private key type'); - } + final armor = Armor.decode(armored).assertType(ArmorType.privateKey); return PrivateKey(PacketList.decode(armor.data)); } diff --git a/lib/src/key/public_key.dart b/lib/src/key/public_key.dart index 9ba3bcee..812fd554 100644 --- a/lib/src/key/public_key.dart +++ b/lib/src/key/public_key.dart @@ -20,10 +20,7 @@ final class PublicKey extends BaseKey { /// Read public key from armored string factory PublicKey.fromArmored(final String armored) { - final armor = Armor.decode(armored); - if (armor.type != ArmorType.publicKey) { - throw ArgumentError('Armored text not of public key type'); - } + final armor = Armor.decode(armored).assertType(ArmorType.publicKey); return PublicKey(PacketList.decode(armor.data)); } diff --git a/lib/src/message/encrypted_message.dart b/lib/src/message/encrypted_message.dart index babd42b7..12d34870 100644 --- a/lib/src/message/encrypted_message.dart +++ b/lib/src/message/encrypted_message.dart @@ -29,10 +29,7 @@ final class EncryptedMessage extends BaseMessage implements EncryptedMessageInte /// Read Literal message from armored string factory EncryptedMessage.fromArmored(final String armored) { - final armor = Armor.decode(armored); - if (armor.type != ArmorType.message) { - throw ArgumentError('Armored text not of message type'); - } + final armor = Armor.decode(armored).assertType(ArmorType.message); return EncryptedMessage(PacketList.decode(armor.data)); } diff --git a/lib/src/message/literal_message.dart b/lib/src/message/literal_message.dart index b134ffdb..8c64c56b 100644 --- a/lib/src/message/literal_message.dart +++ b/lib/src/message/literal_message.dart @@ -41,10 +41,7 @@ final class LiteralMessage extends BaseMessage implements LiteralMessageInterfac /// Read Literal message from armored string factory LiteralMessage.fromArmored(final String armored) { - final armor = Armor.decode(armored); - if (armor.type != ArmorType.message) { - throw ArgumentError('Armored text not of message type'); - } + final armor = Armor.decode(armored).assertType(ArmorType.message); return LiteralMessage(PacketList.decode(armor.data)); } diff --git a/lib/src/message/signature.dart b/lib/src/message/signature.dart index 0276eaa1..bfbe92ca 100644 --- a/lib/src/message/signature.dart +++ b/lib/src/message/signature.dart @@ -28,10 +28,7 @@ final class Signature implements SignatureInterface { /// Read signature from armored string factory Signature.fromArmored(final String armored) { - final armor = Armor.decode(armored); - if (armor.type != ArmorType.signature) { - throw ArgumentError('Armored text not of signature type'); - } + final armor = Armor.decode(armored).assertType(ArmorType.signature); return Signature( PacketList.decode(armor.data).whereType(), ); diff --git a/lib/src/message/signed_message.dart b/lib/src/message/signed_message.dart index ea7da877..7b314935 100644 --- a/lib/src/message/signed_message.dart +++ b/lib/src/message/signed_message.dart @@ -26,10 +26,7 @@ final class SignedMessage extends CleartextMessage implements SignedCleartextMes /// Read signed message from armored string factory SignedMessage.fromArmored(final String armored) { - final armor = Armor.decode(armored); - if (armor.type != ArmorType.signedMessage) { - throw ArgumentError('Armored text not of signed message type'); - } + final armor = Armor.decode(armored).assertType(ArmorType.signedMessage); return SignedMessage( armor.text, Signature( From 5eaceeb7ca6cdbb5288ab0f7020a79be5826414e Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 08:43:43 +0700 Subject: [PATCH 176/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/message/literal_data_test.dart | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/message/literal_data_test.dart b/test/message/literal_data_test.dart index 9d899913..1ca4d96d 100644 --- a/test/message/literal_data_test.dart +++ b/test/message/literal_data_test.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'package:dart_pg/src/common/config.dart'; import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/compression_algorithm.dart'; import 'package:dart_pg/src/enum/preset_rfc.dart'; import 'package:dart_pg/src/openpgp.dart'; import 'package:dart_pg/src/packet/base.dart'; @@ -153,6 +154,23 @@ void main() { expect(literalMessage.literalData.binary, equals(literalData)); }); + test('with compression', () { + final encrytpedMessage = OpenPGP.encryptBinaryData( + literalData, + passwords: [password], + compression: CompressionAlgorithm.zlib, + ); + final literalMessage = OpenPGP.decryptMessage( + encrytpedMessage.armor(), + passwords: [password], + ); + expect( + literalMessage.packetList.whereType().isNotEmpty, + isTrue, + ); + expect(literalMessage.literalData.binary, equals(literalData)); + }); + test('with aead protect', () { Config.presetRfc = PresetRfc.rfc9580; Config.aeadProtect = true; From a53b48ab3c1fde026221f8383d90e0bbb3836f42 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 08:45:49 +0700 Subject: [PATCH 177/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/data/key_data.dart | 2 +- test/key/key_decryption_test.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/data/key_data.dart b/test/data/key_data.dart index bb9230e9..63ebc71d 100644 --- a/test/data/key_data.dart +++ b/test/data/key_data.dart @@ -467,7 +467,7 @@ CgkQunJ9D2tQwoHyvwEA6NmUz1wxurA5xPduxnsqHRbMoy5IZy2WCLV7bi8T8BsA -----END PGP PUBLIC KEY BLOCK----- '''; -const lockDsaElGamalPrivateKey = ''' +const lockedDsaElGamalPrivateKey = ''' -----BEGIN PGP PRIVATE KEY BLOCK----- lQOBBGc/4H8RCAD3gDG7FSfKWRy4/gNe0sgYYgZ4zAFZTLWneObcLYpY+kmwufUI diff --git a/test/key/key_decryption_test.dart b/test/key/key_decryption_test.dart index e1e50bab..25d50640 100644 --- a/test/key/key_decryption_test.dart +++ b/test/key/key_decryption_test.dart @@ -48,7 +48,7 @@ void main() { test('DSA & ElGamal key', () { final privateKey = OpenPGP.decryptPrivateKey( - lockDsaElGamalPrivateKey, + lockedDsaElGamalPrivateKey, passphrase, ); final user = privateKey.users[0]; From 21b2fcc6a5be6a02f843f44c7bab71c2d6b2f5c8 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 08:52:15 +0700 Subject: [PATCH 178/202] WIP Signed-off-by: Nguyen Van Nguyen --- README.md | 71 +++++++++++++------------------ lib/src/key/private_key.dart | 4 +- lib/src/openpgp.dart | 2 +- test/key/key_generation_test.dart | 2 +- 4 files changed, 33 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index 978c4349..6877f6c4 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,14 @@ dependencies: ### Encrypt and decrypt data with a password ```dart -const text = 'Hello Dart PG!'; +const text = 'Hello Dart Privacy Guard!'; const password = 'secret stuff'; -final encryptedMessage = await OpenPGP.encrypt( +final encryptedMessage = OpenPGP.encrypt( OpenPGP.createTextMessage(text), passwords: [password] ); final encrypted = encryptedMessage.armor(); -final decryptedMessage = await OpenPGP.decrypt( +final decryptedMessage = OpenPGP.decrypt( OpenPGP.readMessage(encrypted), passwords: [password] ); final decrypted = decryptedMessage.armor(); @@ -58,20 +58,20 @@ final decrypted = decryptedMessage.armor(); Encryption will use the algorithm preferred by the public (encryption) key (defaults to aes256 for keys generated), and decryption will use the algorithm used for encryption. ```dart -const text = 'Hello Dart PG!'; +const text = 'Hello Dart Privacy Guard!'; const passphrase = 'secret stuff'; const armoredPublicKey = '-----BEGIN PGP PUBLIC KEY BLOCK-----'; const armoredPrivateKey = '-----BEGIN PGP PRIVATE KEY BLOCK-----'; -final publicKey = await OpenPGP.readPublicKey(armoredPublicKey); -final privateKey = await OpenPGP.decryptPrivateKey(armoredPrivateKey, passphrase); +final publicKey = OpenPGP.readPublicKey(armoredPublicKey); +final privateKey = OpenPGP.decryptPrivateKey(armoredPrivateKey, passphrase); -final encryptedMessage = await OpenPGP.encrypt( +final encryptedMessage = OpenPGP.encrypt( OpenPGP.createTextMessage(text), encryptionKeys: [publicKey] ); final encrypted = encryptedMessage.armor(); -final decryptedMessage = await OpenPGP.decrypt( +final decryptedMessage = OpenPGP.decrypt( OpenPGP.readMessage(encrypted), decryptionKeys: [privateKey] ); final decrypted = decryptedMessage.armor(); @@ -79,17 +79,17 @@ final decrypted = decryptedMessage.armor(); Sign message & encrypt with multiple public keys: ```dart -final text = 'Hello Dart PG!'; +final text = 'Hello Dart Privacy Guard!'; const passphrase = 'secret stuff'; const armoredPublicKeys = ['-----BEGIN PGP PUBLIC KEY BLOCK-----']; const armoredPrivateKey = '-----BEGIN PGP PRIVATE KEY BLOCK-----'; -final publicKeys = await Future.wait( +final publicKeys = Future.wait( armoredPublicKeys.map((armored) => OpenPGP.readPublicKey(armored)) ); -final privateKey = await OpenPGP.decryptPrivateKey(armoredPrivateKey, passphrase); +final privateKey = OpenPGP.decryptPrivateKey(armoredPrivateKey, passphrase); -final encryptedMessage = await OpenPGP.encrypt( +final encryptedMessage = OpenPGP.encrypt( OpenPGP.createTextMessage(text), encryptionKeys: publicKeys, signingKeys: [privateKey], @@ -99,35 +99,35 @@ final encrypted = encryptedMessage.armor(); ### Sign and verify cleartext ```dart -const text = 'Hello Dart PG!'; +const text = 'Hello Dart Privacy Guard!'; const passphrase = 'secret stuff'; const armoredPublicKey = '-----BEGIN PGP PUBLIC KEY BLOCK-----'; const armoredPrivateKey = '-----BEGIN PGP PRIVATE KEY BLOCK-----'; -final publicKey = await OpenPGP.readPublicKey(armoredPublicKey); -final privateKey = await OpenPGP.decryptPrivateKey(armoredPrivateKey, passphrase); +final publicKey = OpenPGP.readPublicKey(armoredPublicKey); +final privateKey = OpenPGP.decryptPrivateKey(armoredPrivateKey, passphrase); -final signedMessage = await OpenPGP.sign(text, signingKeys: [privateKey]); +final signedMessage = OpenPGP.sign(text, signingKeys: [privateKey]); final signed = signedMessage.armor(); -final verifiedMessage = await OpenPGP.verify(signed, verificationKeys: [publicKey]); +final verifiedMessage = OpenPGP.verify(signed, verificationKeys: [publicKey]); final verifications = verifiedMessage.verifications; ``` ### Detached sign and verify cleartext ```dart -const text = 'Hello Dart PG!'; +const text = 'Hello Dart Privacy Guard!'; const passphrase = 'secret stuff'; const armoredPublicKey = '-----BEGIN PGP PUBLIC KEY BLOCK-----'; const armoredPrivateKey = '-----BEGIN PGP PRIVATE KEY BLOCK-----'; -final publicKey = await OpenPGP.readPublicKey(armoredPublicKey); -final privateKey = await OpenPGP.decryptPrivateKey(armoredPrivateKey, passphrase); +final publicKey = OpenPGP.readPublicKey(armoredPublicKey); +final privateKey = OpenPGP.decryptPrivateKey(armoredPrivateKey, passphrase); -final signature = await OpenPGP.signDetached(text, signingKeys: [privateKey]); +final signature = OpenPGP.signDetached(text, signingKeys: [privateKey]); final armored = signature.armor(); -final cleartextMessage = await OpenPGP.verifyDetached( +final cleartextMessage = OpenPGP.verifyDetached( text, armored, verificationKeys: [publicKey] ); final verifications = cleartextMessage.verifications; @@ -138,24 +138,11 @@ rsa type: ```dart const passphrase = 'secret stuff'; final userID = [name, '($comment)', '<$email>'].join(' '); -final privateKey = await OpenPGP.generateKey( +final privateKey = OpenPGP.generateKey( [userID], passphrase, - type: KeyGenerationType.rsa, - rsaKeySize: RSAKeySize.s4096, -); -final publicKey = privateKey.toPublic; -``` - -dsa type (uses DSA algorithm for signing & ElGamal algorithm for encryption): -```dart -const passphrase = 'secret stuff'; -final userID = [name, '($comment)', '<$email>'].join(' '); -final privateKey = await OpenPGP.generateKey( - [userID], - passphrase, - type: KeyGenerationType.dsa, - dhKeySize: DHKeySize.l2048n224, + type: KeyType.rsa, + rsaKeySize: RSAKeySize.normal, ); final publicKey = privateKey.toPublic; ``` @@ -165,10 +152,10 @@ secp256k1, secp384r1, secp521r1, brainpoolp256r1, brainpoolp384r1, brainpoolp512 ```dart const passphrase = 'secret stuff'; final userID = [name, '($comment)', '<$email>'].join(' '); -final privateKey = await OpenPGP.generateKey( +final privateKey = OpenPGP.generateKey( [userID], passphrase, - type: KeyGenerationType.ecdsa, + type: KeyType.ecc, curve: CurveInfo.secp521r1, ); final publicKey = privateKey.toPublic; @@ -178,10 +165,10 @@ eddsa type (uses EdDSA algorithm with ed25519 for signing & ECDH algorithm with ```dart const passphrase = 'secret stuff'; final userID = [name, '($comment)', '<$email>'].join(' '); -final privateKey = await OpenPGP.generateKey( +final privateKey = OpenPGP.generateKey( [userID], passphrase, - type: KeyGenerationType.eddsa, + type: KeyType.ecc, ); final publicKey = privateKey.toPublic; ``` diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index c1622221..f3c04d87 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -13,7 +13,6 @@ import '../enum/ecc.dart'; import '../enum/key_algorithm.dart'; import '../enum/key_type.dart'; import '../enum/rsa_key_size.dart'; -import 'base_key.dart'; import '../key/public_key.dart'; import '../key/subkey.dart'; import '../packet/base.dart'; @@ -21,6 +20,7 @@ import '../packet/packet_list.dart'; import '../type/packet.dart'; import '../type/private_key.dart'; import '../type/secret_key_packet.dart'; +import 'base_key.dart'; final class PrivateKey extends BaseKey implements PrivateKeyInterface { PrivateKey(super.packetList); @@ -38,7 +38,7 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { final Iterable userIDs, final String passphrase, { final KeyType type = KeyType.rsa, - final RSAKeySize rsaKeySize = RSAKeySize.high, + final RSAKeySize rsaKeySize = RSAKeySize.normal, final Ecc curve = Ecc.secp521r1, final int keyExpiry = 0, final bool signOnly = false, diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index 37096d86..04d0abd6 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -40,7 +40,7 @@ final class OpenPGP { final Iterable userIDs, final String passphrase, { final KeyType type = KeyType.rsa, - final RSAKeySize rsaKeySize = RSAKeySize.high, + final RSAKeySize rsaKeySize = RSAKeySize.normal, final Ecc curve = Ecc.secp521r1, final int keyExpiry = 0, final DateTime? time, diff --git a/test/key/key_generation_test.dart b/test/key/key_generation_test.dart index a019855a..61d2e0cd 100644 --- a/test/key/key_generation_test.dart +++ b/test/key/key_generation_test.dart @@ -20,7 +20,7 @@ void main() { ); expect(privateKey.version, KeyVersion.v4.value); expect(privateKey.keyAlgorithm, KeyAlgorithm.rsaEncryptSign); - expect(privateKey.keyStrength, RSAKeySize.high.bits); + expect(privateKey.keyStrength, RSAKeySize.normal.bits); expect(privateKey.users[0].userID, userID); final priKey = OpenPGP.readPrivateKey( From d7b246b257266a17c4f138d9efadcf91b8ef3c02 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 09:15:30 +0700 Subject: [PATCH 179/202] WIP Signed-off-by: Nguyen Van Nguyen --- test/message/literal_data_test.dart | 42 ++++++++++++++--------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/test/message/literal_data_test.dart b/test/message/literal_data_test.dart index 1ca4d96d..ee288ec1 100644 --- a/test/message/literal_data_test.dart +++ b/test/message/literal_data_test.dart @@ -143,25 +143,25 @@ void main() { final password = Helper.generatePassword(); test('with password only', () { - final encrytpedMessage = OpenPGP.encryptBinaryData( + final encryptedMessage = OpenPGP.encryptBinaryData( literalData, passwords: [password], ); final literalMessage = OpenPGP.decryptMessage( - encrytpedMessage.armor(), + encryptedMessage.armor(), passwords: [password], ); expect(literalMessage.literalData.binary, equals(literalData)); }); test('with compression', () { - final encrytpedMessage = OpenPGP.encryptBinaryData( + final encryptedMessage = OpenPGP.encryptBinaryData( literalData, passwords: [password], compression: CompressionAlgorithm.zlib, ); final literalMessage = OpenPGP.decryptMessage( - encrytpedMessage.armor(), + encryptedMessage.armor(), passwords: [password], ); expect( @@ -175,16 +175,16 @@ void main() { Config.presetRfc = PresetRfc.rfc9580; Config.aeadProtect = true; - final encrytpedMessage = OpenPGP.encryptBinaryData( + final encryptedMessage = OpenPGP.encryptBinaryData( literalData, passwords: [password], ); - final encryptedPacket = encrytpedMessage.encryptedPacket as SymEncryptedIntegrityProtectedDataPacket; + final encryptedPacket = encryptedMessage.encryptedPacket as SymEncryptedIntegrityProtectedDataPacket; expect(encryptedPacket.aead, Config.preferredAead); final literalMessage = OpenPGP.decryptMessage( - encrytpedMessage.armor(), + encryptedMessage.armor(), passwords: [password], ); expect(literalMessage.literalData.binary, equals(literalData)); @@ -194,39 +194,39 @@ void main() { }); test('to Alice', () { - final encrytpedMessage = OpenPGP.encryptBinaryData( + final encryptedMessage = OpenPGP.encryptBinaryData( literalData, encryptionKeys: [OpenPGP.readPublicKey(alicePublicKey)], passwords: [password], ); final literalMessage = OpenPGP.decryptMessage( - encrytpedMessage.armor(), + encryptedMessage.armor(), decryptionKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], ); expect(literalMessage.literalData.binary, equals(literalData)); }); test('to Bob', () { - final encrytpedMessage = OpenPGP.encryptBinaryData( + final encryptedMessage = OpenPGP.encryptBinaryData( literalData, encryptionKeys: [OpenPGP.readPublicKey(bobPublicKey)], passwords: [password], ); final literalMessage = OpenPGP.decryptMessage( - encrytpedMessage.armor(), + encryptedMessage.armor(), decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); expect(literalMessage.literalData.binary, equals(literalData)); }); test('to rfc9580', () { - final encrytpedMessage = OpenPGP.encryptBinaryData( + final encryptedMessage = OpenPGP.encryptBinaryData( literalData, encryptionKeys: [OpenPGP.readPublicKey(rfc9580PublicKey)], passwords: [password], ); final literalMessage = OpenPGP.decryptMessage( - encrytpedMessage.armor(), + encryptedMessage.armor(), decryptionKeys: [OpenPGP.readPrivateKey(rfc9580PrivateKey)], ); expect(literalMessage.literalData.binary, equals(literalData)); @@ -235,13 +235,13 @@ void main() { group('Sign & encrypt', () { test('from Bob to Alice', () { - final encrytpedMessage = OpenPGP.encryptBinaryData( + final encryptedMessage = OpenPGP.encryptBinaryData( literalData, encryptionKeys: [OpenPGP.readPublicKey(alicePublicKey)], signingKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); final literalMessage = OpenPGP.decryptMessage( - encrytpedMessage.armor(), + encryptedMessage.armor(), decryptionKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], ); expect(literalMessage.literalData.binary, equals(literalData)); @@ -254,13 +254,13 @@ void main() { }); test('from Alice to Bob', () { - final encrytpedMessage = OpenPGP.encryptBinaryData( + final encryptedMessage = OpenPGP.encryptBinaryData( literalData, encryptionKeys: [OpenPGP.readPublicKey(bobPublicKey)], signingKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], ); final literalMessage = OpenPGP.decryptMessage( - encrytpedMessage.armor(), + encryptedMessage.armor(), decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); expect(literalMessage.literalData.binary, equals(literalData)); @@ -273,13 +273,13 @@ void main() { }); test('from Alice to rfc9580', () { - final encrytpedMessage = OpenPGP.encryptBinaryData( + final encryptedMessage = OpenPGP.encryptBinaryData( literalData, encryptionKeys: [OpenPGP.readPublicKey(rfc9580PublicKey)], signingKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], ); final literalMessage = OpenPGP.decryptMessage( - encrytpedMessage.armor(), + encryptedMessage.armor(), decryptionKeys: [OpenPGP.readPrivateKey(rfc9580PrivateKey)], ); expect(literalMessage.literalData.binary, equals(literalData)); @@ -292,13 +292,13 @@ void main() { }); test('from rfc9580 to Alice', () { - final encrytpedMessage = OpenPGP.encryptBinaryData( + final encryptedMessage = OpenPGP.encryptBinaryData( literalData, encryptionKeys: [OpenPGP.readPublicKey(alicePublicKey)], signingKeys: [OpenPGP.readPrivateKey(rfc9580PrivateKey)], ); final literalMessage = OpenPGP.decryptMessage( - encrytpedMessage.armor(), + encryptedMessage.armor(), decryptionKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], ); expect(literalMessage.literalData.binary, equals(literalData)); From 830fe69461ac25ff8bb0213c0ca1638a7f2ed610 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 09:43:01 +0700 Subject: [PATCH 180/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/openpgp.dart | 16 ++++++++-------- test/message/literal_data_test.dart | 28 ++++++++++++++-------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index 04d0abd6..90e2a3b3 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -331,29 +331,29 @@ final class OpenPGP { ); } - /// Decrypt a message with the user's private keys, or passwords. + /// Decrypt a armored encrypted message with + /// the user's private keys, or passwords. /// One of `decryptionKeys` or `passwords` must be specified. static LiteralMessageInterface decrypt( - final EncryptedMessageInterface message, { + final String armored, { final Iterable decryptionKeys = const [], final Iterable passwords = const [], }) { - return message.decrypt( + return decryptMessage( + readEncryptedMessage(armored), decryptionKeys: decryptionKeys, passwords: passwords, ); } - /// Decrypt a armored encrypted string with - /// the user's private keys, or passwords. + /// Decrypt an encrypted message with the user's private keys, or passwords. /// One of `decryptionKeys` or `passwords` must be specified. static LiteralMessageInterface decryptMessage( - final String message, { + final EncryptedMessageInterface message, { final Iterable decryptionKeys = const [], final Iterable passwords = const [], }) { - return decrypt( - readEncryptedMessage(message), + return message.decrypt( decryptionKeys: decryptionKeys, passwords: passwords, ); diff --git a/test/message/literal_data_test.dart b/test/message/literal_data_test.dart index ee288ec1..520ef959 100644 --- a/test/message/literal_data_test.dart +++ b/test/message/literal_data_test.dart @@ -147,7 +147,7 @@ void main() { literalData, passwords: [password], ); - final literalMessage = OpenPGP.decryptMessage( + final literalMessage = OpenPGP.decrypt( encryptedMessage.armor(), passwords: [password], ); @@ -160,7 +160,7 @@ void main() { passwords: [password], compression: CompressionAlgorithm.zlib, ); - final literalMessage = OpenPGP.decryptMessage( + final literalMessage = OpenPGP.decrypt( encryptedMessage.armor(), passwords: [password], ); @@ -183,7 +183,7 @@ void main() { final encryptedPacket = encryptedMessage.encryptedPacket as SymEncryptedIntegrityProtectedDataPacket; expect(encryptedPacket.aead, Config.preferredAead); - final literalMessage = OpenPGP.decryptMessage( + final literalMessage = OpenPGP.decrypt( encryptedMessage.armor(), passwords: [password], ); @@ -199,7 +199,7 @@ void main() { encryptionKeys: [OpenPGP.readPublicKey(alicePublicKey)], passwords: [password], ); - final literalMessage = OpenPGP.decryptMessage( + final literalMessage = OpenPGP.decrypt( encryptedMessage.armor(), decryptionKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], ); @@ -212,7 +212,7 @@ void main() { encryptionKeys: [OpenPGP.readPublicKey(bobPublicKey)], passwords: [password], ); - final literalMessage = OpenPGP.decryptMessage( + final literalMessage = OpenPGP.decrypt( encryptedMessage.armor(), decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); @@ -225,7 +225,7 @@ void main() { encryptionKeys: [OpenPGP.readPublicKey(rfc9580PublicKey)], passwords: [password], ); - final literalMessage = OpenPGP.decryptMessage( + final literalMessage = OpenPGP.decrypt( encryptedMessage.armor(), decryptionKeys: [OpenPGP.readPrivateKey(rfc9580PrivateKey)], ); @@ -240,7 +240,7 @@ void main() { encryptionKeys: [OpenPGP.readPublicKey(alicePublicKey)], signingKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); - final literalMessage = OpenPGP.decryptMessage( + final literalMessage = OpenPGP.decrypt( encryptedMessage.armor(), decryptionKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], ); @@ -259,7 +259,7 @@ void main() { encryptionKeys: [OpenPGP.readPublicKey(bobPublicKey)], signingKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], ); - final literalMessage = OpenPGP.decryptMessage( + final literalMessage = OpenPGP.decrypt( encryptedMessage.armor(), decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); @@ -278,7 +278,7 @@ void main() { encryptionKeys: [OpenPGP.readPublicKey(rfc9580PublicKey)], signingKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], ); - final literalMessage = OpenPGP.decryptMessage( + final literalMessage = OpenPGP.decrypt( encryptedMessage.armor(), decryptionKeys: [OpenPGP.readPrivateKey(rfc9580PrivateKey)], ); @@ -297,7 +297,7 @@ void main() { encryptionKeys: [OpenPGP.readPublicKey(alicePublicKey)], signingKeys: [OpenPGP.readPrivateKey(rfc9580PrivateKey)], ); - final literalMessage = OpenPGP.decryptMessage( + final literalMessage = OpenPGP.decrypt( encryptedMessage.armor(), decryptionKeys: [OpenPGP.readPrivateKey(alicePrivateKey)], ); @@ -341,7 +341,7 @@ lFiOpCG/SDaD4bl7BYYf9o6c6Gs9vKf9mShmWUO0tg2NDlFAzdQTBOzaxw== =BADY -----END PGP MESSAGE----- '''; - final literalMessage = OpenPGP.decryptMessage( + final literalMessage = OpenPGP.decrypt( message, decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); @@ -377,7 +377,7 @@ wqP2oIPIsEeuMG40kub7RpJWnidJMnOoG7ZScRqbwkECF89DN4sYYREJkaaHFr8= =JDtL -----END PGP MESSAGE----- '''; - final literalMessage = OpenPGP.decryptMessage( + final literalMessage = OpenPGP.decrypt( message, decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); @@ -414,7 +414,7 @@ xBfiBCSwAsSjvat9sW5YOIcqo6hUMnY804L61RklAJn93ohVOt0N =Bbti -----END PGP MESSAGE----- '''; - final literalMessage = OpenPGP.decryptMessage( + final literalMessage = OpenPGP.decrypt( message, decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); @@ -464,7 +464,7 @@ vMq9yq4ffP8QAc1kwjaK9aJ/J45fPw== -----END PGP MESSAGE----- '''; - final literalMessage = OpenPGP.decryptMessage( + final literalMessage = OpenPGP.decrypt( message, decryptionKeys: [OpenPGP.readPrivateKey(bobPrivateKey)], ); From 15bc15dcb63d5f9eb02a92d543f344c3fb7fb6f7 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 10:23:49 +0700 Subject: [PATCH 181/202] WIP Signed-off-by: Nguyen Van Nguyen --- example/cleartext_signing.dart | 225 ++++++++++++++++++++++++++++ lib/src/common/armor.dart | 5 +- lib/src/message/signed_message.dart | 8 +- test/common/armor_test.dart | 4 +- 4 files changed, 234 insertions(+), 8 deletions(-) create mode 100644 example/cleartext_signing.dart diff --git a/example/cleartext_signing.dart b/example/cleartext_signing.dart new file mode 100644 index 00000000..514b547f --- /dev/null +++ b/example/cleartext_signing.dart @@ -0,0 +1,225 @@ +import 'package:dart_pg/dart_pg.dart'; +import 'package:dart_pg/src/common/helpers.dart'; + +void main() { + const passphase = '8pM1b;)|5;lD7/SM51o>p1Vp%F}u=AO7'; + + const clearText = ''' +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc felis neque, interdum id iaculis ut, faucibus a ex. +Nam quam tortor, pharetra at dignissim ut, semper nec arcu. Vivamus mollis tortor vitae urna fringilla lacinia id +vel nunc. Ut laoreet pellentesque mattis. Curabitur viverra enim venenatis, mattis velit sed, fringilla lacus. Donec +nulla dui, vestibulum aliquam ultrices hendrerit, euismod iaculis magna. Praesent vitae ipsum id risus feugiat +auctor ac eget tellus. + +What we need from the grocery store: +- tofu +- vegetables +- noodles +'''; + + const rsaKeyData = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +JufkRd7zuLxBKQIPpDhrZEeGvClv +=B8qK +-----END PGP PRIVATE KEY BLOCK----- +'''; + + const eccKeyData = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: PHP Privacy v2 +Comment: https://github.com/web-of-trust/php-privacy + +xcBIBGbzgU4TBSuBBAAjBCMEAGIhXtcV0Xo0x84qunT4ndhgrlKZcXgXYB9dhoPpci/XSsAU2Y6Y +BXOtXZvtj82Ht1GNHa5QgYO85y556aue6rK3AWUkCOZoKvjQ1kNJZwqW7iaXaj1N/cfFn1WQf4+4 +1/VGhGk9DZYLgjP4+qoueOvh4amSA0BxdeXUn48lpkz3pDaD/gcDCKVeda8lbRug4CIojtl/xX/F +quYyDLpETGfH0R6ChYEGXbhO7D8JFmliEtnHiWQXm3YS6cxLRqU+SeZ5OUeUCM6bcUbuKtNfR05W +/CAWG+HcQlN9H4bLni66wpPJq2v0rB3BoLo0B8FLGlyrJjMTV5dbzSpOZ3V5ZW4gVmFuIE5ndXll +biA8bmd1eWVubnYxOTgxQGdtYWlsLmNvbT7CwDgEEBMKAGcFAmbzgU4WIQSD6DIwfTk50CHc8qK6 +ngqnSMkpzwkQup4Kp0jJKc8CGQE8FAAAAAAAEwAgc2FsdEB3ZWJvZnRydXN0Lm9yZzA4+yJiUZc7 +29+BKN8fvsW3GxUlLYNJs0BIz5GjylVLAACwkgII00MsI2571U+ehyJVGJa45YLjsm9IYHJ/FQ/z +LzX76N+eYwmCAnEv282H+THUROP9MOKOYim+MmwbhXcoHnpFPbcCCQFtN55twaj2lIXMhHrUEgKb +numaRWOIRtVM6o56WHU64EeHkRybeAB5GLcVIoJ6C+p3wHuitcMm0Pco2yo6zkrPPs0sTmd1eWVu +IFZhbiBOZ3V5ZW4gPG5ndXllbm52QGl3YXl2aWV0bmFtLmNvbT7CwDUEEBMKAGQFAmbzgU4WIQSD +6DIwfTk50CHc8qK6ngqnSMkpzwkQup4Kp0jJKc88FAAAAAAAEwAgc2FsdEB3ZWJvZnRydXN0Lm9y +ZySIIyp6oeszYilCAQwdChVekXXbHe/dZokPrspS61CDAAB/GgIHbomkBPg4iTdzrmj2lVDLA51N +2sHGCQftbSgqvouyQd6fNOI1xuZXHkEJ8apAqACoPohxwWgmgL2NbfHkbU/jxXkCCQEnmnbo7IGq +NH0KPvIltCoh95kBVyE8nHKgdinRWznMZcyd2ejtAfQ9288/c3nq4pzMWkHqYLmu3MU19jiiScyr +dsfASwRm84FOEgUrgQQAIwQjBAB9absUVmtAo0ss7jsD2yjfVm/IWYA4Y1uTuAZT7fs0ySDe6QDH +d801vADFg5JjwdpdNBDuxb4ygXAC9tjFCAgLNAFcxU78aGu4R5Ft6zMRQqZLqAi1OZrb1Y92zzDB +INlgCZM/sUQAZGQW2XfW6HinCnl53FsV8zJuGjNeuYzH+NxAaQMACgn+BwMI2UR0zjh0xpTgwVA5 +Ga8JTswAj158X/uhCxZaCnX2b2hZw1N34RMqm/ECZxNRCjDB4npSywiDibcmXjHWm5pTf/KBF9ME +bfVusQnp4LWUuG+1FhIKOFQDGNvdR7B0E++7k6yZr+AV09pkSFGnBKdiaMLAOQQYEwoAZwUCZvOB +ThYhBIPoMjB9OTnQIdzyorqeCqdIySnPCRC6ngqnSMkpzwIbDDwUAAAAAAATACBzYWx0QHdlYm9m +dHJ1c3Qub3Jn2faeyqSTJzwKaaVMli8w27FvgzAjc3557HTBqhms5E4AAHqoAgkB1df9SL5XlNfY +QdlLqnKilGjTsezV1oHwCETQfrS0J4OQjFWU2HjW/8ZCi9T2K/aLIGFUweXR7Rs4D//ipI5dHL8C +CQGuZEOaUiVUrykzCiGTJ8g+/RsZnmOSkAafZyFQfTWYnURRJ+xs0HWqVOQsDnuo94+sS9tEgzpi +ZgPDYnRLXCjPFA== +=42yk +-----END PGP PRIVATE KEY BLOCK----- +'''; + + const curve25519KeyData = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xX0GZvOBThsAAAAgNgTM0VxUPpT+EPeltZCRrLVp4PQtdVLqmrEbln7nqkX+HQcLAwgpPq31t9Lk +AOBxvjCfxzD7nRJShX2aDSjNwsQ9xcUoUQ4Xtho0KFR3KD6xWW/Ud0y/PStRBLeqoPjyf8LVfwiq +1Vml/TdgJzUr+sN18MK7Bh8bCAAAAFwFAmbzgU8iIQbGAUcpguX+iT2VYGIph9cSTS2PhUnmMW5E +v5Mpgkkl1AkQxgFHKYLl/okCGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcBCQEHAgkCBwMJ +AwAAAACFOxDChoaac0Z6OStT5W8DATB3qi9uy8j34X1P1BI1AL/hV1+a1rXZhoe75V+xUsnSiX1c +ve+sqiBVCTRf/ipqhKRpmpmXZGaAXeRkoLTCYzXDBs0qTmd1eWVuIFZhbiBOZ3V5ZW4gPG5ndXll +bm52MTk4MUBnbWFpbC5jb20+wr4GEBsIAAAAXwUCZvOBTyIhBsYBRymC5f6JPZVgYimH1xJNLY+F +SeYxbkS/kymCSSXUCRDGAUcpguX+iQIbAwMLBwkEIgECAwUVCAwKDgUWAAECAwIeCw0nBwEJAQcC +CQIHAwkDAhkBAAAAAI19EJiIsqw962c/t8OSfzgWhuV5CXcBAauNrjSEbKRWgL1raLgC7NJNo4HZ +N8bkiiAQYPH2vmHN1h/sA56/ue8HtckXGzyAvWZpRAzQJwGcMT8DzSxOZ3V5ZW4gVmFuIE5ndXll +biA8bmd1eWVubnZAaXdheXZpZXRuYW0uY29tPsK7BhAbCAAAAFwFAmbzgU8iIQbGAUcpguX+iT2V +YGIph9cSTS2PhUnmMW5Ev5Mpgkkl1AkQxgFHKYLl/okCGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMC +HgsNJwcBCQEHAgkCBwMJAwAAAACqCRC+ziQUMGwHLS6z7NphB+Odc5qUKBrPPzUo+t1BsaqPsvo2 +cfuqhqY8crbX7+Vjw9jgdyB2sEmQJNE2xdD9ZP7nI+75bcfVmv2qSTtu9SZfBMd9BmbzgU4ZAAAA +IBX6gFdXX115qhtVtF7BrgqiDPPTr0tmhp95fa1ULk4w/h0HCwMIicK0zjrbJKHgaVqj5JU5kjz/ +v5GhQ/vr7rA3niwCxv8kJJYU/Uv1bJm/2t+Ttz9U/Krp4UdiHuIgjTNPhItboy5c5+gP9NLC0m7p +2PHClQYYGwgAAAA2BQJm84FPIiEGxgFHKYLl/ok9lWBiKYfXEk0tj4VJ5jFuRL+TKYJJJdQJEMYB +RymC5f6JAhsMAAAAAIzeEKdEOhcv059atWcnzJAxsMyjMZKwwBeQPNwB8rnwKt1sd/zIlF5QWoXp +txnK0umqKGDv5XOmkieuVSSB1p+Ka4tg+0mUiHK4Q6eH8FGCrywO1R44TjFeZcQLtExinfj6VUu4 +OWcHlS3cfS2V0+EdtxA= +=xBiu +-----END PGP PRIVATE KEY BLOCK----- +'''; + + const curve448KeyData = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +=aLGb +-----END PGP PRIVATE KEY BLOCK----- +'''; + + final rsaPrivateKey = OpenPGP.decryptPrivateKey(rsaKeyData, passphase); + final eccPrivateKey = OpenPGP.decryptPrivateKey(eccKeyData, passphase); + final curve25519PrivateKey = OpenPGP.decryptPrivateKey(curve25519KeyData, passphase); + final curve448PrivateKey = OpenPGP.decryptPrivateKey(curve448KeyData, passphase); + + print('Sign cleartext message:'); + final signedMessage = OpenPGP.signCleartext(clearText, [ + rsaPrivateKey, + eccPrivateKey, + curve25519PrivateKey, + curve448PrivateKey, + ]); + var armored = signedMessage.armor(); + print(armored); + + print('Verify signed message:'); + var verifications = OpenPGP.verify(armored, [ + rsaPrivateKey.publicKey, + eccPrivateKey.publicKey, + curve25519PrivateKey.publicKey, + curve448PrivateKey.publicKey, + ]); + for (final verification in verifications) { + print('Key ID: ${verification.keyID.toHexadecimal()}'); + print('Signature is verified: ${verification.isVerified}'); + print('Verification error: ${verification.verificationError}'); + } + + print('Sign detached cleartext message:'); + final signature = OpenPGP.signDetachedCleartext(clearText, [ + rsaPrivateKey, + eccPrivateKey, + curve25519PrivateKey, + curve448PrivateKey, + ]); + armored = signature.armor(); + print(armored); + + verifications = OpenPGP.verifyDetached(clearText, armored, [ + rsaPrivateKey.publicKey, + eccPrivateKey.publicKey, + curve25519PrivateKey.publicKey, + curve448PrivateKey.publicKey, + ]); + for (final verification in verifications) { + print('Key ID: ${verification.keyID.toHexadecimal()}'); + print('Signature is verified: ${verification.isVerified}'); + print('Verification error: ${verification.verificationError}'); + } +} diff --git a/lib/src/common/armor.dart b/lib/src/common/armor.dart index 24cc346d..4e159ab8 100644 --- a/lib/src/common/armor.dart +++ b/lib/src/common/armor.dart @@ -130,7 +130,7 @@ class Armor { final ArmorType type, final Uint8List data, { final String text = '', - final String hashAlgo = '', + final Iterable hashAlgos = const [], final int partIndex = 0, final int partTotal = 0, final String customComment = '', @@ -156,9 +156,10 @@ class Armor { ]; break; case ArmorType.signedMessage: + final hashHeaders = hashAlgos.map((hash) => 'Hash: $hash').join('\n'); result = [ '$signedMessageBegin$endOfLine', - 'Hash: $hashAlgo\n\n', + hashHeaders.isNotEmpty ? '$hashHeaders\n\n' : '', '${text.replaceAll(RegExp(r'^-', multiLine: true), '- -')}\n', '$signatureBegin$endOfLine', _addHeader(customComment), diff --git a/lib/src/message/signed_message.dart b/lib/src/message/signed_message.dart index 7b314935..defc60f1 100644 --- a/lib/src/message/signed_message.dart +++ b/lib/src/message/signed_message.dart @@ -41,11 +41,9 @@ final class SignedMessage extends CleartextMessage implements SignedCleartextMes ArmorType.signedMessage, signature.packetList.encode(), text: text, - hashAlgo: signature.hashAlgorithms - .map( - (hash) => hash.name.toUpperCase(), - ) - .join(', '), + hashAlgos: signature.hashAlgorithms.map( + (hash) => hash.name.toUpperCase(), + ), ); } diff --git a/test/common/armor_test.dart b/test/common/armor_test.dart index c16cbab7..b74b423c 100644 --- a/test/common/armor_test.dart +++ b/test/common/armor_test.dart @@ -61,7 +61,9 @@ void main() { ArmorType.signedMessage, bytes, text: text, - hashAlgo: HashAlgorithm.sha256.digestName, + hashAlgos: [ + HashAlgorithm.sha256.digestName.toUpperCase(), + ], ); final beginReg = RegExp(r'BEGIN PGP SIGNED MESSAGE'); From 8eaf7486680926b60255e3e31f9705f477c92860 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 10:39:18 +0700 Subject: [PATCH 182/202] WIP Signed-off-by: Nguyen Van Nguyen --- example/key_reading.dart | 175 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 example/key_reading.dart diff --git a/example/key_reading.dart b/example/key_reading.dart new file mode 100644 index 00000000..fb5d9cab --- /dev/null +++ b/example/key_reading.dart @@ -0,0 +1,175 @@ +import 'package:dart_pg/dart_pg.dart'; +import 'package:dart_pg/src/common/helpers.dart'; + +void main() { + const rsaKeyData = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +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 +=6yWV +-----END PGP PUBLIC KEY BLOCK----- +'''; + + const eccKeyData = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xpMEZvNvEhMFK4EEACMEIwQAhYLZkwYSvw0U+DPzSGOQybF7GZ3OoZpk6kXgyn2CJEk8jLAdvnNG +2lZPHgCbm7PCMorp+lDpwQgRlGOPAWTkoa8Ao6CC/WPqEgu8c9YbWKrMaB1MTqlzsKrpbuOQC1mS +dG4M4/wuc53Y7jK479RsAX+HrO3aShyJ6fWa2fERWVJlk+vNKk5ndXllbiBWYW4gTmd1eWVuIDxu +Z3V5ZW5udjE5ODFAZ21haWwuY29tPsLAOAQQEwoAZwUCZvNvEhYhBIZiN9Hgecmhdbnw0UCmua4K +K7P9CRBAprmuCiuz/QIZATwUAAAAAAATACBzYWx0QHdlYm9mdHJ1c3Qub3JnTHkM5KPVLr04ZPWh +qKfnT9FRgGijUcEruixX0t7jzK4AAKyqAgYi176jK907bBygy73ZwNdProB8NXPbSLXndH5uk+bG +cvYoVuHJztKt7lq6bstUrrLaPC7aAtBKKGEAtoXle0hHpgIJAeXy7hhs05lPC1Da62TTIqY81vfK +GbiXDQ6nRFUwsmX0/gKJv8vlhaU80j8MWC6JEXSb8we2m/hIDUoZua17HlgWzSxOZ3V5ZW4gVmFu +IE5ndXllbiA8bmd1eWVubnZAaXdheXZpZXRuYW0uY29tPsLANQQQEwoAZAUCZvNvEhYhBIZiN9Hg +ecmhdbnw0UCmua4KK7P9CRBAprmuCiuz/TwUAAAAAAATACBzYWx0QHdlYm9mdHJ1c3Qub3JnITBv +QWh34a2PFstAFRfyvySvaLZr4IRPLgEK5IQjq7oAACCKAgj/TsIuuLLm7npWEBy2vDSyIbEdejMq +6kc9jE7ZdJDHn5+2QYM1ABeKrZ5RQIFKGNV6UDEcuC2y5efTfaiXUYXIRwIJAWcKN26eFbNvE0Ox +kPTfvB12Co/9xlPqe6jZu2Tu5zCZihWwNyeMBrbKb2w8z5K6ojRNUzmeKqeWWs3tjXShLcLnzpcE +ZvNvEhIFK4EEACMEIwQA2d2DuFoHSMxLak3N9vIGd6ULDtAU2ND2P+udyCmw7bELsdaHkTLzuP8e +VvXGu9kubwplQ2QPO84Qu2QSw8JGWGwAGsOsZmE/mKQ+kAdrWPegS6EzlbjHY1zDGwU9QRPb0dKI +rnxHLM2Ao8PXIDMWzGtNVSCk/3jae2EPYAO3a0PCqNgDAAoJwsA3BBgTCgBnBQJm828SFiEEhmI3 +0eB5yaF1ufDRQKa5rgors/0JEECmua4KK7P9AhsMPBQAAAAAABMAIHNhbHRAd2Vib2Z0cnVzdC5v +cmcFEoHRwxZoRuqWJKVrwMC99WEqEYlFml390bKcml981QAAtHcCCJEkfTugD4paRzEcnqSTOPRG +J5yYt/JqDKCwUVC90rWc+kBrR5YXGjOev5sUPVwUeATTf5Q4/diHEF5fBe6GQxyVAgdn/JTcu0nF +oRO4ZVyMBu0Exd8BB/S/CNoqXyel6UiKBiNQN/SA7jwuUQqrW+10+84ZHL41TxpiegwAt08fw0Lt +Xg== +=QRfd +-----END PGP PUBLIC KEY BLOCK----- +'''; + + const curve25519KeyData = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xioGZvNvEhsAAAAgHVrZBj0nDKrbf0Tu8fWOjRiPaMulfxm1Mt7lEkY4gZzCuwYfGwgAAABcBQJm +828SIiEGoLT+uXrwY1ghIpvsuo3SXJzh9HEg0y3OT6Vr6OoCSYMJEKC0/rl68GNYAhsDAwsHCQQi +AQIDBRUIDAoOBRYAAQIDAh4LDScHAQkBBwIJAgcDCQMAAAAAud0QHptvcH5rYd3HpH27Oymyc4u5 +2xIfEx0ktluI0xBx5BunedkVFkZCDn4kF8l+LoY2JRHOHELTGOE0tr27dWeNxyWD12JNQFfNmwEn +hjXPRg7NKk5ndXllbiBWYW4gTmd1eWVuIDxuZ3V5ZW5udjE5ODFAZ21haWwuY29tPsK+BhAbCAAA +AF8FAmbzbxIiIQagtP65evBjWCEim+y6jdJcnOH0cSDTLc5PpWvo6gJJgwkQoLT+uXrwY1gCGwMD +CwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcBCQEHAgkCBwMJAwIZAQAAAAAYkhCK+OAsSwTaVp3J +z3lg1pTZ3b1CLmHiovJCIOCNmSD2Xd039LaL1WzjwGtFqkGLZGrljKtctyZA9J0zCPll5wSNtZdp +bZogWYv7tpG9u56zAc0sTmd1eWVuIFZhbiBOZ3V5ZW4gPG5ndXllbm52QGl3YXl2aWV0bmFtLmNv +bT7CuwYQGwgAAABcBQJm828SIiEGoLT+uXrwY1ghIpvsuo3SXJzh9HEg0y3OT6Vr6OoCSYMJEKC0 +/rl68GNYAhsDAwsHCQQiAQIDBRUIDAoOBRYAAQIDAh4LDScHAQkBBwIJAgcDCQMAAAAAyQ4QZBh2 +CYzMWhX/ioY58dO4OCP3PJnZGPTY7fuVFkAmqTWExnJ1/jyohgtYagt8oY/JySc+NtiVnp1SSBmT +yW5WA6ytR8Eo8R5PkZ7o4ZCULg7OKgZm828SGQAAACAsy7WHA7UzeU3ZbTqSAqi6vNiNoxH7cd28 +W46mz0UfF8KVBhgbCAAAADYFAmbzbxIiIQagtP65evBjWCEim+y6jdJcnOH0cSDTLc5PpWvo6gJJ +gwkQoLT+uXrwY1gCGwwAAAAAjRoQ96R96OJ9v5LANj7zanxgHiCii6MYYoI7O46O1iEMJj6qCxN9 +u+rSk/m1dc6B7Xh3iCcDCrAsJeeRzZYmxTyX92iRGG5LN9K2SUZKZixQ8QzVGBs2zEdgqf76xwvE +4nrqGzMWaYMm8a7fXw== +=A0yl +-----END PGP PUBLIC KEY BLOCK----- +'''; + + const curve448KeyData = ''' +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xkMGZvNvExwAAAA5yWvpY7TWFFw8urr787Qb4cDyF9X59bJ6PjDvL2PoibX1jmU6Fuf30HSZnSLq +CAkvFx//A+rAbeEAwsA9Bh8cCgAAAFwFAmbzbxMiIQYwBf+MyThKw0UAWILFQZ6Yjv387mZGsM5P +Yn+mGyPc8QkQMAX/jMk4SsMCGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcBCQEHAgkCBwMJ +AwAAAAA6KyCR2q4SfxJ9R+ojCxLnpl3DLc4rjlEkmNzt9rYBMAb+mCjbwWsIn4Ez+w+CZKlAQ8dz +LMXjhfZGZXIBVJcZ6e+xKWDkJuX61YodNXwfpuEu9PpxfXNDbfIkgBz7M0eivjwaZwZyX8V4rGyc ++s87XomNPvrcSuW6cm1d3JcxCJ7rxPA4S+DTQcbThwerWlamgtALAM0qTmd1eWVuIFZhbiBOZ3V5 +ZW4gPG5ndXllbm52MTk4MUBnbWFpbC5jb20+wsBABhAcCgAAAF8FAmbzbxMiIQYwBf+MyThKw0UA +WILFQZ6Yjv387mZGsM5PYn+mGyPc8QkQMAX/jMk4SsMCGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMC +HgsNJwcBCQEHAgkCBwMJAwIZAQAAAADpZSCb+KoeU6eH6xbJQntrdR5qXdyuAbeGOVwSQLQeuMKk ++bcEigI/rGSWuAOX13KJdnOUrzNMlwkl3YdQ2Av956wbwYlmiukmHDFehyD3uBKegI4xpdDjiEXV +gH8+Vvus5ozRrjKqWtfulGfCNtL4e45x0gmqe1G80kK7QgTTfFwYbEhBMMczCPdlZliq9y0TJEwZ +AM0sTmd1eWVuIFZhbiBOZ3V5ZW4gPG5ndXllbm52QGl3YXl2aWV0bmFtLmNvbT7CwD0GEBwKAAAA +XAUCZvNvEyIhBjAF/4zJOErDRQBYgsVBnpiO/fzuZkawzk9if6YbI9zxCRAwBf+MyThKwwIbAwML +BwkEIgECAwUVCAwKDgUWAAECAwIeCw0nBwEJAQcCCQIHAwkDAAAAAPq1IEHDW8ZVfIDBR+pK4ogX +oxu7etsdcj8yXn44Uc1Pjo9gMbkRfTwnPJzGArJQ8lfDZtuOPxfLpstBQH3bBq3zV7mRVOdkvm24 +8nXKCZsWWrVLdS/2P+PsFoAASiDURs8k7/mFtKvaSsJDkavtkwocXvqtUFgUguk2NiVVmIjZ3UvX +7U0YRbLVoDwtBzckoTzWRSUAzkIGZvNvExoAAAA4lrmeeMZwewtqqZ1VOJf7v9XXb5E6f7C58t1F +3V6CckIIYGG83tcM93nQSbVm0BtiBsKxH4LzOBnCwBcGGBwKAAAANgUCZvNvEyIhBjAF/4zJOErD +RQBYgsVBnpiO/fzuZkawzk9if6YbI9zxCRAwBf+MyThKwwIbDAAAAAAByyC9UL/ZIVdi+x90Vmme +eHW4+3472yq3PbVeLCsHnZQBWT8ocys2wRGxYnVuOUXvMiQJiMsIXJyW5zLf8/wH6jI0yS2aBazW +lohZO//dxuDMMuxa4YyCbhghAP9yUuW7NPeRQ3fPIjaUlORXdMyoallcsyhK3UGj90MWqStbJrvH +Cd9nlIA9bNiev1mPWeDKPpwaANUbud6PmWraXDt68RWZCEG9fzhJU0P61f/zRjXK +=/xA2 +-----END PGP PUBLIC KEY BLOCK----- +'''; + + print('Read RSA public key:'); + final rsaPublicKey = OpenPGP.readPublicKey(rsaKeyData); + print('Key algorithm: ${rsaPublicKey.keyAlgorithm.name}'); + print('Key version: ${rsaPublicKey.version}'); + print('Key fingerprint: ${rsaPublicKey.fingerprint.toHexadecimal()}'); + for (final user in rsaPublicKey.users) { + print('User ID: ${user.userID}'); + } + + print('\nRead Ecc public key:'); + final eccPublicKey = OpenPGP.readPublicKey(eccKeyData); + print('Key algorithm: ${eccPublicKey.keyAlgorithm.name}'); + print('Key version: ${eccPublicKey.version}'); + print('Key fingerprint: ${eccPublicKey.fingerprint.toHexadecimal()}'); + for (final user in eccPublicKey.users) { + print('User ID: ${user.userID}'); + } + + print('\nRead Curve25519 public key:'); + final curve25519PublicKey = OpenPGP.readPublicKey(curve25519KeyData); + print('Key algorithm: ${curve25519PublicKey.keyAlgorithm.name}'); + print('Key version: ${curve25519PublicKey.version}'); + print('Key fingerprint: ${curve25519PublicKey.fingerprint.toHexadecimal()}'); + for (final user in curve25519PublicKey.users) { + print('User ID: ${user.userID}'); + } + + print('\nRead Curve448 public key:'); + final curve448PublicKey = OpenPGP.readPublicKey(curve448KeyData); + print('Key algorithm: ${curve448PublicKey.keyAlgorithm.name}'); + print('Key version: ${curve448PublicKey.version}'); + print('Key fingerprint: ${curve448PublicKey.fingerprint.toHexadecimal()}'); + for (var user in curve448PublicKey.users) { + print('User ID: ${user.userID}'); + } + + print('\nMerge and armor public keys:'); + final armored = OpenPGP.armorPublicKeys([ + rsaPublicKey, + eccPublicKey, + curve25519PublicKey, + curve448PublicKey, + ]); + final publicKeys = OpenPGP.readPublicKeys(armored); + for (final publicKey in publicKeys) { + print('\nKey algorithm: ${publicKey.keyAlgorithm.name}'); + print('Key version: ${publicKey.version}'); + print('Key fingerprint: ${publicKey.fingerprint.toHexadecimal()}'); + for (final user in publicKey.users) { + print('User ID: ${user.userID}'); + } + } +} From 6264fb83ac3a113b6c28a5f303e9cd90dc8f3daf Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 10:51:46 +0700 Subject: [PATCH 183/202] WIP Signed-off-by: Nguyen Van Nguyen --- example/key_decrypting.dart | 198 ++++++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 example/key_decrypting.dart diff --git a/example/key_decrypting.dart b/example/key_decrypting.dart new file mode 100644 index 00000000..9a045e6b --- /dev/null +++ b/example/key_decrypting.dart @@ -0,0 +1,198 @@ +import 'package:dart_pg/dart_pg.dart'; +import 'package:dart_pg/src/common/helpers.dart'; + +void main() { + const passphrase = 'Ax@2bGh;SxD&"A_;El%mPIvLx_!#3Aik'; + + const rsaKeyData = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +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 +qy6toPKEKnsoCZVMlS/fSSTONakq +=fImQ +-----END PGP PRIVATE KEY BLOCK----- +'''; + + const eccKeyData = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xcBIBGbzftYTBSuBBAAjBCMEAWHmrzLEzuTRVDQQTvCZdm/RnY/M9rzN8qzpp4lBAFSDhYY118hv +Jp2Ax0rsD5Qi8tMeTAdFef08HqzP4eeNSHu1AZi518zPlyv3ADpZ3jQ89Sw+WwR17a7+0ZuEV8Jd +0gyhyCX9E+D1cxZsziJBIjpNuygqCIhqd9rNzNVd7JizZVkZ/gcDCL4MeAUz7gZd4Ls+mz+hHTJ0 +Vjfvt42ia0SIS4EQ9Arg5os4IDybW26s6+tK4uaye+Q8OoqNHBqoLHjwAgWIMFSbm2oNTfWOJ3OW +bY1xHF3xU6NthH6xIgexVN1VRo83OfH9i7J5kIxQ2f7G2ts8GW8ZzSpOZ3V5ZW4gVmFuIE5ndXll +biA8bmd1eWVubnYxOTgxQGdtYWlsLmNvbT7CwDgEEBMKAGcFAmbzftcWIQQKaDUnC4VgjGhq6iRK +MOXn515lRAkQSjDl5+deZUQCGQE8FAAAAAAAEwAgc2FsdEB3ZWJvZnRydXN0Lm9yZ9vIagGLvsuT +rhz3HcgczRpxDHZ/isZuk9JjxV2MxOjkAADSMQIJAVrEKFKzFwaPbioc/uo3OiIgEHs5RNMMDt+7 +HNSmSK3/hppyJGlHZ9HJ+PGRjFfefqN3CMbTLca+zIbNjNTtYtHTAgjhRO+F4ecTUZ4FS6p7FYOn +Xh+/6kY/q2Pslvy8h5J/e+BS2tpGNVW8NBkaAodfH72Qfl3DHqCIwBVq3cOB7Y5B2c0sTmd1eWVu +IFZhbiBOZ3V5ZW4gPG5ndXllbm52QGl3YXl2aWV0bmFtLmNvbT7CwDYEEBMKAGQFAmbzftcWIQQK +aDUnC4VgjGhq6iRKMOXn515lRAkQSjDl5+deZUQ8FAAAAAAAEwAgc2FsdEB3ZWJvZnRydXN0Lm9y +Z6VV4IKUt2VuzXWU2m3vBZDIFqafb0vuegRV3Lzin2MOAABbygIJAe+jRxTKvbdW6/7uCkUC9rBr +viuR5XDbfuPEPtY3Zt9BipUBQzEQ4YDRPGi+WmkBC5joVIzO3KgjgEdfTLkhfPBZAgkBtlo1U828 ++T6PzxvqtoEegF0ZQHxJDWPIc/yO6FKjPklMy9Rx11ZyOK6RNqy1gDo/P1VnD8Ijd5LRhiBasSpt +HYXHwEwEZvN+1xIFK4EEACMEIwQAaxyKF0GqutGOvqecwNq4EeU3qikm24tuNJd8CfdEETWymtua +aEZD4FUdFqshi+QmJ0RGYB9R9yFpTWJANvZGfDgB/5auAsBvTh5zlQRYcqN/s79HXGgfsHhvBSAE +ruOW3UdPQcD4wdYYi7D3SJUj3m9BjDHDvKmu3A2MSLCgKD8vuAgDAAoJ/gcDCKFUtRsiD7eW4OUz +B47o7XBi8bWWdbDg4L6zs3vCksArGErMpafTxY4zL3vxwh8LxyGOSEfSO/IAtYQvwzGAccDawDgx +9fvQxvdPqzah58fjZZjwRvLe74B4jIt2uUBitJe5SZViEGvqXJQRslRFFLdRwsA4BBgTCgBnBQJm +837XFiEECmg1JwuFYIxoauokSjDl5+deZUQJEEow5efnXmVEAhsMPBQAAAAAABMAIHNhbHRAd2Vi +b2Z0cnVzdC5vcmdiuw5nTdeo697B284O829/BjCcXC+TUfCyEM6cWhGZMQAAeIkCBiVoTUmbUb3e +9QoEjFMYvEBukbaCrJgryimqeTmJAUXMcgQaAyZLJSjEWi+adSEco9jwxl2ERPF4iJXZrgQiOvLK +AgkBOYQwtx3Bwe9wnbmbPzfF85qcoXQ2Lz6t5fmqzlqf3r3OtWzSyWbMEzC1ZdWTFE/RKcLsOhLZ +h2en6HvwU70R0BQ= +=15wT +-----END PGP PRIVATE KEY BLOCK----- +'''; + + const curve25519KeyData = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- + +xX0GZvN+1xsAAAAgMtES26hjSSQoUx6STK6IMe64fLvldCRkg7w50pfRxqD+HQcLAwjPsmWimBb8 +n+B+mPc9X1kryrwErHX2103ACvuNq1yGPKvU6q3HygdPL8n7DHv/U5S88R6KQnNQfaIRGsYRpye+ ++xw+S2xbvPOA2gsXPMK7Bh8bCAAAAFwFAmbzftciIQYLfjtdj/UV5gHlZfcFMJ6Ke9ev9rKvkcOH +pWTraKKGagkQC347XY/1FeYCGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcBCQEHAgkCBwMJ +AwAAAACvoRDdi30uoJ4Qx1mM90rST6SweRqem4vCvIeBio+IASNXehHpunU6cKwMVZDn95CBDNPZ +z2nJgNuGKdAJQDWyOiZiNKI9E6wrV3upyecXMG62C80qTmd1eWVuIFZhbiBOZ3V5ZW4gPG5ndXll +bm52MTk4MUBnbWFpbC5jb20+wr4GEBsIAAAAXwUCZvN+1yIhBgt+O12P9RXmAeVl9wUwnop716/2 +sq+Rw4elZOtoooZqCRALfjtdj/UV5gIbAwMLBwkEIgECAwUVCAwKDgUWAAECAwIeCw0nBwEJAQcC +CQIHAwkDAhkBAAAAAKGEECKDSaFeJVntGQNUD76ZK95pLGIIVCXvL9rDH92lz/XrdtMkR5pAKJMc +zP8SiSPx3+zt7pXkFu3eZV2UTLYfGBJK8tUG/yMSCp06noXIHyoLzSxOZ3V5ZW4gVmFuIE5ndXll +biA8bmd1eWVubnZAaXdheXZpZXRuYW0uY29tPsK7BhAbCAAAAFwFAmbzftciIQYLfjtdj/UV5gHl +ZfcFMJ6Ke9ev9rKvkcOHpWTraKKGagkQC347XY/1FeYCGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMC +HgsNJwcBCQEHAgkCBwMJAwAAAABUZxBN8BwLTv+kUMGiDjnhQmFp3c2kxE0pxPt8bVxECyC4dnQy +D7tm7ADA4ARHAewWJBpbrk+ebJQ7UE82t49FWMRafi0Fa+GSgOdwdGONSx19C8d9BmbzftcZAAAA +IBOea5n9NJN1t+BwL3DQ0CSSzz7px4fG2+mUGOMDtUM6/h0HCwMIzDB2lTsp8LzglO8FYN79WOnZ +Fh1xEf4FY+t34w/7G+j50WKZOfSoAUxF9iA+hDmV2eBsEvhIK1lxDQmsk3aKjWC80/hMNPwiChdN +WoTClQYYGwgAAAA2BQJm837XIiEGC347XY/1FeYB5WX3BTCeinvXr/ayr5HDh6Vk62iihmoJEAt+ +O12P9RXmAhsMAAAAAFyXEINWSWM0kFOrQi9dSyWz3QwlxuNVkw18lJNG1ERQvqsrk9h4gfATRvIG +iifJtGDxIsDjLBx8u9lEBTPFPKQbMrh4Gx7OL87S2a3yKJlkdsoJ1RexF+OhHlcTHORBJRF1UMiY +9jMDW8IANQ== +=kJoZ +-----END PGP PRIVATE KEY BLOCK----- +'''; + + const curve448KeyData = ''' +-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: PHP Privacy v2 +Comment: https://github.com/web-of-trust/php-privacy + +xa8GZvN+1xwAAAA5Hf47PIbsLCoImil+MuXWGvUvzElvjIiq3fJrRIf+k1cXre22GEKF01R2q1Ma +mkSDoofsPPeNeUUA/h0HCwMITySmmq2jzrTgYtxFzyFzKGtQNbFTZXcTXmmxqT4UjfG+S5s5tFYy +PG741SHOfNpz10FGD5UzaVRamxnO+or2oUJZcxS6qKR895rPQImQVoQdy8xYfEZkVE2Tl8rXX0M8 +YXsyimIqwsA9Bh8cCgAAAFwFAmbzftgiIQajZpX32QU9YiluDfY6DUi6J3MNnD1PcbgqWMinyQ/q +IAkQo2aV99kFPWICGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcBCQEHAgkCBwMJAwAAAAAj +xiD+Lo7uvlINPBCHEnAKtdp/Ez8sgWC0M0HsRidCx3TOwdRtseFf4sO758Ti7hyO1BPGsXeHZgqC +XZlPWAOkdyLf56RwS4hS5Zdkj0VXUpbR/178kHmuz+0IAKbvDlFqJPU+2B+4UHbMnd2WVgloRvKF +42yTmmhLX+/iFF7bo100Y9nds9WZd7ueYdM8e3xlfxsaAM0qTmd1eWVuIFZhbiBOZ3V5ZW4gPG5n +dXllbm52MTk4MUBnbWFpbC5jb20+wsBABhAcCgAAAF8FAmbzftgiIQajZpX32QU9YiluDfY6DUi6 +J3MNnD1PcbgqWMinyQ/qIAkQo2aV99kFPWICGwMDCwcJBCIBAgMFFQgMCg4FFgABAgMCHgsNJwcB +CQEHAgkCBwMJAwIZAQAAAABgWCAyhNd2vP10QOYnK1iKdYewmPtPOyNP/y/iKOe0plKKeC2D5rEH +XJGLr0nsx9gJa85GPJG8vzKbrjYkLkzaQj3aKskW796RqslR9o4McmVjH3fkmjukK2CEABLYAzFs +kNSSvtZPvT574FN21frEWF7K5pu8GslClN+2AL6109MMArgPMcAhEWVBy3Hgd6h57zk/AM0sTmd1 +eWVuIFZhbiBOZ3V5ZW4gPG5ndXllbm52QGl3YXl2aWV0bmFtLmNvbT7CwD0GEBwKAAAAXAUCZvN+ +2CIhBqNmlffZBT1iKW4N9joNSLoncw2cPU9xuCpYyKfJD+ogCRCjZpX32QU9YgIbAwMLBwkEIgEC +AwUVCAwKDgUWAAECAwIeCw0nBwEJAQcCCQIHAwkDAAAAAPFWIAJ2Cs6NuGo1flcRWegIsTzYN3/u +G3DIXNuRxLAirLsYWpS3gn7a0aS1GARGaJGJiaIuREYJjHpo20JLEQzj0HKKXoSg42JY9ExaGrRP +yVL2hgjWAD4D1aGACFW1Ij/C0YcoPaOcAkz0K6HCDuAi3Xk8JBvt7tIIRGhJiJG/2WCinMRe1S52 +lyVKlnqqekfQtykAx60GZvN+1xoAAAA4toeLSE1P+jKskZLQ9QfTOJ2q0/36nw6KcoLzJlout9+r +u/aZQcnbAxTHM6pPol9NmVsfWk4HYrj+HQcLAwgbbgPlGehL+uCaJnK8VYk+uRjvEZkC2MUJm+wI +OewG+h8IsGSxSh8ua6+owE6rZjmXA/UpDqLiR3Jvy9vxg8QQmVlUZbh+Ztta6I1YmrfGMVCUGc69 +TKl7KpcignYL99Lz5W3xL8LAFwYYHAoAAAA2BQJm837YIiEGo2aV99kFPWIpbg32Og1IuidzDZw9 +T3G4KljIp8kP6iAJEKNmlffZBT1iAhsMAAAAAOvKIJ7+IJ4dztxkR2rRmf5DphiMHDJ+lSXr+Svs +qbDWAvsfIsNeKdP88/5gc94Dxh10xPSaS82akzoJ7RHMQ74zGIwexOlKRoBa9qLeDmkbO8rTE77m +3ngJsSoA2EqaRkPmjChELp6nGWPCZPyOa7ExcBkKrjiq8xMsnvjWdVsZ0BL8b93ER626rRzE9j5C +nWWFLDsA1Ri3Rbu551qo3Xk4o5gVhSC0J+7ZMZyQq2g= +=YGCQ +-----END PGP PRIVATE KEY BLOCK----- +'''; + + print('Decrypt RSA private key:'); + final rsaPrivateKey = OpenPGP.decryptPrivateKey(rsaKeyData, passphrase); + print('Key algorithm: ${rsaPrivateKey.keyAlgorithm.name}'); + print('Key version: ${rsaPrivateKey.version}'); + print('Key fingerprint: ${rsaPrivateKey.fingerprint.toHexadecimal()}'); + for (final user in rsaPrivateKey.users) { + print('User ID: ${user.userID}'); + } + + print('\nDecrypt Ecc private key:'); + final eccPrivateKey = OpenPGP.decryptPrivateKey(eccKeyData, passphrase); + print('Key algorithm: ${eccPrivateKey.keyAlgorithm.name}'); + print('Key version: ${eccPrivateKey.version}'); + print('Key fingerprint: ${eccPrivateKey.fingerprint.toHexadecimal()}'); + for (final user in eccPrivateKey.users) { + print('User ID: ${user.userID}'); + } + + print('\nDecrypt Curve25519 private key:'); + final curve25519PrivateKey = OpenPGP.decryptPrivateKey(curve25519KeyData, passphrase); + print('Key algorithm: ${curve25519PrivateKey.keyAlgorithm.name}'); + print('Key version: ${curve25519PrivateKey.version}'); + print('Key fingerprint: ${curve25519PrivateKey.fingerprint.toHexadecimal()}'); + for (final user in curve25519PrivateKey.users) { + print('User ID: ${user.userID}'); + } + + print('\nDecrypt Curve25519 private key:'); + final curve448PrivateKey = OpenPGP.decryptPrivateKey(curve448KeyData, passphrase); + print('Key algorithm: ${curve448PrivateKey.keyAlgorithm.name}'); + print('Key version: ${curve448PrivateKey.version}'); + print('Key fingerprint: ${curve448PrivateKey.fingerprint.toHexadecimal()}'); + for (final user in curve448PrivateKey.users) { + print('User ID: ${user.userID}'); + } +} From 8b9baa80b56b57d286cdb0cba877cd327fa33d52 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 10:59:49 +0700 Subject: [PATCH 184/202] WIP Signed-off-by: Nguyen Van Nguyen --- example/key_generation.dart | 48 +++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 example/key_generation.dart diff --git a/example/key_generation.dart b/example/key_generation.dart new file mode 100644 index 00000000..c5154b7b --- /dev/null +++ b/example/key_generation.dart @@ -0,0 +1,48 @@ +import 'package:dart_pg/dart_pg.dart'; +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/ecc.dart'; +import 'package:dart_pg/src/enum/key_type.dart'; +import 'package:dart_pg/src/enum/rsa_key_size.dart'; + +void main() { + const userIDs = [ + 'Dart Privacy Guard ', + 'Nguyen Van Nguyen ', + ]; + final passphrase = Helper.generatePassword(); + print('Generate passphase: $passphrase'); + + print('Generate RSA private key:'); + final rsaPrivateKey = OpenPGP.generateKey( + userIDs, + passphrase, + type: KeyType.rsa, + rsaKeySize: RSAKeySize.normal, + ); + print(rsaPrivateKey.armor()); + + print('Generate Ecc private key:'); + final eccPrivateKey = OpenPGP.generateKey( + userIDs, + passphrase, + type: KeyType.ecc, + curve: Ecc.secp521r1, + ); + print(eccPrivateKey.armor()); + + print('Generate Curve25519 private key:'); + final curve25519PrivateKey = OpenPGP.generateKey( + userIDs, + passphrase, + type: KeyType.curve25519, + ); + print(curve25519PrivateKey.armor()); + + print('Generate Curve448 private key:'); + final curve448PrivateKey = OpenPGP.generateKey( + userIDs, + passphrase, + type: KeyType.curve448, + ); + print(curve448PrivateKey.armor()); +} From 362ecec17e933d9335050797ff88ef2df7689f41 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 12:27:42 +0700 Subject: [PATCH 185/202] WIP Signed-off-by: Nguyen Van Nguyen --- example/README.md | 17 ++ example/literal_data.dart | 317 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 334 insertions(+) create mode 100644 example/README.md create mode 100644 example/literal_data.dart diff --git a/example/README.md b/example/README.md new file mode 100644 index 00000000..9be3dac6 --- /dev/null +++ b/example/README.md @@ -0,0 +1,17 @@ +Dart Dart Privacy Guard Examples +================================ + +* [Cleartext signing example](cleartext_signing.dart): a basic cleartext signing example. +* [Key decrypting example](key_decrypting.dart): a basic key decrypting example. +* [Key generation example](key_generation.dart): a basic key generation example. +* [Key reading example](key_reading.dart): a basic key reading example. +* [Literal data example](literal_data.dart): a basic literal data example. + +## Run examples +```bash +dart run cleartext_signing.dart +dart run key_decrypting.dart +dart run key_generation.dart +dart run key_reading.dart +dart run literal_data.dart +``` diff --git a/example/literal_data.dart b/example/literal_data.dart new file mode 100644 index 00000000..f73fd43c --- /dev/null +++ b/example/literal_data.dart @@ -0,0 +1,317 @@ +import 'dart:convert'; + +import 'package:dart_pg/dart_pg.dart'; +import 'package:dart_pg/src/common/config.dart'; +import 'package:dart_pg/src/common/helpers.dart'; +import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; + +void main() { + const passphrase = 'NU=WM<;ev3(^^M@)dIt,O|9k* Date: Mon, 9 Dec 2024 13:20:21 +0700 Subject: [PATCH 186/202] WIP Signed-off-by: Nguyen Van Nguyen --- README.md | 93 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 6877f6c4..d9ea460c 100644 --- a/README.md +++ b/README.md @@ -41,24 +41,24 @@ dependencies: ### Encrypt and decrypt data with a password ```dart -const text = 'Hello Dart Privacy Guard!'; +const literalText = 'Hello Dart Privacy Guard!'; const password = 'secret stuff'; -final encryptedMessage = OpenPGP.encrypt( - OpenPGP.createTextMessage(text), passwords: [password] +final encryptedMessage = OpenPGP.encryptCleartext( + literalText, passwords: [password] ); -final encrypted = encryptedMessage.armor(); -final decryptedMessage = OpenPGP.decrypt( - OpenPGP.readMessage(encrypted), passwords: [password] +final armored = encryptedMessage.armor(); +final literalMessage = OpenPGP.decrypt( + armored, passwords: [password] ); -final decrypted = decryptedMessage.armor(); +final literalData = literalMessage.literalData; ``` ### Encrypt and decrypt data with PGP keys Encryption will use the algorithm preferred by the public (encryption) key (defaults to aes256 for keys generated), and decryption will use the algorithm used for encryption. ```dart -const text = 'Hello Dart Privacy Guard!'; +const literalText = 'Hello Dart Privacy Guard!'; const passphrase = 'secret stuff'; const armoredPublicKey = '-----BEGIN PGP PUBLIC KEY BLOCK-----'; const armoredPrivateKey = '-----BEGIN PGP PRIVATE KEY BLOCK-----'; @@ -66,35 +66,38 @@ const armoredPrivateKey = '-----BEGIN PGP PRIVATE KEY BLOCK-----'; final publicKey = OpenPGP.readPublicKey(armoredPublicKey); final privateKey = OpenPGP.decryptPrivateKey(armoredPrivateKey, passphrase); -final encryptedMessage = OpenPGP.encrypt( - OpenPGP.createTextMessage(text), encryptionKeys: [publicKey] +final encryptedMessage = OpenPGP.encryptCleartext( + literalText, encryptionKeys: [publicKey] ); -final encrypted = encryptedMessage.armor(); +final armored = encryptedMessage.armor(); -final decryptedMessage = OpenPGP.decrypt( - OpenPGP.readMessage(encrypted), decryptionKeys: [privateKey] +final literalMessage = OpenPGP.decrypt( + armored, decryptionKeys: [privateKey] ); -final decrypted = decryptedMessage.armor(); +final literalData = literalMessage.literalData; ``` Sign message & encrypt with multiple public keys: ```dart -final text = 'Hello Dart Privacy Guard!'; +final literalText = 'Hello Dart Privacy Guard!'; const passphrase = 'secret stuff'; const armoredPublicKeys = ['-----BEGIN PGP PUBLIC KEY BLOCK-----']; const armoredPrivateKey = '-----BEGIN PGP PRIVATE KEY BLOCK-----'; -final publicKeys = Future.wait( - armoredPublicKeys.map((armored) => OpenPGP.readPublicKey(armored)) -); +final publicKeys = armoredPublicKeys.map((armored) => OpenPGP.readPublicKey(armored)); final privateKey = OpenPGP.decryptPrivateKey(armoredPrivateKey, passphrase); -final encryptedMessage = OpenPGP.encrypt( - OpenPGP.createTextMessage(text), +final encryptedMessage = OpenPGP.encryptCleartext( + literalText, encryptionKeys: publicKeys, signingKeys: [privateKey], ); -final encrypted = encryptedMessage.armor(); +final armored = encryptedMessage.armor(); + +final literalMessage = OpenPGP.decrypt( + armored, decryptionKeys: [privateKey] +); +final literalData = literalMessage.literalData; ``` ### Sign and verify cleartext @@ -107,10 +110,10 @@ const armoredPrivateKey = '-----BEGIN PGP PRIVATE KEY BLOCK-----'; final publicKey = OpenPGP.readPublicKey(armoredPublicKey); final privateKey = OpenPGP.decryptPrivateKey(armoredPrivateKey, passphrase); -final signedMessage = OpenPGP.sign(text, signingKeys: [privateKey]); -final signed = signedMessage.armor(); +final signedMessage = OpenPGP.signCleartext(text, signingKeys: [privateKey]); +final armored = signedMessage.armor(); -final verifiedMessage = OpenPGP.verify(signed, verificationKeys: [publicKey]); +final verifiedMessage = OpenPGP.verify(armored, verificationKeys: [publicKey]); final verifications = verifiedMessage.verifications; ``` @@ -124,13 +127,12 @@ const armoredPrivateKey = '-----BEGIN PGP PRIVATE KEY BLOCK-----'; final publicKey = OpenPGP.readPublicKey(armoredPublicKey); final privateKey = OpenPGP.decryptPrivateKey(armoredPrivateKey, passphrase); -final signature = OpenPGP.signDetached(text, signingKeys: [privateKey]); +final signature = OpenPGP.signDetachedCleartext(text, signingKeys: [privateKey]); final armored = signature.armor(); -final cleartextMessage = OpenPGP.verifyDetached( +final verifications = OpenPGP.verifyDetached( text, armored, verificationKeys: [publicKey] ); -final verifications = cleartextMessage.verifications; ``` ### Generate new key pair @@ -144,11 +146,11 @@ final privateKey = OpenPGP.generateKey( type: KeyType.rsa, rsaKeySize: RSAKeySize.normal, ); -final publicKey = privateKey.toPublic; +final publicKey = privateKey.publicKey; ``` ecdsa type (uses ECDSA algorithm for signing & ECDH algorithm for encryption): Possible values for curve are -secp256k1, secp384r1, secp521r1, brainpoolp256r1, brainpoolp384r1, brainpoolp512r1 and prime256v1 +secp256k1, secp384r1, secp521r1, brainpoolp256r1, brainpoolp384r1, brainpoolp512r1 ```dart const passphrase = 'secret stuff'; final userID = [name, '($comment)', '<$email>'].join(' '); @@ -156,12 +158,12 @@ final privateKey = OpenPGP.generateKey( [userID], passphrase, type: KeyType.ecc, - curve: CurveInfo.secp521r1, + curve: Ecc.secp521r1, ); -final publicKey = privateKey.toPublic; +final publicKey = privateKey.publicKey; ``` -eddsa type (uses EdDSA algorithm with ed25519 for signing & ECDH algorithm with curve25519 for encryption): +eddsa type (uses EdDSA legacy algorithm with ed25519 for signing & ECDH algorithm with curve25519 for encryption): ```dart const passphrase = 'secret stuff'; final userID = [name, '($comment)', '<$email>'].join(' '); @@ -169,8 +171,33 @@ final privateKey = OpenPGP.generateKey( [userID], passphrase, type: KeyType.ecc, + curve: Ecc.ed25519, +); +final publicKey = privateKey.publicKey; +``` + +Curve25519 key type (uses Ed25519 algorithm for signing & X25519 algorithm for encryption): +```dart +const passphrase = 'secret stuff'; +final userID = [name, '($comment)', '<$email>'].join(' '); +final privateKey = OpenPGP.generateKey( + [userID], + passphrase, + type: KeyType.curve25519, +); +final publicKey = privateKey.publicKey; +``` + +Curve448 key type (uses Ed448 algorithm for signing & X448 algorithm for encryption): +```dart +const passphrase = 'secret stuff'; +final userID = [name, '($comment)', '<$email>'].join(' '); +final privateKey = OpenPGP.generateKey( + [userID], + passphrase, + type: KeyType.curve448, ); -final publicKey = privateKey.toPublic; +final publicKey = privateKey.publicKey; ``` ## Development From d3a51b7d05831bc02f36dad5313edfc251103317 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 13:22:44 +0700 Subject: [PATCH 187/202] WIP Signed-off-by: Nguyen Van Nguyen --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d9ea460c..f1f2873a 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ digital signatures, compression, and key management. [OCB](https://tools.ietf.org/html/rfc7253), [GCM](https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf). * Support hash algorithms: SHA-256, SHA-384, SHA-512, SHA-224, SHA3-256, SHA3-512. -* Support compression algorithms: Zip, Zlib, BZip2. +* Support compression algorithms: Zip, Zlib. * Support [ECC](https://en.wikipedia.org/wiki/Elliptic-curve_cryptography) curves: [secp256r1, secp384r1, secp521r1](https://www.rfc-editor.org/rfc/rfc6090), [brainpoolP256r1, brainpoolP384r1, brainpoolP512r1](https://www.rfc-editor.org/rfc/rfc5639), From 908d201e1c81bb9e5ddd5f176499e5f10a4b9bb6 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 13:53:51 +0700 Subject: [PATCH 188/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base_key.dart | 41 +++++++++++++++++-------------- lib/src/key/user.dart | 6 ++--- lib/src/message/signature.dart | 14 +++++++---- lib/src/message/verification.dart | 8 ++++-- lib/src/type/key.dart | 6 ++--- lib/src/type/user.dart | 4 +-- lib/src/type/verification.dart | 5 +++- 7 files changed, 49 insertions(+), 35 deletions(-) diff --git a/lib/src/key/base_key.dart b/lib/src/key/base_key.dart index 0d774fbb..dc46c2e6 100644 --- a/lib/src/key/base_key.dart +++ b/lib/src/key/base_key.dart @@ -153,42 +153,45 @@ abstract class BaseKey implements KeyInterface { } @override - isCertified({ - final KeyInterface? verifyKey, + isCertified( + final KeyInterface verifyKey, { final SignaturePacketInterface? certificate, final DateTime? time, }) { for (var user in users) { - if (user.isPrimary) { - return user.isCertified( - verifyKey: verifyKey, - certificate: certificate, - time: time, - ); + if (user.isPrimary && + user.isCertified( + verifyKey, + certificate: certificate, + time: time, + )) { + return true; } } return false; } @override - verify([final String userID = '', final DateTime? time]) { - for (final signature in directSignatures) { - if (!signature.verify( - publicKey.keyPacket, - keyPacket.signBytes, - time, - )) { - return false; + verify({final String userID = '', final DateTime? time}) { + if (userID.isEmpty) { + for (final signature in directSignatures) { + if (signature.verify( + publicKey.keyPacket, + keyPacket.signBytes, + time, + )) { + return true; + } } } for (var user in users) { if (userID.isEmpty || user.userID == userID) { - if (!user.verify(time)) { - return false; + if (user.verify(time)) { + return true; } } } - return true; + return false; } _readPacketList(final PacketListInterface packetList) { diff --git a/lib/src/key/user.dart b/lib/src/key/user.dart index 571a9421..0447651f 100644 --- a/lib/src/key/user.dart +++ b/lib/src/key/user.dart @@ -85,14 +85,14 @@ final class User implements UserInterface { } @override - isCertified({ - final KeyInterface? verifyKey, + isCertified( + final KeyInterface verifyKey, { final SignaturePacketInterface? certificate, final DateTime? time, }) { if (otherSignatures.isNotEmpty) { final keyID = certificate?.issuerKeyID; - final keyPacket = verifyKey?.publicKey.keyPacket ?? mainKey.keyPacket; + final keyPacket = verifyKey.publicKey.keyPacket; for (final signature in otherSignatures) { if (keyID == null || signature.issuerKeyID.equals(keyID)) { if (signature.verify( diff --git a/lib/src/message/signature.dart b/lib/src/message/signature.dart index bfbe92ca..5f3ad626 100644 --- a/lib/src/message/signature.dart +++ b/lib/src/message/signature.dart @@ -77,11 +77,15 @@ final class Signature implements SignatureInterface { } verifications.add(Verification( - keyPacket.keyID, - packet, - isVerified, - verificationError, - )); + keyPacket.keyID, + packet, + isVerified, + verificationError, + key.users + .where( + (user) => user.userID.isNotEmpty, + ) + .map((user) => user.userID))); } } } diff --git a/lib/src/message/verification.dart b/lib/src/message/verification.dart index 1e11da8d..64ee2315 100644 --- a/lib/src/message/verification.dart +++ b/lib/src/message/verification.dart @@ -24,10 +24,14 @@ class Verification implements VerificationInterface { @override final String verificationError; + @override + final Iterable userIDs; + Verification( this.keyID, this.signaturePacket, this.isVerified, - this.verificationError, - ); + this.verificationError, [ + this.userIDs = const [], + ]); } diff --git a/lib/src/type/key.dart b/lib/src/type/key.dart index 0921c75f..4c434f03 100644 --- a/lib/src/type/key.dart +++ b/lib/src/type/key.dart @@ -71,13 +71,13 @@ abstract interface class KeyInterface implements ArmorableInterface, PacketConta }); /// Check if the key is certified - bool isCertified({ - final KeyInterface? verifyKey, + bool isCertified( + final KeyInterface verifyKey, { final SignaturePacketInterface? certificate, final DateTime? time, }); /// Verify the key. /// Check for existence and validity of direct & user signature. - bool verify([final String userID = '', final DateTime? time]); + bool verify({final String userID = '', final DateTime? time}); } diff --git a/lib/src/type/user.dart b/lib/src/type/user.dart index ef6a6747..cf23182b 100644 --- a/lib/src/type/user.dart +++ b/lib/src/type/user.dart @@ -38,8 +38,8 @@ abstract interface class UserInterface implements PacketContainerInterface { bool isRevoked([final DateTime? time]); /// Check if the key is certified - bool isCertified({ - final KeyInterface? verifyKey, + bool isCertified( + final KeyInterface verifyKey, { final SignaturePacketInterface? certificate, final DateTime? time, }); diff --git a/lib/src/type/verification.dart b/lib/src/type/verification.dart index 6da811b3..701659ff 100644 --- a/lib/src/type/verification.dart +++ b/lib/src/type/verification.dart @@ -17,9 +17,12 @@ abstract interface class VerificationInterface { /// Get signature packet SignaturePacketInterface get signaturePacket; - /// GGet verification error + /// Get verification error String get verificationError; /// Is verified bool get isVerified; + + /// Return verification user IDs + Iterable get userIDs; } From 7c0ca15d7582e1e26c2180c1af950a03471b3db3 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 13:58:01 +0700 Subject: [PATCH 189/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base_key.dart | 5 ++--- lib/src/key/private_key.dart | 2 +- lib/src/key/public_key.dart | 2 +- lib/src/key/subkey.dart | 2 +- lib/src/key/user.dart | 2 +- lib/src/message/cleartext_message.dart | 2 +- lib/src/message/encrypted_message.dart | 2 +- lib/src/message/literal_message.dart | 2 +- lib/src/message/signature.dart | 2 +- lib/src/message/signed_message.dart | 1 - lib/src/packet/aead_encrypted_data.dart | 2 +- lib/src/packet/{base.dart => base_packet.dart} | 0 lib/src/packet/compressed_data.dart | 2 +- lib/src/packet/literal_data.dart | 2 +- lib/src/packet/marker.dart | 2 +- lib/src/packet/one_pass_signature.dart | 2 +- lib/src/packet/packet_list.dart | 2 +- lib/src/packet/padding.dart | 2 +- lib/src/packet/public_key.dart | 2 +- lib/src/packet/public_key_encrypted_session_key.dart | 2 +- lib/src/packet/secret_key.dart | 2 +- lib/src/packet/signature.dart | 2 +- lib/src/packet/sym_encrypted_data.dart | 2 +- lib/src/packet/sym_encrypted_integrity_protected_data.dart | 2 +- lib/src/packet/sym_encrypted_session_key.dart | 2 +- lib/src/packet/trust.dart | 2 +- lib/src/packet/user_attribute.dart | 2 +- lib/src/packet/user_id.dart | 2 +- test/common/s2k_test.dart | 2 +- test/message/literal_data_test.dart | 2 +- test/packet/encryption_test.dart | 2 +- test/packet/key_packet_test.dart | 2 +- test/packet/signature_test.dart | 2 +- 33 files changed, 32 insertions(+), 34 deletions(-) rename lib/src/packet/{base.dart => base_packet.dart} (100%) diff --git a/lib/src/key/base_key.dart b/lib/src/key/base_key.dart index dc46c2e6..ae876dcb 100644 --- a/lib/src/key/base_key.dart +++ b/lib/src/key/base_key.dart @@ -6,11 +6,10 @@ library; import 'dart:typed_data'; -import 'package:dart_pg/src/packet/signature/features.dart'; - import '../common/helpers.dart'; -import '../packet/base.dart'; +import '../packet/base_packet.dart'; import '../packet/packet_list.dart'; +import '../packet/signature/features.dart'; import '../type/key.dart'; import '../type/key_packet.dart'; import '../type/packet_list.dart'; diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index f3c04d87..d4403b37 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -15,7 +15,7 @@ import '../enum/key_type.dart'; import '../enum/rsa_key_size.dart'; import '../key/public_key.dart'; import '../key/subkey.dart'; -import '../packet/base.dart'; +import '../packet/base_packet.dart'; import '../packet/packet_list.dart'; import '../type/packet.dart'; import '../type/private_key.dart'; diff --git a/lib/src/key/public_key.dart b/lib/src/key/public_key.dart index 812fd554..322bd190 100644 --- a/lib/src/key/public_key.dart +++ b/lib/src/key/public_key.dart @@ -8,7 +8,7 @@ import 'dart:typed_data'; import '../common/armor.dart'; import '../enum/armor_type.dart'; -import '../packet/base.dart'; +import '../packet/base_packet.dart'; import '../packet/packet_list.dart'; import '../type/key.dart'; import 'base_key.dart'; diff --git a/lib/src/key/subkey.dart b/lib/src/key/subkey.dart index 29b571dd..0a8e5eef 100644 --- a/lib/src/key/subkey.dart +++ b/lib/src/key/subkey.dart @@ -6,13 +6,13 @@ library; import 'dart:typed_data'; -import 'base_key.dart'; import '../packet/packet_list.dart'; import '../packet/signature/key_flags.dart'; import '../type/key.dart'; import '../type/signature_packet.dart'; import '../type/subkey.dart'; import '../type/subkey_packet.dart'; +import 'base_key.dart'; /// OpenPGP subkey class /// Author Nguyen Van Nguyen diff --git a/lib/src/key/user.dart b/lib/src/key/user.dart index 0447651f..600e4f4a 100644 --- a/lib/src/key/user.dart +++ b/lib/src/key/user.dart @@ -7,7 +7,7 @@ library; import 'dart:typed_data'; import '../common/helpers.dart'; -import '../packet/base.dart'; +import '../packet/base_packet.dart'; import '../packet/packet_list.dart'; import '../type/key.dart'; import '../type/signature_packet.dart'; diff --git a/lib/src/message/cleartext_message.dart b/lib/src/message/cleartext_message.dart index 18d194f6..a7c1aafd 100644 --- a/lib/src/message/cleartext_message.dart +++ b/lib/src/message/cleartext_message.dart @@ -7,7 +7,7 @@ library; import '../common/helpers.dart'; import '../message/signature.dart'; import '../message/signed_message.dart'; -import '../packet/base.dart'; +import '../packet/base_packet.dart'; import '../type/cleartext_message.dart'; import '../type/key.dart'; import '../type/notation_data.dart'; diff --git a/lib/src/message/encrypted_message.dart b/lib/src/message/encrypted_message.dart index 12d34870..873bfe26 100644 --- a/lib/src/message/encrypted_message.dart +++ b/lib/src/message/encrypted_message.dart @@ -9,7 +9,7 @@ import '../common/helpers.dart'; import '../enum/armor_type.dart'; import '../message/base_message.dart'; import '../message/literal_message.dart'; -import '../packet/base.dart'; +import '../packet/base_packet.dart'; import '../packet/packet_list.dart'; import '../type/encrypted_data_packet.dart'; import '../type/encrypted_message.dart'; diff --git a/lib/src/message/literal_message.dart b/lib/src/message/literal_message.dart index 8c64c56b..d49f48a0 100644 --- a/lib/src/message/literal_message.dart +++ b/lib/src/message/literal_message.dart @@ -16,7 +16,7 @@ import '../enum/symmetric_algorithm.dart'; import '../message/base_message.dart'; import '../message/encrypted_message.dart'; import '../message/signature.dart'; -import '../packet/base.dart'; +import '../packet/base_packet.dart'; import '../packet/key/session_key.dart'; import '../packet/packet_list.dart'; import '../type/key.dart'; diff --git a/lib/src/message/signature.dart b/lib/src/message/signature.dart index 5f3ad626..c27e31aa 100644 --- a/lib/src/message/signature.dart +++ b/lib/src/message/signature.dart @@ -9,7 +9,7 @@ import '../common/helpers.dart'; import '../enum/armor_type.dart'; import '../enum/hash_algorithm.dart'; import '../message/verification.dart'; -import '../packet/base.dart'; +import '../packet/base_packet.dart'; import '../packet/packet_list.dart'; import '../type/cleartext_message.dart'; import '../type/key.dart'; diff --git a/lib/src/message/signed_message.dart b/lib/src/message/signed_message.dart index defc60f1..d4814263 100644 --- a/lib/src/message/signed_message.dart +++ b/lib/src/message/signed_message.dart @@ -12,7 +12,6 @@ import '../type/key.dart'; import '../type/signature.dart'; import '../type/signature_packet.dart'; import '../type/signed_cleartext_message.dart'; - import 'cleartext_message.dart'; /// Signed message class that represents an OpenPGP cleartext signed message. diff --git a/lib/src/packet/aead_encrypted_data.dart b/lib/src/packet/aead_encrypted_data.dart index 71c817be..fbf0d7b4 100644 --- a/lib/src/packet/aead_encrypted_data.dart +++ b/lib/src/packet/aead_encrypted_data.dart @@ -11,7 +11,7 @@ import '../enum/aead_algorithm.dart'; import '../enum/symmetric_algorithm.dart'; import '../type/encrypted_data_packet.dart'; import '../type/packet_list.dart'; -import 'base.dart'; +import 'base_packet.dart'; import 'packet_list.dart'; /// Implementation of the Symmetrically Encrypted Authenticated Encryption with diff --git a/lib/src/packet/base.dart b/lib/src/packet/base_packet.dart similarity index 100% rename from lib/src/packet/base.dart rename to lib/src/packet/base_packet.dart diff --git a/lib/src/packet/compressed_data.dart b/lib/src/packet/compressed_data.dart index 38e05dc9..67a252fd 100644 --- a/lib/src/packet/compressed_data.dart +++ b/lib/src/packet/compressed_data.dart @@ -9,7 +9,7 @@ import 'dart:typed_data'; import '../enum/compression_algorithm.dart'; import '../type/packet_list.dart'; -import 'base.dart'; +import 'base_packet.dart'; import 'packet_list.dart'; /// Implementation of the Compressed Data (COMP) Packet - Type 11 diff --git a/lib/src/packet/literal_data.dart b/lib/src/packet/literal_data.dart index e1a2050a..e5859426 100644 --- a/lib/src/packet/literal_data.dart +++ b/lib/src/packet/literal_data.dart @@ -10,7 +10,7 @@ import 'dart:typed_data'; import '../common/helpers.dart'; import '../enum/literal_format.dart'; import '../type/literal_data.dart'; -import 'base.dart'; +import 'base_packet.dart'; /// Implementation of the Literal Data (LIT) Packet - Type 11 /// Author Nguyen Van Nguyen diff --git a/lib/src/packet/marker.dart b/lib/src/packet/marker.dart index d23fcd64..2a397818 100644 --- a/lib/src/packet/marker.dart +++ b/lib/src/packet/marker.dart @@ -6,7 +6,7 @@ library; import 'dart:convert'; -import 'base.dart'; +import 'base_packet.dart'; /// Implementation of the Marker (MARKER) Packet - Type 10 /// Author Nguyen Van Nguyen diff --git a/lib/src/packet/one_pass_signature.dart b/lib/src/packet/one_pass_signature.dart index 29624356..ba7674a4 100644 --- a/lib/src/packet/one_pass_signature.dart +++ b/lib/src/packet/one_pass_signature.dart @@ -10,7 +10,7 @@ import '../enum/hash_algorithm.dart'; import '../enum/key_algorithm.dart'; import '../enum/signature_type.dart'; import '../type/signature_packet.dart'; -import 'base.dart'; +import 'base_packet.dart'; /// Implementation an OpenPGP One-Pass (OPS) Signature Packet - Type 4. /// Author Nguyen Van Nguyen diff --git a/lib/src/packet/packet_list.dart b/lib/src/packet/packet_list.dart index e84bce42..9a27b636 100644 --- a/lib/src/packet/packet_list.dart +++ b/lib/src/packet/packet_list.dart @@ -9,7 +9,7 @@ import 'dart:typed_data'; import '../type/packet.dart'; import '../type/packet_list.dart'; -import 'base.dart'; +import 'base_packet.dart'; /// This class represents a list of OpenPGP packets. /// Author Nguyen Van Nguyen diff --git a/lib/src/packet/padding.dart b/lib/src/packet/padding.dart index f5cb2a2d..15863ada 100644 --- a/lib/src/packet/padding.dart +++ b/lib/src/packet/padding.dart @@ -7,7 +7,7 @@ library; import 'dart:typed_data'; import '../common/helpers.dart'; -import 'base.dart'; +import 'base_packet.dart'; /// Implementation of the Padding (PADDING) Packet - Type 21. /// Author Nguyen Van Nguyen diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index ee4630bc..6edad936 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -18,7 +18,7 @@ import '../type/key_material.dart'; import '../type/key_packet.dart'; import '../type/subkey_packet.dart'; import 'key/public_material.dart'; -import 'base.dart'; +import 'base_packet.dart'; /// Implementation of the Public Key (PUBKEY) Packet - Type 6 /// Author Nguyen Van Nguyen diff --git a/lib/src/packet/public_key_encrypted_session_key.dart b/lib/src/packet/public_key_encrypted_session_key.dart index 5f237e74..0a817f17 100644 --- a/lib/src/packet/public_key_encrypted_session_key.dart +++ b/lib/src/packet/public_key_encrypted_session_key.dart @@ -12,7 +12,7 @@ import '../enum/montgomery_curve.dart'; import '../type/secret_key_packet.dart'; import '../type/session_key.dart'; import '../type/session_key_cryptor.dart'; -import 'base.dart'; +import 'base_packet.dart'; import 'key/public_material.dart'; import 'key/session_key_cryptor.dart'; diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 78338f32..6a6c9509 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -29,7 +29,7 @@ import '../type/secret_key_packet.dart'; import '../type/subkey_packet.dart'; import 'key/public_material.dart'; import 'key/secret_material.dart'; -import 'base.dart'; +import 'base_packet.dart'; /// Implementation of the Secret Key (SECKEY) Packet - Type 5 /// Author Nguyen Van Nguyen diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 06da7d14..db675d8e 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -29,7 +29,7 @@ import '../type/subkey_packet.dart'; import '../type/subpacket.dart'; import '../type/user_id_packet.dart'; import '../type/verification_key_material.dart'; -import 'base.dart'; +import 'base_packet.dart'; import 'signature_subpacket.dart'; import 'subpacket_reader.dart'; diff --git a/lib/src/packet/sym_encrypted_data.dart b/lib/src/packet/sym_encrypted_data.dart index 984b7f1e..fe4da98a 100644 --- a/lib/src/packet/sym_encrypted_data.dart +++ b/lib/src/packet/sym_encrypted_data.dart @@ -13,7 +13,7 @@ import '../common/config.dart'; import '../common/helpers.dart'; import '../cryptor/symmetric/buffered_cipher.dart'; import '../enum/symmetric_algorithm.dart'; -import 'base.dart'; +import 'base_packet.dart'; import 'packet_list.dart'; /// Implementation of the Symmetrically Encrypted Data (SED) Packet - Type 9 diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index 8887e9f9..fe1acd9a 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -15,7 +15,7 @@ import '../enum/hash_algorithm.dart'; import '../enum/symmetric_algorithm.dart'; import '../type/encrypted_data_packet.dart'; import '../type/packet_list.dart'; -import 'base.dart'; +import 'base_packet.dart'; import 'packet_list.dart'; /// Implementation of the Sym. Encrypted Integrity Protected Data (SEIPD) Packet - Type 18 diff --git a/lib/src/packet/sym_encrypted_session_key.dart b/lib/src/packet/sym_encrypted_session_key.dart index 347fa4ef..639efb4c 100644 --- a/lib/src/packet/sym_encrypted_session_key.dart +++ b/lib/src/packet/sym_encrypted_session_key.dart @@ -16,7 +16,7 @@ import '../enum/s2k_type.dart'; import '../enum/symmetric_algorithm.dart'; import '../type/s2k.dart'; import '../type/session_key.dart'; -import 'base.dart'; +import 'base_packet.dart'; import 'key/session_key.dart'; /// Implementation of the Symmetric Key Encrypted Session Key (SKESK) Packet - Type 3 diff --git a/lib/src/packet/trust.dart b/lib/src/packet/trust.dart index 83857723..c87160b9 100644 --- a/lib/src/packet/trust.dart +++ b/lib/src/packet/trust.dart @@ -6,7 +6,7 @@ library; import 'dart:typed_data'; -import 'base.dart'; +import 'base_packet.dart'; /// Implementation of the Trust (TRUST) Packet - Type 12 /// Author Nguyen Van Nguyen diff --git a/lib/src/packet/user_attribute.dart b/lib/src/packet/user_attribute.dart index 6822fbe1..2298fa25 100644 --- a/lib/src/packet/user_attribute.dart +++ b/lib/src/packet/user_attribute.dart @@ -8,7 +8,7 @@ import 'dart:typed_data'; import '../type/user_id_packet.dart'; import '../common/extensions.dart'; -import 'base.dart'; +import 'base_packet.dart'; import 'image_user_attribute.dart'; import 'subpacket_reader.dart'; import 'user_attribute_subpacket.dart'; diff --git a/lib/src/packet/user_id.dart b/lib/src/packet/user_id.dart index 21a6d10d..c032e976 100644 --- a/lib/src/packet/user_id.dart +++ b/lib/src/packet/user_id.dart @@ -9,7 +9,7 @@ import 'dart:typed_data'; import '../common/helpers.dart'; import '../type/user_id_packet.dart'; -import 'base.dart'; +import 'base_packet.dart'; /// Implementation of the User ID (UID) Packet - Type 13 /// Author Nguyen Van Nguyen diff --git a/test/common/s2k_test.dart b/test/common/s2k_test.dart index 4bdd99db..ca0421d4 100644 --- a/test/common/s2k_test.dart +++ b/test/common/s2k_test.dart @@ -7,7 +7,7 @@ import 'package:dart_pg/src/common/generic_s2k.dart'; import 'package:dart_pg/src/enum/hash_algorithm.dart'; import 'package:dart_pg/src/enum/s2k_type.dart'; import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; -import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/base_packet.dart'; import 'package:dart_pg/src/packet/packet_list.dart'; import 'package:test/test.dart'; diff --git a/test/message/literal_data_test.dart b/test/message/literal_data_test.dart index 520ef959..623b6767 100644 --- a/test/message/literal_data_test.dart +++ b/test/message/literal_data_test.dart @@ -5,7 +5,7 @@ import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/enum/compression_algorithm.dart'; import 'package:dart_pg/src/enum/preset_rfc.dart'; import 'package:dart_pg/src/openpgp.dart'; -import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/base_packet.dart'; import 'package:test/test.dart'; import '../data/key_data.dart'; diff --git a/test/packet/encryption_test.dart b/test/packet/encryption_test.dart index afc96b58..b3b64e21 100644 --- a/test/packet/encryption_test.dart +++ b/test/packet/encryption_test.dart @@ -6,7 +6,7 @@ import 'package:dart_pg/src/common/helpers.dart'; import 'package:dart_pg/src/enum/aead_algorithm.dart'; import 'package:dart_pg/src/enum/key_algorithm.dart'; import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; -import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/base_packet.dart'; import 'package:dart_pg/src/packet/key/session_key.dart'; import 'package:dart_pg/src/packet/packet_list.dart'; import 'package:dart_pg/src/type/literal_data.dart'; diff --git a/test/packet/key_packet_test.dart b/test/packet/key_packet_test.dart index 5f069fa6..3baa4528 100644 --- a/test/packet/key_packet_test.dart +++ b/test/packet/key_packet_test.dart @@ -5,7 +5,7 @@ import 'package:dart_pg/src/enum/aead_algorithm.dart'; import 'package:dart_pg/src/enum/ecc.dart'; import 'package:dart_pg/src/enum/key_algorithm.dart'; import 'package:dart_pg/src/enum/symmetric_algorithm.dart'; -import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/base_packet.dart'; import 'package:dart_pg/src/packet/key/public_material.dart'; import 'package:test/test.dart'; diff --git a/test/packet/signature_test.dart b/test/packet/signature_test.dart index 68f8a518..0af368fe 100644 --- a/test/packet/signature_test.dart +++ b/test/packet/signature_test.dart @@ -6,7 +6,7 @@ import 'package:dart_pg/src/enum/key_flag.dart'; import 'package:dart_pg/src/enum/signature_subpacket_type.dart'; import 'package:dart_pg/src/enum/signature_type.dart'; import 'package:dart_pg/src/enum/support_feature.dart'; -import 'package:dart_pg/src/packet/base.dart'; +import 'package:dart_pg/src/packet/base_packet.dart'; import 'package:dart_pg/src/packet/signature_subpacket.dart'; import 'package:dart_pg/src/packet/subpacket_reader.dart'; import 'package:test/test.dart'; From a0cc65234d3ec6338cd6aaed64591660c0678fca Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 14:01:21 +0700 Subject: [PATCH 190/202] WIP Signed-off-by: Nguyen Van Nguyen --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index acf0dc4b..129461db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,3 +61,12 @@ ## 1.5.2 (2024-11-15) - Fix encode SKESK packet to bytes + +## 1.5.3 (2024-11-18) +- Refactor AEAD crypt + +## 1.5.4 (2024-11-27) +- Fix ecdh ephemeral key for curve 25519 + +## 1.5.5 (2024-12-6) +- Fix read public key list ignore last index From bb8eb14145fd17176b9f7aff557372ce6adf17ed Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 14:10:12 +0700 Subject: [PATCH 191/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/session_key.dart | 12 +++++++++++- lib/src/type/session_key.dart | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/src/packet/key/session_key.dart b/lib/src/packet/key/session_key.dart index d2a73d79..8dce52d4 100644 --- a/lib/src/packet/key/session_key.dart +++ b/lib/src/packet/key/session_key.dart @@ -6,6 +6,7 @@ library; import 'dart:typed_data'; +import '../../enum/aead_algorithm.dart'; import '../../type/session_key.dart'; import '../../common/helpers.dart'; import '../../enum/symmetric_algorithm.dart'; @@ -19,14 +20,23 @@ class SessionKey implements SessionKeyInterface { @override final Uint8List encryptionKey; - SessionKey(this.encryptionKey, [this.symmetric = SymmetricAlgorithm.aes128]); + @override + final AeadAlgorithm? aead; + + SessionKey( + this.encryptionKey, [ + this.symmetric = SymmetricAlgorithm.aes128, + this.aead, + ]); factory SessionKey.produceKey([ final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + final AeadAlgorithm? aead, ]) => SessionKey( Helper.generateEncryptionKey(symmetric), symmetric, + aead, ); factory SessionKey.fromBytes(final Uint8List data) { diff --git a/lib/src/type/session_key.dart b/lib/src/type/session_key.dart index bbea5977..93e443e2 100644 --- a/lib/src/type/session_key.dart +++ b/lib/src/type/session_key.dart @@ -6,6 +6,7 @@ library; import 'dart:typed_data'; +import '../enum/aead_algorithm.dart'; import '../enum/symmetric_algorithm.dart'; /// Session key interface @@ -17,6 +18,9 @@ abstract interface class SessionKeyInterface { /// Get algorithm to encrypt the message with SymmetricAlgorithm get symmetric; + /// Get aead algorithm + AeadAlgorithm? get aead; + /// Checksum the encryption key void checksum(final Uint8List checksum); From ddac3941e390dcb70d5344cab5168a06caae8d14 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Mon, 9 Dec 2024 17:21:53 +0700 Subject: [PATCH 192/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/key/base_key.dart | 77 ++++++++++++++++--- lib/src/message/base_message.dart | 29 +++++++ lib/src/message/literal_message.dart | 14 +--- lib/src/packet/aead_encrypted_data.dart | 19 ++--- .../signature/preferred_aead_ciphers.dart | 34 +++++++- .../preferred_symmetric_algorithms.dart | 8 +- ...ym_encrypted_integrity_protected_data.dart | 8 +- lib/src/packet/sym_encrypted_session_key.dart | 4 +- lib/src/type/key.dart | 11 +++ test/packet/encryption_test.dart | 3 - 10 files changed, 165 insertions(+), 42 deletions(-) diff --git a/lib/src/key/base_key.dart b/lib/src/key/base_key.dart index ae876dcb..05d89983 100644 --- a/lib/src/key/base_key.dart +++ b/lib/src/key/base_key.dart @@ -7,9 +7,13 @@ library; import 'dart:typed_data'; import '../common/helpers.dart'; +import '../enum/aead_algorithm.dart'; +import '../enum/symmetric_algorithm.dart'; import '../packet/base_packet.dart'; import '../packet/packet_list.dart'; import '../packet/signature/features.dart'; +import '../packet/signature/preferred_aead_ciphers.dart'; +import '../packet/signature/preferred_symmetric_algorithms.dart'; import '../type/key.dart'; import '../type/key_packet.dart'; import '../type/packet_list.dart'; @@ -79,8 +83,16 @@ abstract class BaseKey implements KeyInterface { keyPacket, ...revocationSignatures, ...directSignatures, - ...users.map((user) => user.packetList).expand((packet) => packet), - ...subkeys.map((subkey) => subkey.packetList).expand((packet) => packet), + ...users + .map( + (user) => user.packetList, + ) + .expand((packet) => packet), + ...subkeys + .map( + (subkey) => subkey.packetList, + ) + .expand((packet) => packet), ...keyPacket.isV6Key ? [ PaddingPacket.createPadding( @@ -96,16 +108,16 @@ abstract class BaseKey implements KeyInterface { @override bool get aeadSupported { for (final signature in directSignatures) { - final features = signature.getSubpacket(); - if (features != null && features.supportVersion2SEIPD) { + final subpacket = signature.getSubpacket(); + if (subpacket?.supportVersion2SEIPD ?? false) { return true; } } for (final user in users) { - if (user.isPrimary) { + if (user.isPrimary && !user.isRevoked()) { for (final signature in user.selfSignatures) { - final features = signature.getSubpacket(); - if (features != null && features.supportVersion2SEIPD) { + final subpacket = signature.getSubpacket(); + if (subpacket?.supportVersion2SEIPD ?? false) { return true; } } @@ -114,6 +126,41 @@ abstract class BaseKey implements KeyInterface { return false; } + @override + get preferredSymmetrics { + for (final signature in directSignatures) { + final subpacket = signature.getSubpacket(); + if (subpacket?.preferences.isNotEmpty ?? false) { + return subpacket!.preferences; + } + } + for (final user in users) { + if (user.isPrimary && !user.isRevoked()) { + for (final signature in user.selfSignatures) { + final subpacket = signature.getSubpacket(); + if (subpacket?.preferences.isNotEmpty ?? false) { + return subpacket!.preferences; + } + } + } + } + return []; + } + + @override + isPreferredAeadCiphers([ + final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + final AeadAlgorithm aead = AeadAlgorithm.gcm, + ]) { + for (final signature in directSignatures) { + final subpacket = signature.getSubpacket(); + if (subpacket?.isPreferred(symmetric, aead) ?? false) { + return true; + } + } + return false; + } + @override getEncryptionKeyPacket([final Uint8List? keyID]) { subkeys.sort( @@ -123,7 +170,7 @@ abstract class BaseKey implements KeyInterface { ); for (final subkey in subkeys) { if (keyID == null || subkey.keyID.equals(keyID)) { - if (subkey.isEncryptionKey) { + if (subkey.isEncryptionKey && !subkey.isRevoked()) { return subkey.keyPacket; } } @@ -182,13 +229,21 @@ abstract class BaseKey implements KeyInterface { return true; } } - } - for (var user in users) { - if (userID.isEmpty || user.userID == userID) { + for (var user in users) { if (user.verify(time)) { return true; } } + } else { + for (var user in users) { + if (user.userID == userID) { + if (user.isRevoked(time)) { + return false; + } else if (user.verify(time)) { + return true; + } + } + } } return false; } diff --git a/lib/src/message/base_message.dart b/lib/src/message/base_message.dart index e7376f4b..0d168d99 100644 --- a/lib/src/message/base_message.dart +++ b/lib/src/message/base_message.dart @@ -5,10 +5,15 @@ library; import '../common/armor.dart'; +import '../common/config.dart'; import '../enum/armor_type.dart'; +import '../enum/symmetric_algorithm.dart'; +import '../packet/key/session_key.dart'; import '../type/armorable.dart'; +import '../type/key.dart'; import '../type/packet_container.dart'; import '../type/packet_list.dart'; +import '../type/session_key.dart'; /// Base abstract OpenPGP message class /// Author Nguyen Van Nguyen @@ -20,4 +25,28 @@ abstract class BaseMessage implements ArmorableInterface, PacketContainerInterfa @override armor() => Armor.encode(ArmorType.message, packetList.encode()); + + static SessionKeyInterface generateSessionKey( + final Iterable encryptionKeys, [ + final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + ]) { + var aeadProtect = Config.aeadProtect; + final aead = Config.preferredAead; + for (final key in encryptionKeys) { + if (key.aeadSupported) { + if (!key.isPreferredAeadCiphers(symmetric, aead)) { + throw StateError('Symmetric and aead not compatible with the given `encryptionKeys`'); + } + } else { + if (key.preferredSymmetrics.isNotEmpty && !key.preferredSymmetrics.contains(symmetric)) { + throw StateError('Symmetric not compatible with the given `encryptionKeys`'); + } + aeadProtect = false; + } + } + return SessionKey.produceKey( + symmetric, + aeadProtect ? aead : null, + ); + } } diff --git a/lib/src/message/literal_message.dart b/lib/src/message/literal_message.dart index d49f48a0..905cefe5 100644 --- a/lib/src/message/literal_message.dart +++ b/lib/src/message/literal_message.dart @@ -17,7 +17,6 @@ import '../message/base_message.dart'; import '../message/encrypted_message.dart'; import '../message/signature.dart'; import '../packet/base_packet.dart'; -import '../packet/key/session_key.dart'; import '../packet/packet_list.dart'; import '../type/key.dart'; import '../type/literal_data.dart'; @@ -95,16 +94,13 @@ final class LiteralMessage extends BaseMessage implements LiteralMessageInterfac throw ArgumentError('No encryption keys or passwords provided.'); } var addPadding = Config.presetRfc == PresetRfc.rfc9580; - var aeadSupported = Config.aeadSupported; for (final key in encryptionKeys) { - if (!key.aeadSupported) { - aeadSupported = false; - } if (!key.keyPacket.isV6Key) { addPadding = false; } } - final sessionKey = SessionKey.produceKey( + final sessionKey = BaseMessage.generateSessionKey( + encryptionKeys, symmetric ?? Config.preferredSymmetric, ); @@ -131,15 +127,13 @@ final class LiteralMessage extends BaseMessage implements LiteralMessageInterfac password, sessionKey: sessionKey, symmetric: symmetric ?? Config.preferredSymmetric, - aead: Config.preferredAead, - aeadProtect: aeadSupported && Config.aeadProtect, + aead: sessionKey.aead, )), SymEncryptedIntegrityProtectedDataPacket.encryptPackets( sessionKey.encryptionKey, packetList, symmetric: symmetric ?? Config.preferredSymmetric, - aead: Config.preferredAead, - aeadProtect: aeadSupported && Config.aeadProtect, + aead: sessionKey.aead, ), ])); } diff --git a/lib/src/packet/aead_encrypted_data.dart b/lib/src/packet/aead_encrypted_data.dart index fbf0d7b4..844b5200 100644 --- a/lib/src/packet/aead_encrypted_data.dart +++ b/lib/src/packet/aead_encrypted_data.dart @@ -143,10 +143,10 @@ class AeadEncryptedDataPacket extends BasePacket implements EncryptedDataPacketI /// En/decrypt the payload. static Uint8List _crypt( - bool forEncryption, - Uint8List key, - Uint8List data, { - Uint8List? finalChunk, + final bool forEncryption, + final Uint8List key, + final Uint8List data, { + final Uint8List? finalChunk, final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, AeadAlgorithm aead = AeadAlgorithm.gcm, final chunkSizeByte = 0, @@ -174,27 +174,28 @@ class AeadEncryptedDataPacket extends BasePacket implements EncryptedDataPacketI final crypted = Uint8List( processed + (forEncryption ? aead.tagLength : 0), ); - for (var chunkIndex = 0; chunkIndex == 0 || data.isNotEmpty;) { + var chunkData = data; + for (var chunkIndex = 0; chunkIndex == 0 || chunkData.isNotEmpty;) { /// We take a chunk of data, en/decrypt it, /// and shift `data` to the next chunk. final chunkIndexData = adataBuffer.sublist(5, 13); - final size = chunkSize < data.length ? chunkSize : data.length; + final size = chunkSize < chunkData.length ? chunkSize : chunkData.length; crypted.setAll( chunkIndex * size, forEncryption ? cipher.encrypt( - data.sublist(0, size), + chunkData.sublist(0, size), cipher.getNonce(iv ?? Uint8List(aead.ivLength), chunkIndexData), adataBuffer, ) : cipher.decrypt( - data.sublist(0, size), + chunkData.sublist(0, size), cipher.getNonce(iv ?? Uint8List(aead.ivLength), chunkIndexData), adataBuffer, ), ); - data = data.sublist(size); + chunkData = chunkData.sublist(size); adataBuffer.setAll(9, (++chunkIndex).pack32()); } diff --git a/lib/src/packet/signature/preferred_aead_ciphers.dart b/lib/src/packet/signature/preferred_aead_ciphers.dart index 19501d9d..e71650ce 100644 --- a/lib/src/packet/signature/preferred_aead_ciphers.dart +++ b/lib/src/packet/signature/preferred_aead_ciphers.dart @@ -6,11 +6,12 @@ library; import 'dart:typed_data'; +import '../../enum/aead_algorithm.dart'; import '../../enum/signature_subpacket_type.dart'; +import '../../enum/symmetric_algorithm.dart'; import '../signature_subpacket.dart'; -/// The IntendedRecipientFingerprint sub-packet class -/// Giving the intended recipient fingerprint. +/// The PreferredAeadCiphers sub-packet class /// Author Nguyen Van Nguyen class PreferredAeadCiphers extends SignatureSubpacket { PreferredAeadCiphers( @@ -21,4 +22,33 @@ class PreferredAeadCiphers extends SignatureSubpacket { SignatureSubpacketType.preferredAeadCiphers, data, ); + + bool isPreferred( + final SymmetricAlgorithm symmetric, + final AeadAlgorithm aead, + ) { + if (data.isEmpty) { + return true; + } else { + const chunkSize = 2; + var data = this.data; + while (data.isNotEmpty) { + final size = chunkSize < data.length ? chunkSize : data.length; + final ciphers = data.sublist(0, size); + if (ciphers.elementAtOrNull(1) != null) { + final preferredSymmetric = SymmetricAlgorithm.values.firstWhere( + (alg) => alg.value == ciphers.elementAt(0), + ); + final preferredAead = AeadAlgorithm.values.firstWhere( + (alg) => alg.value == ciphers.elementAt(1), + ); + if (symmetric == preferredSymmetric && aead == preferredAead) { + return true; + } + } + data = data.sublist(size); + } + return false; + } + } } diff --git a/lib/src/packet/signature/preferred_symmetric_algorithms.dart b/lib/src/packet/signature/preferred_symmetric_algorithms.dart index ba09daeb..d2caff93 100644 --- a/lib/src/packet/signature/preferred_symmetric_algorithms.dart +++ b/lib/src/packet/signature/preferred_symmetric_algorithms.dart @@ -21,5 +21,11 @@ class PreferredSymmetricAlgorithms extends SignatureSubpacket { }) : super(SignatureSubpacketType.preferredSymmetricAlgorithms, data); List get preferences => - data.map((pref) => SymmetricAlgorithm.values.firstWhere((alg) => alg.value == pref)).toList(growable: false); + data + .map( + (pref) => SymmetricAlgorithm.values.firstWhere( + (alg) => alg.value == pref, + ), + ) + .toList(growable: false); } diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index fe1acd9a..f7bfce1c 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -130,11 +130,11 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc final Uint8List key, final PacketListInterface packets, { final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - final AeadAlgorithm aead = AeadAlgorithm.gcm, - final bool aeadProtect = false, + final AeadAlgorithm? aead, }) { Helper.assertSymmetric(symmetric); + final aeadProtect = aead != null; final version = aeadProtect ? 2 : 1; final salt = aeadProtect ? Helper.randomBytes(saltSize) : null; final chunkSize = aeadProtect ? Config.aeadChunkSize : 0; @@ -261,8 +261,8 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc final Uint8List data, { final Uint8List? finalChunk, final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - AeadAlgorithm aead = AeadAlgorithm.gcm, - chunkSizeByte = 0, + final AeadAlgorithm aead = AeadAlgorithm.gcm, + final chunkSizeByte = 0, final Uint8List? salt, }) { final dataLength = data.length; diff --git a/lib/src/packet/sym_encrypted_session_key.dart b/lib/src/packet/sym_encrypted_session_key.dart index 639efb4c..3f5f5a1f 100644 --- a/lib/src/packet/sym_encrypted_session_key.dart +++ b/lib/src/packet/sym_encrypted_session_key.dart @@ -126,11 +126,11 @@ class SymEncryptedSessionKeyPacket extends BasePacket { final String password, { final SessionKeyInterface? sessionKey, final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - final AeadAlgorithm aead = AeadAlgorithm.gcm, - final bool aeadProtect = false, + final AeadAlgorithm? aead, }) { Helper.assertSymmetric(symmetric); + final aeadProtect = aead != null; final version = aeadProtect && sessionKey != null ? 6 : 4; final s2k = version == 6 ? Helper.stringToKey( diff --git a/lib/src/type/key.dart b/lib/src/type/key.dart index 4c434f03..5d7e9c37 100644 --- a/lib/src/type/key.dart +++ b/lib/src/type/key.dart @@ -6,7 +6,9 @@ library; import 'dart:typed_data'; +import '../enum/aead_algorithm.dart'; import '../enum/key_algorithm.dart'; +import '../enum/symmetric_algorithm.dart'; import 'armorable.dart'; import 'key_packet.dart'; import 'packet_container.dart'; @@ -48,6 +50,9 @@ abstract interface class KeyInterface implements ArmorableInterface, PacketConta /// Return aead supported bool get aeadSupported; + /// Return preferred symmetrics + List get preferredSymmetrics; + /// Get revocation signatures List get revocationSignatures; @@ -60,6 +65,12 @@ abstract interface class KeyInterface implements ArmorableInterface, PacketConta /// Get subkeys List get subkeys; + /// Return aead ciphers is preferred + bool isPreferredAeadCiphers([ + final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + final AeadAlgorithm aead = AeadAlgorithm.gcm, + ]); + /// Get encryption key packet KeyPacketInterface? getEncryptionKeyPacket([final Uint8List? keyID]); diff --git a/test/packet/encryption_test.dart b/test/packet/encryption_test.dart index b3b64e21..d607767f 100644 --- a/test/packet/encryption_test.dart +++ b/test/packet/encryption_test.dart @@ -241,7 +241,6 @@ void main() { packets, symmetric: SymmetricAlgorithm.aes128, aead: AeadAlgorithm.gcm, - aeadProtect: true, ); expect(encrypted.version, 2); expect(encrypted.symmetric, SymmetricAlgorithm.aes128); @@ -317,14 +316,12 @@ void main() { sessionKey: sessionKey, symmetric: SymmetricAlgorithm.aes256, aead: Config.preferredAead, - aeadProtect: true, ); final seipd = SymEncryptedIntegrityProtectedDataPacket.encryptPackets( sessionKey.encryptionKey, PacketList([LiteralDataPacket.fromText(literalText)]), symmetric: sessionKey.symmetric, aead: Config.preferredAead, - aeadProtect: true, ); expect(skesk.version, 6); expect(skesk.symmetric, SymmetricAlgorithm.aes256); From b701127361474aebb964d54238abe5a4715c8ae8 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 10 Dec 2024 09:07:44 +0700 Subject: [PATCH 193/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/common/armor.dart | 4 +-- lib/src/key/base_key.dart | 8 +++-- lib/src/key/private_key.dart | 2 -- lib/src/key/subkey.dart | 1 - lib/src/key/user.dart | 1 - lib/src/message/base_message.dart | 36 ++++--------------- lib/src/message/cleartext_message.dart | 1 - lib/src/message/encrypted_message.dart | 1 - lib/src/message/literal_message.dart | 33 ++++++++++++++--- lib/src/openpgp.dart | 18 +++++----- lib/src/packet/public_key.dart | 2 -- .../public_key_encrypted_session_key.dart | 1 - lib/src/packet/signature.dart | 3 -- lib/src/type/cleartext_message.dart | 1 - lib/src/type/key.dart | 4 +++ lib/src/type/key_packet.dart | 6 ++++ lib/src/type/literal_message.dart | 1 - lib/src/type/secret_key_packet.dart | 1 - 18 files changed, 63 insertions(+), 61 deletions(-) diff --git a/lib/src/common/armor.dart b/lib/src/common/armor.dart index 4e159ab8..7920499f 100644 --- a/lib/src/common/armor.dart +++ b/lib/src/common/armor.dart @@ -106,7 +106,7 @@ class Armor { final data = base64.decode(dataLines.join().trim()); if ((checksum != _crc24Checksum(data)) && (checksum.isNotEmpty || Config.checksumRequired)) { - throw StateError('Ascii armor integrity check failed'); + throw AssertionError('Ascii armor integrity check failed'); } return Armor( @@ -120,7 +120,7 @@ class Armor { /// Assert armor type Armor assertType(ArmorType type) { if (type != this.type) { - throw ArgumentError('Armored text not of ${type.name} type.'); + throw AssertionError('Armored text not of ${type.name} type.'); } return this; } diff --git a/lib/src/key/base_key.dart b/lib/src/key/base_key.dart index 05d89983..57954733 100644 --- a/lib/src/key/base_key.dart +++ b/lib/src/key/base_key.dart @@ -18,13 +18,15 @@ import '../type/key.dart'; import '../type/key_packet.dart'; import '../type/packet_list.dart'; import '../type/signature_packet.dart'; -import '../type/subkey.dart'; -import '../type/subkey_packet.dart'; -import '../type/user.dart'; import '../type/user_id_packet.dart'; import 'subkey.dart'; import 'user.dart'; +export 'private_key.dart'; +export 'public_key.dart'; +export 'subkey.dart'; +export 'user.dart'; + /// Base abstract OpenPGP key class /// Author Nguyen Van Nguyen abstract class BaseKey implements KeyInterface { diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index d4403b37..3d2b70d3 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -13,8 +13,6 @@ import '../enum/ecc.dart'; import '../enum/key_algorithm.dart'; import '../enum/key_type.dart'; import '../enum/rsa_key_size.dart'; -import '../key/public_key.dart'; -import '../key/subkey.dart'; import '../packet/base_packet.dart'; import '../packet/packet_list.dart'; import '../type/packet.dart'; diff --git a/lib/src/key/subkey.dart b/lib/src/key/subkey.dart index 0a8e5eef..65246187 100644 --- a/lib/src/key/subkey.dart +++ b/lib/src/key/subkey.dart @@ -10,7 +10,6 @@ import '../packet/packet_list.dart'; import '../packet/signature/key_flags.dart'; import '../type/key.dart'; import '../type/signature_packet.dart'; -import '../type/subkey.dart'; import '../type/subkey_packet.dart'; import 'base_key.dart'; diff --git a/lib/src/key/user.dart b/lib/src/key/user.dart index 600e4f4a..86094476 100644 --- a/lib/src/key/user.dart +++ b/lib/src/key/user.dart @@ -11,7 +11,6 @@ import '../packet/base_packet.dart'; import '../packet/packet_list.dart'; import '../type/key.dart'; import '../type/signature_packet.dart'; -import '../type/user.dart'; import '../type/user_id_packet.dart'; /// OpenPGP user class diff --git a/lib/src/message/base_message.dart b/lib/src/message/base_message.dart index 0d168d99..7a10197b 100644 --- a/lib/src/message/base_message.dart +++ b/lib/src/message/base_message.dart @@ -5,15 +5,17 @@ library; import '../common/armor.dart'; -import '../common/config.dart'; import '../enum/armor_type.dart'; -import '../enum/symmetric_algorithm.dart'; -import '../packet/key/session_key.dart'; import '../type/armorable.dart'; -import '../type/key.dart'; import '../type/packet_container.dart'; import '../type/packet_list.dart'; -import '../type/session_key.dart'; + +export 'cleartext_message.dart'; +export 'encrypted_message.dart'; +export 'literal_message.dart'; +export 'signature.dart'; +export 'signed_message.dart'; +export 'verification.dart'; /// Base abstract OpenPGP message class /// Author Nguyen Van Nguyen @@ -25,28 +27,4 @@ abstract class BaseMessage implements ArmorableInterface, PacketContainerInterfa @override armor() => Armor.encode(ArmorType.message, packetList.encode()); - - static SessionKeyInterface generateSessionKey( - final Iterable encryptionKeys, [ - final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, - ]) { - var aeadProtect = Config.aeadProtect; - final aead = Config.preferredAead; - for (final key in encryptionKeys) { - if (key.aeadSupported) { - if (!key.isPreferredAeadCiphers(symmetric, aead)) { - throw StateError('Symmetric and aead not compatible with the given `encryptionKeys`'); - } - } else { - if (key.preferredSymmetrics.isNotEmpty && !key.preferredSymmetrics.contains(symmetric)) { - throw StateError('Symmetric not compatible with the given `encryptionKeys`'); - } - aeadProtect = false; - } - } - return SessionKey.produceKey( - symmetric, - aeadProtect ? aead : null, - ); - } } diff --git a/lib/src/message/cleartext_message.dart b/lib/src/message/cleartext_message.dart index a7c1aafd..3f5eb722 100644 --- a/lib/src/message/cleartext_message.dart +++ b/lib/src/message/cleartext_message.dart @@ -11,7 +11,6 @@ import '../packet/base_packet.dart'; import '../type/cleartext_message.dart'; import '../type/key.dart'; import '../type/notation_data.dart'; -import '../type/private_key.dart'; import '../type/signature.dart'; /// Cleartext message class that represents an OpenPGP cleartext message. diff --git a/lib/src/message/encrypted_message.dart b/lib/src/message/encrypted_message.dart index 873bfe26..d022399a 100644 --- a/lib/src/message/encrypted_message.dart +++ b/lib/src/message/encrypted_message.dart @@ -8,7 +8,6 @@ import '../common/armor.dart'; import '../common/helpers.dart'; import '../enum/armor_type.dart'; import '../message/base_message.dart'; -import '../message/literal_message.dart'; import '../packet/base_packet.dart'; import '../packet/packet_list.dart'; import '../type/encrypted_data_packet.dart'; diff --git a/lib/src/message/literal_message.dart b/lib/src/message/literal_message.dart index 905cefe5..6f99a983 100644 --- a/lib/src/message/literal_message.dart +++ b/lib/src/message/literal_message.dart @@ -14,16 +14,15 @@ import '../enum/compression_algorithm.dart'; import '../enum/preset_rfc.dart'; import '../enum/symmetric_algorithm.dart'; import '../message/base_message.dart'; -import '../message/encrypted_message.dart'; -import '../message/signature.dart'; import '../packet/base_packet.dart'; +import '../packet/key/session_key.dart'; import '../packet/packet_list.dart'; import '../type/key.dart'; import '../type/literal_data.dart'; import '../type/literal_message.dart'; import '../type/notation_data.dart'; import '../type/packet_list.dart'; -import '../type/private_key.dart'; +import '../type/session_key.dart'; import '../type/signature.dart'; import '../type/signature_packet.dart'; import '../type/signed_message.dart'; @@ -58,6 +57,32 @@ final class LiteralMessage extends BaseMessage implements LiteralMessageInterfac ])); } + /// Generate a new session key object, taking the algorithm preferences + /// of the passed public keys into account, if any. + static SessionKeyInterface generateSessionKey( + final Iterable encryptionKeys, [ + final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + ]) { + var aeadProtect = Config.aeadProtect; + final aead = Config.preferredAead; + for (final key in encryptionKeys) { + if (key.aeadSupported) { + if (!key.isPreferredAeadCiphers(symmetric, aead)) { + throw AssertionError('Symmetric and aead not compatible with the given `encryptionKeys`'); + } + } else { + if (key.preferredSymmetrics.isNotEmpty && !key.preferredSymmetrics.contains(symmetric)) { + throw AssertionError('Symmetric not compatible with the given `encryptionKeys`'); + } + aeadProtect = false; + } + } + return SessionKey.produceKey( + symmetric, + aeadProtect ? aead : null, + ); + } + @override get literalData => _unwrapCompressed().whereType().first; @@ -99,7 +124,7 @@ final class LiteralMessage extends BaseMessage implements LiteralMessageInterfac addPadding = false; } } - final sessionKey = BaseMessage.generateSessionKey( + final sessionKey = generateSessionKey( encryptionKeys, symmetric ?? Config.preferredSymmetric, ); diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index 90e2a3b3..31221994 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -12,19 +12,14 @@ import 'enum/ecc.dart'; import 'enum/key_type.dart'; import 'enum/rsa_key_size.dart'; import 'enum/symmetric_algorithm.dart'; -import 'key/private_key.dart'; -import 'key/public_key.dart'; -import 'message/cleartext_message.dart'; -import 'message/encrypted_message.dart'; -import 'message/literal_message.dart'; -import 'message/signature.dart'; -import 'message/signed_message.dart'; +import 'key/base_key.dart'; +import 'message/base_message.dart'; import 'type/cleartext_message.dart'; import 'type/encrypted_message.dart'; import 'type/key.dart'; import 'type/literal_message.dart'; import 'type/notation_data.dart'; -import 'type/private_key.dart'; +import 'type/session_key.dart'; import 'type/signature.dart'; import 'type/signed_cleartext_message.dart'; import 'type/signed_message.dart'; @@ -358,4 +353,11 @@ final class OpenPGP { passwords: passwords, ); } + + static SessionKeyInterface generateSessionKey( + final Iterable encryptionKeys, [ + final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + ]) { + return LiteralMessage.generateSessionKey(encryptionKeys, symmetric); + } } diff --git a/lib/src/packet/public_key.dart b/lib/src/packet/public_key.dart index 6edad936..e335bfa8 100644 --- a/lib/src/packet/public_key.dart +++ b/lib/src/packet/public_key.dart @@ -14,9 +14,7 @@ import '../enum/key_version.dart'; import '../enum/montgomery_curve.dart'; import '../enum/key_algorithm.dart'; import '../enum/rsa_key_size.dart'; -import '../type/key_material.dart'; import '../type/key_packet.dart'; -import '../type/subkey_packet.dart'; import 'key/public_material.dart'; import 'base_packet.dart'; diff --git a/lib/src/packet/public_key_encrypted_session_key.dart b/lib/src/packet/public_key_encrypted_session_key.dart index 0a817f17..63da7696 100644 --- a/lib/src/packet/public_key_encrypted_session_key.dart +++ b/lib/src/packet/public_key_encrypted_session_key.dart @@ -9,7 +9,6 @@ import 'dart:typed_data'; import '../type/key_packet.dart'; import '../enum/key_algorithm.dart'; import '../enum/montgomery_curve.dart'; -import '../type/secret_key_packet.dart'; import '../type/session_key.dart'; import '../type/session_key_cryptor.dart'; import 'base_packet.dart'; diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index db675d8e..dfdfc8ea 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -22,13 +22,10 @@ import '../type/key.dart'; import '../type/key_packet.dart'; import '../type/literal_data.dart'; import '../type/notation_data.dart'; -import '../type/secret_key_packet.dart'; import '../type/signature_packet.dart'; import '../type/signing_key_material.dart'; -import '../type/subkey_packet.dart'; import '../type/subpacket.dart'; import '../type/user_id_packet.dart'; -import '../type/verification_key_material.dart'; import 'base_packet.dart'; import 'signature_subpacket.dart'; import 'subpacket_reader.dart'; diff --git a/lib/src/type/cleartext_message.dart b/lib/src/type/cleartext_message.dart index b9e324d7..6cba25e3 100644 --- a/lib/src/type/cleartext_message.dart +++ b/lib/src/type/cleartext_message.dart @@ -6,7 +6,6 @@ library; import 'key.dart'; import 'notation_data.dart'; -import 'private_key.dart'; import 'signature.dart'; import 'signed_cleartext_message.dart'; import 'verification.dart'; diff --git a/lib/src/type/key.dart b/lib/src/type/key.dart index 5d7e9c37..e8b5b7e1 100644 --- a/lib/src/type/key.dart +++ b/lib/src/type/key.dart @@ -16,6 +16,10 @@ import 'signature_packet.dart'; import 'subkey.dart'; import 'user.dart'; +export 'private_key.dart'; +export 'subkey.dart'; +export 'user.dart'; + /// Transferable key interface /// That represents a key packet, the relevant signatures, users and subkeys. /// Author Nguyen Van Nguyen diff --git a/lib/src/type/key_packet.dart b/lib/src/type/key_packet.dart index fc8f6e97..5e80110f 100644 --- a/lib/src/type/key_packet.dart +++ b/lib/src/type/key_packet.dart @@ -10,6 +10,12 @@ import '../enum/key_algorithm.dart'; import 'key_material.dart'; import 'packet.dart'; +export 'key_material.dart'; +export 'secret_key_material.dart'; +export 'secret_key_packet.dart'; +export 'subkey_packet.dart'; +export 'verification_key_material.dart'; + /// Key packet interface /// Author Nguyen Van Nguyen abstract interface class KeyPacketInterface implements PacketInterface { diff --git a/lib/src/type/literal_message.dart b/lib/src/type/literal_message.dart index d169dfb2..a3389d04 100644 --- a/lib/src/type/literal_message.dart +++ b/lib/src/type/literal_message.dart @@ -12,7 +12,6 @@ import 'key.dart'; import 'literal_data.dart'; import 'notation_data.dart'; import 'packet_container.dart'; -import 'private_key.dart'; import 'signature.dart'; import 'verification.dart'; diff --git a/lib/src/type/secret_key_packet.dart b/lib/src/type/secret_key_packet.dart index cfb793fd..436592c0 100644 --- a/lib/src/type/secret_key_packet.dart +++ b/lib/src/type/secret_key_packet.dart @@ -8,7 +8,6 @@ import '../enum/hash_algorithm.dart'; import '../enum/symmetric_algorithm.dart'; import '../enum/aead_algorithm.dart'; import 'key_packet.dart'; -import 'secret_key_material.dart'; /// Secret key packet interface /// Author Nguyen Van Nguyen From 069d1f48e4105fc821fd0b59995982dc249c019a Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 10 Dec 2024 10:23:40 +0700 Subject: [PATCH 194/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/message/literal_message.dart | 4 ---- lib/src/openpgp.dart | 16 ++++++++-------- lib/src/type/cleartext_message.dart | 2 ++ lib/src/type/literal_message.dart | 6 ++++++ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/src/message/literal_message.dart b/lib/src/message/literal_message.dart index 6f99a983..17bd8884 100644 --- a/lib/src/message/literal_message.dart +++ b/lib/src/message/literal_message.dart @@ -20,13 +20,9 @@ import '../packet/packet_list.dart'; import '../type/key.dart'; import '../type/literal_data.dart'; import '../type/literal_message.dart'; -import '../type/notation_data.dart'; import '../type/packet_list.dart'; import '../type/session_key.dart'; -import '../type/signature.dart'; import '../type/signature_packet.dart'; -import '../type/signed_message.dart'; -import '../type/verification.dart'; /// OpenPGP literal message class /// Author Nguyen Van Nguyen diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index 31221994..2fc9c5e6 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -6,6 +6,7 @@ library; import 'dart:typed_data'; +import 'common/config.dart'; import 'common/extensions.dart'; import 'enum/compression_algorithm.dart'; import 'enum/ecc.dart'; @@ -15,15 +16,9 @@ import 'enum/symmetric_algorithm.dart'; import 'key/base_key.dart'; import 'message/base_message.dart'; import 'type/cleartext_message.dart'; -import 'type/encrypted_message.dart'; import 'type/key.dart'; import 'type/literal_message.dart'; -import 'type/notation_data.dart'; import 'type/session_key.dart'; -import 'type/signature.dart'; -import 'type/signed_cleartext_message.dart'; -import 'type/signed_message.dart'; -import 'type/verification.dart'; /// Export high level API for developers. /// Author Nguyen Van Nguyen @@ -354,10 +349,15 @@ final class OpenPGP { ); } + /// Generate a new session key object, taking the algorithm preferences + /// of the passed public keys into account, if any. static SessionKeyInterface generateSessionKey( final Iterable encryptionKeys, [ - final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, + final SymmetricAlgorithm? symmetric, ]) { - return LiteralMessage.generateSessionKey(encryptionKeys, symmetric); + return LiteralMessage.generateSessionKey( + encryptionKeys, + symmetric ?? Config.preferredSymmetric, + ); } } diff --git a/lib/src/type/cleartext_message.dart b/lib/src/type/cleartext_message.dart index 6cba25e3..058d6103 100644 --- a/lib/src/type/cleartext_message.dart +++ b/lib/src/type/cleartext_message.dart @@ -10,6 +10,8 @@ import 'signature.dart'; import 'signed_cleartext_message.dart'; import 'verification.dart'; +export 'signed_cleartext_message.dart'; + /// Cleartext message interface /// Author Nguyen Van Nguyen abstract interface class CleartextMessageInterface { diff --git a/lib/src/type/literal_message.dart b/lib/src/type/literal_message.dart index a3389d04..cbe355eb 100644 --- a/lib/src/type/literal_message.dart +++ b/lib/src/type/literal_message.dart @@ -15,6 +15,12 @@ import 'packet_container.dart'; import 'signature.dart'; import 'verification.dart'; +export 'encrypted_message.dart'; +export 'notation_data.dart'; +export 'signature.dart'; +export 'signed_message.dart'; +export 'verification.dart'; + /// Literal message interface /// Author Nguyen Van Nguyen abstract interface class LiteralMessageInterface implements ArmorableInterface, PacketContainerInterface { From e241e8706366dc827d25055a3f219b6db3ec2000 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 10 Dec 2024 11:24:45 +0700 Subject: [PATCH 195/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/enum/support_feature.dart | 8 ++-- lib/src/key/base_key.dart | 4 +- lib/src/packet/signature.dart | 58 ++++++++++++++++---------- lib/src/packet/signature/features.dart | 9 ++-- test/packet/signature_test.dart | 18 ++++---- 5 files changed, 56 insertions(+), 41 deletions(-) diff --git a/lib/src/enum/support_feature.dart b/lib/src/enum/support_feature.dart index 76a15df7..ca82e21a 100644 --- a/lib/src/enum/support_feature.dart +++ b/lib/src/enum/support_feature.dart @@ -8,16 +8,16 @@ library; /// Author Nguyen Van Nguyen enum SupportFeature { /// 0x01 - Modification Detection (packets 18 and 19) - version1SEIPD(1), + seipdV1(1), /// 0x02 - AEAD Encrypted Data Packet (packet 20) and version 5 Symmetric-Key Encrypted Session Key Packets (packet 3) - aeadEncrypted(2), + aead(2), /// 0x04 - Version 5 Public-Key Packet format and corresponding new fingerprint format - version5PublicKey(4), + publicKeyV5(4), /// 0x08 - VeVersion 2 Symmetrically Encrypted and Integrity Protected Data packet - version2SEIPD(8); + seipdV2(8); final int value; diff --git a/lib/src/key/base_key.dart b/lib/src/key/base_key.dart index 57954733..9882cf5a 100644 --- a/lib/src/key/base_key.dart +++ b/lib/src/key/base_key.dart @@ -111,7 +111,7 @@ abstract class BaseKey implements KeyInterface { bool get aeadSupported { for (final signature in directSignatures) { final subpacket = signature.getSubpacket(); - if (subpacket?.supportVersion2SEIPD ?? false) { + if (subpacket?.seidpV2Supported ?? false) { return true; } } @@ -119,7 +119,7 @@ abstract class BaseKey implements KeyInterface { if (user.isPrimary && !user.isRevoked()) { for (final signature in user.selfSignatures) { final subpacket = signature.getSubpacket(); - if (subpacket?.supportVersion2SEIPD ?? false) { + if (subpacket?.seidpV2Supported ?? false) { return true; } } diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index dfdfc8ea..4c64b8c4 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -492,21 +492,24 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { } static List _keySubpackets(final int version) { - final symmetrics = Uint8List.fromList([ - SymmetricAlgorithm.aes128.value, - SymmetricAlgorithm.aes256.value, - ]); - final aeads = Uint8List.fromList([ - ...AeadAlgorithm.values.map((algo) => algo.value), - ]); final subpackets = [ - KeyFlags.fromFlags(KeyFlag.certifyKeys.value | KeyFlag.signData.value), - PreferredSymmetricAlgorithms(symmetrics), - PreferredAeadAlgorithms(aeads), + KeyFlags.fromFlags( + KeyFlag.certifyKeys.value | KeyFlag.signData.value, + ), + PreferredSymmetricAlgorithms(Uint8List.fromList([ + SymmetricAlgorithm.aes128.value, + SymmetricAlgorithm.aes192.value, + SymmetricAlgorithm.aes256.value, + ])), + PreferredAeadAlgorithms(Uint8List.fromList([ + AeadAlgorithm.gcm.value, + AeadAlgorithm.ocb.value, + AeadAlgorithm.eax.value, + ])), PreferredHashAlgorithms(Uint8List.fromList([ HashAlgorithm.sha256.value, - HashAlgorithm.sha3_256.value, HashAlgorithm.sha512.value, + HashAlgorithm.sha3_256.value, HashAlgorithm.sha3_512.value, ])), PreferredCompressionAlgorithms(Uint8List.fromList([ @@ -515,19 +518,32 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { CompressionAlgorithm.zlib.value, ])), Features.fromFeatures( - SupportFeature.version1SEIPD.value | SupportFeature.aeadEncrypted.value | SupportFeature.version2SEIPD.value, + SupportFeature.seipdV1.value | SupportFeature.aead.value | SupportFeature.seipdV2.value, ), ]; if (version == KeyVersion.v6.value) { - subpackets.add(PreferredAeadCiphers(Uint8List.fromList(aeads - .map((aead) => [ - symmetrics[0], - aead, - symmetrics[1], - aead, - ]) - .expand((ciphers) => ciphers) - .toList()))); + subpackets.add(PreferredAeadCiphers(Uint8List.fromList([ + ...[ + SymmetricAlgorithm.aes128.value, + AeadAlgorithm.gcm.value, + SymmetricAlgorithm.aes128.value, + AeadAlgorithm.ocb.value, + SymmetricAlgorithm.aes128.value, + AeadAlgorithm.eax.value, + ], + ...[ + SymmetricAlgorithm.aes192.value, + AeadAlgorithm.gcm.value, + SymmetricAlgorithm.aes192.value, + AeadAlgorithm.ocb.value, + ], + ...[ + SymmetricAlgorithm.aes256.value, + AeadAlgorithm.gcm.value, + SymmetricAlgorithm.aes256.value, + AeadAlgorithm.ocb.value, + ], + ]))); } return subpackets; } diff --git a/lib/src/packet/signature/features.dart b/lib/src/packet/signature/features.dart index 9c3d0f32..be62729b 100644 --- a/lib/src/packet/signature/features.dart +++ b/lib/src/packet/signature/features.dart @@ -26,12 +26,11 @@ class Features extends SignatureSubpacket { }) => Features(Uint8List.fromList([features]), critical: critical); - bool get supprtVersion1SEIPD => (data[0] & SupportFeature.version1SEIPD.value) == SupportFeature.version1SEIPD.value; + bool get seipdV1Supported => (data[0] & SupportFeature.seipdV1.value) == SupportFeature.seipdV1.value; - bool get supportAeadEncrypted => (data[0] & SupportFeature.aeadEncrypted.value) == SupportFeature.aeadEncrypted.value; + bool get aeadSupported => (data[0] & SupportFeature.aead.value) == SupportFeature.aead.value; - bool get supportVersion5PublicKey => - (data[0] & SupportFeature.version5PublicKey.value) == SupportFeature.version5PublicKey.value; + bool get publicKeyV5Supported => (data[0] & SupportFeature.publicKeyV5.value) == SupportFeature.publicKeyV5.value; - bool get supportVersion2SEIPD => (data[0] & SupportFeature.version2SEIPD.value) == SupportFeature.version2SEIPD.value; + bool get seidpV2Supported => (data[0] & SupportFeature.seipdV2.value) == SupportFeature.seipdV2.value; } diff --git a/test/packet/signature_test.dart b/test/packet/signature_test.dart index 0af368fe..3ce108eb 100644 --- a/test/packet/signature_test.dart +++ b/test/packet/signature_test.dart @@ -76,15 +76,15 @@ void main() { test('Features', () { final features = Features.fromFeatures( - SupportFeature.version1SEIPD.value | - SupportFeature.aeadEncrypted.value | - SupportFeature.version5PublicKey.value | - SupportFeature.version2SEIPD.value, - ); - expect(features.supportVersion2SEIPD, isTrue); - expect(features.supportAeadEncrypted, isTrue); - expect(features.supportVersion5PublicKey, isTrue); - expect(features.supportVersion2SEIPD, isTrue); + SupportFeature.seipdV1.value | + SupportFeature.aead.value | + SupportFeature.publicKeyV5.value | + SupportFeature.seipdV2.value, + ); + expect(features.seidpV2Supported, isTrue); + expect(features.aeadSupported, isTrue); + expect(features.publicKeyV5Supported, isTrue); + expect(features.seidpV2Supported, isTrue); }); test('Salt notation', () { From 9a094631a17258282d17cde301669b9c7a11c7e2 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 10 Dec 2024 11:56:48 +0700 Subject: [PATCH 196/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/message/literal_message.dart | 4 ++-- lib/src/openpgp.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/src/message/literal_message.dart b/lib/src/message/literal_message.dart index 17bd8884..753c15bc 100644 --- a/lib/src/message/literal_message.dart +++ b/lib/src/message/literal_message.dart @@ -53,8 +53,8 @@ final class LiteralMessage extends BaseMessage implements LiteralMessageInterfac ])); } - /// Generate a new session key object, taking the algorithm preferences - /// of the passed public keys into account, if any. + /// Generate a new session key object. + /// Checking the algorithm preferences of the passed encryption keys. static SessionKeyInterface generateSessionKey( final Iterable encryptionKeys, [ final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index 2fc9c5e6..e396c80f 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -349,8 +349,8 @@ final class OpenPGP { ); } - /// Generate a new session key object, taking the algorithm preferences - /// of the passed public keys into account, if any. + /// Generate a new session key object. + /// Checking the algorithm preferences of the passed encryption keys. static SessionKeyInterface generateSessionKey( final Iterable encryptionKeys, [ final SymmetricAlgorithm? symmetric, From 7986584fef43224bb209fd785bfa496c1ffef080 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 10 Dec 2024 12:25:51 +0700 Subject: [PATCH 197/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/message/encrypted_message.dart | 53 +++++++++++++++----------- lib/src/message/literal_message.dart | 40 +++++++++++++------ lib/src/openpgp.dart | 27 +++++++++++++ 3 files changed, 86 insertions(+), 34 deletions(-) diff --git a/lib/src/message/encrypted_message.dart b/lib/src/message/encrypted_message.dart index d022399a..c31f9bf5 100644 --- a/lib/src/message/encrypted_message.dart +++ b/lib/src/message/encrypted_message.dart @@ -12,6 +12,7 @@ import '../packet/base_packet.dart'; import '../packet/packet_list.dart'; import '../type/encrypted_data_packet.dart'; import '../type/encrypted_message.dart'; +import '../type/packet_list.dart'; import '../type/private_key.dart'; import '../type/session_key.dart'; @@ -32,34 +33,15 @@ final class EncryptedMessage extends BaseMessage implements EncryptedMessageInte return EncryptedMessage(PacketList.decode(armor.data)); } - @override - get encryptedPacket => packetList.whereType().first; - - @override - get sessionKey => _sessionKey; - - @override - decrypt({ + /// Decrypt symmetric session keys using private keys or passwords (not both). + static SessionKeyInterface decryptSessionKey( + final PacketListInterface packetList, { final Iterable decryptionKeys = const [], final Iterable passwords = const [], }) { if (decryptionKeys.isEmpty && passwords.isEmpty) { throw ArgumentError('No decryption keys or passwords provided.'); } - _sessionKey = _decryptSessionKey(decryptionKeys, passwords); - - return LiteralMessage(encryptedPacket - .decrypt( - _sessionKey!.encryptionKey, - _sessionKey!.symmetric, - ) - .packets!); - } - - SessionKeyInterface _decryptSessionKey( - final Iterable decryptionKeys, - final Iterable passwords, - ) { final errors = []; final sessionKeys = []; if (passwords.isNotEmpty) { @@ -92,8 +74,33 @@ final class EncryptedMessage extends BaseMessage implements EncryptedMessageInte } if (sessionKeys.isEmpty) { - throw StateError('Session key decryption failed.\n${errors.join('\n')}'); + throw AssertionError('Session key decryption failed.\n${errors.join('\n')}'); } return sessionKeys.first; } + + @override + get encryptedPacket => packetList.whereType().first; + + @override + get sessionKey => _sessionKey; + + @override + decrypt({ + final Iterable decryptionKeys = const [], + final Iterable passwords = const [], + }) { + _sessionKey = decryptSessionKey( + packetList, + decryptionKeys: decryptionKeys, + passwords: passwords, + ); + + return LiteralMessage(encryptedPacket + .decrypt( + _sessionKey!.encryptionKey, + _sessionKey!.symmetric, + ) + .packets!); + } } diff --git a/lib/src/message/literal_message.dart b/lib/src/message/literal_message.dart index 753c15bc..32b12cf3 100644 --- a/lib/src/message/literal_message.dart +++ b/lib/src/message/literal_message.dart @@ -79,6 +79,31 @@ final class LiteralMessage extends BaseMessage implements LiteralMessageInterfac ); } + /// Encrypt a session key either with public keys, passwords, or both at once. + static PacketListInterface encryptSessionKey( + SessionKeyInterface sessionKey, { + final Iterable encryptionKeys = const [], + final Iterable passwords = const [], + }) { + if (encryptionKeys.isEmpty && passwords.isEmpty) { + throw ArgumentError('No encryption keys or passwords provided.'); + } + return PacketList([ + ...encryptionKeys.map( + (key) => PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( + key.publicKey.getEncryptionKeyPacket()!, + sessionKey, + ), + ), + ...passwords.map((password) => SymEncryptedSessionKeyPacket.encryptSessionKey( + password, + sessionKey: sessionKey, + symmetric: sessionKey.symmetric, + aead: sessionKey.aead, + )), + ]); + } + @override get literalData => _unwrapCompressed().whereType().first; @@ -138,18 +163,11 @@ final class LiteralMessage extends BaseMessage implements LiteralMessageInterfac : this.packetList; return EncryptedMessage(PacketList([ - ...encryptionKeys.map( - (key) => PublicKeyEncryptedSessionKeyPacket.encryptSessionKey( - key.publicKey.getEncryptionKeyPacket()!, - sessionKey, - ), + ...encryptSessionKey( + sessionKey, + encryptionKeys: encryptionKeys, + passwords: passwords, ), - ...passwords.map((password) => SymEncryptedSessionKeyPacket.encryptSessionKey( - password, - sessionKey: sessionKey, - symmetric: symmetric ?? Config.preferredSymmetric, - aead: sessionKey.aead, - )), SymEncryptedIntegrityProtectedDataPacket.encryptPackets( sessionKey.encryptionKey, packetList, diff --git a/lib/src/openpgp.dart b/lib/src/openpgp.dart index e396c80f..17c42147 100644 --- a/lib/src/openpgp.dart +++ b/lib/src/openpgp.dart @@ -18,6 +18,7 @@ import 'message/base_message.dart'; import 'type/cleartext_message.dart'; import 'type/key.dart'; import 'type/literal_message.dart'; +import 'type/packet_list.dart'; import 'type/session_key.dart'; /// Export high level API for developers. @@ -360,4 +361,30 @@ final class OpenPGP { symmetric ?? Config.preferredSymmetric, ); } + + /// Encrypt a session key either with public keys, passwords, or both at once. + static PacketListInterface encryptSessionKey( + SessionKeyInterface sessionKey, { + final Iterable encryptionKeys = const [], + final Iterable passwords = const [], + }) { + return LiteralMessage.encryptSessionKey( + sessionKey, + encryptionKeys: encryptionKeys, + passwords: passwords, + ); + } + + /// Decrypt symmetric session keys using private keys or passwords (not both). + static SessionKeyInterface decryptSessionKey( + final PacketListInterface packetList, { + final Iterable decryptionKeys = const [], + final Iterable passwords = const [], + }) { + return EncryptedMessage.decryptSessionKey( + packetList, + decryptionKeys: decryptionKeys, + passwords: passwords, + ); + } } From 0a4a158ba10dd5e105e42bf9d806490cc2677058 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 10 Dec 2024 12:37:27 +0700 Subject: [PATCH 198/202] WIP Signed-off-by: Nguyen Van Nguyen --- example/literal_data.dart | 3 ++- lib/src/message/encrypted_message.dart | 3 +++ lib/src/type/encrypted_message.dart | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/example/literal_data.dart b/example/literal_data.dart index f73fd43c..c8ca512b 100644 --- a/example/literal_data.dart +++ b/example/literal_data.dart @@ -270,8 +270,9 @@ dYZIvg0A1SCEq4w8mfkSbFRJrixxFabTL7COFHgvL25joDV8UIvxYg== ], symmetric: SymmetricAlgorithm.aes256, ); - Config.aeadProtect = false; + print('AEAD protected: ${encryptedMessage.aeadProtected}'); armored = encryptedMessage.armor(); + Config.aeadProtect = false; print('\nDecrypt with password & verify signatures:'); literalMessage = OpenPGP.decrypt(armored, passwords: [password]); diff --git a/lib/src/message/encrypted_message.dart b/lib/src/message/encrypted_message.dart index c31f9bf5..82e4d40a 100644 --- a/lib/src/message/encrypted_message.dart +++ b/lib/src/message/encrypted_message.dart @@ -82,6 +82,9 @@ final class EncryptedMessage extends BaseMessage implements EncryptedMessageInte @override get encryptedPacket => packetList.whereType().first; + @override + get aeadProtected => packetList.whereType().firstOrNull?.aead != null; + @override get sessionKey => _sessionKey; diff --git a/lib/src/type/encrypted_message.dart b/lib/src/type/encrypted_message.dart index 2d6196ba..e57dab07 100644 --- a/lib/src/type/encrypted_message.dart +++ b/lib/src/type/encrypted_message.dart @@ -17,6 +17,9 @@ abstract interface class EncryptedMessageInterface implements ArmorableInterface /// Return encrypted packet. EncryptedDataPacketInterface get encryptedPacket; + /// Return encrypted packet is aead protected. + bool get aeadProtected; + /// Return session key. SessionKeyInterface? get sessionKey; From 1098e5f4499415e9b5877ea60a90aaac75631a43 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 10 Dec 2024 12:40:50 +0700 Subject: [PATCH 199/202] WIP Signed-off-by: Nguyen Van Nguyen --- example/literal_data.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/example/literal_data.dart b/example/literal_data.dart index c8ca512b..9c1d9fd3 100644 --- a/example/literal_data.dart +++ b/example/literal_data.dart @@ -188,7 +188,6 @@ dYZIvg0A1SCEq4w8mfkSbFRJrixxFabTL7COFHgvL25joDV8UIvxYg== passwords: [password], ); var armored = encryptedMessage.armor(); - print(armored); print('Decrypt with password:'); var literalMessage = OpenPGP.decrypt(armored, passwords: [password]); print(utf8.decode(literalMessage.literalData.binary)); @@ -208,8 +207,8 @@ dYZIvg0A1SCEq4w8mfkSbFRJrixxFabTL7COFHgvL25joDV8UIvxYg== curve448PrivateKey, ], ); + print('AEAD protected: ${encryptedMessage.aeadProtected}'); armored = encryptedMessage.armor(); - print(armored); print('\nDecrypt with passphrase & verify signatures:'); literalMessage = OpenPGP.decrypt(armored, passwords: [password]); From bf7f8ae7ddfb838a66836c6b2712a9cbf5f66072 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 10 Dec 2024 13:04:55 +0700 Subject: [PATCH 200/202] WIP Signed-off-by: Nguyen Van Nguyen --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 7642ae72..8620e825 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2022-present, Nguyen Van Nguyen and other contributors. All rights reserved. +Copyright (c) 2024-present by Dart Privacy Guard project. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: From bc11e1c19c19c3cbc22a15d8bacdaee0791804e9 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 10 Dec 2024 13:30:40 +0700 Subject: [PATCH 201/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/cryptor/aead/ocb.dart | 6 +++--- lib/src/cryptor/asymmetric/dsa.dart | 2 +- lib/src/cryptor/asymmetric/elgamal.dart | 2 +- lib/src/cryptor/symmetric/blowfish.dart | 2 +- lib/src/cryptor/symmetric/camellia.dart | 10 +++++----- lib/src/cryptor/symmetric/cast5.dart | 2 +- lib/src/cryptor/symmetric/idea.dart | 2 +- lib/src/cryptor/symmetric/twofish.dart | 2 +- lib/src/key/base_key.dart | 4 ++-- lib/src/key/private_key.dart | 2 +- lib/src/message/encrypted_message.dart | 2 +- lib/src/message/literal_message.dart | 2 +- lib/src/packet/key/key_wrapper.dart | 2 +- lib/src/packet/key/session_key.dart | 2 +- lib/src/packet/secret_key.dart | 4 ++-- lib/src/packet/signature.dart | 4 ++-- lib/src/packet/sym_encrypted_data.dart | 2 +- .../packet/sym_encrypted_integrity_protected_data.dart | 2 +- 18 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/src/cryptor/aead/ocb.dart b/lib/src/cryptor/aead/ocb.dart index 7b64e75d..fc1a276d 100644 --- a/lib/src/cryptor/aead/ocb.dart +++ b/lib/src/cryptor/aead/ocb.dart @@ -273,7 +273,7 @@ class OCBCipher implements AEADCipher { _xor(_mainBlock, pad); if (output.length < (outOff + _mainBlockPos)) { - throw StateError('Output buffer too short'); + throw AssertionError('Output buffer too short'); } output.setAll(outOff, _mainBlock.sublist(0, _mainBlockPos)); @@ -297,7 +297,7 @@ class OCBCipher implements AEADCipher { if (_forEncryption) { if (output.length < (outOff + resultLen + _macSize)) { - throw StateError('Output buffer too short'); + throw AssertionError('Output buffer too short'); } /// Append tag to the message @@ -306,7 +306,7 @@ class OCBCipher implements AEADCipher { } else { /// Compare the tag from the message with the calculated one if (!_macBlock.equals(tag ?? Uint8List(0))) { - throw InvalidCipherTextException('Mac check in OCB failed'); + throw AssertionError('Mac check in OCB failed'); } } diff --git a/lib/src/cryptor/asymmetric/dsa.dart b/lib/src/cryptor/asymmetric/dsa.dart index d2276166..1d0deb5d 100644 --- a/lib/src/cryptor/asymmetric/dsa.dart +++ b/lib/src/cryptor/asymmetric/dsa.dart @@ -60,7 +60,7 @@ class DSASigner implements Signer { @override DSASignature generateSignature(final Uint8List message) { if (!_forSigning) { - throw StateError('DSA signer not initialised for signature generation'); + throw AssertionError('DSA signer not initialised for signature generation'); } final pri = _key as DSAPrivateKey; diff --git a/lib/src/cryptor/asymmetric/elgamal.dart b/lib/src/cryptor/asymmetric/elgamal.dart index 6483545c..49f1b580 100644 --- a/lib/src/cryptor/asymmetric/elgamal.dart +++ b/lib/src/cryptor/asymmetric/elgamal.dart @@ -75,7 +75,7 @@ class ElGamalEngine implements AsymmetricBlockCipher { final int outOff, ) { if (_key == null) { - throw StateError('$algorithmName not initialised'); + throw AssertionError('$algorithmName not initialised'); } if (inLength > inputBlockSize) { diff --git a/lib/src/cryptor/symmetric/blowfish.dart b/lib/src/cryptor/symmetric/blowfish.dart index 8858f500..97c10e46 100644 --- a/lib/src/cryptor/symmetric/blowfish.dart +++ b/lib/src/cryptor/symmetric/blowfish.dart @@ -337,7 +337,7 @@ class BlowfishEngine extends BaseEngine { final int outOff, ) { if (_workingKey.isEmpty) { - throw StateError('$algorithmName not initialised'); + throw AssertionError('$algorithmName not initialised'); } if ((inOff + _blockSize) > input.length) { throw ArgumentError('input buffer too short for $algorithmName engine'); diff --git a/lib/src/cryptor/symmetric/camellia.dart b/lib/src/cryptor/symmetric/camellia.dart index 0cfd3715..ab489c3d 100644 --- a/lib/src/cryptor/symmetric/camellia.dart +++ b/lib/src/cryptor/symmetric/camellia.dart @@ -239,7 +239,7 @@ class CamelliaEngine extends BaseEngine { @override init(final bool forEncryption, final CipherParameters? params) { if (params is! KeyParameter) { - throw Exception('only simple KeyParameter expected.'); + throw AssertionError('only simple KeyParameter expected.'); } _setKey(forEncryption, params.key); @@ -255,14 +255,14 @@ class CamelliaEngine extends BaseEngine { final int outOff, ) { if (!initialised) { - throw Exception('Camellia engine not initialised'); + throw AssertionError('Camellia engine not initialised'); } if ((inOff + _blockSize) > input.length) { - throw Exception('input buffer too short'); + throw AssertionError('input buffer too short'); } if ((outOff + _blockSize) > output.length) { - throw Exception('output buffer too short'); + throw AssertionError('output buffer too short'); } if (_keyIs128) { return _processBlock128(input, inOff, output, outOff); @@ -423,7 +423,7 @@ class CamelliaEngine extends BaseEngine { _keyIs128 = false; break; default: - throw Exception('key sizes are only 16/24/32 bytes.'); + throw AssertionError('key sizes are only 16/24/32 bytes.'); } for (var i = 0; i < 4; i++) { diff --git a/lib/src/cryptor/symmetric/cast5.dart b/lib/src/cryptor/symmetric/cast5.dart index c4b4a36f..4aa97be9 100644 --- a/lib/src/cryptor/symmetric/cast5.dart +++ b/lib/src/cryptor/symmetric/cast5.dart @@ -600,7 +600,7 @@ class CAST5Engine extends BaseEngine { final int outOff, ) { if (_workingKey.isEmpty) { - throw StateError('$algorithmName not initialised'); + throw AssertionError('$algorithmName not initialised'); } if ((inOff + _blockSize) > input.length) { throw ArgumentError('input buffer too short for $algorithmName engine'); diff --git a/lib/src/cryptor/symmetric/idea.dart b/lib/src/cryptor/symmetric/idea.dart index 382c2944..4f6c1999 100644 --- a/lib/src/cryptor/symmetric/idea.dart +++ b/lib/src/cryptor/symmetric/idea.dart @@ -45,7 +45,7 @@ class IDEAEngine extends BaseEngine { final int outOff, ) { if (_workingKey.isEmpty) { - throw StateError('$algorithmName not initialised'); + throw AssertionError('$algorithmName not initialised'); } if ((inOff + _blockSize) > input.length) { throw ArgumentError('input buffer too short for $algorithmName engine'); diff --git a/lib/src/cryptor/symmetric/twofish.dart b/lib/src/cryptor/symmetric/twofish.dart index 3b350648..fc79dbc8 100644 --- a/lib/src/cryptor/symmetric/twofish.dart +++ b/lib/src/cryptor/symmetric/twofish.dart @@ -409,7 +409,7 @@ class TwofishEngine extends BaseEngine { int outOff, ) { if (_workingKey.isEmpty) { - throw StateError('algorithmName not initialised'); + throw AssertionError('algorithmName not initialised'); } if ((inOff + _blockSize) > input.length) { throw ArgumentError('input buffer too short for algorithmName engine'); diff --git a/lib/src/key/base_key.dart b/lib/src/key/base_key.dart index 9882cf5a..8b9c1209 100644 --- a/lib/src/key/base_key.dart +++ b/lib/src/key/base_key.dart @@ -256,10 +256,10 @@ abstract class BaseKey implements KeyInterface { ); if (keyPacketList.isEmpty) { - throw StateError('Key packet not found in packet list.'); + throw AssertionError('Key packet not found in packet list.'); } if (keyPacketList.length > 1) { - throw StateError('Key block contains multiple key packets.'); + throw AssertionError('Key block contains multiple key packets.'); } keyPacket = keyPacketList.whereType().first; diff --git a/lib/src/key/private_key.dart b/lib/src/key/private_key.dart index 3d2b70d3..1e74ff7e 100644 --- a/lib/src/key/private_key.dart +++ b/lib/src/key/private_key.dart @@ -177,7 +177,7 @@ final class PrivateKey extends BaseKey implements PrivateKeyInterface { throw ArgumentError('Passphrase are required for key encryption.'); } if (!secretKeyPacket.isDecrypted) { - throw StateError('Private key must be decrypted before encrypting.'); + throw AssertionError('Private key must be decrypted before encrypting.'); } final aead = aeadProtected && Config.aeadProtect ? Config.preferredAead : null; diff --git a/lib/src/message/encrypted_message.dart b/lib/src/message/encrypted_message.dart index 82e4d40a..8b8c9192 100644 --- a/lib/src/message/encrypted_message.dart +++ b/lib/src/message/encrypted_message.dart @@ -23,7 +23,7 @@ final class EncryptedMessage extends BaseMessage implements EncryptedMessageInte EncryptedMessage(super.packetList) { if (packetList.whereType().isEmpty) { - throw StateError('No encrypted data in packet list.'); + throw AssertionError('No encrypted data in packet list.'); } } diff --git a/lib/src/message/literal_message.dart b/lib/src/message/literal_message.dart index 32b12cf3..5bf7ef6b 100644 --- a/lib/src/message/literal_message.dart +++ b/lib/src/message/literal_message.dart @@ -29,7 +29,7 @@ import '../type/signature_packet.dart'; final class LiteralMessage extends BaseMessage implements LiteralMessageInterface, SignedMessageInterface { LiteralMessage(super.packetList) { if (_unwrapCompressed().whereType().isEmpty) { - throw StateError('No literal data in packet list.'); + throw AssertionError('No literal data in packet list.'); } } diff --git a/lib/src/packet/key/key_wrapper.dart b/lib/src/packet/key/key_wrapper.dart index 8aef4ec1..6acbe7dc 100644 --- a/lib/src/packet/key/key_wrapper.dart +++ b/lib/src/packet/key/key_wrapper.dart @@ -89,7 +89,7 @@ abstract class KeyWrapper { } } if (!a.equals(Uint8List.fromList(iv))) { - throw StateError('Integrity check failed.'); + throw AssertionError('Integrity check failed.'); } return r; diff --git a/lib/src/packet/key/session_key.dart b/lib/src/packet/key/session_key.dart index 8dce52d4..85aadcf9 100644 --- a/lib/src/packet/key/session_key.dart +++ b/lib/src/packet/key/session_key.dart @@ -69,7 +69,7 @@ class SessionKey implements SessionKeyInterface { checksum(final Uint8List checksum) { final computedChecksum = computeChecksum(); if (!((computedChecksum[0] == checksum[0]) && (computedChecksum[1] == checksum[1]))) { - throw StateError('Session key checksum mismatch!'); + throw AssertionError('Session key checksum mismatch!'); } } } diff --git a/lib/src/packet/secret_key.dart b/lib/src/packet/secret_key.dart index 6a6c9509..1a977ae9 100644 --- a/lib/src/packet/secret_key.dart +++ b/lib/src/packet/secret_key.dart @@ -210,7 +210,7 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { final checksum = keyData.sublist(keyData.length - 2); keyData = keyData.sublist(0, keyData.length - 2); if (!checksum.equals(_computeChecksum(keyData))) { - throw StateError('Key checksum mismatch!'); + throw AssertionError('Key checksum mismatch!'); } } keyMaterial = _readKeyMaterial( @@ -549,7 +549,7 @@ class SecretKeyPacket extends BasePacket implements SecretKeyPacketInterface { }; if (!keyMaterial.isValid) { - throw StateError('Key material is not consistent.'); + throw AssertionError('Key material is not consistent.'); } return keyMaterial; diff --git a/lib/src/packet/signature.dart b/lib/src/packet/signature.dart index 4c64b8c4..10d9a637 100644 --- a/lib/src/packet/signature.dart +++ b/lib/src/packet/signature.dart @@ -463,7 +463,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { ); } if (isExpired(time)) { - throw StateError('Signature is expired.'); + throw AssertionError('Signature is expired.'); } final message = Uint8List.fromList([ @@ -478,7 +478,7 @@ class SignaturePacket extends BasePacket implements SignaturePacketInterface { final hash = Helper.hashDigest(message, hashAlgorithm); if (signedHashValue[0] != hash[0] || signedHashValue[1] != hash[1]) { - throw StateError('Signed digest did not match!'); + throw AssertionError('Signed digest did not match!'); } final keyMaterial = verifyKey.keyMaterial; diff --git a/lib/src/packet/sym_encrypted_data.dart b/lib/src/packet/sym_encrypted_data.dart index fe4da98a..07a5244e 100644 --- a/lib/src/packet/sym_encrypted_data.dart +++ b/lib/src/packet/sym_encrypted_data.dart @@ -73,7 +73,7 @@ class SymEncryptedDataPacket extends BasePacket implements EncryptedDataPacketIn final SymmetricAlgorithm symmetric = SymmetricAlgorithm.aes128, ]) { if (!Config.allowUnauthenticated) { - throw StateError('Message is not authenticated.'); + throw AssertionError('Message is not authenticated.'); } final blockSize = symmetric.blockSize; final cipher = BufferedCipher(symmetric.cfbCipherEngine) diff --git a/lib/src/packet/sym_encrypted_integrity_protected_data.dart b/lib/src/packet/sym_encrypted_integrity_protected_data.dart index f7bfce1c..8e74f340 100644 --- a/lib/src/packet/sym_encrypted_integrity_protected_data.dart +++ b/lib/src/packet/sym_encrypted_integrity_protected_data.dart @@ -236,7 +236,7 @@ class SymEncryptedIntegrityProtectedDataPacket extends BasePacket implements Enc Helper.hashDigest(toHash, HashAlgorithm.sha1), ); if (!verifyHash) { - throw StateError('Modification detected.'); + throw AssertionError('Modification detected.'); } packetBytes = toHash.sublist( cipherSymmetric.blockSize + 2, From ba5ef48f03960c9c366e29a80ee0eb1b225cb762 Mon Sep 17 00:00:00 2001 From: Nguyen Van Nguyen Date: Tue, 10 Dec 2024 13:33:21 +0700 Subject: [PATCH 202/202] WIP Signed-off-by: Nguyen Van Nguyen --- lib/src/packet/key/session_key_cryptor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/packet/key/session_key_cryptor.dart b/lib/src/packet/key/session_key_cryptor.dart index a9c90a1d..799aef5c 100644 --- a/lib/src/packet/key/session_key_cryptor.dart +++ b/lib/src/packet/key/session_key_cryptor.dart @@ -11,11 +11,11 @@ import '../../type/session_key.dart'; import '../../type/session_key_cryptor.dart'; import 'session_key.dart'; -export 'session_key.dart'; export 'ecdh_session_key_cryptor.dart'; export 'elgamal_session_key_cryptor.dart'; export 'montgomery_session_key_cryptor.dart'; export 'rsa_session_key_cryptor.dart'; +export 'session_key.dart'; /// Abstract session key cryptor class /// Author Nguyen Van Nguyen