commit b04ef25d748e6af424ecb6c4c4cfc3690104bef8
parent 325109d7b8d15b12b07e79beba1626bf5fcd771a
Author: William Casarin <jb55@jb55.com>
Date: Tue, 11 Feb 2025 15:03:56 -0800
Merge Remove rust-nostr dependency
Daniel D’Aquino (1):
Remove rust-nostr dependency
Diffstat:
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"
+ }
+ ]
+ }
+ }
+}