damus

nostr ios client
git clone git://jb55.com/damus
Log | Files | Refs | README | LICENSE

commit a6e123e92839d139c99160abb2e80a53d29dfab9
parent 9e7943e0e962ef2ac16f07864390800553795f51
Author: Daniel D’Aquino <daniel@daquino.me>
Date:   Mon, 10 Feb 2025 15:07:36 -0800

Remove rust-nostr dependency

This commit removes rust-nostr dependency, and replaces the NIP-44 logic
with a new NIP-44 module based on the Swift NostrSDK implementation.

The decision to move away from rust-nostr and the Swift NostrSDK was
made for the following reasons:
1. `rust-nostr` caused the app size to double
2. We only need NIP44 functionality, and we don't need to bring
   everything else
3. The Swift NostrSDK caused conflicts around the secp256k1 dependency
   that is hard to address
4. The way we do things in the codebase is far different from the Swift
   NostrSDK, and we optimize it for use with NostrDB. Bringing it an
   outside library causes significant complexity in integration with
   NostrDB, and would effectively cause the codebase to be split into
   two different ways of achieving the same results. Therefore it is
   cleaner if we stick to our own Nostr structures and functions and
   focus on maintaining them.

However, the library CryptoSwift was added as a dependency, to bring in
ChaCha20 which is not supported by CryptoKit (CryptoKit supports the
ChaCha20-Poly1305 cipher, but NIP-44 uses ChaCha20 with HMAC-SHA256
instead)

Closes: https://github.com/damus-io/damus/issues/2849
Changelog-Changed: Made internal changes to reduce the app binary size
Signed-off-by: Daniel D’Aquino <daniel@daquino.me>

Diffstat:
AACKNOWLEDGEMENTS.md | 5+++++
Mdamus.xcodeproj/project.pbxproj | 98++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mdamus.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 18+++++++++---------
Mdamus/NIP37/NIP37Draft.swift | 37+++++--------------------------------
Adamus/NIP44/NIP44.swift | 357+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdamus/Util/Keys.swift | 11+++++++++++
AdamusTests/LICENSES | 23+++++++++++++++++++++++
AdamusTests/NIP44v2EncryptionTests.swift | 432+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AdamusTests/nip44.vectors.json | 648+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 1557 insertions(+), 72 deletions(-)

