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>
This commit is contained in:
Daniel D’Aquino
2025-02-10 15:07:36 -08:00
parent 9e7943e0e9
commit a6e123e928
9 changed files with 1559 additions and 74 deletions

5
ACKNOWLEDGEMENTS.md Normal file
View File

@@ -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)

View File

@@ -1500,9 +1500,6 @@
D7ADD3E22B538E3500F104C4 /* DamusPurpleVerifyNpubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7ADD3E12B538E3500F104C4 /* DamusPurpleVerifyNpubView.swift */; }; D7ADD3E22B538E3500F104C4 /* DamusPurpleVerifyNpubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7ADD3E12B538E3500F104C4 /* DamusPurpleVerifyNpubView.swift */; };
D7B76C902C825042003A16CB /* PushNotificationClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */; }; D7B76C902C825042003A16CB /* PushNotificationClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */; };
D7B76C912C82507F003A16CB /* NIP98AuthenticatedRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C6787D2B2D34CC00BCEAFB /* NIP98AuthenticatedRequest.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 */; }; D7BEE6F92D37B37400CF659F /* DraftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BEE6F82D37B37400CF659F /* DraftTests.swift */; };
D7C48C0B2D12DE0C00A3BACF /* SwiftyCrop in Frameworks */ = {isa = PBXBuildFile; productRef = D7C48C0A2D12DE0C00A3BACF /* SwiftyCrop */; }; D7C48C0B2D12DE0C00A3BACF /* SwiftyCrop in Frameworks */ = {isa = PBXBuildFile; productRef = D7C48C0A2D12DE0C00A3BACF /* SwiftyCrop */; };
D7C48C0D2D12E34900A3BACF /* SwiftyCrop in Frameworks */ = {isa = PBXBuildFile; productRef = D7C48C0C2D12E34900A3BACF /* 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 */; }; D7D2A3812BF815D000E4B42B /* PushNotificationClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D2A3802BF815D000E4B42B /* PushNotificationClient.swift */; };
D7D68FF92C9E01BE0015A515 /* KFClickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D68FF82C9E01B60015A515 /* KFClickable.swift */; }; D7D68FF92C9E01BE0015A515 /* KFClickable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7D68FF82C9E01B60015A515 /* KFClickable.swift */; };
D7D68FFA2C9E01BE0015A515 /* 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 */; }; D7DBD41F2B02F15E002A6197 /* NostrKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3BEFD32819DE8F00B3DE84 /* NostrKind.swift */; };
D7DEEF2F2A8C021E00E0C99F /* NostrEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DEEF2E2A8C021E00E0C99F /* NostrEventTests.swift */; }; D7DEEF2F2A8C021E00E0C99F /* NostrEventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7DEEF2E2A8C021E00E0C99F /* NostrEventTests.swift */; };
D7EB00B02CD59C8D00660C07 /* PresentFullScreenItemNotify.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7EB00AF2CD59C8300660C07 /* PresentFullScreenItemNotify.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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; D7EDED1B2B1178FE0018B19C /* NoteContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteContent.swift; sourceTree = "<group>"; };
@@ -2523,8 +2534,8 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D7BEE6F32D37AE1B00CF659F /* NostrSDK in Frameworks */,
4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */, 4C06670428FC7EC500038D2A /* Kingfisher in Frameworks */,
D7DB1FE42D5A9AC900CF06DA /* CryptoSwift in Frameworks */,
3A0A30BB2C21397A00F8C9BC /* EmojiPicker in Frameworks */, 3A0A30BB2C21397A00F8C9BC /* EmojiPicker in Frameworks */,
D70D90982CDED61800CD0534 /* CodeScanner in Frameworks */, D70D90982CDED61800CD0534 /* CodeScanner in Frameworks */,
D7C48C0B2D12DE0C00A3BACF /* SwiftyCrop in Frameworks */, D7C48C0B2D12DE0C00A3BACF /* SwiftyCrop in Frameworks */,
@@ -2554,8 +2565,8 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D7BEE6F52D37B20400CF659F /* NostrSDK in Frameworks */,
82D6FC862CD9A4A600C925F4 /* MarkdownUI in Frameworks */, 82D6FC862CD9A4A600C925F4 /* MarkdownUI in Frameworks */,
D7DB1FEC2D5A9F6500CF06DA /* CryptoSwift in Frameworks */,
82D6FC8A2CD9A54600C925F4 /* SwipeActions in Frameworks */, 82D6FC8A2CD9A54600C925F4 /* SwipeActions in Frameworks */,
D7F360292CEBBE34009D34DA /* CodeScanner in Frameworks */, D7F360292CEBBE34009D34DA /* CodeScanner in Frameworks */,
D7C48C0D2D12E34900A3BACF /* SwiftyCrop in Frameworks */, D7C48C0D2D12E34900A3BACF /* SwiftyCrop in Frameworks */,
@@ -2571,7 +2582,7 @@
files = ( files = (
D703D7AF2C670FB700A400EA /* MarkdownUI in Frameworks */, D703D7AF2C670FB700A400EA /* MarkdownUI in Frameworks */,
D73E5F9D2C6AA8E3007EB227 /* SwipeActions in Frameworks */, D73E5F9D2C6AA8E3007EB227 /* SwipeActions in Frameworks */,
D7BEE6F72D37B21400CF659F /* NostrSDK in Frameworks */, D7DB1FE82D5A9F5300CF06DA /* CryptoSwift in Frameworks */,
D73E5F762C6A997E007EB227 /* EmojiPicker in Frameworks */, D73E5F762C6A997E007EB227 /* EmojiPicker in Frameworks */,
D703D7192C66E47100A400EA /* UniformTypeIdentifiers.framework in Frameworks */, D703D7192C66E47100A400EA /* UniformTypeIdentifiers.framework in Frameworks */,
D7C48C0F2D12E35600A3BACF /* SwiftyCrop in Frameworks */, D7C48C0F2D12E35600A3BACF /* SwiftyCrop in Frameworks */,
@@ -2587,6 +2598,7 @@
files = ( files = (
D789D1202AFEFBF20083A7AB /* secp256k1 in Frameworks */, D789D1202AFEFBF20083A7AB /* secp256k1 in Frameworks */,
D7EDED312B1290B80018B19C /* MarkdownUI in Frameworks */, D7EDED312B1290B80018B19C /* MarkdownUI in Frameworks */,
D7DB1FEA2D5A9F5A00CF06DA /* CryptoSwift in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -2753,6 +2765,8 @@
children = ( children = (
4C0C03982A61E27B0098B3B8 /* bool_setting.wasm */, 4C0C03982A61E27B0098B3B8 /* bool_setting.wasm */,
4C0C03972A61E27B0098B3B8 /* primal.wasm */, 4C0C03972A61E27B0098B3B8 /* primal.wasm */,
D7DB1FF22D5AC5E400CF06DA /* LICENSES */,
D7DB1FF02D5AC5D700CF06DA /* nip44.vectors.json */,
); );
name = Fixtures; name = Fixtures;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -3579,6 +3593,7 @@
4CE6DEE527F7A08100C66700 /* damus */ = { 4CE6DEE527F7A08100C66700 /* damus */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D7DB1FDC2D5A77E500CF06DA /* NIP44 */,
D755B28B2D3E7D6500BBEEFA /* NIP37 */, D755B28B2D3E7D6500BBEEFA /* NIP37 */,
4C45E5002BED4CE10025A428 /* NIP10 */, 4C45E5002BED4CE10025A428 /* NIP10 */,
4C1D4FB32A7967990024F453 /* build-git-hash.txt */, 4C1D4FB32A7967990024F453 /* build-git-hash.txt */,
@@ -3616,6 +3631,7 @@
4CE6DEF627F7A08200C66700 /* damusTests */ = { 4CE6DEF627F7A08200C66700 /* damusTests */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D7DB1FED2D5AC50F00CF06DA /* NIP44v2EncryptionTests.swift */,
D7A0D8742D1FE66A00DCBE59 /* EditPictureControlTests.swift */, D7A0D8742D1FE66A00DCBE59 /* EditPictureControlTests.swift */,
E06336A72B7582D600A88E6B /* Assets */, E06336A72B7582D600A88E6B /* Assets */,
D72A2D032AD9C165002AFF62 /* Mocking */, D72A2D032AD9C165002AFF62 /* Mocking */,
@@ -3946,6 +3962,14 @@
path = Utils; path = Utils;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D7DB1FDC2D5A77E500CF06DA /* NIP44 */ = {
isa = PBXGroup;
children = (
D7DB1FDD2D5A78CE00CF06DA /* NIP44.swift */,
);
path = NIP44;
sourceTree = "<group>";
};
E06336A72B7582D600A88E6B /* Assets */ = { E06336A72B7582D600A88E6B /* Assets */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -4012,7 +4036,7 @@
D78DB8582C1CE9CA00F0AB12 /* SwipeActions */, D78DB8582C1CE9CA00F0AB12 /* SwipeActions */,
D70D90972CDED61800CD0534 /* CodeScanner */, D70D90972CDED61800CD0534 /* CodeScanner */,
D7C48C0A2D12DE0C00A3BACF /* SwiftyCrop */, D7C48C0A2D12DE0C00A3BACF /* SwiftyCrop */,
D7BEE6F22D37AE1B00CF659F /* NostrSDK */, D7DB1FE32D5A9AC900CF06DA /* CryptoSwift */,
); );
productName = damus; productName = damus;
productReference = 4CE6DEE327F7A08100C66700 /* damus.app */; productReference = 4CE6DEE327F7A08100C66700 /* damus.app */;
@@ -4079,7 +4103,7 @@
82D6FC892CD9A54600C925F4 /* SwipeActions */, 82D6FC892CD9A54600C925F4 /* SwipeActions */,
D7F360282CEBBE34009D34DA /* CodeScanner */, D7F360282CEBBE34009D34DA /* CodeScanner */,
D7C48C0C2D12E34900A3BACF /* SwiftyCrop */, D7C48C0C2D12E34900A3BACF /* SwiftyCrop */,
D7BEE6F42D37B20400CF659F /* NostrSDK */, D7DB1FEB2D5A9F6500CF06DA /* CryptoSwift */,
); );
productName = "share extension"; productName = "share extension";
productReference = 82D6FA972CD9820500C925F4 /* ShareExtension.appex */; productReference = 82D6FA972CD9820500C925F4 /* ShareExtension.appex */;
@@ -4108,7 +4132,7 @@
D73E5F9C2C6AA8E3007EB227 /* SwipeActions */, D73E5F9C2C6AA8E3007EB227 /* SwipeActions */,
D70D909B2CDED7B200CD0534 /* CodeScanner */, D70D909B2CDED7B200CD0534 /* CodeScanner */,
D7C48C0E2D12E35600A3BACF /* SwiftyCrop */, D7C48C0E2D12E35600A3BACF /* SwiftyCrop */,
D7BEE6F62D37B21400CF659F /* NostrSDK */, D7DB1FE72D5A9F5300CF06DA /* CryptoSwift */,
); );
productName = "highlighter action extension"; productName = "highlighter action extension";
productReference = D703D7172C66E47100A400EA /* HighlighterActionExtension.appex */; productReference = D703D7172C66E47100A400EA /* HighlighterActionExtension.appex */;
@@ -4131,6 +4155,7 @@
packageProductDependencies = ( packageProductDependencies = (
D789D11F2AFEFBF20083A7AB /* secp256k1 */, D789D11F2AFEFBF20083A7AB /* secp256k1 */,
D7EDED302B1290B80018B19C /* MarkdownUI */, D7EDED302B1290B80018B19C /* MarkdownUI */,
D7DB1FE92D5A9F5A00CF06DA /* CryptoSwift */,
); );
productName = DamusNotificationService; productName = DamusNotificationService;
productReference = D79C4C142AFEB061003A41B4 /* DamusNotificationService.appex */; productReference = D79C4C142AFEB061003A41B4 /* DamusNotificationService.appex */;
@@ -4218,7 +4243,7 @@
D78DB8572C1CE9CA00F0AB12 /* XCRemoteSwiftPackageReference "SwipeActions" */, D78DB8572C1CE9CA00F0AB12 /* XCRemoteSwiftPackageReference "SwipeActions" */,
D70D90962CDED61800CD0534 /* XCRemoteSwiftPackageReference "CodeScanner" */, D70D90962CDED61800CD0534 /* XCRemoteSwiftPackageReference "CodeScanner" */,
D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */, D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */,
D7BEE6F12D37AE1B00CF659F /* XCRemoteSwiftPackageReference "nostr-sdk-swift" */, D7DB1FE22D5A9AC900CF06DA /* XCRemoteSwiftPackageReference "CryptoSwift" */,
); );
productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */; productRefGroup = 4CE6DEE427F7A08100C66700 /* Products */;
projectDirPath = ""; projectDirPath = "";
@@ -4258,7 +4283,9 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
E06336AB2B75850100A88E6B /* img_with_location.jpeg in Resources */, E06336AB2B75850100A88E6B /* img_with_location.jpeg in Resources */,
D7DB1FF12D5AC5D700CF06DA /* nip44.vectors.json in Resources */,
4C0C039A2A61E27B0098B3B8 /* bool_setting.wasm in Resources */, 4C0C039A2A61E27B0098B3B8 /* bool_setting.wasm in Resources */,
D7DB1FF32D5AC5EA00CF06DA /* LICENSES in Resources */,
4C0C03992A61E27B0098B3B8 /* primal.wasm in Resources */, 4C0C03992A61E27B0098B3B8 /* primal.wasm in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -4718,6 +4745,7 @@
50A60D142A28BEEE00186190 /* RelayLog.swift in Sources */, 50A60D142A28BEEE00186190 /* RelayLog.swift in Sources */,
D7EDED212B117DCA0018B19C /* SequenceUtils.swift in Sources */, D7EDED212B117DCA0018B19C /* SequenceUtils.swift in Sources */,
BA37598A2ABCCDE40018D73B /* ImageResizer.swift in Sources */, BA37598A2ABCCDE40018D73B /* ImageResizer.swift in Sources */,
D7DB1FDE2D5A78CE00CF06DA /* NIP44.swift in Sources */,
B51C1CEB2B55A60A00E312A9 /* MuteDurationMenu.swift in Sources */, B51C1CEB2B55A60A00E312A9 /* MuteDurationMenu.swift in Sources */,
4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */, 4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */,
4C32B9512A9AD44700DC3548 /* FlatbuffersErrors.swift in Sources */, 4C32B9512A9AD44700DC3548 /* FlatbuffersErrors.swift in Sources */,
@@ -4831,6 +4859,7 @@
3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */, 3ACBCB78295FE5C70037388A /* TimeAgoTests.swift in Sources */,
D72A2D072AD9C1FB002AFF62 /* MockProfiles.swift in Sources */, D72A2D072AD9C1FB002AFF62 /* MockProfiles.swift in Sources */,
B5A75C2A2B546D94007AFBC0 /* MuteItemTests.swift in Sources */, B5A75C2A2B546D94007AFBC0 /* MuteItemTests.swift in Sources */,
D7DB1FEE2D5AC51B00CF06DA /* NIP44v2EncryptionTests.swift in Sources */,
4C4F14A72A2A61A30045A0B9 /* NostrScriptTests.swift in Sources */, 4C4F14A72A2A61A30045A0B9 /* NostrScriptTests.swift in Sources */,
D78525252A7B2EA4002FA637 /* NoteContentViewTests.swift in Sources */, D78525252A7B2EA4002FA637 /* NoteContentViewTests.swift in Sources */,
4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */, 4C3EA67B28FF7B3900C48A62 /* InvoiceTests.swift in Sources */,
@@ -5036,6 +5065,7 @@
82D6FB452CD99F7900C925F4 /* InputDismissKeyboard.swift in Sources */, 82D6FB452CD99F7900C925F4 /* InputDismissKeyboard.swift in Sources */,
82D6FB462CD99F7900C925F4 /* Constants.swift in Sources */, 82D6FB462CD99F7900C925F4 /* Constants.swift in Sources */,
82D6FB472CD99F7900C925F4 /* LinkView.swift in Sources */, 82D6FB472CD99F7900C925F4 /* LinkView.swift in Sources */,
D7DB1FDF2D5A78CE00CF06DA /* NIP44.swift in Sources */,
82D6FB482CD99F7900C925F4 /* PreviewCache.swift in Sources */, 82D6FB482CD99F7900C925F4 /* PreviewCache.swift in Sources */,
82D6FB492CD99F7900C925F4 /* Theme.swift in Sources */, 82D6FB492CD99F7900C925F4 /* Theme.swift in Sources */,
82D6FB4A2CD99F7900C925F4 /* NIP05.swift in Sources */, 82D6FB4A2CD99F7900C925F4 /* NIP05.swift in Sources */,
@@ -5767,6 +5797,7 @@
D703D7712C670B6D00A400EA /* NdbProfile.swift in Sources */, D703D7712C670B6D00A400EA /* NdbProfile.swift in Sources */,
D703D7A22C670E1A00A400EA /* list.c in Sources */, D703D7A22C670E1A00A400EA /* list.c in Sources */,
D703D7A42C670E3C00A400EA /* midl.c in Sources */, D703D7A42C670E3C00A400EA /* midl.c in Sources */,
D7DB1FE02D5A78CE00CF06DA /* NIP44.swift in Sources */,
D703D7982C670DF200A400EA /* utf8.c in Sources */, D703D7982C670DF200A400EA /* utf8.c in Sources */,
D703D78B2C670C9500A400EA /* MakeZapRequest.swift in Sources */, D703D78B2C670C9500A400EA /* MakeZapRequest.swift in Sources */,
D703D7862C670C6500A400EA /* NewUnmutesNotify.swift in Sources */, D703D7862C670C6500A400EA /* NewUnmutesNotify.swift in Sources */,
@@ -6777,14 +6808,6 @@
minimumVersion = 1.14.1; minimumVersion = 1.14.1;
}; };
}; };
D7BEE6F12D37AE1B00CF659F /* XCRemoteSwiftPackageReference "nostr-sdk-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/rust-nostr/nostr-sdk-swift";
requirement = {
kind = revision;
revision = 27711a03ea7d977162595eea1d9b2d5a45f0b628;
};
};
D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */ = { D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */ = {
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/benedom/SwiftyCrop"; repositoryURL = "https://github.com/benedom/SwiftyCrop";
@@ -6793,6 +6816,14 @@
revision = 454d0a0d4faf6f3a19c8d817ab9d7d27524bd79f; revision = 454d0a0d4faf6f3a19c8d817ab9d7d27524bd79f;
}; };
}; };
D7DB1FE22D5A9AC900CF06DA /* XCRemoteSwiftPackageReference "CryptoSwift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/krzyzanowskim/CryptoSwift.git";
requirement = {
kind = revision;
revision = e74bbbfbef939224b242ae7c342a90e60b88b5ce;
};
};
/* End XCRemoteSwiftPackageReference section */ /* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */ /* Begin XCSwiftPackageProductDependency section */
@@ -6906,21 +6937,6 @@
package = D7A343EC2AD0D77C00CED48B /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */; package = D7A343EC2AD0D77C00CED48B /* XCRemoteSwiftPackageReference "swift-snapshot-testing" */;
productName = SnapshotTesting; 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 */ = { D7C48C0A2D12DE0C00A3BACF /* SwiftyCrop */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */; package = D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */;
@@ -6936,6 +6952,26 @@
package = D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */; package = D7C48C092D12DE0C00A3BACF /* XCRemoteSwiftPackageReference "SwiftyCrop" */;
productName = 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 */ = { D7EDED242B117F7C0018B19C /* MarkdownUI */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = 4C27C9302A64766F007DBC75 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */; package = 4C27C9302A64766F007DBC75 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */;

View File

@@ -1,5 +1,5 @@
{ {
"originHash" : "fa2b0ad84b4bd1a962ffbe49810548db7c9d7131f4a1fd4b4af06ff4c6de0a44", "originHash" : "085cf0f645323bf77edb52886489bf77b309a0a2d2b78a54beaf8520b540d596",
"pins" : [ "pins" : [
{ {
"identity" : "codescanner", "identity" : "codescanner",
@@ -9,6 +9,14 @@
"revision" : "9fa582f4b36c69c2a55bff5fb3377eb170ae273c" "revision" : "9fa582f4b36c69c2a55bff5fb3377eb170ae273c"
} }
}, },
{
"identity" : "cryptoswift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/krzyzanowskim/CryptoSwift.git",
"state" : {
"revision" : "e74bbbfbef939224b242ae7c342a90e60b88b5ce"
}
},
{ {
"identity" : "emojikit", "identity" : "emojikit",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
@@ -45,14 +53,6 @@
"version" : "7.6.1" "version" : "7.6.1"
} }
}, },
{
"identity" : "nostr-sdk-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/rust-nostr/nostr-sdk-swift",
"state" : {
"revision" : "27711a03ea7d977162595eea1d9b2d5a45f0b628"
}
},
{ {
"identity" : "secp256k1.swift", "identity" : "secp256k1.swift",
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",

View File

@@ -4,7 +4,6 @@
// //
// Created by Daniel DAquino on 2025-01-20. // Created by Daniel DAquino on 2025-01-20.
// //
import NostrSDK
import Foundation import Foundation
/// This models a NIP-37 draft. /// 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 { guard let note_json_string = String(data: note_json_data, encoding: .utf8) else {
throw NIP37DraftEventError.encoding_error throw NIP37DraftEventError.encoding_error
} }
guard let secret_key = SecretKey.from(privkey: keypair.privkey) else { guard let contents = try? NIP44v2Encryption.encrypt(plaintext: note_json_string, privateKeyA: keypair.privkey, publicKeyB: keypair.pubkey) 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 {
return nil return nil
} }
var tags = [ var tags = [
@@ -111,16 +104,10 @@ struct NIP37Draft {
static func unwrap(note: NdbNote, keypair: FullKeypair) throws -> NdbNote? { static func unwrap(note: NdbNote, keypair: FullKeypair) throws -> NdbNote? {
let wrapped_note = note let wrapped_note = note
guard wrapped_note.known_kind == .draft else { return nil } guard wrapped_note.known_kind == .draft else { return nil }
guard let private_key = SecretKey.from(privkey: keypair.privkey) else { guard let draft_event_json = try? NIP44v2Encryption.decrypt(
throw NIP37DraftEventError.invalid_keypair payload: wrapped_note.content,
} privateKeyA: keypair.privkey,
guard let pubkey = PublicKey.from(pubkey: keypair.pubkey) else { publicKeyB: keypair.pubkey
throw NIP37DraftEventError.invalid_keypair
}
guard let draft_event_json = try? nip44Decrypt(
secretKey: private_key,
publicKey: pubkey,
payload: wrapped_note.content
) else { return nil } ) else { return nil }
return NdbNote.owned_from_json(json: draft_event_json) return NdbNote.owned_from_json(json: draft_event_json)
} }
@@ -130,17 +117,3 @@ struct NIP37Draft {
case encoding_error 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())
}
}

357
damus/NIP44/NIP44.swift Normal file
View File

@@ -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 DAquino 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
}
}

View File

@@ -21,6 +21,17 @@ let ANON_PUBKEY = Pubkey(Data([
struct FullKeypair: Equatable { struct FullKeypair: Equatable {
let pubkey: Pubkey let pubkey: Pubkey
let privkey: Privkey 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 { func to_keypair() -> Keypair {
return Keypair(pubkey: pubkey, privkey: privkey) return Keypair(pubkey: pubkey, privkey: privkey)

23
damusTests/LICENSES Normal file
View File

@@ -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.

View File

@@ -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 DAquino 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
}
}

View File

@@ -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鷗Œé逍Üߪąñ丂㐀𠀀",
"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"
}
]
}
}
}