diff --git a/ACKNOWLEDGEMENTS.md b/ACKNOWLEDGEMENTS.md @@ -0,0 +1,5 @@ +### Acknowledgements and licenses + +1. This product contains code derived from [Nostr SDK iOS](https://github.com/nostr-sdk/nostr-sdk-ios). [License](https://github.com/nostr-sdk/nostr-sdk-ios/blob/40df800c6749d7ce0b6fd7328e76cbc0dc71c87b/LICENSE) +2. This product includes software developed by the "Marcin Krzyzanowski" (http://krzyzanowskim.com/). [License](https://github.com/krzyzanowskim/CryptoSwift/blob/e74bbbfbef939224b242ae7c342a90e60b88b5ce/LICENSE) + diff --git a/damus.xcodeproj/project.pbxproj b/damus.xcodeproj/project.pbxproj @@ -1500,9 +1500,6 @@ D7ADD3E22B538E3500F104C4 /* DamusPurpleVerifyNpubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7ADD3E12B538E3500F104C4 /* DamusPurpleVerifyNpubView.swift */; }; D7B76C902C825042003A16CB /* PushNotificationClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */; }; D7B76C912C82507F003A16CB /* NIP98AuthenticatedRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C6787D2B2D34CC00BCEAFB /* NIP98AuthenticatedRequest.swift */; }; - D7BEE6F32D37AE1B00CF659F /* NostrSDK in Frameworks */ = {isa = PBXBuildFile; productRef = D7BEE6F22D37AE1B00CF659F /* NostrSDK */; }; - D7BEE6F52D37B20400CF659F /* NostrSDK in Frameworks */ = {isa = PBXBuildFile; productRef = D7BEE6F42D37B20400CF659F /* NostrSDK */; }; - D7BEE6F72D37B21400CF659F /* NostrSDK in Frameworks */ = {isa = PBXBuildFile; productRef = D7BEE6F62D37B21400CF659F /* NostrSDK */; }; D7BEE6F92D37B37400CF659F /* DraftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BEE6F82D37B37400CF659F /* DraftTests.swift */; }; D7C48C0B2D12DE0C00A3BACF /* SwiftyCrop in Frameworks */ = {isa = PBXBuildFile; productRef = D7C48C0A2D12DE0C00A3BACF /* SwiftyCrop */; }; D7C48C0D2D12E34900A3BACF /* SwiftyCrop in Frameworks */ = {isa = PBXBuildFile; productRef = D7C48C0C2D12E34900A3BACF /* SwiftyCrop */; }; @@ -1607,6 +1604,16 @@ D7D2A3812BF815D000E4B42B /* PushNotificationClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */; }; D7D68FF92C9E01BE0015A515 /* KFClickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D68FF82C9E01B60015A515 /* KFClickable.swift */; }; D7D68FFA2C9E01BE0015A515 /* KFClickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D68FF82C9E01B60015A515 /* KFClickable.swift */; }; + D7DB1FDE2D5A78CE00CF06DA /* NIP44.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DB1FDD2D5A78CE00CF06DA /* NIP44.swift */; }; + D7DB1FDF2D5A78CE00CF06DA /* NIP44.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DB1FDD2D5A78CE00CF06DA /* NIP44.swift */; }; + D7DB1FE02D5A78CE00CF06DA /* NIP44.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DB1FDD2D5A78CE00CF06DA /* NIP44.swift */; }; + D7DB1FE42D5A9AC900CF06DA /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = D7DB1FE32D5A9AC900CF06DA /* CryptoSwift */; }; + D7DB1FE82D5A9F5300CF06DA /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = D7DB1FE72D5A9F5300CF06DA /* CryptoSwift */; }; + D7DB1FEA2D5A9F5A00CF06DA /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = D7DB1FE92D5A9F5A00CF06DA /* CryptoSwift */; }; + D7DB1FEC2D5A9F6500CF06DA /* CryptoSwift in Frameworks */ = {isa = PBXBuildFile; productRef = D7DB1FEB2D5A9F6500CF06DA /* CryptoSwift */; }; + D7DB1FEE2D5AC51B00CF06DA /* NIP44v2EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DB1FED2D5AC50F00CF06DA /* NIP44v2EncryptionTests.swift */; }; + D7DB1FF12D5AC5D700CF06DA /* nip44.vectors.json in Resources */ = {isa = PBXBuildFile; fileRef = D7DB1FF02D5AC5D700CF06DA /* nip44.vectors.json */; }; + D7DB1FF32D5AC5EA00CF06DA /* LICENSES in Resources */ = {isa = PBXBuildFile; fileRef = D7DB1FF22D5AC5E400CF06DA /* LICENSES */; }; D7DBD41F2B02F15E002A6197 /* NostrKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */; }; D7DEEF2F2A8C021E00E0C99F /* NostrEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DEEF2E2A8C021E00E0C99F /* NostrEventTests.swift */; }; D7EB00B02CD59C8D00660C07 /* PresentFullScreenItemNotify.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EB00AF2CD59C8300660C07 /* PresentFullScreenItemNotify.swift */; }; @@ -2480,6 +2487,10 @@ D7CBD1D52B8D509800BFD889 /* DamusPurpleImpendingExpirationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusPurpleImpendingExpirationTests.swift; sourceTree = "<group>"; }; D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushNotificationClient.swift; sourceTree = "<group>"; }; D7D68FF82C9E01B60015A515 /* KFClickable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KFClickable.swift; sourceTree = "<group>"; }; + D7DB1FDD2D5A78CE00CF06DA /* NIP44.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NIP44.swift; sourceTree = "<group>"; }; + D7DB1FED2D5AC50F00CF06DA /* NIP44v2EncryptionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NIP44v2EncryptionTests.swift; sourceTree = "<group>"; }; + D7DB1FF02D5AC5D700CF06DA /* nip44.vectors.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = nip44.vectors.json; sourceTree = "<group>"; }; + D7DB1FF22D5AC5E400CF06DA /* LICENSES */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSES; sourceTree = "<group>"; }; D7DEEF2E2A8C021E00E0C99F /* NostrEventTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NostrEventTests.swift; sourceTree = "<group>"; }; D7EB00AF2CD59C8300660C07 /* PresentFullScreenItemNotify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentFullScreenItemNotify.swift; sourceTree = "<group>"; }; D7EDED1B2B1178FE0018B19C /* NoteContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteContent.swift; sourceTree = "<group>"; }; @@ -2523,8 +2534,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D7BEE6F32D37AE1B00CF659F /* NostrSDK in Frameworks */, 4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */, + D7DB1FE42D5A9AC900CF06DA /* CryptoSwift in Frameworks */, 3A0A30BB2C21397A00F8C9BC /* EmojiPicker in Frameworks */, D70D90982CDED61800CD0534 /* CodeScanner in Frameworks */, D7C48C0B2D12DE0C00A3BACF /* SwiftyCrop in Frameworks */, @@ -2554,8 +2565,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D7BEE6F52D37B20400CF659F /* NostrSDK in Frameworks */, 82D6FC862CD9A4A600C925F4 /* MarkdownUI in Frameworks */, + D7DB1FEC2D5A9F6500CF06DA /* CryptoSwift in Frameworks */, 82D6FC8A2CD9A54600C925F4 /* SwipeActions in Frameworks */, D7F360292CEBBE34009D34DA /* CodeScanner in Frameworks */, D7C48C0D2D12E34900A3BACF /* SwiftyCrop in Frameworks */, @@ -2571,7 +2582,7 @@ files = ( D703D7AF2C670FB700A400EA /* MarkdownUI in Frameworks */, D73E5F9D2C6AA8E3007EB227 /* SwipeActions in Frameworks */, - D7BEE6F72D37B21400CF659F /* NostrSDK in Frameworks */, + D7DB1FE82D5A9F5300CF06DA /* CryptoSwift in Frameworks */, D73E5F762C6A997E007EB227 /* EmojiPicker in Frameworks */, D703D7192C66E47100A400EA /* UniformTypeIdentifiers.framework in Frameworks */, D7C48C0F2D12E35600A3BACF /* SwiftyCrop in Frameworks */, @@ -2587,6 +2598,7 @@ files = ( D789D1202AFEFBF20083A7AB /* secp256k1 in Frameworks */, D7EDED312B1290B80018B19C /* MarkdownUI in Frameworks */, + D7DB1FEA2D5A9F5A00CF06DA /* CryptoSwift in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2753,6 +2765,8 @@ children = ( 4C0C03982A61E27B0098B3B8 /* bool_setting.wasm */, 4C0C03972A61E27B0098B3B8 /* primal.wasm */, + D7DB1FF22D5AC5E400CF06DA /* LICENSES */, + D7DB1FF02D5AC5D700CF06DA /* nip44.vectors.json */, ); name = Fixtures; sourceTree = "<group>"; @@ -3579,6 +3593,7 @@ 4CE6DEE527F7A08100C66700 /* damus */ = { isa = PBXGroup; children = ( + D7DB1FDC2D5A77E500CF06DA /* NIP44 */, D755B28B2D3E7D6500BBEEFA /* NIP37 */, 4C45E5002BED4CE10025A428 /* NIP10 */, 4C1D4FB32A7967990024F453 /* build-git-hash.txt */, @@ -3616,6 +3631,7 @@ 4CE6DEF627F7A08200C66700 /* damusTests */ = { isa = PBXGroup; children = ( + D7DB1FED2D5AC50F00CF06DA /* NIP44v2EncryptionTests.swift */, D7A0D8742D1FE66A00DCBE59 /* EditPictureControlTests.swift */, E06336A72B7582D600A88E6B /* Assets */, D72A2D032AD9C165002AFF62 /* Mocking */, @@ -3946,6 +3962,14 @@ path = Utils; sourceTree = "<group>"; }; + D7DB1FDC2D5A77E500CF06DA /* NIP44 */ = { + isa = PBXGroup; + children = ( + D7DB1FDD2D5A78CE00CF06DA /* NIP44.swift */, + ); + path = NIP44; + sourceTree = "<group>"; + }; E06336A72B7582D600A88E6B /* Assets */ = { isa = PBXGroup; children = ( @@ -4012,7 +4036,7 @@ D78DB8582C1CE9CA00F0AB12 /* SwipeActions */, D70D90972CDED61800CD0534 /* CodeScanner */, D7C48C0A2D12DE0C00A3BACF /* SwiftyCrop */, - D7BEE6F22D37AE1B00CF659F /* NostrSDK */, + D7DB1FE32D5A9AC900CF06DA /* CryptoSwift */, ); productName = damus; productReference = 4CE6DEE327F7A08100C66700 /* damus.app */; @@ -4079,7 +4103,7 @@ 82D6FC892CD9A54600C925F4 /* SwipeActions */, D7F360282CEBBE34009D34DA /* CodeScanner */, D7C48C0C2D12E34900A3BACF /* SwiftyCrop */, - D7BEE6F42D37B20400CF659F /* NostrSDK */, + D7DB1FEB2D5A9F6500CF06DA /* CryptoSwift */, ); productName = "share extension"; productReference = 82D6FA972CD9820500C925F4 /* ShareExtension.appex */; @@ -4108,7 +4132,7 @@ D73E5F9C2C6AA8E3007EB227 /* SwipeActions */, D70D909B2CDED7B200CD0534 /* CodeScanner */, D7C48C0E2D12E35600A3BACF /* SwiftyCrop */, - D7BEE6F62D37B21400CF659F /* NostrSDK */, + D7DB1FE72D5A9F5300CF06DA /* CryptoSwift */, ); productName = "highlighter action extension"; productReference = D703D7172C66E47100A400EA /* HighlighterActionExtension.appex */; @@ -4131,6 +4155,7 @@ packageProductDependencies = ( D789D11F2AFEFBF20083A7AB /* secp256k1 */, D7EDED302B1290B80018B19C /* MarkdownUI */, + D7DB1FE92D5A9F5A00CF06DA /* CryptoSwift */, ); productName = DamusNotificationService; productReference = D79C4C142AFEB061003A41B4 /* DamusNotificationService.appex */; @@ -4218,7 +4243,7 @@ D78DB8572C1CE9CA00F0AB12 /* XCRemoteSwiftPackageReference "SwipeActions" */, D70D90962CDED61800CD0534 /* XCRemoteSwiftPackageReference "CodeScanner" */, D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */, - D7BEE6F12D37AE1B00CF659F /* XCRemoteSwiftPackageReference "nostr-sdk-swift" */, + D7DB1FE22D5A9AC900CF06DA /* XCRemoteSwiftPackageReference "CryptoSwift" */, ); productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */; projectDirPath = ""; @@ -4258,7 +4283,9 @@ buildActionMask = 2147483647; files = ( E06336AB2B75850100A88E6B /* img_with_location.jpeg in Resources */, + D7DB1FF12D5AC5D700CF06DA /* nip44.vectors.json in Resources */, 4C0C039A2A61E27B0098B3B8 /* bool_setting.wasm in Resources */, + D7DB1FF32D5AC5EA00CF06DA /* LICENSES in Resources */, 4C0C03992A61E27B0098B3B8 /* primal.wasm in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -4718,6 +4745,7 @@ 50A60D142A28BEEE00186190 /* RelayLog.swift in Sources */, D7EDED212B117DCA0018B19C /* SequenceUtils.swift in Sources */, BA37598A2ABCCDE40018D73B /* ImageResizer.swift in Sources */, + D7DB1FDE2D5A78CE00CF06DA /* NIP44.swift in Sources */, B51C1CEB2B55A60A00E312A9 /* MuteDurationMenu.swift in Sources */, 4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */, 4C32B9512A9AD44700DC3548 /* FlatbuffersErrors.swift in Sources */, @@ -4831,6 +4859,7 @@ 3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */, D72A2D072AD9C1FB002AFF62 /* MockProfiles.swift in Sources */, B5A75C2A2B546D94007AFBC0 /* MuteItemTests.swift in Sources */, + D7DB1FEE2D5AC51B00CF06DA /* NIP44v2EncryptionTests.swift in Sources */, 4C4F14A72A2A61A30045A0B9 /* NostrScriptTests.swift in Sources */, D78525252A7B2EA4002FA637 /* NoteContentViewTests.swift in Sources */, 4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */, @@ -5036,6 +5065,7 @@ 82D6FB452CD99F7900C925F4 /* InputDismissKeyboard.swift in Sources */, 82D6FB462CD99F7900C925F4 /* Constants.swift in Sources */, 82D6FB472CD99F7900C925F4 /* LinkView.swift in Sources */, + D7DB1FDF2D5A78CE00CF06DA /* NIP44.swift in Sources */, 82D6FB482CD99F7900C925F4 /* PreviewCache.swift in Sources */, 82D6FB492CD99F7900C925F4 /* Theme.swift in Sources */, 82D6FB4A2CD99F7900C925F4 /* NIP05.swift in Sources */, @@ -5767,6 +5797,7 @@ D703D7712C670B6D00A400EA /* NdbProfile.swift in Sources */, D703D7A22C670E1A00A400EA /* list.c in Sources */, D703D7A42C670E3C00A400EA /* midl.c in Sources */, + D7DB1FE02D5A78CE00CF06DA /* NIP44.swift in Sources */, D703D7982C670DF200A400EA /* utf8.c in Sources */, D703D78B2C670C9500A400EA /* MakeZapRequest.swift in Sources */, D703D7862C670C6500A400EA /* NewUnmutesNotify.swift in Sources */, @@ -6777,20 +6808,20 @@ minimumVersion = 1.14.1; }; }; - D7BEE6F12D37AE1B00CF659F /* XCRemoteSwiftPackageReference "nostr-sdk-swift" */ = { + D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/rust-nostr/nostr-sdk-swift"; + repositoryURL = "https://github.com/benedom/SwiftyCrop"; requirement = { kind = revision; - revision = 27711a03ea7d977162595eea1d9b2d5a45f0b628; + revision = 454d0a0d4faf6f3a19c8d817ab9d7d27524bd79f; }; }; - D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */ = { + D7DB1FE22D5A9AC900CF06DA /* XCRemoteSwiftPackageReference "CryptoSwift" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/benedom/SwiftyCrop"; + repositoryURL = "https://github.com/krzyzanowskim/CryptoSwift.git"; requirement = { kind = revision; - revision = 454d0a0d4faf6f3a19c8d817ab9d7d27524bd79f; + revision = e74bbbfbef939224b242ae7c342a90e60b88b5ce; }; }; /* End XCRemoteSwiftPackageReference section */ @@ -6906,21 +6937,6 @@ package = D7A343EC2AD0D77C00CED48B /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */; productName = SnapshotTesting; }; - D7BEE6F22D37AE1B00CF659F /* NostrSDK */ = { - isa = XCSwiftPackageProductDependency; - package = D7BEE6F12D37AE1B00CF659F /* XCRemoteSwiftPackageReference "nostr-sdk-swift" */; - productName = NostrSDK; - }; - D7BEE6F42D37B20400CF659F /* NostrSDK */ = { - isa = XCSwiftPackageProductDependency; - package = D7BEE6F12D37AE1B00CF659F /* XCRemoteSwiftPackageReference "nostr-sdk-swift" */; - productName = NostrSDK; - }; - D7BEE6F62D37B21400CF659F /* NostrSDK */ = { - isa = XCSwiftPackageProductDependency; - package = D7BEE6F12D37AE1B00CF659F /* XCRemoteSwiftPackageReference "nostr-sdk-swift" */; - productName = NostrSDK; - }; D7C48C0A2D12DE0C00A3BACF /* SwiftyCrop */ = { isa = XCSwiftPackageProductDependency; package = D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */; @@ -6936,6 +6952,26 @@ package = D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */; productName = SwiftyCrop; }; + D7DB1FE32D5A9AC900CF06DA /* CryptoSwift */ = { + isa = XCSwiftPackageProductDependency; + package = D7DB1FE22D5A9AC900CF06DA /* XCRemoteSwiftPackageReference "CryptoSwift" */; + productName = CryptoSwift; + }; + D7DB1FE72D5A9F5300CF06DA /* CryptoSwift */ = { + isa = XCSwiftPackageProductDependency; + package = D7DB1FE22D5A9AC900CF06DA /* XCRemoteSwiftPackageReference "CryptoSwift" */; + productName = CryptoSwift; + }; + D7DB1FE92D5A9F5A00CF06DA /* CryptoSwift */ = { + isa = XCSwiftPackageProductDependency; + package = D7DB1FE22D5A9AC900CF06DA /* XCRemoteSwiftPackageReference "CryptoSwift" */; + productName = CryptoSwift; + }; + D7DB1FEB2D5A9F6500CF06DA /* CryptoSwift */ = { + isa = XCSwiftPackageProductDependency; + package = D7DB1FE22D5A9AC900CF06DA /* XCRemoteSwiftPackageReference "CryptoSwift" */; + productName = CryptoSwift; + }; D7EDED242B117F7C0018B19C /* MarkdownUI */ = { isa = XCSwiftPackageProductDependency; package = 4C27C9302A64766F007DBC75 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */; diff --git a/damus.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/damus.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "fa2b0ad84b4bd1a962ffbe49810548db7c9d7131f4a1fd4b4af06ff4c6de0a44", + "originHash" : "085cf0f645323bf77edb52886489bf77b309a0a2d2b78a54beaf8520b540d596", "pins" : [ { "identity" : "codescanner", @@ -10,6 +10,14 @@ } }, { + "identity" : "cryptoswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/krzyzanowskim/CryptoSwift.git", + "state" : { + "revision" : "e74bbbfbef939224b242ae7c342a90e60b88b5ce" + } + }, + { "identity" : "emojikit", "kind" : "remoteSourceControl", "location" : "https://github.com/tyiu/EmojiKit", @@ -46,14 +54,6 @@ } }, { - "identity" : "nostr-sdk-swift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/rust-nostr/nostr-sdk-swift", - "state" : { - "revision" : "27711a03ea7d977162595eea1d9b2d5a45f0b628" - } - }, - { "identity" : "secp256k1.swift", "kind" : "remoteSourceControl", "location" : "https://github.com/jb55/secp256k1.swift", diff --git a/damus/NIP37/NIP37Draft.swift b/damus/NIP37/NIP37Draft.swift @@ -4,7 +4,6 @@ // // Created by Daniel D’Aquino on 2025-01-20. // -import NostrSDK import Foundation /// This models a NIP-37 draft. @@ -77,13 +76,7 @@ struct NIP37Draft { guard let note_json_string = String(data: note_json_data, encoding: .utf8) else { throw NIP37DraftEventError.encoding_error } - guard let secret_key = SecretKey.from(privkey: keypair.privkey) else { - throw NIP37DraftEventError.invalid_keypair - } - guard let pubkey = PublicKey.from(pubkey: keypair.pubkey) else { - throw NIP37DraftEventError.invalid_keypair - } - guard let contents = try? nip44Encrypt(secretKey: secret_key, publicKey: pubkey, content: note_json_string, version: Nip44Version.v2) else { + guard let contents = try? NIP44v2Encryption.encrypt(plaintext: note_json_string, privateKeyA: keypair.privkey, publicKeyB: keypair.pubkey) else { return nil } var tags = [ @@ -111,16 +104,10 @@ struct NIP37Draft { static func unwrap(note: NdbNote, keypair: FullKeypair) throws -> NdbNote? { let wrapped_note = note guard wrapped_note.known_kind == .draft else { return nil } - guard let private_key = SecretKey.from(privkey: keypair.privkey) else { - throw NIP37DraftEventError.invalid_keypair - } - guard let pubkey = PublicKey.from(pubkey: keypair.pubkey) else { - throw NIP37DraftEventError.invalid_keypair - } - guard let draft_event_json = try? nip44Decrypt( - secretKey: private_key, - publicKey: pubkey, - payload: wrapped_note.content + guard let draft_event_json = try? NIP44v2Encryption.decrypt( + payload: wrapped_note.content, + privateKeyA: keypair.privkey, + publicKeyB: keypair.pubkey ) else { return nil } return NdbNote.owned_from_json(json: draft_event_json) } @@ -130,17 +117,3 @@ struct NIP37Draft { case encoding_error } } - -// MARK: - Convenience extensions - -fileprivate extension PublicKey { - static func from(pubkey: Pubkey) -> PublicKey? { - return try? PublicKey.parse(publicKey: pubkey.hex()) - } -} - -fileprivate extension SecretKey { - static func from(privkey: Privkey) -> SecretKey? { - return try? SecretKey.parse(secretKey: privkey.hex()) - } -} diff --git a/damus/NIP44/NIP44.swift b/damus/NIP44/NIP44.swift @@ -0,0 +1,357 @@ +// +// NIP44.swift +// damus +// +// Based on NIP44v2Encrypting.swift created by Terry Yiu on 3/16/24, from https://github.com/nostr-sdk/nostr-sdk-ios, which is MIT licensed. +// +// MIT License +// +// Copyright (c) 2023 Nostr SDK +// +// 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. +// +// +// Adapted by Daniel D’Aquino on 2025-02-10. +// +import Foundation +import CryptoKit +import CryptoSwift +import secp256k1 + +struct NIP44v2Encryption { + + /// Produces a `String` containing `plaintext` that has been encrypted using the `privateKey` of user A and the `publicKey` of user B. + /// + /// The result is non-deterministic because a cryptographically secure pseudorandom generated nonce is used each time, + /// but can be decrypted deterministically with a call to ``NIP44v2Encryption/decrypt(payload:privateKeyA:publicKeyB:)``, + /// where user A and user B are interchangeable. + /// + /// This function can `throw` an error from ``EncryptionError`` if it fails to encrypt the plaintext. + /// + /// - Parameters: + /// - plaintext: The plaintext to encrypt. + /// - privateKeyA: The private key of user A. + /// - publicKeyB: The public key of user B. + /// - Returns: The encrypted ciphertext. + static func encrypt(plaintext: String, privateKeyA: Privkey, publicKeyB: Pubkey) throws -> String { + let conversationKey = try conversationKey(privateKeyA: privateKeyA, publicKeyB: publicKeyB) + + return try encrypt(plaintext: plaintext, conversationKey: conversationKey) + } + + /// Produces a `String` containing `payload` that has been decrypted using the `privateKey` of user A and the `publicKey` of user B, + /// and the result is identical to if the `privateKey` of user B and `publicKey` of user A were used to decrypt `payload` instead. + /// + /// Any ciphertext returned from the call to ``NIP44v2Encryption/encrypt(plaintext:privateKeyA:publicKeyB:)`` + /// can be decrypted, where user A and B are interchangeable. + /// + /// This function can `throw` an error from ``EncryptionError`` if it fails to decrypt the payload. + /// + /// - Parameters: + /// - payload: The payload to decrypt. + /// - privateKeyA: The private key of user A. + /// - publicKeyB: The public key of user B. + /// - Returns: The decrypted plaintext message. + static func decrypt(payload: String, privateKeyA: Privkey, publicKeyB: Pubkey) throws -> String { + let conversationKey = try conversationKey(privateKeyA: privateKeyA, publicKeyB: publicKeyB) + + return try decrypt(payload: payload, conversationKey: conversationKey) + } + + /// Calculates length of the padded byte array. + static func calculatePaddedLength(_ unpaddedLength: Int) throws -> Int { + guard unpaddedLength > 0 else { + throw EncryptionError.unpaddedLengthInvalid(unpaddedLength) + } + if unpaddedLength <= 32 { + return 32 + } + + let nextPower = 1 << (Int(floor(log2(Double(unpaddedLength) - 1))) + 1) + let chunk: Int + + if nextPower <= 256 { + chunk = 32 + } else { + chunk = nextPower / 8 + } + + return chunk * (Int(floor((Double(unpaddedLength) - 1) / Double(chunk))) + 1) + } + + /// Converts unpadded plaintext to padded bytes. + static func pad(_ plaintext: String) throws -> Data { + guard let unpadded = plaintext.data(using: .utf8) else { + throw EncryptionError.utf8EncodingFailed + } + + let unpaddedLength = unpadded.count + + guard 1...65535 ~= unpaddedLength else { + throw EncryptionError.plaintextLengthInvalid(unpaddedLength) + } + + var prefix = Data(count: 2) + prefix.withUnsafeMutableBytes { (ptr: UnsafeMutableRawBufferPointer) in + ptr.storeBytes(of: UInt16(unpaddedLength).bigEndian, as: UInt16.self) + } + + let suffix = Data(count: try calculatePaddedLength(unpaddedLength) - unpaddedLength) + + return prefix + unpadded + suffix + } + + /// Converts padded bytes to unpadded plaintext. + static func unpad(_ padded: Data) throws -> String { + guard padded.count >= 2 else { + throw EncryptionError.paddingInvalid + } + + let unpaddedLength = (Int(padded[0]) << 8) | Int(padded[1]) + + guard 2+unpaddedLength <= padded.count else { + throw EncryptionError.paddingInvalid + } + + let unpadded = toBytes(from: padded)[2..<2+unpaddedLength] + let paddedLength = try calculatePaddedLength(unpaddedLength) + + guard unpaddedLength > 0, + unpadded.count == unpaddedLength, + padded.count == 2 + paddedLength, + let result = String(data: Data(unpadded), encoding: .utf8) else { + throw EncryptionError.paddingInvalid + } + + return result + } + + static func decodePayload(_ payload: String) throws -> DecodedPayload { + let payloadLength = payload.count + + guard payloadLength > 0 && payload.first != "#" else { + throw EncryptionError.unknownVersion() + } + guard 132...87472 ~= payloadLength else { + throw EncryptionError.payloadSizeInvalid(payloadLength) + } + + guard let data = Data(base64Encoded: payload) else { + throw EncryptionError.base64EncodingFailed + } + + let dataLength = data.count + + guard 99...65603 ~= dataLength else { + throw EncryptionError.dataSizeInvalid(dataLength) + } + + guard let version = data.first else { + throw EncryptionError.unknownVersion() + } + + guard version == 2 else { + throw EncryptionError.unknownVersion(Int(version)) + } + + let nonce = data[data.index(data.startIndex, offsetBy: 1)..<data.index(data.startIndex, offsetBy: 33)] + let ciphertext = data[data.index(data.startIndex, offsetBy: 33)..<data.index(data.startIndex, offsetBy: dataLength - 32)] + let mac = data[data.index(data.startIndex, offsetBy: dataLength - 32)..<data.index(data.startIndex, offsetBy: dataLength)] + + return DecodedPayload(nonce: nonce, ciphertext: ciphertext, mac: mac) + } + + static func hmacAad(key: Data, message: Data, aad: Data) throws -> Data { + guard aad.count == 32 else { + throw EncryptionError.aadLengthInvalid(aad.count) + } + + let combined = aad + message + + return Data(CryptoKit.HMAC<CryptoKit.SHA256>.authenticationCode(for: combined, using: SymmetricKey(data: key))) + } + + static func toBytes(from data: Data) -> [UInt8] { + data.withUnsafeBytes { bytesPointer in Array(bytesPointer) } + } + + static func preparePublicKeyBytes(from publicKey: Pubkey) throws -> [UInt8] { + let publicKeyBytes = publicKey.bytes + + let prefix = Data([2]) + let prefixBytes = toBytes(from: prefix) + + return prefixBytes + publicKeyBytes + } + + static func parsePublicKey(from bytes: [UInt8]) throws -> secp256k1_pubkey { + var publicKey = secp256k1_pubkey() + guard secp256k1_ec_pubkey_parse(secp256k1.Context.raw, &publicKey, bytes, bytes.count) == 1 else { + throw EncryptionError.publicKeyInvalid + } + return publicKey + } + + static func computeSharedSecret(using publicKey: secp256k1_pubkey, and privateKeyBytes: [UInt8]) throws -> [UInt8] { + var sharedSecret = [UInt8](repeating: 0, count: 32) + var mutablePublicKey = publicKey + + // Multiplication of point B by scalar a (a ⋅ B), defined in [BIP340](https://github.com/bitcoin/bips/blob/e918b50731397872ad2922a1b08a5a4cd1d6d546/bip-0340.mediawiki). + // The operation produces a shared point, and we encode the shared point's 32-byte x coordinate, using method bytes(P) from BIP340. + // Private and public keys must be validated as per BIP340: pubkey must be a valid, on-curve point, and private key must be a scalar in range [1, secp256k1_order - 1] + guard secp256k1_ecdh(secp256k1.Context.raw, &sharedSecret, &mutablePublicKey, privateKeyBytes, { (output, x32, _, _) in + memcpy(output, x32, 32) + return 1 + }, nil) != 0 else { + throw EncryptionError.sharedSecretComputationFailed + } + return sharedSecret + } + + /// Calculates long-term key between users A and B. + /// The conversation key of A's private key and B's public key is equal to the conversation key of B's private key and A's public key. + static func conversationKey(privateKeyA: Privkey, publicKeyB: Pubkey) throws -> ContiguousBytes { + let privateKeyABytes = privateKeyA.bytes + let publicKeyBBytes = try preparePublicKeyBytes(from: publicKeyB) + let parsedPublicKeyB = try parsePublicKey(from: publicKeyBBytes) + let sharedSecret = try computeSharedSecret(using: parsedPublicKeyB, and: privateKeyABytes) + + return CryptoKit.HKDF<CryptoKit.SHA256>.extract(inputKeyMaterial: SymmetricKey(data: sharedSecret), salt: Data("nip44-v2".utf8)) + } + + /// Calculates unique per-message key. + static func messageKeys(conversationKey: ContiguousBytes, nonce: Data) throws -> MessageKeys { + let conversationKeyByteCount = conversationKey.bytes.count + guard conversationKeyByteCount == 32 else { + throw EncryptionError.conversationKeyLengthInvalid(conversationKeyByteCount) + } + + guard nonce.count == 32 else { + throw EncryptionError.nonceLengthInvalid(nonce.count) + } + + let keys = CryptoKit.HKDF<CryptoKit.SHA256>.expand(pseudoRandomKey: conversationKey, info: nonce, outputByteCount: 76) + let keysBytes = keys.bytes + + let chaChaKey = Data(keysBytes[0..<32]) + let chaChaNonce = Data(keysBytes[32..<44]) + let hmacKey = Data(keysBytes[44..<76]) + + return MessageKeys(chaChaKey: chaChaKey, chaChaNonce: chaChaNonce, hmacKey: hmacKey) + } + + static func encrypt(plaintext: String, conversationKey: ContiguousBytes, nonce: Data? = nil) throws -> String { + let nonceData: Data + if let nonce { + nonceData = nonce + } else { + // Fetches randomness from CSPRNG. + nonceData = Data.secureRandomBytes(count: 32) + } + + let messageKeys = try messageKeys(conversationKey: conversationKey, nonce: nonceData) + let padded = try pad(plaintext) + let paddedBytes = toBytes(from: padded) + + let chaChaKey = toBytes(from: messageKeys.chaChaKey) + let chaChaNonce = toBytes(from: messageKeys.chaChaNonce) + + let ciphertext = try ChaCha20(key: chaChaKey, iv: chaChaNonce).encrypt(paddedBytes) + let ciphertextData = Data(ciphertext) + + let mac = try hmacAad(key: messageKeys.hmacKey, message: ciphertextData, aad: nonceData) + + let data = Data([2]) + nonceData + ciphertextData + mac + return data.base64EncodedString() + } + + static func decrypt(payload: String, conversationKey: ContiguousBytes) throws -> String { + let decodedPayload = try decodePayload(payload) + let nonce = decodedPayload.nonce + let ciphertext = decodedPayload.ciphertext + let ciphertextBytes = toBytes(from: ciphertext) + let mac = decodedPayload.mac + + let messageKeys = try messageKeys(conversationKey: conversationKey, nonce: nonce) + + let calculatedMac = try hmacAad(key: messageKeys.hmacKey, message: ciphertext, aad: nonce) + + guard calculatedMac == mac else { + throw EncryptionError.macInvalid + } + + let chaChaNonce = toBytes(from: messageKeys.chaChaNonce) + let chaChaKey = toBytes(from: messageKeys.chaChaKey) + + let paddedPlaintext = try ChaCha20(key: chaChaKey, iv: chaChaNonce).decrypt(ciphertextBytes) + let paddedPlaintextData = Data(paddedPlaintext.bytes) + + return try unpad(paddedPlaintextData) + } +} + + +// MARK: - Helper structures and extensions + +extension Data { + /// Random data of a given size, from CSPRNG + /// - Parameter count: The size of the data, in bytes + /// - Returns: Bytes randomly generated from CSPRNG + static func secureRandomBytes(count: Int) -> Data { + var bytes = [Int8](repeating: 0, count: count) + guard SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes) == errSecSuccess else { + fatalError("can't copy secure random data") + } + return Data(bytes: bytes, count: count) + } +} + +extension NIP44v2Encryption { + struct DecodedPayload { + let nonce: Data + let ciphertext: Data + let mac: Data + } + + struct MessageKeys { + let chaChaKey: Data + let chaChaNonce: Data + let hmacKey: Data + } + + public enum EncryptionError: Error { + case aadLengthInvalid(Int) + case base64EncodingFailed + case chaCha20DecryptionFailed + case chaCha20EncryptionFailed + case conversationKeyLengthInvalid(Int) + case dataSizeInvalid(Int) + case macInvalid + case nonceLengthInvalid(Int) + case paddingInvalid + case payloadSizeInvalid(Int) + case plaintextLengthInvalid(Int) + case privateKeyInvalid + case publicKeyInvalid + case sharedSecretComputationFailed + case unknownVersion(Int? = nil) + case unpaddedLengthInvalid(Int) + case utf8EncodingFailed + } +} diff --git a/damus/Util/Keys.swift b/damus/Util/Keys.swift @@ -21,6 +21,17 @@ let ANON_PUBKEY = Pubkey(Data([ struct FullKeypair: Equatable { let pubkey: Pubkey let privkey: Privkey + + init(pubkey: Pubkey, privkey: Privkey) { + self.pubkey = pubkey + self.privkey = privkey + } + + init?(privkey: Privkey) { + self.privkey = privkey + guard let pubkey = privkey_to_pubkey_raw(sec: privkey.bytes) else { return nil } + self.pubkey = pubkey + } func to_keypair() -> Keypair { return Keypair(pubkey: pubkey, privkey: privkey) diff --git a/damusTests/LICENSES b/damusTests/LICENSES @@ -0,0 +1,23 @@ +Some of the fixtures in this folder are taken from https://github.com/nostr-sdk/nostr-sdk-ios under the MIT license: + +// MIT License +// +// Copyright (c) 2023 Nostr SDK +// +// 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/damusTests/NIP44v2EncryptionTests.swift b/damusTests/NIP44v2EncryptionTests.swift @@ -0,0 +1,432 @@ +// +// NIP44v2EncryptionTests.swift +// damus +// +// Based on NIP44v2EncryptingTests.swift, taken from https://github.com/nostr-sdk/nostr-sdk-ios under the MIT license: +// +// MIT License +// +// Copyright (c) 2023 Nostr SDK +// +// 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. +// +// +// Adapted by Daniel D’Aquino for damus on 2025-02-10. +// +import XCTest +import CryptoKit +@testable import damus + +final class NIP44v2EncryptingTests: XCTestCase { + + private lazy var vectors: NIP44Vectors = try! decodeFixture(filename: "nip44.vectors") // swiftlint:disable:this force_try + + /// Calculate the conversation key from secret key, sec1, and public key, pub2. + func testValidConversationKey() throws { + let conversationKeyVectors = try XCTUnwrap(vectors.v2.valid.getConversationKey) + + try conversationKeyVectors.forEach { vector in + let expectedConversationKey = try XCTUnwrap(vector.conversationKey) + let privateKeyA = try XCTUnwrap(Privkey(hex: vector.sec1)) + let publicKeyB = try XCTUnwrap(Pubkey(hex: vector.pub2)) + let conversationKeyBytes = try NIP44v2Encryption.conversationKey( + privateKeyA: privateKeyA, + publicKeyB: publicKeyB + ).bytes + let conversationKey = Data(conversationKeyBytes).hexString + XCTAssertEqual(conversationKey, expectedConversationKey) + } + } + + /// Calculate ChaCha key, ChaCha nonce, and HMAC key from conversation key and nonce. + func testValidMessageKeys() throws { + let messageKeyVectors = try XCTUnwrap(vectors.v2.valid.getMessageKeys) + let conversationKey = messageKeyVectors.conversationKey + let conversationKeyBytes = try XCTUnwrap(conversationKey.hexDecoded?.bytes) + let keys = messageKeyVectors.keys + + try keys.forEach { vector in + let nonce = try XCTUnwrap(vector.nonce.hexDecoded) + let messageKeys = try NIP44v2Encryption.messageKeys(conversationKey: conversationKeyBytes, nonce: nonce) + XCTAssertEqual(messageKeys.chaChaKey.hexString, vector.chaChaKey) + XCTAssertEqual(messageKeys.chaChaNonce.hexString, vector.chaChaNonce) + XCTAssertEqual(messageKeys.hmacKey.hexString, vector.hmacKey) + } + } + + /// Take unpadded length (first value), calculate padded length (second value). + func testValidCalculatePaddedLength() throws { + let calculatePaddedLengthVectors = try XCTUnwrap(vectors.v2.valid.calculatePaddedLength) + try calculatePaddedLengthVectors.forEach { vector in + XCTAssertEqual(vector.count, 2) + let paddedLength = try NIP44v2Encryption.calculatePaddedLength(vector[0]) + XCTAssertEqual(paddedLength, vector[1]) + } + } + + /// Emulate real conversation with a hardcoded nonce. + /// Calculate pub2 from sec2, verify conversation key from (sec1, pub2), encrypt, verify payload. + /// Then calculate pub1 from sec1, verify conversation key from (sec2, pub1), decrypt, verify plaintext. + func testValidEncryptDecrypt() throws { + let encryptDecryptVectors = try XCTUnwrap(vectors.v2.valid.encryptDecrypt) + try encryptDecryptVectors.forEach { vector in + let sec1 = vector.sec1 + let sec2 = vector.sec2 + let expectedConversationKey = vector.conversationKey + let nonce = try XCTUnwrap(vector.nonce.hexDecoded) + let plaintext = vector.plaintext + let payload = vector.payload + + let privateKeyA = try XCTUnwrap(Privkey(hex: vector.sec1)) + let privateKeyB = try XCTUnwrap(Privkey(hex: vector.sec2)) + let keypair1 = try XCTUnwrap(FullKeypair(privkey: privateKeyA)) + let keypair2 = try XCTUnwrap(FullKeypair(privkey: privateKeyB)) + + // Conversation key from sec1 and pub2. + let conversationKey1Bytes = try NIP44v2Encryption.conversationKey( + privateKeyA: keypair1.privkey, + publicKeyB: keypair2.pubkey + ).bytes + XCTAssertEqual(expectedConversationKey, Data(conversationKey1Bytes).hexString) + + // Verify payload. + let ciphertext = try NIP44v2Encryption.encrypt( + plaintext: plaintext, + conversationKey: conversationKey1Bytes, + nonce: nonce + ) + XCTAssertEqual(payload, ciphertext) + + // Conversation key from sec2 and pub1. + let conversationKey2Bytes = try NIP44v2Encryption.conversationKey( + privateKeyA: keypair2.privkey, + publicKeyB: keypair1.pubkey + ).bytes + XCTAssertEqual(expectedConversationKey, Data(conversationKey2Bytes).hexString) + + // Verify that decrypted data equals the plaintext that we started off with. + let decrypted = try NIP44v2Encryption.decrypt(payload: payload, conversationKey: conversationKey2Bytes) + XCTAssertEqual(decrypted, plaintext) + } + } + + /// Same as previous step, but instead of a full plaintext and payload, their checksum is provided. + func testValidEncryptDecryptLongMessage() throws { + let encryptDecryptVectors = try XCTUnwrap(vectors.v2.valid.encryptDecryptLongMessage) + try encryptDecryptVectors.forEach { vector in + let conversationKey = vector.conversationKey + let conversationKeyData = try XCTUnwrap(conversationKey.hexDecoded) + let conversationKeyBytes = conversationKeyData.bytes + + let nonce = try XCTUnwrap(vector.nonce.hexDecoded) + let expectedPlaintextSHA256 = vector.plaintextSHA256 + + let plaintext = String(repeating: vector.pattern, count: vector.repeatCount) + let plaintextData = try XCTUnwrap(plaintext.data(using: .utf8)) + let plaintextSHA256 = plaintextData.sha256() + + XCTAssertEqual(plaintextSHA256.hexString, expectedPlaintextSHA256) + + let payloadSHA256 = vector.payloadSHA256 + + let ciphertext = try NIP44v2Encryption.encrypt( + plaintext: plaintext, + conversationKey: conversationKeyBytes, + nonce: nonce + ) + let ciphertextData = try XCTUnwrap(ciphertext.data(using: .utf8)) + let ciphertextSHA256 = ciphertextData.sha256().hexString + XCTAssertEqual(ciphertextSHA256, payloadSHA256) + + let decrypted = try NIP44v2Encryption.decrypt(payload: ciphertext, conversationKey: conversationKeyBytes) + XCTAssertEqual(decrypted, plaintext) + } + } + + /// Emulate real conversation with only the public encrypt and decrypt functions, + /// where the nonce used for encryption is a cryptographically secure pseudorandom generated series of bytes. + func testValidEncryptDecryptRandomNonce() throws { + let encryptDecryptVectors = try XCTUnwrap(vectors.v2.valid.encryptDecrypt) + try encryptDecryptVectors.forEach { vector in + let sec1 = vector.sec1 + let sec2 = vector.sec2 + let plaintext = vector.plaintext + + let privateKeyA = try XCTUnwrap(Privkey(hex: vector.sec1)) + let privateKeyB = try XCTUnwrap(Privkey(hex: vector.sec2)) + + let keypair1 = try XCTUnwrap(FullKeypair(privkey: privateKeyA)) + let keypair2 = try XCTUnwrap(FullKeypair(privkey: privateKeyB)) + + // Encrypt plaintext with user A's private key and user B's public key. + let ciphertext = try NIP44v2Encryption.encrypt( + plaintext: plaintext, + privateKeyA: keypair1.privkey, + publicKeyB: keypair2.pubkey + ) + + // Decrypt ciphertext with user B's private key and user A's public key. + let decrypted = try NIP44v2Encryption.decrypt(payload: ciphertext, privateKeyA: keypair2.privkey, publicKeyB: keypair1.pubkey) + XCTAssertEqual(decrypted, plaintext) + } + } + + /// Encrypting a plaintext message that is not at a minimum of 1 byte and maximum of 65535 bytes must throw an error. + func testInvalidEncryptMessageLengths() throws { + let encryptMessageLengthsVectors = try XCTUnwrap(vectors.v2.invalid.encryptMessageLengths) + try encryptMessageLengthsVectors.forEach { length in + let randomBytes = Data.secureRandomBytes(count: 32) + XCTAssertThrowsError(try NIP44v2Encryption.encrypt(plaintext: String(repeating: "a", count: length), conversationKey: randomBytes)) + } + } + + /// Calculating conversation key must throw an error. + func testInvalidConversationKey() throws { + let conversationKeyVectors = try XCTUnwrap(vectors.v2.invalid.getConversationKey) + + try conversationKeyVectors.forEach { vector in + let privateKeyA = try XCTUnwrap(Privkey(hex: vector.sec1)) + let publicKeyB = try XCTUnwrap(Pubkey(hex: vector.pub2)) + XCTAssertThrowsError(try NIP44v2Encryption.conversationKey(privateKeyA: privateKeyA, publicKeyB: publicKeyB), vector.note ?? "") + } + } + + /// Decrypting message content must throw an error + func testInvalidDecrypt() throws { + let decryptVectors = try XCTUnwrap(vectors.v2.invalid.decrypt) + try decryptVectors.forEach { vector in + let conversationKey = try XCTUnwrap(vector.conversationKey.hexDecoded).bytes + let payload = vector.payload + XCTAssertThrowsError(try NIP44v2Encryption.decrypt(payload: payload, conversationKey: conversationKey), vector.note) + } + } + +} + + +struct NIP44Vectors: Decodable { + let v2: NIP44VectorsV2 + + private enum CodingKeys: String, CodingKey { + case v2 + } +} + +struct NIP44VectorsV2: Decodable { + let valid: NIP44VectorsV2Valid + let invalid: NIP44VectorsV2Invalid + + private enum CodingKeys: String, CodingKey { + case valid + case invalid + } +} + +struct NIP44VectorsV2Valid: Decodable { + let getConversationKey: [NIP44VectorsV2GetConversationKey] + let getMessageKeys: NIP44VectorsV2GetMessageKeys + let calculatePaddedLength: [[Int]] + let encryptDecrypt: [NIP44VectorsV2EncryptDecrypt] + let encryptDecryptLongMessage: [NIP44VectorsV2EncryptDecryptLongMessage] + + private enum CodingKeys: String, CodingKey { + case getConversationKey = "get_conversation_key" + case getMessageKeys = "get_message_keys" + case calculatePaddedLength = "calc_padded_len" + case encryptDecrypt = "encrypt_decrypt" + case encryptDecryptLongMessage = "encrypt_decrypt_long_msg" + } +} + +struct NIP44VectorsV2Invalid: Decodable { + let encryptMessageLengths: [Int] + let getConversationKey: [NIP44VectorsV2GetConversationKey] + let decrypt: [NIP44VectorsDecrypt] + + private enum CodingKeys: String, CodingKey { + case encryptMessageLengths = "encrypt_msg_lengths" + case getConversationKey = "get_conversation_key" + case decrypt + } +} + +struct NIP44VectorsDecrypt: Decodable { + let conversationKey: String + let nonce: String + let plaintext: String + let payload: String + let note: String + + private enum CodingKeys: String, CodingKey { + case conversationKey = "conversation_key" + case nonce + case plaintext + case payload + case note + } +} + +struct NIP44VectorsV2GetConversationKey: Decodable { + let sec1: String + let pub2: String + let conversationKey: String? + let note: String? + + private enum CodingKeys: String, CodingKey { + case sec1 + case pub2 + case conversationKey = "conversation_key" + case note + } +} + +struct NIP44VectorsV2GetMessageKeys: Decodable { + let conversationKey: String + let keys: [NIP44VectorsV2MessageKeys] + + private enum CodingKeys: String, CodingKey { + case conversationKey = "conversation_key" + case keys + } +} + +struct NIP44VectorsV2MessageKeys: Decodable { + let nonce: String + let chaChaKey: String + let chaChaNonce: String + let hmacKey: String + + private enum CodingKeys: String, CodingKey { + case nonce + case chaChaKey = "chacha_key" + case chaChaNonce = "chacha_nonce" + case hmacKey = "hmac_key" + } +} + +struct NIP44VectorsV2EncryptDecrypt: Decodable { + let sec1: String + let sec2: String + let conversationKey: String + let nonce: String + let plaintext: String + let payload: String + + private enum CodingKeys: String, CodingKey { + case sec1 + case sec2 + case conversationKey = "conversation_key" + case nonce + case plaintext + case payload + } +} + +struct NIP44VectorsV2EncryptDecryptLongMessage: Decodable { + let conversationKey: String + let nonce: String + let pattern: String + let repeatCount: Int + let plaintextSHA256: String + let payloadSHA256: String + + private enum CodingKeys: String, CodingKey { + case conversationKey = "conversation_key" + case nonce + case pattern + case repeatCount = "repeat" + case plaintextSHA256 = "plaintext_sha256" + case payloadSHA256 = "payload_sha256" + } +} + +fileprivate extension Data { + var hexString: String { + let hexDigits = Array("0123456789abcdef".utf16) + var hexChars = [UTF16.CodeUnit]() + hexChars.reserveCapacity(bytes.count * 2) + + for byte in self { + let (index1, index2) = Int(byte).quotientAndRemainder(dividingBy: 16) + hexChars.append(hexDigits[index1]) + hexChars.append(hexDigits[index2]) + } + + return String(utf16CodeUnits: hexChars, count: hexChars.count) + } +} + +extension String { + var hexDecoded: Data? { + guard self.count.isMultiple(of: 2) else { return nil } + + // https://stackoverflow.com/a/62517446/982195 + let stringArray = Array(self) + var data = Data() + for i in stride(from: 0, to: count, by: 2) { + let pair = String(stringArray[i]) + String(stringArray[i + 1]) + if let byteNum = UInt8(pair, radix: 16) { + let byte = Data([byteNum]) + data.append(byte) + } else { + return nil + } + } + return data + } +} + +extension NIP44v2EncryptingTests { + func loadFixtureString(_ filename: String) throws -> String? { + let data = try self.loadFixtureData(filename) + + guard let originalString = String(data: data, encoding: .utf8) else { + throw FixtureLoadingError.decodingError + } + + let trimmedString = originalString.filter { !"\n\t\r".contains($0) } + return trimmedString + } + + func loadFixtureData(_ filename: String) throws -> Data { + guard let bundleData = try? readBundleFile(name: filename, ext: "json") else { + throw FixtureLoadingError.missingFile + } + return bundleData + } + + func decodeFixture<T: Decodable>(filename: String) throws -> T { + let data = try self.loadFixtureData(filename) + return try JSONDecoder().decode(T.self, from: data) + } + + func readBundleFile(name: String, ext: String) throws -> Data { + let bundle = Bundle(for: type(of: self)) + guard let fileURL = bundle.url(forResource: name, withExtension: ext) else { + throw CocoaError(.fileReadNoSuchFile) + } + + return try Data(contentsOf: fileURL) + } + + enum FixtureLoadingError: Error { + case missingFile + case decodingError + } +} diff --git a/damusTests/nip44.vectors.json b/damusTests/nip44.vectors.json @@ -0,0 +1,648 @@ +{ + "v2": { + "valid": { + "get_conversation_key": [ + { + "sec1": "315e59ff51cb9209768cf7da80791ddcaae56ac9775eb25b6dee1234bc5d2268", + "pub2": "c2f9d9948dc8c7c38321e4b85c8558872eafa0641cd269db76848a6073e69133", + "conversation_key": "3dfef0ce2a4d80a25e7a328accf73448ef67096f65f79588e358d9a0eb9013f1" + }, + { + "sec1": "a1e37752c9fdc1273be53f68c5f74be7c8905728e8de75800b94262f9497c86e", + "pub2": "03bb7947065dde12ba991ea045132581d0954f042c84e06d8c00066e23c1a800", + "conversation_key": "4d14f36e81b8452128da64fe6f1eae873baae2f444b02c950b90e43553f2178b" + }, + { + "sec1": "98a5902fd67518a0c900f0fb62158f278f94a21d6f9d33d30cd3091195500311", + "pub2": "aae65c15f98e5e677b5050de82e3aba47a6fe49b3dab7863cf35d9478ba9f7d1", + "conversation_key": "9c00b769d5f54d02bf175b7284a1cbd28b6911b06cda6666b2243561ac96bad7" + }, + { + "sec1": "86ae5ac8034eb2542ce23ec2f84375655dab7f836836bbd3c54cefe9fdc9c19f", + "pub2": "59f90272378089d73f1339710c02e2be6db584e9cdbe86eed3578f0c67c23585", + "conversation_key": "19f934aafd3324e8415299b64df42049afaa051c71c98d0aa10e1081f2e3e2ba" + }, + { + "sec1": "2528c287fe822421bc0dc4c3615878eb98e8a8c31657616d08b29c00ce209e34", + "pub2": "f66ea16104c01a1c532e03f166c5370a22a5505753005a566366097150c6df60", + "conversation_key": "c833bbb292956c43366145326d53b955ffb5da4e4998a2d853611841903f5442" + }, + { + "sec1": "49808637b2d21129478041813aceb6f2c9d4929cd1303cdaf4fbdbd690905ff2", + "pub2": "74d2aab13e97827ea21baf253ad7e39b974bb2498cc747cdb168582a11847b65", + "conversation_key": "4bf304d3c8c4608864c0fe03890b90279328cd24a018ffa9eb8f8ccec06b505d" + }, + { + "sec1": "af67c382106242c5baabf856efdc0629cc1c5b4061f85b8ceaba52aa7e4b4082", + "pub2": "bdaf0001d63e7ec994fad736eab178ee3c2d7cfc925ae29f37d19224486db57b", + "conversation_key": "a3a575dd66d45e9379904047ebfb9a7873c471687d0535db00ef2daa24b391db" + }, + { + "sec1": "0e44e2d1db3c1717b05ffa0f08d102a09c554a1cbbf678ab158b259a44e682f1", + "pub2": "1ffa76c5cc7a836af6914b840483726207cb750889753d7499fb8b76aa8fe0de", + "conversation_key": "a39970a667b7f861f100e3827f4adbf6f464e2697686fe1a81aeda817d6b8bdf" + }, + { + "sec1": "5fc0070dbd0666dbddc21d788db04050b86ed8b456b080794c2a0c8e33287bb6", + "pub2": "31990752f296dd22e146c9e6f152a269d84b241cc95bb3ff8ec341628a54caf0", + "conversation_key": "72c21075f4b2349ce01a3e604e02a9ab9f07e35dd07eff746de348b4f3c6365e" + }, + { + "sec1": "1b7de0d64d9b12ddbb52ef217a3a7c47c4362ce7ea837d760dad58ab313cba64", + "pub2": "24383541dd8083b93d144b431679d70ef4eec10c98fceef1eff08b1d81d4b065", + "conversation_key": "dd152a76b44e63d1afd4dfff0785fa07b3e494a9e8401aba31ff925caeb8f5b1" + }, + { + "sec1": "df2f560e213ca5fb33b9ecde771c7c0cbd30f1cf43c2c24de54480069d9ab0af", + "pub2": "eeea26e552fc8b5e377acaa03e47daa2d7b0c787fac1e0774c9504d9094c430e", + "conversation_key": "770519e803b80f411c34aef59c3ca018608842ebf53909c48d35250bd9323af6" + }, + { + "sec1": "cffff919fcc07b8003fdc63bc8a00c0f5dc81022c1c927c62c597352190d95b9", + "pub2": "eb5c3cca1a968e26684e5b0eb733aecfc844f95a09ac4e126a9e58a4e4902f92", + "conversation_key": "46a14ee7e80e439ec75c66f04ad824b53a632b8409a29bbb7c192e43c00bb795" + }, + { + "sec1": "64ba5a685e443e881e9094647ddd32db14444bb21aa7986beeba3d1c4673ba0a", + "pub2": "50e6a4339fac1f3bf86f2401dd797af43ad45bbf58e0801a7877a3984c77c3c4", + "conversation_key": "968b9dbbfcede1664a4ca35a5d3379c064736e87aafbf0b5d114dff710b8a946" + }, + { + "sec1": "dd0c31ccce4ec8083f9b75dbf23cc2878e6d1b6baa17713841a2428f69dee91a", + "pub2": "b483e84c1339812bed25be55cff959778dfc6edde97ccd9e3649f442472c091b", + "conversation_key": "09024503c7bde07eb7865505891c1ea672bf2d9e25e18dd7a7cea6c69bf44b5d" + }, + { + "sec1": "af71313b0d95c41e968a172b33ba5ebd19d06cdf8a7a98df80ecf7af4f6f0358", + "pub2": "2a5c25266695b461ee2af927a6c44a3c598b8095b0557e9bd7f787067435bc7c", + "conversation_key": "fe5155b27c1c4b4e92a933edae23726a04802a7cc354a77ac273c85aa3c97a92" + }, + { + "sec1": "6636e8a389f75fe068a03b3edb3ea4a785e2768e3f73f48ffb1fc5e7cb7289dc", + "pub2": "514eb2064224b6a5829ea21b6e8f7d3ea15ff8e70e8555010f649eb6e09aec70", + "conversation_key": "ff7afacd4d1a6856d37ca5b546890e46e922b508639214991cf8048ddbe9745c" + }, + { + "sec1": "94b212f02a3cfb8ad147d52941d3f1dbe1753804458e6645af92c7b2ea791caa", + "pub2": "f0cac333231367a04b652a77ab4f8d658b94e86b5a8a0c472c5c7b0d4c6a40cc", + "conversation_key": "e292eaf873addfed0a457c6bd16c8effde33d6664265697f69f420ab16f6669b" + }, + { + "sec1": "aa61f9734e69ae88e5d4ced5aae881c96f0d7f16cca603d3bed9eec391136da6", + "pub2": "4303e5360a884c360221de8606b72dd316da49a37fe51e17ada4f35f671620a6", + "conversation_key": "8e7d44fd4767456df1fb61f134092a52fcd6836ebab3b00766e16732683ed848" + }, + { + "sec1": "5e914bdac54f3f8e2cba94ee898b33240019297b69e96e70c8a495943a72fc98", + "pub2": "5bd097924f606695c59f18ff8fd53c174adbafaaa71b3c0b4144a3e0a474b198", + "conversation_key": "f5a0aecf2984bf923c8cd5e7bb8be262d1a8353cb93959434b943a07cf5644bc" + }, + { + "sec1": "8b275067add6312ddee064bcdbeb9d17e88aa1df36f430b2cea5cc0413d8278a", + "pub2": "65bbbfca819c90c7579f7a82b750a18c858db1afbec8f35b3c1e0e7b5588e9b8", + "conversation_key": "2c565e7027eb46038c2263563d7af681697107e975e9914b799d425effd248d6" + }, + { + "sec1": "1ac848de312285f85e0f7ec208aac20142a1f453402af9b34ec2ec7a1f9c96fc", + "pub2": "45f7318fe96034d23ee3ddc25b77f275cc1dd329664dd51b89f89c4963868e41", + "conversation_key": "b56e970e5057a8fd929f8aad9248176b9af87819a708d9ddd56e41d1aec74088" + }, + { + "sec1": "295a1cf621de401783d29d0e89036aa1c62d13d9ad307161b4ceb535ba1b40e6", + "pub2": "840115ddc7f1034d3b21d8e2103f6cb5ab0b63cf613f4ea6e61ae3d016715cdd", + "conversation_key": "b4ee9c0b9b9fef88975773394f0a6f981ca016076143a1bb575b9ff46e804753" + }, + { + "sec1": "a28eed0fe977893856ab9667e06ace39f03abbcdb845c329a1981be438ba565d", + "pub2": "b0f38b950a5013eba5ab4237f9ed29204a59f3625c71b7e210fec565edfa288c", + "conversation_key": "9d3a802b45bc5aeeb3b303e8e18a92ddd353375710a31600d7f5fff8f3a7285b" + }, + { + "sec1": "7ab65af72a478c05f5c651bdc4876c74b63d20d04cdbf71741e46978797cd5a4", + "pub2": "f1112159161b568a9cb8c9dd6430b526c4204bcc8ce07464b0845b04c041beda", + "conversation_key": "943884cddaca5a3fef355e9e7f08a3019b0b66aa63ec90278b0f9fdb64821e79" + }, + { + "sec1": "95c79a7b75ba40f2229e85756884c138916f9d103fc8f18acc0877a7cceac9fe", + "pub2": "cad76bcbd31ca7bbda184d20cc42f725ed0bb105b13580c41330e03023f0ffb3", + "conversation_key": "81c0832a669eea13b4247c40be51ccfd15bb63fcd1bba5b4530ce0e2632f301b" + }, + { + "sec1": "baf55cc2febd4d980b4b393972dfc1acf49541e336b56d33d429bce44fa12ec9", + "pub2": "0c31cf87fe565766089b64b39460ebbfdedd4a2bc8379be73ad3c0718c912e18", + "conversation_key": "37e2344da9ecdf60ae2205d81e89d34b280b0a3f111171af7e4391ded93b8ea6" + }, + { + "sec1": "6eeec45acd2ed31693c5256026abf9f072f01c4abb61f51cf64e6956b6dc8907", + "pub2": "e501b34ed11f13d816748c0369b0c728e540df3755bab59ed3327339e16ff828", + "conversation_key": "afaa141b522ddb27bb880d768903a7f618bb8b6357728cae7fb03af639b946e6" + }, + { + "sec1": "261a076a9702af1647fb343c55b3f9a4f1096273002287df0015ba81ce5294df", + "pub2": "b2777c863878893ae100fb740c8fab4bebd2bf7be78c761a75593670380a6112", + "conversation_key": "76f8d2853de0734e51189ced523c09427c3e46338b9522cd6f74ef5e5b475c74" + }, + { + "sec1": "ed3ec71ca406552ea41faec53e19f44b8f90575eda4b7e96380f9cc73c26d6f3", + "pub2": "86425951e61f94b62e20cae24184b42e8e17afcf55bafa58645efd0172624fae", + "conversation_key": "f7ffc520a3a0e9e9b3c0967325c9bf12707f8e7a03f28b6cd69ae92cf33f7036" + }, + { + "sec1": "5a788fc43378d1303ac78639c59a58cb88b08b3859df33193e63a5a3801c722e", + "pub2": "a8cba2f87657d229db69bee07850fd6f7a2ed070171a06d006ec3a8ac562cf70", + "conversation_key": "7d705a27feeedf78b5c07283362f8e361760d3e9f78adab83e3ae5ce7aeb6409" + }, + { + "sec1": "63bffa986e382b0ac8ccc1aa93d18a7aa445116478be6f2453bad1f2d3af2344", + "pub2": "b895c70a83e782c1cf84af558d1038e6b211c6f84ede60408f519a293201031d", + "conversation_key": "3a3b8f00d4987fc6711d9be64d9c59cf9a709c6c6481c2cde404bcc7a28f174e" + }, + { + "sec1": "e4a8bcacbf445fd3721792b939ff58e691cdcba6a8ba67ac3467b45567a03e5c", + "pub2": "b54053189e8c9252c6950059c783edb10675d06d20c7b342f73ec9fa6ed39c9d", + "conversation_key": "7b3933b4ef8189d347169c7955589fc1cfc01da5239591a08a183ff6694c44ad" + }, + { + "sec1": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139", + "pub2": "0000000000000000000000000000000000000000000000000000000000000002", + "conversation_key": "8b6392dbf2ec6a2b2d5b1477fc2be84d63ef254b667cadd31bd3f444c44ae6ba", + "note": "sec1 = n-2, pub2: random, 0x02" + }, + { + "sec1": "0000000000000000000000000000000000000000000000000000000000000002", + "pub2": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdeb", + "conversation_key": "be234f46f60a250bef52a5ee34c758800c4ca8e5030bf4cc1a31d37ba2104d43", + "note": "sec1 = 2, pub2: rand" + }, + { + "sec1": "0000000000000000000000000000000000000000000000000000000000000001", + "pub2": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "conversation_key": "3b4610cb7189beb9cc29eb3716ecc6102f1247e8f3101a03a1787d8908aeb54e", + "note": "sec1 == pub2" + } + ], + "get_message_keys": { + "conversation_key": "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54", + "keys": [ + { + "nonce": "e1e6f880560d6d149ed83dcc7e5861ee62a5ee051f7fde9975fe5d25d2a02d72", + "chacha_key": "f145f3bed47cb70dbeaac07f3a3fe683e822b3715edb7c4fe310829014ce7d76", + "chacha_nonce": "c4ad129bb01180c0933a160c", + "hmac_key": "027c1db445f05e2eee864a0975b0ddef5b7110583c8c192de3732571ca5838c4" + }, + { + "nonce": "e1d6d28c46de60168b43d79dacc519698512ec35e8ccb12640fc8e9f26121101", + "chacha_key": "e35b88f8d4a8f1606c5082f7a64b100e5d85fcdb2e62aeafbec03fb9e860ad92", + "chacha_nonce": "22925e920cee4a50a478be90", + "hmac_key": "46a7c55d4283cb0df1d5e29540be67abfe709e3b2e14b7bf9976e6df994ded30" + }, + { + "nonce": "cfc13bef512ac9c15951ab00030dfaf2626fdca638dedb35f2993a9eeb85d650", + "chacha_key": "020783eb35fdf5b80ef8c75377f4e937efb26bcbad0e61b4190e39939860c4bf", + "chacha_nonce": "d3594987af769a52904656ac", + "hmac_key": "237ec0ccb6ebd53d179fa8fd319e092acff599ef174c1fdafd499ef2b8dee745" + }, + { + "nonce": "ea6eb84cac23c5c1607c334e8bdf66f7977a7e374052327ec28c6906cbe25967", + "chacha_key": "ff68db24b34fa62c78ac5ffeeaf19533afaedf651fb6a08384e46787f6ce94be", + "chacha_nonce": "50bb859aa2dde938cc49ec7a", + "hmac_key": "06ff32e1f7b29753a727d7927b25c2dd175aca47751462d37a2039023ec6b5a6" + }, + { + "nonce": "8c2e1dd3792802f1f9f7842e0323e5d52ad7472daf360f26e15f97290173605d", + "chacha_key": "2f9daeda8683fdeede81adac247c63cc7671fa817a1fd47352e95d9487989d8b", + "chacha_nonce": "400224ba67fc2f1b76736916", + "hmac_key": "465c05302aeeb514e41c13ed6405297e261048cfb75a6f851ffa5b445b746e4b" + }, + { + "nonce": "05c28bf3d834fa4af8143bf5201a856fa5fac1a3aee58f4c93a764fc2f722367", + "chacha_key": "1e3d45777025a035be566d80fd580def73ed6f7c043faec2c8c1c690ad31c110", + "chacha_nonce": "021905b1ea3afc17cb9bf96f", + "hmac_key": "74a6e481a89dcd130aaeb21060d7ec97ad30f0007d2cae7b1b11256cc70dfb81" + }, + { + "nonce": "5e043fb153227866e75a06d60185851bc90273bfb93342f6632a728e18a07a17", + "chacha_key": "1ea72c9293841e7737c71567d8120145a58991aaa1c436ef77bf7adb83f882f1", + "chacha_nonce": "72f69a5a5f795465cee59da8", + "hmac_key": "e9daa1a1e9a266ecaa14e970a84bce3fbbf329079bbccda626582b4e66a0d4c9" + }, + { + "nonce": "7be7338eaf06a87e274244847fe7a97f5c6a91f44adc18fcc3e411ad6f786dbf", + "chacha_key": "881e7968a1f0c2c80742ee03cd49ea587e13f22699730f1075ade01931582bf6", + "chacha_nonce": "6e69be92d61c04a276021565", + "hmac_key": "901afe79e74b19967c8829af23617d7d0ffbf1b57190c096855c6a03523a971b" + }, + { + "nonce": "94571c8d590905bad7becd892832b472f2aa5212894b6ce96e5ba719c178d976", + "chacha_key": "f80873dd48466cb12d46364a97b8705c01b9b4230cb3ec3415a6b9551dc42eef", + "chacha_nonce": "3dda53569cfcb7fac1805c35", + "hmac_key": "e9fc264345e2839a181affebc27d2f528756e66a5f87b04bf6c5f1997047051e" + }, + { + "nonce": "13a6ee974b1fd759135a2c2010e3cdda47081c78e771125e4f0c382f0284a8cb", + "chacha_key": "bc5fb403b0bed0d84cf1db872b6522072aece00363178c98ad52178d805fca85", + "chacha_nonce": "65064239186e50304cc0f156", + "hmac_key": "e872d320dde4ed3487958a8e43b48aabd3ced92bc24bb8ff1ccb57b590d9701a" + }, + { + "nonce": "082fecdb85f358367b049b08be0e82627ae1d8edb0f27327ccb593aa2613b814", + "chacha_key": "1fbdb1cf6f6ea816349baf697932b36107803de98fcd805ebe9849b8ad0e6a45", + "chacha_nonce": "2e605e1d825a3eaeb613db9c", + "hmac_key": "fae910f591cf3c7eb538c598583abad33bc0a03085a96ca4ea3a08baf17c0eec" + }, + { + "nonce": "4c19020c74932c30ec6b2d8cd0d5bb80bd0fc87da3d8b4859d2fb003810afd03", + "chacha_key": "1ab9905a0189e01cda82f843d226a82a03c4f5b6dbea9b22eb9bc953ba1370d4", + "chacha_nonce": "cbb2530ea653766e5a37a83a", + "hmac_key": "267f68acac01ac7b34b675e36c2cef5e7b7a6b697214add62a491bedd6efc178" + }, + { + "nonce": "67723a3381497b149ce24814eddd10c4c41a1e37e75af161930e6b9601afd0ff", + "chacha_key": "9ecbd25e7e2e6c97b8c27d376dcc8c5679da96578557e4e21dba3a7ef4e4ac07", + "chacha_nonce": "ef649fcf335583e8d45e3c2e", + "hmac_key": "04dbbd812fa8226fdb45924c521a62e3d40a9e2b5806c1501efdeba75b006bf1" + }, + { + "nonce": "42063fe80b093e8619b1610972b4c3ab9e76c14fd908e642cd4997cafb30f36c", + "chacha_key": "211c66531bbcc0efcdd0130f9f1ebc12a769105eb39608994bcb188fa6a73a4a", + "chacha_nonce": "67803605a7e5010d0f63f8c8", + "hmac_key": "e840e4e8921b57647369d121c5a19310648105dbdd008200ebf0d3b668704ff8" + }, + { + "nonce": "b5ac382a4be7ac03b554fe5f3043577b47ea2cd7cfc7e9ca010b1ffbb5cf1a58", + "chacha_key": "b3b5f14f10074244ee42a3837a54309f33981c7232a8b16921e815e1f7d1bb77", + "chacha_nonce": "4e62a0073087ed808be62469", + "hmac_key": "c8efa10230b5ea11633816c1230ca05fa602ace80a7598916d83bae3d3d2ccd7" + }, + { + "nonce": "e9d1eba47dd7e6c1532dc782ff63125db83042bb32841db7eeafd528f3ea7af9", + "chacha_key": "54241f68dc2e50e1db79e892c7c7a471856beeb8d51b7f4d16f16ab0645d2f1a", + "chacha_nonce": "a963ed7dc29b7b1046820a1d", + "hmac_key": "aba215c8634530dc21c70ddb3b3ee4291e0fa5fa79be0f85863747bde281c8b2" + }, + { + "nonce": "a94ecf8efeee9d7068de730fad8daf96694acb70901d762de39fa8a5039c3c49", + "chacha_key": "c0565e9e201d2381a2368d7ffe60f555223874610d3d91fbbdf3076f7b1374dd", + "chacha_nonce": "329bb3024461e84b2e1c489b", + "hmac_key": "ac42445491f092481ce4fa33b1f2274700032db64e3a15014fbe8c28550f2fec" + }, + { + "nonce": "533605ea214e70c25e9a22f792f4b78b9f83a18ab2103687c8a0075919eaaa53", + "chacha_key": "ab35a5e1e54d693ff023db8500d8d4e79ad8878c744e0eaec691e96e141d2325", + "chacha_nonce": "653d759042b85194d4d8c0a7", + "hmac_key": "b43628e37ba3c31ce80576f0a1f26d3a7c9361d29bb227433b66f49d44f167ba" + }, + { + "nonce": "7f38df30ceea1577cb60b355b4f5567ff4130c49e84fed34d779b764a9cc184c", + "chacha_key": "a37d7f211b84a551a127ff40908974eb78415395d4f6f40324428e850e8c42a3", + "chacha_nonce": "b822e2c959df32b3cb772a7c", + "hmac_key": "1ba31764f01f69b5c89ded2d7c95828e8052c55f5d36f1cd535510d61ba77420" + }, + { + "nonce": "11b37f9dbc4d0185d1c26d5f4ed98637d7c9701fffa65a65839fa4126573a4e5", + "chacha_key": "964f38d3a31158a5bfd28481247b18dd6e44d69f30ba2a40f6120c6d21d8a6ba", + "chacha_nonce": "5f72c5b87c590bcd0f93b305", + "hmac_key": "2fc4553e7cedc47f29690439890f9f19c1077ef3e9eaeef473d0711e04448918" + }, + { + "nonce": "8be790aa483d4cdd843189f71f135b3ec7e31f381312c8fe9f177aab2a48eafa", + "chacha_key": "95c8c74d633721a131316309cf6daf0804d59eaa90ea998fc35bac3d2fbb7a94", + "chacha_nonce": "409a7654c0e4bf8c2c6489be", + "hmac_key": "21bb0b06eb2b460f8ab075f497efa9a01c9cf9146f1e3986c3bf9da5689b6dc4" + }, + { + "nonce": "19fd2a718ea084827d6bd73f509229ddf856732108b59fc01819f611419fd140", + "chacha_key": "cc6714b9f5616c66143424e1413d520dae03b1a4bd202b82b0a89b0727f5cdc8", + "chacha_nonce": "1b7fd2534f015a8f795d8f32", + "hmac_key": "2bef39c4ce5c3c59b817e86351373d1554c98bc131c7e461ed19d96cfd6399a0" + }, + { + "nonce": "3c2acd893952b2f6d07d8aea76f545ca45961a93fe5757f6a5a80811d5e0255d", + "chacha_key": "c8de6c878cb469278d0af894bc181deb6194053f73da5014c2b5d2c8db6f2056", + "chacha_nonce": "6ffe4f1971b904a1b1a81b99", + "hmac_key": "df1cd69dd3646fca15594284744d4211d70e7d8472e545d276421fbb79559fd4" + }, + { + "nonce": "7dbea4cead9ac91d4137f1c0a6eebb6ba0d1fb2cc46d829fbc75f8d86aca6301", + "chacha_key": "c8e030f6aa680c3d0b597da9c92bb77c21c4285dd620c5889f9beba7446446b0", + "chacha_nonce": "a9b5a67d081d3b42e737d16f", + "hmac_key": "355a85f551bc3cce9a14461aa60994742c9bbb1c81a59ca102dc64e61726ab8e" + }, + { + "nonce": "45422e676cdae5f1071d3647d7a5f1f5adafb832668a578228aa1155a491f2f3", + "chacha_key": "758437245f03a88e2c6a32807edfabff51a91c81ca2f389b0b46f2c97119ea90", + "chacha_nonce": "263830a065af33d9c6c5aa1f", + "hmac_key": "7c581cf3489e2de203a95106bfc0de3d4032e9d5b92b2b61fb444acd99037e17" + }, + { + "nonce": "babc0c03fad24107ad60678751f5db2678041ff0d28671ede8d65bdf7aa407e9", + "chacha_key": "bd68a28bd48d9ffa3602db72c75662ac2848a0047a313d2ae2d6bc1ac153d7e9", + "chacha_nonce": "d0f9d2a1ace6c758f594ffdd", + "hmac_key": "eb435e3a642adfc9d59813051606fc21f81641afd58ea6641e2f5a9f123bb50a" + }, + { + "nonce": "7a1b8aac37d0d20b160291fad124ab697cfca53f82e326d78fef89b4b0ea8f83", + "chacha_key": "9e97875b651a1d30d17d086d1e846778b7faad6fcbc12e08b3365d700f62e4fe", + "chacha_nonce": "ccdaad5b3b7645be430992eb", + "hmac_key": "6f2f55cf35174d75752f63c06cc7cbc8441759b142999ed2d5a6d09d263e1fc4" + }, + { + "nonce": "8370e4e32d7e680a83862cab0da6136ef607014d043e64cdf5ecc0c4e20b3d9a", + "chacha_key": "1472bed5d19db9c546106de946e0649cd83cc9d4a66b087a65906e348dcf92e2", + "chacha_nonce": "ed02dece5fc3a186f123420b", + "hmac_key": "7b3f7739f49d30c6205a46b174f984bb6a9fc38e5ccfacef2dac04fcbd3b184e" + }, + { + "nonce": "9f1c5e8a29cd5677513c2e3a816551d6833ee54991eb3f00d5b68096fc8f0183", + "chacha_key": "5e1a7544e4d4dafe55941fcbdf326f19b0ca37fc49c4d47e9eec7fb68cde4975", + "chacha_nonce": "7d9acb0fdc174e3c220f40de", + "hmac_key": "e265ab116fbbb86b2aefc089a0986a0f5b77eda50c7410404ad3b4f3f385c7a7" + }, + { + "nonce": "c385aa1c37c2bfd5cc35fcdbdf601034d39195e1cabff664ceb2b787c15d0225", + "chacha_key": "06bf4e60677a13e54c4a38ab824d2ef79da22b690da2b82d0aa3e39a14ca7bdd", + "chacha_nonce": "26b450612ca5e905b937e147", + "hmac_key": "22208152be2b1f5f75e6bfcc1f87763d48bb7a74da1be3d102096f257207f8b3" + }, + { + "nonce": "3ff73528f88a50f9d35c0ddba4560bacee5b0462d0f4cb6e91caf41847040ce4", + "chacha_key": "850c8a17a23aa761d279d9901015b2bbdfdff00adbf6bc5cf22bd44d24ecabc9", + "chacha_nonce": "4a296a1fb0048e5020d3b129", + "hmac_key": "b1bf49a533c4da9b1d629b7ff30882e12d37d49c19abd7b01b7807d75ee13806" + }, + { + "nonce": "2dcf39b9d4c52f1cb9db2d516c43a7c6c3b8c401f6a4ac8f131a9e1059957036", + "chacha_key": "17f8057e6156ba7cc5310d01eda8c40f9aa388f9fd1712deb9511f13ecc37d27", + "chacha_nonce": "a8188daff807a1182200b39d", + "hmac_key": "47b89da97f68d389867b5d8a2d7ba55715a30e3d88a3cc11f3646bc2af5580ef" + } + ] + }, + "calc_padded_len": [ + [16, 32], + [32, 32], + [33, 64], + [37, 64], + [45, 64], + [49, 64], + [64, 64], + [65, 96], + [100, 128], + [111, 128], + [200, 224], + [250, 256], + [320, 320], + [383, 384], + [384, 384], + [400, 448], + [500, 512], + [512, 512], + [515, 640], + [700, 768], + [800, 896], + [900, 1024], + [1020, 1024], + [65536, 65536] + ], + "encrypt_decrypt": [ + { + "sec1": "0000000000000000000000000000000000000000000000000000000000000001", + "sec2": "0000000000000000000000000000000000000000000000000000000000000002", + "conversation_key": "c41c775356fd92eadc63ff5a0dc1da211b268cbea22316767095b2871ea1412d", + "nonce": "0000000000000000000000000000000000000000000000000000000000000001", + "plaintext": "a", + "payload": "AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABee0G5VSK0/9YypIObAtDKfYEAjD35uVkHyB0F4DwrcNaCXlCWZKaArsGrY6M9wnuTMxWfp1RTN9Xga8no+kF5Vsb" + }, + { + "sec1": "0000000000000000000000000000000000000000000000000000000000000002", + "sec2": "0000000000000000000000000000000000000000000000000000000000000001", + "conversation_key": "c41c775356fd92eadc63ff5a0dc1da211b268cbea22316767095b2871ea1412d", + "nonce": "f00000000000000000000000000000f00000000000000000000000000000000f", + "plaintext": "🍕🫃", + "payload": "AvAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAPSKSK6is9ngkX2+cSq85Th16oRTISAOfhStnixqZziKMDvB0QQzgFZdjLTPicCJaV8nDITO+QfaQ61+KbWQIOO2Yj" + }, + { + "sec1": "5c0c523f52a5b6fad39ed2403092df8cebc36318b39383bca6c00808626fab3a", + "sec2": "4b22aa260e4acb7021e32f38a6cdf4b673c6a277755bfce287e370c924dc936d", + "conversation_key": "3e2b52a63be47d34fe0a80e34e73d436d6963bc8f39827f327057a9986c20a45", + "nonce": "b635236c42db20f021bb8d1cdff5ca75dd1a0cc72ea742ad750f33010b24f73b", + "plaintext": "表ポあA鷗ŒéB逍Üߪąñ丂㐀𠀀", + "payload": "ArY1I2xC2yDwIbuNHN/1ynXdGgzHLqdCrXUPMwELJPc7s7JqlCMJBAIIjfkpHReBPXeoMCyuClwgbT419jUWU1PwaNl4FEQYKCDKVJz+97Mp3K+Q2YGa77B6gpxB/lr1QgoqpDf7wDVrDmOqGoiPjWDqy8KzLueKDcm9BVP8xeTJIxs=" + }, + { + "sec1": "8f40e50a84a7462e2b8d24c28898ef1f23359fff50d8c509e6fb7ce06e142f9c", + "sec2": "b9b0a1e9cc20100c5faa3bbe2777303d25950616c4c6a3fa2e3e046f936ec2ba", + "conversation_key": "d5a2f879123145a4b291d767428870f5a8d9e5007193321795b40183d4ab8c2b", + "nonce": "b20989adc3ddc41cd2c435952c0d59a91315d8c5218d5040573fc3749543acaf", + "plaintext": "ability🤝的 ȺȾ", + "payload": "ArIJia3D3cQc0sQ1lSwNWakTFdjFIY1QQFc/w3SVQ6yvbG2S0x4Yu86QGwPTy7mP3961I1XqB6SFFTzqDZZavhxoWMj7mEVGMQIsh2RLWI5EYQaQDIePSnXPlzf7CIt+voTD" + }, + { + "sec1": "875adb475056aec0b4809bd2db9aa00cff53a649e7b59d8edcbf4e6330b0995c", + "sec2": "9c05781112d5b0a2a7148a222e50e0bd891d6b60c5483f03456e982185944aae", + "conversation_key": "3b15c977e20bfe4b8482991274635edd94f366595b1a3d2993515705ca3cedb8", + "nonce": "8d4442713eb9d4791175cb040d98d6fc5be8864d6ec2f89cf0895a2b2b72d1b1", + "plaintext": "pepper👀їжак", + "payload": "Ao1EQnE+udR5EXXLBA2Y1vxb6IZNbsL4nPCJWisrctGxY3AduCS+jTUgAAnfvKafkmpy15+i9YMwCdccisRa8SvzW671T2JO4LFSPX31K4kYUKelSAdSPwe9NwO6LhOsnoJ+" + }, + { + "sec1": "eba1687cab6a3101bfc68fd70f214aa4cc059e9ec1b79fdb9ad0a0a4e259829f", + "sec2": "dff20d262bef9dfd94666548f556393085e6ea421c8af86e9d333fa8747e94b3", + "conversation_key": "4f1538411098cf11c8af216836444787c462d47f97287f46cf7edb2c4915b8a5", + "nonce": "2180b52ae645fcf9f5080d81b1f0b5d6f2cd77ff3c986882bb549158462f3407", + "plaintext": "( ͡° ͜ʖ ͡°)", + "payload": "AiGAtSrmRfz59QgNgbHwtdbyzXf/PJhogrtUkVhGLzQHv4qhKQwnFQ54OjVMgqCea/Vj0YqBSdhqNR777TJ4zIUk7R0fnizp6l1zwgzWv7+ee6u+0/89KIjY5q1wu6inyuiv" + }, + { + "sec1": "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e", + "sec2": "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214", + "conversation_key": "75fe686d21a035f0c7cd70da64ba307936e5ca0b20710496a6b6b5f573377bdd", + "nonce": "e4cd5f7ce4eea024bc71b17ad456a986a74ac426c2c62b0a15eb5c5c8f888b68", + "plaintext": "مُنَاقَشَةُ سُبُلِ اِسْتِخْدَامِ اللُّغَةِ فِي النُّظُمِ الْقَائِمَةِ وَفِيم يَخُصَّ التَّطْبِيقَاتُ الْحاسُوبِيَّةُ،", + "payload": "AuTNX3zk7qAkvHGxetRWqYanSsQmwsYrChXrXFyPiItoIBsWu1CB+sStla2M4VeANASHxM78i1CfHQQH1YbBy24Tng7emYW44ol6QkFD6D8Zq7QPl+8L1c47lx8RoODEQMvNCbOk5ffUV3/AhONHBXnffrI+0025c+uRGzfqpYki4lBqm9iYU+k3Tvjczq9wU0mkVDEaM34WiQi30MfkJdRbeeYaq6kNvGPunLb3xdjjs5DL720d61Flc5ZfoZm+CBhADy9D9XiVZYLKAlkijALJur9dATYKci6OBOoc2SJS2Clai5hOVzR0yVeyHRgRfH9aLSlWW5dXcUxTo7qqRjNf8W5+J4jF4gNQp5f5d0YA4vPAzjBwSP/5bGzNDslKfcAH" + }, + { + "sec1": "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e", + "sec2": "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214", + "conversation_key": "75fe686d21a035f0c7cd70da64ba307936e5ca0b20710496a6b6b5f573377bdd", + "nonce": "38d1ca0abef9e5f564e89761a86cee04574b6825d3ef2063b10ad75899e4b023", + "plaintext": "الكل في المجمو عة (5)", + "payload": "AjjRygq++eX1ZOiXYahs7gRXS2gl0+8gY7EK11iZ5LAjbOTrlfrxak5Lki42v2jMPpLSicy8eHjsWkkMtF0i925vOaKG/ZkMHh9ccQBdfTvgEGKzztedqDCAWb5TP1YwU1PsWaiiqG3+WgVvJiO4lUdMHXL7+zKKx8bgDtowzz4QAwI=" + }, + { + "sec1": "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e", + "sec2": "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214", + "conversation_key": "75fe686d21a035f0c7cd70da64ba307936e5ca0b20710496a6b6b5f573377bdd", + "nonce": "4f1a31909f3483a9e69c8549a55bbc9af25fa5bbecf7bd32d9896f83ef2e12e0", + "plaintext": "𝖑𝖆𝖟𝖞 社會科學院語學研究所", + "payload": "Ak8aMZCfNIOp5pyFSaVbvJryX6W77Pe9MtmJb4PvLhLgh/TsxPLFSANcT67EC1t/qxjru5ZoADjKVEt2ejdx+xGvH49mcdfbc+l+L7gJtkH7GLKpE9pQNQWNHMAmj043PAXJZ++fiJObMRR2mye5VHEANzZWkZXMrXF7YjuG10S1pOU=" + }, + { + "sec1": "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e", + "sec2": "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214", + "conversation_key": "75fe686d21a035f0c7cd70da64ba307936e5ca0b20710496a6b6b5f573377bdd", + "nonce": "a3e219242d85465e70adcd640b564b3feff57d2ef8745d5e7a0663b2dccceb54", + "plaintext": "🙈 🙉 🙊 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 Powerلُلُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ冗", + "payload": "AqPiGSQthUZecK3NZAtWSz/v9X0u+HRdXnoGY7LczOtUf05aMF89q1FLwJvaFJYICZoMYgRJHFLwPiOHce7fuAc40kX0wXJvipyBJ9HzCOj7CgtnC1/cmPCHR3s5AIORmroBWglm1LiFMohv1FSPEbaBD51VXxJa4JyWpYhreSOEjn1wd0lMKC9b+osV2N2tpbs+rbpQem2tRen3sWflmCqjkG5VOVwRErCuXuPb5+hYwd8BoZbfCrsiAVLd7YT44dRtKNBx6rkabWfddKSLtreHLDysOhQUVOp/XkE7OzSkWl6sky0Hva6qJJ/V726hMlomvcLHjE41iKmW2CpcZfOedg==" + } + ], + "encrypt_decrypt_long_msg": [ + { + "conversation_key": "8fc262099ce0d0bb9b89bac05bb9e04f9bc0090acc181fef6840ccee470371ed", + "nonce": "326bcb2c943cd6bb717588c9e5a7e738edf6ed14ec5f5344caa6ef56f0b9cff7", + "pattern": "x", + "repeat": 65535, + "plaintext_sha256": "09ab7495d3e61a76f0deb12cb0306f0696cbb17ffc12131368c7a939f12f56d3", + "payload_sha256": "90714492225faba06310bff2f249ebdc2a5e609d65a629f1c87f2d4ffc55330a" + }, + { + "conversation_key": "56adbe3720339363ab9c3b8526ffce9fd77600927488bfc4b59f7a68ffe5eae0", + "nonce": "ad68da81833c2a8ff609c3d2c0335fd44fe5954f85bb580c6a8d467aa9fc5dd0", + "pattern": "!", + "repeat": 65535, + "plaintext_sha256": "6af297793b72ae092c422e552c3bb3cbc310da274bd1cf9e31023a7fe4a2d75e", + "payload_sha256": "8013e45a109fad3362133132b460a2d5bce235fe71c8b8f4014793fb52a49844" + }, + { + "conversation_key": "7fc540779979e472bb8d12480b443d1e5eb1098eae546ef2390bee499bbf46be", + "nonce": "34905e82105c20de9a2f6cd385a0d541e6bcc10601d12481ff3a7575dc622033", + "pattern": "🦄", + "repeat": 16383, + "plaintext_sha256": "a249558d161b77297bc0cb311dde7d77190f6571b25c7e4429cd19044634a61f", + "payload_sha256": "b3348422471da1f3c59d79acfe2fe103f3cd24488109e5b18734cdb5953afd15" + } + ] + }, + "invalid": { + "encrypt_msg_lengths": [0, 65536, 100000, 10000000], + "get_conversation_key": [ + { + "sec1": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "pub2": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "note": "sec1 higher than curve.n" + }, + { + "sec1": "0000000000000000000000000000000000000000000000000000000000000000", + "pub2": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "note": "sec1 is 0" + }, + { + "sec1": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139", + "pub2": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "note": "pub2 is invalid, no sqrt, all-ff" + }, + { + "sec1": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "pub2": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "note": "sec1 == curve.n" + }, + { + "sec1": "0000000000000000000000000000000000000000000000000000000000000002", + "pub2": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "note": "pub2 is invalid, no sqrt" + }, + { + "sec1": "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "pub2": "0000000000000000000000000000000000000000000000000000000000000000", + "note": "pub2 is point of order 3 on twist" + }, + { + "sec1": "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "pub2": "eb1f7200aecaa86682376fb1c13cd12b732221e774f553b0a0857f88fa20f86d", + "note": "pub2 is point of order 13 on twist" + }, + { + "sec1": "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + "pub2": "709858a4c121e4a84eb59c0ded0261093c71e8ca29efeef21a6161c447bcaf9f", + "note": "pub2 is point of order 3319 on twist" + } + ], + "decrypt": [ + { + "conversation_key": "ca2527a037347b91bea0c8a30fc8d9600ffd81ec00038671e3a0f0cb0fc9f642", + "nonce": "daaea5ca345b268e5b62060ca72c870c48f713bc1e00ff3fc0ddb78e826f10db", + "plaintext": "n o b l e", + "payload": "#Atqupco0WyaOW2IGDKcshwxI9xO8HgD/P8Ddt46CbxDbrhdG8VmJdU0MIDf06CUvEvdnr1cp1fiMtlM/GrE92xAc1K5odTpCzUB+mjXgbaqtntBUbTToSUoT0ovrlPwzGjyp", + "note": "unknown encryption version" + }, + { + "conversation_key": "36f04e558af246352dcf73b692fbd3646a2207bd8abd4b1cd26b234db84d9481", + "nonce": "ad408d4be8616dc84bb0bf046454a2a102edac937c35209c43cd7964c5feb781", + "plaintext": "⚠️", + "payload": "AK1AjUvoYW3IS7C/BGRUoqEC7ayTfDUgnEPNeWTF/reBZFaha6EAIRueE9D1B1RuoiuFScC0Q94yjIuxZD3JStQtE8JMNacWFs9rlYP+ZydtHhRucp+lxfdvFlaGV/sQlqZz", + "note": "unknown encryption version 0" + }, + { + "conversation_key": "ca2527a037347b91bea0c8a30fc8d9600ffd81ec00038671e3a0f0cb0fc9f642", + "nonce": "daaea5ca345b268e5b62060ca72c870c48f713bc1e00ff3fc0ddb78e826f10db", + "plaintext": "n o s t r", + "payload": "Atфupco0WyaOW2IGDKcshwxI9xO8HgD/P8Ddt46CbxDbrhdG8VmJZE0UICD06CUvEvdnr1cp1fiMtlM/GrE92xAc1EwsVCQEgWEu2gsHUVf4JAa3TpgkmFc3TWsax0v6n/Wq", + "note": "invalid base64" + }, + { + "conversation_key": "cff7bd6a3e29a450fd27f6c125d5edeb0987c475fd1e8d97591e0d4d8a89763c", + "nonce": "09ff97750b084012e15ecb84614ce88180d7b8ec0d468508a86b6d70c0361a25", + "plaintext": "¯\\_(ツ)_/¯", + "payload": "Agn/l3ULCEAS4V7LhGFM6IGA17jsDUaFCKhrbXDANholyySBfeh+EN8wNB9gaLlg4j6wdBYh+3oK+mnxWu3NKRbSvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "note": "invalid MAC" + }, + { + "conversation_key": "cfcc9cf682dfb00b11357f65bdc45e29156b69db424d20b3596919074f5bf957", + "nonce": "65b14b0b949aaa7d52c417eb753b390e8ad6d84b23af4bec6d9bfa3e03a08af4", + "plaintext": "🥎", + "payload": "AmWxSwuUmqp9UsQX63U7OQ6K1thLI69L7G2b+j4DoIr0oRWQ8avl4OLqWZiTJ10vIgKrNqjoaX+fNhE9RqmR5g0f6BtUg1ijFMz71MO1D4lQLQfW7+UHva8PGYgQ1QpHlKgR", + "note": "invalid MAC" + }, + { + "conversation_key": "5254827d29177622d40a7b67cad014fe7137700c3c523903ebbe3e1b74d40214", + "nonce": "7ab65dbb8bbc2b8e35cafb5745314e1f050325a864d11d0475ef75b3660d91c1", + "plaintext": "elliptic-curve cryptography", + "payload": "Anq2XbuLvCuONcr7V0UxTh8FAyWoZNEdBHXvdbNmDZHB573MI7R7rrTYftpqmvUpahmBC2sngmI14/L0HjOZ7lWGJlzdh6luiOnGPc46cGxf08MRC4CIuxx3i2Lm0KqgJ7vA", + "note": "invalid padding" + }, + { + "conversation_key": "fea39aca9aa8340c3a78ae1f0902aa7e726946e4efcd7783379df8096029c496", + "nonce": "7d4283e3b54c885d6afee881f48e62f0a3f5d7a9e1cb71ccab594a7882c39330", + "plaintext": "noble", + "payload": "An1Cg+O1TIhdav7ogfSOYvCj9dep4ctxzKtZSniCw5MwRrrPJFyAQYZh5VpjC2QYzny5LIQ9v9lhqmZR4WBYRNJ0ognHVNMwiFV1SHpvUFT8HHZN/m/QarflbvDHAtO6pY16", + "note": "invalid padding" + }, + { + "conversation_key": "0c4cffb7a6f7e706ec94b2e879f1fc54ff8de38d8db87e11787694d5392d5b3f", + "nonce": "6f9fd72667c273acd23ca6653711a708434474dd9eb15c3edb01ce9a95743e9b", + "plaintext": "censorship-resistant and global social network", + "payload": "Am+f1yZnwnOs0jymZTcRpwhDRHTdnrFcPtsBzpqVdD6b2NZDaNm/TPkZGr75kbB6tCSoq7YRcbPiNfJXNch3Tf+o9+zZTMxwjgX/nm3yDKR2kHQMBhVleCB9uPuljl40AJ8kXRD0gjw+aYRJFUMK9gCETZAjjmrsCM+nGRZ1FfNsHr6Z", + "note": "invalid padding" + }, + { + "conversation_key": "5cd2d13b9e355aeb2452afbd3786870dbeecb9d355b12cb0a3b6e9da5744cd35", + "nonce": "b60036976a1ada277b948fd4caa065304b96964742b89d26f26a25263a5060bd", + "plaintext": "0", + "payload": "", + "note": "invalid payload length: 0" + }, + { + "conversation_key": "d61d3f09c7dfe1c0be91af7109b60a7d9d498920c90cbba1e137320fdd938853", + "nonce": "1a29d02c8b4527745a2ccb38bfa45655deb37bc338ab9289d756354cea1fd07c", + "plaintext": "1", + "payload": "Ag==", + "note": "invalid payload length: 4" + }, + { + "conversation_key": "873bb0fc665eb950a8e7d5971965539f6ebd645c83c08cd6a85aafbad0f0bc47", + "nonce": "c826d3c38e765ab8cc42060116cd1464b2a6ce01d33deba5dedfb48615306d4a", + "plaintext": "2", + "payload": "AqxgToSh3H7iLYRJjoWAM+vSv/Y1mgNlm6OWWjOYUClrFF8=", + "note": "invalid payload length: 48" + }, + { + "conversation_key": "9f2fef8f5401ac33f74641b568a7a30bb19409c76ffdc5eae2db6b39d2617fbe", + "nonce": "9ff6484642545221624eaac7b9ea27133a4cc2356682a6033aceeef043549861", + "plaintext": "3", + "payload": "Ap/2SEZCVFIhYk6qx7nqJxM6TMI1ZoKmAzrO7vBDVJhhuZXWiM20i/tIsbjT0KxkJs2MZjh1oXNYMO9ggfk7i47WQA==", + "note": "invalid payload length: 92" + } + ] + } + } +}