mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Updated to the latest blinding behaviour
Added a couple more dependencies for unit testing injection Updated the MessageSender to set the sender of the message to the appropriate blinded/unblinded key Updated the OpenGroup Message to handle verification of both blinded and unblinded messages Updated the MessageSender to use dependency injection for it's sendToOpenGroupDestination method Updated the JSONDecoder to support getting dependencies (for signature verification) Fixed tests broken by updating the signing logic
This commit is contained in:
parent
ef09d4d5aa
commit
1edd500dab
|
@ -776,7 +776,6 @@
|
||||||
FD5D201127AA331F00FEA984 /* ConfigurationMessage+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D201027AA331F00FEA984 /* ConfigurationMessage+Convenience.swift */; };
|
FD5D201127AA331F00FEA984 /* ConfigurationMessage+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D201027AA331F00FEA984 /* ConfigurationMessage+Convenience.swift */; };
|
||||||
FD5D201E27B0D87C00FEA984 /* IdPrefix.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D201D27B0D87C00FEA984 /* IdPrefix.swift */; };
|
FD5D201E27B0D87C00FEA984 /* IdPrefix.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D201D27B0D87C00FEA984 /* IdPrefix.swift */; };
|
||||||
FD5D202027B0E67900FEA984 /* String+Encoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D201F27B0E67800FEA984 /* String+Encoding.swift */; };
|
FD5D202027B0E67900FEA984 /* String+Encoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D201F27B0E67800FEA984 /* String+Encoding.swift */; };
|
||||||
FD5D202227B1D74F00FEA984 /* ECKeyPair+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D202127B1D74F00FEA984 /* ECKeyPair+Conversion.swift */; };
|
|
||||||
FD659AC027A7649600F12C02 /* MessageRequestsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD659ABF27A7649600F12C02 /* MessageRequestsViewController.swift */; };
|
FD659AC027A7649600F12C02 /* MessageRequestsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD659ABF27A7649600F12C02 /* MessageRequestsViewController.swift */; };
|
||||||
FD705A8C278CDB5600F16121 /* SAEScreenLockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A8B278CDB5600F16121 /* SAEScreenLockViewController.swift */; };
|
FD705A8C278CDB5600F16121 /* SAEScreenLockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A8B278CDB5600F16121 /* SAEScreenLockViewController.swift */; };
|
||||||
FD705A8E278CE29800F16121 /* String+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A8D278CE29800F16121 /* String+Localization.swift */; };
|
FD705A8E278CE29800F16121 /* String+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A8D278CE29800F16121 /* String+Localization.swift */; };
|
||||||
|
@ -784,6 +783,12 @@
|
||||||
FD705A92278D051200F16121 /* ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A91278D051200F16121 /* ReusableView.swift */; };
|
FD705A92278D051200F16121 /* ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A91278D051200F16121 /* ReusableView.swift */; };
|
||||||
FD705A94278D052B00F16121 /* UITableView+ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A93278D052B00F16121 /* UITableView+ReusableView.swift */; };
|
FD705A94278D052B00F16121 /* UITableView+ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A93278D052B00F16121 /* UITableView+ReusableView.swift */; };
|
||||||
FD705A98278E9F4D00F16121 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A97278E9F4D00F16121 /* UIColor+Extensions.swift */; };
|
FD705A98278E9F4D00F16121 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A97278E9F4D00F16121 /* UIColor+Extensions.swift */; };
|
||||||
|
FD859EF227BF6BA200510D0C /* Data+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF127BF6BA200510D0C /* Data+Utilities.swift */; };
|
||||||
|
FD859EF427C2F49200510D0C /* TestSodium.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF327C2F49200510D0C /* TestSodium.swift */; };
|
||||||
|
FD859EF627C2F52C00510D0C /* TestSign.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF527C2F52C00510D0C /* TestSign.swift */; };
|
||||||
|
FD859EF827C2F58900510D0C /* TestAeadXChaCha20Poly1305Ietf.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF727C2F58900510D0C /* TestAeadXChaCha20Poly1305Ietf.swift */; };
|
||||||
|
FD859EFA27C2F5C500510D0C /* TestGenericHash.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EF927C2F5C500510D0C /* TestGenericHash.swift */; };
|
||||||
|
FD859EFC27C2F60700510D0C /* TestEd25519.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EFB27C2F60700510D0C /* TestEd25519.swift */; };
|
||||||
FD88BAD927A7439C00BBC442 /* MessageRequestsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */; };
|
FD88BAD927A7439C00BBC442 /* MessageRequestsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */; };
|
||||||
FD88BADB27A750F200BBC442 /* MessageRequestsMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */; };
|
FD88BADB27A750F200BBC442 /* MessageRequestsMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */; };
|
||||||
FDC4380927B31D4E00C60D73 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4380827B31D4E00C60D73 /* Error.swift */; };
|
FDC4380927B31D4E00C60D73 /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC4380827B31D4E00C60D73 /* Error.swift */; };
|
||||||
|
@ -1912,7 +1917,6 @@
|
||||||
FD5D201027AA331F00FEA984 /* ConfigurationMessage+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfigurationMessage+Convenience.swift"; sourceTree = "<group>"; };
|
FD5D201027AA331F00FEA984 /* ConfigurationMessage+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfigurationMessage+Convenience.swift"; sourceTree = "<group>"; };
|
||||||
FD5D201D27B0D87C00FEA984 /* IdPrefix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdPrefix.swift; sourceTree = "<group>"; };
|
FD5D201D27B0D87C00FEA984 /* IdPrefix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdPrefix.swift; sourceTree = "<group>"; };
|
||||||
FD5D201F27B0E67800FEA984 /* String+Encoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Encoding.swift"; sourceTree = "<group>"; };
|
FD5D201F27B0E67800FEA984 /* String+Encoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Encoding.swift"; sourceTree = "<group>"; };
|
||||||
FD5D202127B1D74F00FEA984 /* ECKeyPair+Conversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ECKeyPair+Conversion.swift"; sourceTree = "<group>"; };
|
|
||||||
FD659ABF27A7649600F12C02 /* MessageRequestsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsViewController.swift; sourceTree = "<group>"; };
|
FD659ABF27A7649600F12C02 /* MessageRequestsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsViewController.swift; sourceTree = "<group>"; };
|
||||||
FD705A8B278CDB5600F16121 /* SAEScreenLockViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SAEScreenLockViewController.swift; sourceTree = "<group>"; };
|
FD705A8B278CDB5600F16121 /* SAEScreenLockViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SAEScreenLockViewController.swift; sourceTree = "<group>"; };
|
||||||
FD705A8D278CE29800F16121 /* String+Localization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Localization.swift"; sourceTree = "<group>"; };
|
FD705A8D278CE29800F16121 /* String+Localization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Localization.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -1920,6 +1924,14 @@
|
||||||
FD705A91278D051200F16121 /* ReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReusableView.swift; sourceTree = "<group>"; };
|
FD705A91278D051200F16121 /* ReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReusableView.swift; sourceTree = "<group>"; };
|
||||||
FD705A93278D052B00F16121 /* UITableView+ReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+ReusableView.swift"; sourceTree = "<group>"; };
|
FD705A93278D052B00F16121 /* UITableView+ReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+ReusableView.swift"; sourceTree = "<group>"; };
|
||||||
FD705A97278E9F4D00F16121 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = "<group>"; };
|
FD705A97278E9F4D00F16121 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
|
FD859EEF27BF207700510D0C /* SessionProtos.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = SessionProtos.proto; sourceTree = "<group>"; };
|
||||||
|
FD859EF027BF207C00510D0C /* WebSocketResources.proto */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.protobuf; path = WebSocketResources.proto; sourceTree = "<group>"; };
|
||||||
|
FD859EF127BF6BA200510D0C /* Data+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+Utilities.swift"; sourceTree = "<group>"; };
|
||||||
|
FD859EF327C2F49200510D0C /* TestSodium.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSodium.swift; sourceTree = "<group>"; };
|
||||||
|
FD859EF527C2F52C00510D0C /* TestSign.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSign.swift; sourceTree = "<group>"; };
|
||||||
|
FD859EF727C2F58900510D0C /* TestAeadXChaCha20Poly1305Ietf.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestAeadXChaCha20Poly1305Ietf.swift; sourceTree = "<group>"; };
|
||||||
|
FD859EF927C2F5C500510D0C /* TestGenericHash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestGenericHash.swift; sourceTree = "<group>"; };
|
||||||
|
FD859EFB27C2F60700510D0C /* TestEd25519.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestEd25519.swift; sourceTree = "<group>"; };
|
||||||
FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsCell.swift; sourceTree = "<group>"; };
|
FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsCell.swift; sourceTree = "<group>"; };
|
||||||
FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsMigration.swift; sourceTree = "<group>"; };
|
FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsMigration.swift; sourceTree = "<group>"; };
|
||||||
FD9039443F7CB729CF71350E /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
FD9039443F7CB729CF71350E /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
@ -3384,6 +3396,7 @@
|
||||||
C33FDB01255A580700E217F9 /* AppReadiness.h */,
|
C33FDB01255A580700E217F9 /* AppReadiness.h */,
|
||||||
C33FDB75255A581000E217F9 /* AppReadiness.m */,
|
C33FDB75255A581000E217F9 /* AppReadiness.m */,
|
||||||
FDC4383D27B4708600C60D73 /* Atomic.swift */,
|
FDC4383D27B4708600C60D73 /* Atomic.swift */,
|
||||||
|
FD859EF127BF6BA200510D0C /* Data+Utilities.swift */,
|
||||||
C38EF309255B6DBE007E1867 /* DeviceSleepManager.swift */,
|
C38EF309255B6DBE007E1867 /* DeviceSleepManager.swift */,
|
||||||
C37F53E8255BA9BB002AEA92 /* Environment.h */,
|
C37F53E8255BA9BB002AEA92 /* Environment.h */,
|
||||||
C37F5402255BA9ED002AEA92 /* Environment.m */,
|
C37F5402255BA9ED002AEA92 /* Environment.m */,
|
||||||
|
@ -3423,7 +3436,6 @@
|
||||||
C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */,
|
C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */,
|
||||||
C3E7134E251C867C009649BB /* Sodium+Utilities.swift */,
|
C3E7134E251C867C009649BB /* Sodium+Utilities.swift */,
|
||||||
FDC4386827B4E6B700C60D73 /* String+Utlities.swift */,
|
FDC4386827B4E6B700C60D73 /* String+Utlities.swift */,
|
||||||
FD5D202127B1D74F00FEA984 /* ECKeyPair+Conversion.swift */,
|
|
||||||
FDC4387327B5BB9B00C60D73 /* Promise+Utilities.swift */,
|
FDC4387327B5BB9B00C60D73 /* Promise+Utilities.swift */,
|
||||||
C33FDB31255A580A00E217F9 /* SSKEnvironment.h */,
|
C33FDB31255A580A00E217F9 /* SSKEnvironment.h */,
|
||||||
C33FDAF4255A580600E217F9 /* SSKEnvironment.m */,
|
C33FDAF4255A580600E217F9 /* SSKEnvironment.m */,
|
||||||
|
@ -3539,6 +3551,8 @@
|
||||||
C3C2A7802553AA6300C340D1 /* Protos */ = {
|
C3C2A7802553AA6300C340D1 /* Protos */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
FD859EEF27BF207700510D0C /* SessionProtos.proto */,
|
||||||
|
FD859EF027BF207C00510D0C /* WebSocketResources.proto */,
|
||||||
C3C2A7812553AA9000C340D1 /* Generated */,
|
C3C2A7812553AA9000C340D1 /* Generated */,
|
||||||
);
|
);
|
||||||
path = Protos;
|
path = Protos;
|
||||||
|
@ -3938,6 +3952,11 @@
|
||||||
children = (
|
children = (
|
||||||
FDC438BC27BB2AB400C60D73 /* Mockable.swift */,
|
FDC438BC27BB2AB400C60D73 /* Mockable.swift */,
|
||||||
FDC4389C27BA01F000C60D73 /* TestStorage.swift */,
|
FDC4389C27BA01F000C60D73 /* TestStorage.swift */,
|
||||||
|
FD859EF327C2F49200510D0C /* TestSodium.swift */,
|
||||||
|
FD859EF527C2F52C00510D0C /* TestSign.swift */,
|
||||||
|
FD859EF727C2F58900510D0C /* TestAeadXChaCha20Poly1305Ietf.swift */,
|
||||||
|
FD859EF927C2F5C500510D0C /* TestGenericHash.swift */,
|
||||||
|
FD859EFB27C2F60700510D0C /* TestEd25519.swift */,
|
||||||
);
|
);
|
||||||
path = _TestUtilities;
|
path = _TestUtilities;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -5110,6 +5129,7 @@
|
||||||
C32C5A13256DB7A5003C73A2 /* PushNotificationAPI.swift in Sources */,
|
C32C5A13256DB7A5003C73A2 /* PushNotificationAPI.swift in Sources */,
|
||||||
FDC4385F27B4C4A200C60D73 /* PinnedMessage.swift in Sources */,
|
FDC4385F27B4C4A200C60D73 /* PinnedMessage.swift in Sources */,
|
||||||
C32A026325A801AA000ED5D4 /* NSData+messagePadding.m in Sources */,
|
C32A026325A801AA000ED5D4 /* NSData+messagePadding.m in Sources */,
|
||||||
|
FD859EF227BF6BA200510D0C /* Data+Utilities.swift in Sources */,
|
||||||
FDC4384927B47F4D00C60D73 /* LegacyCompactPollResponse.swift in Sources */,
|
FDC4384927B47F4D00C60D73 /* LegacyCompactPollResponse.swift in Sources */,
|
||||||
C352A3932557883D00338F3E /* JobDelegate.swift in Sources */,
|
C352A3932557883D00338F3E /* JobDelegate.swift in Sources */,
|
||||||
C32C5B84256DC54F003C73A2 /* SSKEnvironment.m in Sources */,
|
C32C5B84256DC54F003C73A2 /* SSKEnvironment.m in Sources */,
|
||||||
|
@ -5168,7 +5188,6 @@
|
||||||
FDC4382827B37FD300C60D73 /* LegacyModeratorsResponse.swift in Sources */,
|
FDC4382827B37FD300C60D73 /* LegacyModeratorsResponse.swift in Sources */,
|
||||||
C32C5B3F256DC1DF003C73A2 /* TSQuotedMessage+Conversion.swift in Sources */,
|
C32C5B3F256DC1DF003C73A2 /* TSQuotedMessage+Conversion.swift in Sources */,
|
||||||
B8EB20EE2640F28000773E52 /* VisibleMessage+OpenGroupInvitation.swift in Sources */,
|
B8EB20EE2640F28000773E52 /* VisibleMessage+OpenGroupInvitation.swift in Sources */,
|
||||||
FD5D202227B1D74F00FEA984 /* ECKeyPair+Conversion.swift in Sources */,
|
|
||||||
FDC4381C27B354AC00C60D73 /* LegacyPublicKeyBody.swift in Sources */,
|
FDC4381C27B354AC00C60D73 /* LegacyPublicKeyBody.swift in Sources */,
|
||||||
FDC4382F27B383AF00C60D73 /* UnregisterResponse.swift in Sources */,
|
FDC4382F27B383AF00C60D73 /* UnregisterResponse.swift in Sources */,
|
||||||
FDC4386327B4D94E00C60D73 /* OGMessage.swift in Sources */,
|
FDC4386327B4D94E00C60D73 /* OGMessage.swift in Sources */,
|
||||||
|
@ -5463,8 +5482,13 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
FD859EFA27C2F5C500510D0C /* TestGenericHash.swift in Sources */,
|
||||||
|
FD859EF827C2F58900510D0C /* TestAeadXChaCha20Poly1305Ietf.swift in Sources */,
|
||||||
|
FD859EF427C2F49200510D0C /* TestSodium.swift in Sources */,
|
||||||
|
FD859EFC27C2F60700510D0C /* TestEd25519.swift in Sources */,
|
||||||
FDC4389A27BA002500C60D73 /* OpenGroupAPIV2Tests.swift in Sources */,
|
FDC4389A27BA002500C60D73 /* OpenGroupAPIV2Tests.swift in Sources */,
|
||||||
FDC438BD27BB2AB400C60D73 /* Mockable.swift in Sources */,
|
FDC438BD27BB2AB400C60D73 /* Mockable.swift in Sources */,
|
||||||
|
FD859EF627C2F52C00510D0C /* TestSign.swift in Sources */,
|
||||||
FDC4389D27BA01F000C60D73 /* TestStorage.swift in Sources */,
|
FDC4389D27BA01F000C60D73 /* TestStorage.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
|
@ -62,13 +62,13 @@ extension OpenGroupAPI {
|
||||||
// MARK: - Convenience
|
// MARK: - Convenience
|
||||||
|
|
||||||
public extension Decodable {
|
public extension Decodable {
|
||||||
static func decoded(from data: Data) throws -> Self {
|
static func decoded(from data: Data, customError: Error, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) throws -> Self {
|
||||||
return try JSONDecoder().decode(Self.self, from: data)
|
return try data.decoded(as: Self.self, customError: customError, using: dependencies)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Promise where T == (OnionRequestResponseInfoType, Data?) {
|
extension Promise where T == (OnionRequestResponseInfoType, Data?) {
|
||||||
func decoded(as types: OpenGroupAPI.BatchResponseTypes, on queue: DispatchQueue? = nil, error: Error) -> Promise<OpenGroupAPI.BatchResponse> {
|
func decoded(as types: OpenGroupAPI.BatchResponseTypes, on queue: DispatchQueue? = nil, error: Error, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> Promise<OpenGroupAPI.BatchResponse> {
|
||||||
self.map(on: queue) { responseInfo, maybeData -> OpenGroupAPI.BatchResponse in
|
self.map(on: queue) { responseInfo, maybeData -> OpenGroupAPI.BatchResponse in
|
||||||
// Need to split the data into an array of data so each item can be Decoded correctly
|
// Need to split the data into an array of data so each item can be Decoded correctly
|
||||||
guard let data: Data = maybeData else { throw OpenGroupAPI.Error.parsingFailed }
|
guard let data: Data = maybeData else { throw OpenGroupAPI.Error.parsingFailed }
|
||||||
|
@ -82,7 +82,7 @@ extension Promise where T == (OnionRequestResponseInfoType, Data?) {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
return try zip(dataArray, types)
|
return try zip(dataArray, types)
|
||||||
.map { data, type in try type.decoded(from: data) }
|
.map { data, type in try type.decoded(from: data, customError: error, using: dependencies) }
|
||||||
.map { data in (responseInfo, data) }
|
.map { data in (responseInfo, data) }
|
||||||
}
|
}
|
||||||
catch _ {
|
catch _ {
|
||||||
|
|
|
@ -47,14 +47,30 @@ extension OpenGroupAPI.Message {
|
||||||
guard let sender: String = maybeSender, let data = Data(base64Encoded: base64EncodedData), let signature = Data(base64Encoded: base64EncodedSignature) else {
|
guard let sender: String = maybeSender, let data = Data(base64Encoded: base64EncodedData), let signature = Data(base64Encoded: base64EncodedSignature) else {
|
||||||
throw OpenGroupAPI.Error.parsingFailed
|
throw OpenGroupAPI.Error.parsingFailed
|
||||||
}
|
}
|
||||||
|
guard let dependencies: OpenGroupAPI.Dependencies = decoder.userInfo[OpenGroupAPI.Dependencies.userInfoKey] as? OpenGroupAPI.Dependencies else {
|
||||||
let publicKey: Data = Data(hex: sender.removingIdPrefixIfNeeded())
|
|
||||||
let isValid: Bool = ((try? Ed25519.verifySignature(signature, publicKey: publicKey, data: data)) ?? false)
|
|
||||||
|
|
||||||
guard isValid else {
|
|
||||||
SNLog("Ignoring message with invalid signature.")
|
|
||||||
throw OpenGroupAPI.Error.parsingFailed
|
throw OpenGroupAPI.Error.parsingFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify the signature based on the IdPrefix
|
||||||
|
let publicKey: Data = Data(hex: sender.removingIdPrefixIfNeeded())
|
||||||
|
|
||||||
|
switch IdPrefix(with: sender) {
|
||||||
|
case .blinded:
|
||||||
|
guard dependencies.sign.verify(message: data.bytes, publicKey: publicKey.bytes, signature: signature.bytes) else {
|
||||||
|
SNLog("Ignoring message with invalid signature.")
|
||||||
|
throw OpenGroupAPI.Error.parsingFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
case .standard, .unblinded:
|
||||||
|
guard (try? dependencies.ed25519.verifySignature(signature, publicKey: publicKey, data: data)) == true else {
|
||||||
|
SNLog("Ignoring message with invalid signature.")
|
||||||
|
throw OpenGroupAPI.Error.parsingFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
case .none:
|
||||||
|
SNLog("Ignoring message with invalid sender.")
|
||||||
|
throw OpenGroupAPI.Error.parsingFailed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self = OpenGroupAPI.Message(
|
self = OpenGroupAPI.Message(
|
||||||
|
|
|
@ -125,7 +125,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: responseTypes, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: responseTypes, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
.map { result in
|
.map { result in
|
||||||
result.enumerated()
|
result.enumerated()
|
||||||
.reduce(into: [:]) { prev, next in
|
.reduce(into: [:]) { prev, next in
|
||||||
|
@ -156,7 +156,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
|
|
||||||
// TODO: Handle a `412` response (ie. a required capability isn't supported)
|
// TODO: Handle a `412` response (ie. a required capability isn't supported)
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: responseTypes, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: responseTypes, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
.map { result in
|
.map { result in
|
||||||
result.enumerated()
|
result.enumerated()
|
||||||
.reduce(into: [:]) { prev, next in
|
.reduce(into: [:]) { prev, next in
|
||||||
|
@ -176,7 +176,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
|
|
||||||
// TODO: Handle a `412` response (ie. a required capability isn't supported)
|
// TODO: Handle a `412` response (ie. a required capability isn't supported)
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: Capabilities.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: Capabilities.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Room
|
// MARK: - Room
|
||||||
|
@ -188,7 +188,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: [Room].self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: [Room].self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func room(for roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, Room)> {
|
public static func room(for roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, Room)> {
|
||||||
|
@ -198,7 +198,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: Room.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: Room.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func roomPollInfo(lastUpdated: Int64, for roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, RoomPollInfo)> {
|
public static func roomPollInfo(lastUpdated: Int64, for roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, RoomPollInfo)> {
|
||||||
|
@ -208,7 +208,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: RoomPollInfo.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: RoomPollInfo.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Messages
|
// MARK: - Messages
|
||||||
|
@ -221,13 +221,13 @@ public final class OpenGroupAPI: NSObject {
|
||||||
whisperMods: Bool,
|
whisperMods: Bool,
|
||||||
using dependencies: Dependencies = Dependencies()
|
using dependencies: Dependencies = Dependencies()
|
||||||
) -> Promise<(OnionRequestResponseInfoType, Message)> {
|
) -> Promise<(OnionRequestResponseInfoType, Message)> {
|
||||||
guard let signedMessage: (data: Data, signature: Data) = sign(message: plaintext, to: roomToken, on: server, using: dependencies) else {
|
guard let signResult: (publicKey: String, signature: Bytes) = sign(plaintext.bytes, for: server, using: dependencies) else {
|
||||||
return Promise(error: Error.signingFailed)
|
return Promise(error: Error.signingFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
let requestBody: SendMessageRequest = SendMessageRequest(
|
let requestBody: SendMessageRequest = SendMessageRequest(
|
||||||
data: signedMessage.data,
|
data: plaintext,
|
||||||
signature: signedMessage.signature,
|
signature: Data(signResult.signature),
|
||||||
whisperTo: whisperTo,
|
whisperTo: whisperTo,
|
||||||
whisperMods: whisperMods,
|
whisperMods: whisperMods,
|
||||||
fileIds: nil // TODO: Add support for 'fileIds'.
|
fileIds: nil // TODO: Add support for 'fileIds'.
|
||||||
|
@ -245,7 +245,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: Message.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: Message.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func message(_ id: Int64, in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, Message)> {
|
public static func message(_ id: Int64, in roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, Message)> {
|
||||||
|
@ -255,7 +255,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: Message.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: Message.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func messageUpdate(
|
public static func messageUpdate(
|
||||||
|
@ -265,13 +265,13 @@ public final class OpenGroupAPI: NSObject {
|
||||||
on server: String,
|
on server: String,
|
||||||
using dependencies: Dependencies = Dependencies()
|
using dependencies: Dependencies = Dependencies()
|
||||||
) -> Promise<(OnionRequestResponseInfoType, Data?)> {
|
) -> Promise<(OnionRequestResponseInfoType, Data?)> {
|
||||||
guard let signedMessage: (data: Data, signature: Data) = sign(message: plaintext, to: roomToken, on: server, using: dependencies) else {
|
guard let signResult: (publicKey: String, signature: Bytes) = sign(plaintext.bytes, for: server, using: dependencies) else {
|
||||||
return Promise(error: Error.signingFailed)
|
return Promise(error: Error.signingFailed)
|
||||||
}
|
}
|
||||||
|
|
||||||
let requestBody: UpdateMessageRequest = UpdateMessageRequest(
|
let requestBody: UpdateMessageRequest = UpdateMessageRequest(
|
||||||
data: signedMessage.data,
|
data: plaintext,
|
||||||
signature: signedMessage.signature
|
signature: Data(signResult.signature)
|
||||||
)
|
)
|
||||||
|
|
||||||
guard let body: Data = try? JSONEncoder().encode(requestBody) else {
|
guard let body: Data = try? JSONEncoder().encode(requestBody) else {
|
||||||
|
@ -302,7 +302,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: [Message].self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: [Message].self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is the direct request to retrieve recent messages from an Open Group so should be retrieved automatically from the `poll()`
|
/// This is the direct request to retrieve recent messages from an Open Group so should be retrieved automatically from the `poll()`
|
||||||
|
@ -319,7 +319,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: [Message].self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: [Message].self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is the direct request to retrieve recent messages from an Open Group so should be retrieved automatically from the `poll()`
|
/// This is the direct request to retrieve recent messages from an Open Group so should be retrieved automatically from the `poll()`
|
||||||
|
@ -335,7 +335,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: [Message].self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: [Message].self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Pinning
|
// MARK: - Pinning
|
||||||
|
@ -385,7 +385,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: FileUploadResponse.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: FileUploadResponse.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Warning: This approach is less efficient as it expects the data to be base64Encoded (with is 33% larger than binary), please use the binary approach
|
/// Warning: This approach is less efficient as it expects the data to be base64Encoded (with is 33% larger than binary), please use the binary approach
|
||||||
|
@ -400,7 +400,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: FileUploadResponse.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: FileUploadResponse.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func downloadFile(_ fileId: Int64, from roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, Data)> {
|
public static func downloadFile(_ fileId: Int64, from roomToken: String, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, Data)> {
|
||||||
|
@ -424,7 +424,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
// TODO: This endpoint is getting rewritten to return just data (properties would come through as headers).
|
// TODO: This endpoint is getting rewritten to return just data (properties would come through as headers).
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: FileDownloadResponse.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: FileDownloadResponse.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Inbox (Message Requests)
|
// MARK: - Inbox (Message Requests)
|
||||||
|
@ -436,7 +436,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: [DirectMessage].self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: [DirectMessage].self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func messageRequestsSince(id: Int64, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [DirectMessage])> {
|
public static func messageRequestsSince(id: Int64, on server: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [DirectMessage])> {
|
||||||
|
@ -446,7 +446,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: [DirectMessage].self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: [DirectMessage].self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func sendMessageRequest(_ plaintext: Data, to blindedSessionId: String, on server: String, with serverPublicKey: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [DirectMessage])> {
|
public static func sendMessageRequest(_ plaintext: Data, to blindedSessionId: String, on server: String, with serverPublicKey: String, using dependencies: Dependencies = Dependencies()) -> Promise<(OnionRequestResponseInfoType, [DirectMessage])> {
|
||||||
|
@ -471,7 +471,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: [DirectMessage].self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: [DirectMessage].self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Users
|
// MARK: - Users
|
||||||
|
@ -581,79 +581,45 @@ public final class OpenGroupAPI: NSObject {
|
||||||
)
|
)
|
||||||
|
|
||||||
return send(request, using: dependencies)
|
return send(request, using: dependencies)
|
||||||
.decoded(as: UserDeleteMessagesResponse.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed)
|
.decoded(as: UserDeleteMessagesResponse.self, on: OpenGroupAPI.workQueue, error: Error.parsingFailed, using: dependencies)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Authentication
|
// MARK: - Authentication
|
||||||
|
|
||||||
/// Sign a message to be sent to SOGS (handles both un-blinded and blinded signing based on the server capabilities)
|
/// Sign a message to be sent to SOGS (handles both un-blinded and blinded signing based on the server capabilities)
|
||||||
public static func sign(message: Data, to roomToken: String, on serverName: String, using dependencies: Dependencies = Dependencies()) -> (data: Data, signature: Data)? {
|
public static func sign(_ messageBytes: Bytes, for serverName: String, using dependencies: Dependencies = Dependencies()) -> (publicKey: String, signature: Bytes)? {
|
||||||
|
guard let userEdKeyPair: Box.KeyPair = dependencies.storage.getUserED25519KeyPair() else { return nil }
|
||||||
|
guard let serverPublicKey: String = dependencies.storage.getOpenGroupPublicKey(for: serverName) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
let server: Server? = dependencies.storage.getOpenGroupServer(name: serverName)
|
let server: Server? = dependencies.storage.getOpenGroupServer(name: serverName)
|
||||||
let targetKeyPair: ECKeyPair
|
|
||||||
|
|
||||||
// Determine if we want to sign using standard or blinded keys based on the server capabilities (assume
|
// Check if the server supports blinded keys, if so then sign using the blinded key
|
||||||
// unblinded if we have none)
|
|
||||||
// TODO: Remove this (blinding will be required)
|
|
||||||
if server?.capabilities.capabilities.contains(.blinding) == true {
|
if server?.capabilities.capabilities.contains(.blinding) == true {
|
||||||
// TODO: Validate this 'openGroupId' is correct for the 'getOpenGroup' call
|
guard let blindedKeyPair: Box.KeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: serverPublicKey, edKeyPair: userEdKeyPair, genericHash: dependencies.genericHash) else {
|
||||||
let openGroupId: String = "\(serverName).\(roomToken)"
|
|
||||||
|
|
||||||
// TODO: Validate this is the correct logic (Most likely not)
|
|
||||||
guard let openGroup: OpenGroup = Storage.shared.getOpenGroup(for: openGroupId) else { return nil }
|
|
||||||
guard let userEdKeyPair: Box.KeyPair = dependencies.storage.getUserED25519KeyPair() else { return nil }
|
|
||||||
guard let blindedKeyPair: Box.KeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: openGroup.publicKey, edKeyPair: userEdKeyPair, genericHash: dependencies.genericHash) else {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
targetKeyPair = blindedKeyPair
|
guard let signatureResult: Bytes = dependencies.sodium.sogsSignature(message: messageBytes, secretKey: userEdKeyPair.secretKey, blindedSecretKey: blindedKeyPair.secretKey, blindedPublicKey: blindedKeyPair.publicKey) else {
|
||||||
}
|
return nil
|
||||||
else {
|
}
|
||||||
guard let userKeyPair: ECKeyPair = dependencies.storage.getUserKeyPair() else { return nil }
|
|
||||||
|
return (
|
||||||
targetKeyPair = userKeyPair
|
publicKey: IdPrefix.blinded.hexEncodedPublicKey(for: blindedKeyPair.publicKey),
|
||||||
|
signature: signatureResult
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let signature = try? Ed25519.sign(message, with: targetKeyPair) else {
|
// Otherwise fall back to sign using the unblinded key
|
||||||
SNLog("Failed to sign open group message.")
|
guard let signatureResult: Bytes = dependencies.sign.signature(message: messageBytes, secretKey: userEdKeyPair.secretKey) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return (message, signature)
|
return (
|
||||||
}
|
publicKey: IdPrefix.unblinded.hexEncodedPublicKey(for: userEdKeyPair.publicKey),
|
||||||
|
signature: signatureResult
|
||||||
/// Sign a blinded message request to be sent to a users inbox via SOGS v4
|
)
|
||||||
private static func sign(message: Data, to blindedSessionId: String, on serverName: String, with serverPublicKey: String, using dependencies: Dependencies = Dependencies()) -> Data? {
|
|
||||||
guard let userEdKeyPair: Box.KeyPair = dependencies.storage.getUserED25519KeyPair() else { return nil }
|
|
||||||
guard let blindedKeyPair: BlindedECKeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: serverPublicKey, edKeyPair: userEdKeyPair, genericHash: dependencies.genericHash) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
guard let blindedRecipientPublicKey: Data = String(blindedSessionId.suffix(from: blindedSessionId.index(blindedSessionId.startIndex, offsetBy: IdPrefix.blinded.rawValue.count))).dataFromHex() else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate the sharedSecret by "a kB || kA || kB" where
|
|
||||||
/// a, A are the users private and public keys respectively,
|
|
||||||
/// kA is the users blinded public key
|
|
||||||
/// kB is the recipients blinded public key
|
|
||||||
let maybeSharedSecret: Data? = dependencies.sodium
|
|
||||||
.sharedEdSecret(userEdKeyPair.secretKey, blindedRecipientPublicKey.bytes)?
|
|
||||||
.appending(blindedKeyPair.publicKey.bytes)
|
|
||||||
.appending(blindedRecipientPublicKey.bytes)
|
|
||||||
|
|
||||||
guard let sharedSecret: Data = maybeSharedSecret else { return nil }
|
|
||||||
guard let intermediateHash: Bytes = dependencies.genericHash.hash(message: sharedSecret.bytes) else { return nil }
|
|
||||||
|
|
||||||
/// Generate the inner message by "message || A" where
|
|
||||||
/// A is the sender's ed25519 master pubkey (**not** kA blinded pubkey)
|
|
||||||
let innerMessage: Bytes = (message.bytes + userEdKeyPair.publicKey)
|
|
||||||
guard let (ciphertext, nonce) = dependencies.aeadXChaCha20Poly1305Ietf.encrypt(message: innerMessage, secretKey: intermediateHash) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate the final data by "b'\x00' + ciphertext + nonce"
|
|
||||||
let finalData: Bytes = [0] + ciphertext + nonce
|
|
||||||
|
|
||||||
return Data(finalData)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sign a request to be sent to SOGS (handles both un-blinded and blinded signing based on the server capabilities)
|
/// Sign a request to be sent to SOGS (handles both un-blinded and blinded signing based on the server capabilities)
|
||||||
|
@ -666,13 +632,9 @@ public final class OpenGroupAPI: NSObject {
|
||||||
let method: String = (request.httpMethod ?? "GET")
|
let method: String = (request.httpMethod ?? "GET")
|
||||||
let timestamp: Int = Int(floor(dependencies.date.timeIntervalSince1970))
|
let timestamp: Int = Int(floor(dependencies.date.timeIntervalSince1970))
|
||||||
let nonce: Data = Data(dependencies.nonceGenerator.nonce())
|
let nonce: Data = Data(dependencies.nonceGenerator.nonce())
|
||||||
let server: Server? = dependencies.storage.getOpenGroupServer(name: serverName)
|
|
||||||
let userPublicKeyHex: String
|
|
||||||
let signatureBytes: Bytes
|
|
||||||
|
|
||||||
guard let serverPublicKeyData: Data = serverPublicKey.dataFromHex() else { return nil }
|
guard let serverPublicKeyData: Data = serverPublicKey.dataFromHex() else { return nil }
|
||||||
guard let timestampBytes: Bytes = "\(timestamp)".data(using: .ascii)?.bytes else { return nil }
|
guard let timestampBytes: Bytes = "\(timestamp)".data(using: .ascii)?.bytes else { return nil }
|
||||||
guard let userEdKeyPair: Box.KeyPair = dependencies.storage.getUserED25519KeyPair() else { return nil }
|
|
||||||
|
|
||||||
/// Get a hash of any body content
|
/// Get a hash of any body content
|
||||||
let bodyHash: Bytes? = {
|
let bodyHash: Bytes? = {
|
||||||
|
@ -693,51 +655,24 @@ public final class OpenGroupAPI: NSObject {
|
||||||
/// `Method`
|
/// `Method`
|
||||||
/// `Path`
|
/// `Path`
|
||||||
/// `Body` is a Blake2b hash of the data (if there is a body)
|
/// `Body` is a Blake2b hash of the data (if there is a body)
|
||||||
let signatureMessageBytes: Bytes = serverPublicKeyData.bytes
|
let messageBytes: Bytes = serverPublicKeyData.bytes
|
||||||
.appending(nonce.bytes)
|
.appending(nonce.bytes)
|
||||||
.appending(timestampBytes)
|
.appending(timestampBytes)
|
||||||
.appending(method.bytes)
|
.appending(method.bytes)
|
||||||
.appending(path.bytes)
|
.appending(path.bytes)
|
||||||
.appending(bodyHash ?? [])
|
.appending(bodyHash ?? [])
|
||||||
|
|
||||||
// Determine if we want to sign using standard or blinded keys based on the server capabilities (assume
|
/// Sign the above message
|
||||||
// unblinded if we have none)
|
guard let signResult: (publicKey: String, signature: Bytes) = sign(messageBytes, for: serverName, using: dependencies) else {
|
||||||
// TODO: Remove this (blinding will be required)
|
return nil
|
||||||
if server?.capabilities.capabilities.contains(.blinding) == true {
|
|
||||||
// TODO: More testing of this blinded id signing (though it seems to be working!!!)
|
|
||||||
guard let blindedKeyPair: Box.KeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: serverPublicKey, edKeyPair: userEdKeyPair, genericHash: dependencies.genericHash) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
userPublicKeyHex = IdPrefix.blinded.hexEncodedPublicKey(for: blindedKeyPair.publicKey)
|
|
||||||
|
|
||||||
guard let signatureResult: Bytes = Sodium().sogsSignature(message: signatureMessageBytes, secretKey: userEdKeyPair.secretKey, blindedSecretKey: blindedKeyPair.secretKey, blindedPublicKey: blindedKeyPair.publicKey) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
signatureBytes = signatureResult
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
userPublicKeyHex = IdPrefix.unblinded.hexEncodedPublicKey(for: userEdKeyPair.publicKey)
|
|
||||||
|
|
||||||
// TODO: shift this to dependencies
|
|
||||||
guard let signatureResult: Bytes = Sodium().sign.signature(message: signatureMessageBytes, secretKey: userEdKeyPair.secretKey) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
signatureBytes = signatureResult
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print("RAWR X-SOGS-Pubkey: \(userPublicKeyHex)")
|
|
||||||
print("RAWR X-SOGS-Timestamp: \(timestamp)")
|
|
||||||
print("RAWR X-SOGS-Nonce: \(nonce.base64EncodedString())")
|
|
||||||
print("RAWR X-SOGS-Signature: \(signatureBytes.toBase64())")
|
|
||||||
updatedRequest.allHTTPHeaderFields = (request.allHTTPHeaderFields ?? [:])
|
updatedRequest.allHTTPHeaderFields = (request.allHTTPHeaderFields ?? [:])
|
||||||
.updated(with: [
|
.updated(with: [
|
||||||
Header.sogsPubKey.rawValue: userPublicKeyHex,
|
Header.sogsPubKey.rawValue: signResult.publicKey,
|
||||||
Header.sogsTimestamp.rawValue: "\(timestamp)",
|
Header.sogsTimestamp.rawValue: "\(timestamp)",
|
||||||
Header.sogsNonce.rawValue: nonce.base64EncodedString(),
|
Header.sogsNonce.rawValue: nonce.base64EncodedString(),
|
||||||
Header.sogsSignature.rawValue: signatureBytes.toBase64()
|
Header.sogsSignature.rawValue: signResult.signature.toBase64()
|
||||||
])
|
])
|
||||||
|
|
||||||
return updatedRequest
|
return updatedRequest
|
||||||
|
@ -756,7 +691,7 @@ public final class OpenGroupAPI: NSObject {
|
||||||
urlRequest.httpBody = request.body
|
urlRequest.httpBody = request.body
|
||||||
|
|
||||||
if request.useOnionRouting {
|
if request.useOnionRouting {
|
||||||
guard let publicKey = SNMessagingKitConfiguration.shared.storage.getOpenGroupPublicKey(for: request.server) else {
|
guard let publicKey = dependencies.storage.getOpenGroupPublicKey(for: request.server) else {
|
||||||
return Promise(error: Error.noPublicKey)
|
return Promise(error: Error.noPublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,16 +10,21 @@ extension OpenGroupAPI {
|
||||||
let storage: SessionMessagingKitStorageProtocol
|
let storage: SessionMessagingKitStorageProtocol
|
||||||
let sodium: SodiumType
|
let sodium: SodiumType
|
||||||
let aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType
|
let aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType
|
||||||
|
let sign: SignType
|
||||||
let genericHash: GenericHashType
|
let genericHash: GenericHashType
|
||||||
|
let ed25519: Ed25519Type.Type
|
||||||
let nonceGenerator: NonceGenerator16ByteType
|
let nonceGenerator: NonceGenerator16ByteType
|
||||||
let date: Date
|
let date: Date
|
||||||
|
|
||||||
public init(
|
public init(
|
||||||
api: OnionRequestAPIType.Type = OnionRequestAPI.self,
|
api: OnionRequestAPIType.Type = OnionRequestAPI.self,
|
||||||
storage: SessionMessagingKitStorageProtocol = SNMessagingKitConfiguration.shared.storage,
|
storage: SessionMessagingKitStorageProtocol = SNMessagingKitConfiguration.shared.storage,
|
||||||
|
// TODO: Shift the next 3 to be abstracted behind a single "signing" class?
|
||||||
sodium: SodiumType = Sodium(),
|
sodium: SodiumType = Sodium(),
|
||||||
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
|
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
|
||||||
|
sign: SignType? = nil,
|
||||||
genericHash: GenericHashType? = nil,
|
genericHash: GenericHashType? = nil,
|
||||||
|
ed25519: Ed25519Type.Type = Ed25519.self,
|
||||||
nonceGenerator: NonceGenerator16ByteType = NonceGenerator16Byte(),
|
nonceGenerator: NonceGenerator16ByteType = NonceGenerator16Byte(),
|
||||||
date: Date = Date()
|
date: Date = Date()
|
||||||
) {
|
) {
|
||||||
|
@ -27,7 +32,9 @@ extension OpenGroupAPI {
|
||||||
self.storage = storage
|
self.storage = storage
|
||||||
self.sodium = sodium
|
self.sodium = sodium
|
||||||
self.aeadXChaCha20Poly1305Ietf = (aeadXChaCha20Poly1305Ietf ?? sodium.getAeadXChaCha20Poly1305Ietf())
|
self.aeadXChaCha20Poly1305Ietf = (aeadXChaCha20Poly1305Ietf ?? sodium.getAeadXChaCha20Poly1305Ietf())
|
||||||
|
self.sign = (sign ?? sodium.getSign())
|
||||||
self.genericHash = (genericHash ?? sodium.getGenericHash())
|
self.genericHash = (genericHash ?? sodium.getGenericHash())
|
||||||
|
self.ed25519 = ed25519
|
||||||
self.nonceGenerator = nonceGenerator
|
self.nonceGenerator = nonceGenerator
|
||||||
self.date = date
|
self.date = date
|
||||||
}
|
}
|
||||||
|
@ -39,7 +46,9 @@ extension OpenGroupAPI {
|
||||||
storage: SessionMessagingKitStorageProtocol? = nil,
|
storage: SessionMessagingKitStorageProtocol? = nil,
|
||||||
sodium: SodiumType? = nil,
|
sodium: SodiumType? = nil,
|
||||||
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
|
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
|
||||||
|
sign: SignType? = nil,
|
||||||
genericHash: GenericHashType? = nil,
|
genericHash: GenericHashType? = nil,
|
||||||
|
ed25519: Ed25519Type.Type? = nil,
|
||||||
nonceGenerator: NonceGenerator16ByteType? = nil,
|
nonceGenerator: NonceGenerator16ByteType? = nil,
|
||||||
date: Date? = nil
|
date: Date? = nil
|
||||||
) -> Dependencies {
|
) -> Dependencies {
|
||||||
|
@ -48,7 +57,9 @@ extension OpenGroupAPI {
|
||||||
storage: (storage ?? self.storage),
|
storage: (storage ?? self.storage),
|
||||||
sodium: (sodium ?? self.sodium),
|
sodium: (sodium ?? self.sodium),
|
||||||
aeadXChaCha20Poly1305Ietf: (aeadXChaCha20Poly1305Ietf ?? self.aeadXChaCha20Poly1305Ietf),
|
aeadXChaCha20Poly1305Ietf: (aeadXChaCha20Poly1305Ietf ?? self.aeadXChaCha20Poly1305Ietf),
|
||||||
|
sign: (sign ?? self.sign),
|
||||||
genericHash: (genericHash ?? self.genericHash),
|
genericHash: (genericHash ?? self.genericHash),
|
||||||
|
ed25519: (ed25519 ?? self.ed25519),
|
||||||
nonceGenerator: (nonceGenerator ?? self.nonceGenerator),
|
nonceGenerator: (nonceGenerator ?? self.nonceGenerator),
|
||||||
date: (date ?? self.date)
|
date: (date ?? self.date)
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,20 +2,32 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Sodium
|
import Sodium
|
||||||
|
import Curve25519Kit
|
||||||
|
|
||||||
public protocol SodiumType {
|
public protocol SodiumType {
|
||||||
func getGenericHash() -> GenericHashType
|
func getGenericHash() -> GenericHashType
|
||||||
func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType
|
func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType
|
||||||
|
func getSign() -> SignType
|
||||||
|
|
||||||
func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair?
|
func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair?
|
||||||
func sharedEdSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Sodium.SharedSecret?
|
func sogsSignature(message: Bytes, secretKey: Bytes, blindedSecretKey ka: Bytes, blindedPublicKey kA: Bytes) -> Bytes?
|
||||||
func sharedSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Sodium.SharedSecret?
|
|
||||||
|
func sharedEdSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Bytes?
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol AeadXChaCha20Poly1305IetfType {
|
public protocol AeadXChaCha20Poly1305IetfType {
|
||||||
func encrypt(message: Bytes, secretKey: Aead.XChaCha20Poly1305Ietf.Key, additionalData: Bytes?) -> (authenticatedCipherText: Bytes, nonce: Aead.XChaCha20Poly1305Ietf.Nonce)?
|
func encrypt(message: Bytes, secretKey: Aead.XChaCha20Poly1305Ietf.Key, additionalData: Bytes?) -> (authenticatedCipherText: Bytes, nonce: Aead.XChaCha20Poly1305Ietf.Nonce)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public protocol Ed25519Type {
|
||||||
|
static func verifySignature(_ signature: Data, publicKey: Data, data: Data) throws -> Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol SignType {
|
||||||
|
func signature(message: Bytes, secretKey: Bytes) -> Bytes?
|
||||||
|
func verify(message: Bytes, publicKey: Bytes, signature: Bytes) -> Bool
|
||||||
|
}
|
||||||
|
|
||||||
public protocol GenericHashType {
|
public protocol GenericHashType {
|
||||||
func hash(message: Bytes, key: Bytes?) -> Bytes?
|
func hash(message: Bytes, key: Bytes?) -> Bytes?
|
||||||
func hash(message: Bytes, outputLength: Int) -> Bytes?
|
func hash(message: Bytes, outputLength: Int) -> Bytes?
|
||||||
|
@ -42,6 +54,7 @@ extension GenericHashType {
|
||||||
|
|
||||||
extension Sodium: SodiumType {
|
extension Sodium: SodiumType {
|
||||||
public func getGenericHash() -> GenericHashType { return genericHash }
|
public func getGenericHash() -> GenericHashType { return genericHash }
|
||||||
|
public func getSign() -> SignType { return sign }
|
||||||
public func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return aead.xchacha20poly1305ietf }
|
public func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return aead.xchacha20poly1305ietf }
|
||||||
|
|
||||||
public func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair) -> Box.KeyPair? {
|
public func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair) -> Box.KeyPair? {
|
||||||
|
@ -50,4 +63,6 @@ extension Sodium: SodiumType {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Aead.XChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType {}
|
extension Aead.XChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType {}
|
||||||
|
extension Sign: SignType {}
|
||||||
extension GenericHash: GenericHashType {}
|
extension GenericHash: GenericHashType {}
|
||||||
|
extension Ed25519: Ed25519Type {}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import PromiseKit
|
import PromiseKit
|
||||||
import SessionSnodeKit
|
import SessionSnodeKit
|
||||||
import SessionUtilitiesKit
|
import SessionUtilitiesKit
|
||||||
|
import Sodium
|
||||||
|
|
||||||
@objc(SNMessageSender)
|
@objc(SNMessageSender)
|
||||||
public final class MessageSender : NSObject {
|
public final class MessageSender : NSObject {
|
||||||
|
@ -277,22 +278,34 @@ public final class MessageSender : NSObject {
|
||||||
|
|
||||||
// MARK: - Open Groups
|
// MARK: - Open Groups
|
||||||
|
|
||||||
internal static func sendToOpenGroupDestination(_ destination: Message.Destination, message: Message, using transaction: Any) -> Promise<Void> {
|
internal static func sendToOpenGroupDestination(_ destination: Message.Destination, message: Message, using transaction: Any, dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> Promise<Void> {
|
||||||
let (promise, seal) = Promise<Void>.pending()
|
let (promise, seal) = Promise<Void>.pending()
|
||||||
let storage = SNMessagingKitConfiguration.shared.storage
|
|
||||||
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
let transaction = transaction as! YapDatabaseReadWriteTransaction
|
||||||
|
|
||||||
// Set the timestamp, sender and recipient
|
// Set the timestamp, sender and recipient
|
||||||
if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set
|
if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set
|
||||||
message.sentTimestamp = NSDate.millisecondTimestamp()
|
message.sentTimestamp = UInt64(dependencies.date.timeIntervalSince1970 * 1000) // Should be in ms
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let threadId: String = message.threadID, let openGroup = Storage.shared.getOpenGroup(for: threadId) else {
|
guard let threadId: String = message.threadID, let openGroup = dependencies.storage.getOpenGroup(for: threadId) else {
|
||||||
preconditionFailure()
|
preconditionFailure()
|
||||||
}
|
}
|
||||||
// TODO: Check if blinding is enabled on this server?
|
guard let userEdKeyPair: Box.KeyPair = dependencies.storage.getUserED25519KeyPair() else { preconditionFailure() }
|
||||||
if let userDerivedKey: ECKeyPair = try? OWSIdentityManager.shared().identityKeyPair()?.convert(to: .blinded, with: openGroup.publicKey) {
|
|
||||||
message.sender = userDerivedKey.hexEncodedPublicKey
|
let server: OpenGroupAPI.Server? = dependencies.storage.getOpenGroupServer(name: openGroup.server)
|
||||||
|
|
||||||
|
if server?.capabilities.capabilities.contains(.blinding) == true {
|
||||||
|
guard let serverPublicKey = dependencies.storage.getOpenGroupPublicKey(for: openGroup.server) else {
|
||||||
|
preconditionFailure()
|
||||||
|
}
|
||||||
|
guard let blindedKeyPair: Box.KeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: serverPublicKey, edKeyPair: userEdKeyPair, genericHash: dependencies.genericHash) else {
|
||||||
|
preconditionFailure()
|
||||||
|
}
|
||||||
|
|
||||||
|
message.sender = IdPrefix.blinded.hexEncodedPublicKey(for: blindedKeyPair.publicKey)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
message.sender = IdPrefix.unblinded.hexEncodedPublicKey(for: userEdKeyPair.publicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch destination {
|
switch destination {
|
||||||
|
@ -332,12 +345,12 @@ public final class MessageSender : NSObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach the user's profile
|
// Attach the user's profile
|
||||||
guard let name = storage.getUser()?.name else {
|
guard let name = dependencies.storage.getUser()?.name else {
|
||||||
handleFailure(with: Error.noUsername, using: transaction)
|
handleFailure(with: Error.noUsername, using: transaction)
|
||||||
return promise
|
return promise
|
||||||
}
|
}
|
||||||
|
|
||||||
if let profileKey = storage.getUser()?.profileEncryptionKey?.keyData, let profilePictureURL = storage.getUser()?.profilePictureURL {
|
if let profileKey = dependencies.storage.getUser()?.profileEncryptionKey?.keyData, let profilePictureURL = dependencies.storage.getUser()?.profilePictureURL {
|
||||||
message.profile = VisibleMessage.Profile(displayName: name, profileKey: profileKey, profilePictureURL: profilePictureURL)
|
message.profile = VisibleMessage.Profile(displayName: name, profileKey: profileKey, profilePictureURL: profilePictureURL)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -379,15 +392,15 @@ public final class MessageSender : NSObject {
|
||||||
.done(on: DispatchQueue.global(qos: .userInitiated)) { responseInfo, data in
|
.done(on: DispatchQueue.global(qos: .userInitiated)) { responseInfo, data in
|
||||||
message.openGroupServerMessageID = given(data.seqNo) { UInt64($0) }
|
message.openGroupServerMessageID = given(data.seqNo) { UInt64($0) }
|
||||||
|
|
||||||
Storage.shared.write { transaction in
|
dependencies.storage.write { transaction in
|
||||||
MessageSender.handleSuccessfulMessageSend(message, to: destination, serverTimestamp: UInt64(floor(data.posted)), using: transaction)
|
MessageSender.handleSuccessfulMessageSend(message, to: destination, serverTimestamp: UInt64(floor(data.posted)), using: transaction)
|
||||||
seal.fulfill(())
|
seal.fulfill(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
|
.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
|
||||||
storage.write(with: { transaction in
|
dependencies.storage.write { transaction in
|
||||||
handleFailure(with: error, using: transaction as! YapDatabaseReadWriteTransaction)
|
handleFailure(with: error, using: transaction as! YapDatabaseReadWriteTransaction)
|
||||||
}, completion: { })
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return promise
|
return promise
|
||||||
|
|
23
SessionMessagingKit/Utilities/Data+Utilities.swift
Normal file
23
SessionMessagingKit/Utilities/Data+Utilities.swift
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// MARK: - Decoding
|
||||||
|
|
||||||
|
extension OpenGroupAPI.Dependencies {
|
||||||
|
static let userInfoKey: CodingUserInfoKey = CodingUserInfoKey(rawValue: "io.oxen.dependencies.codingOptions")!
|
||||||
|
}
|
||||||
|
|
||||||
|
public extension Data {
|
||||||
|
func decoded<T: Decodable>(as type: T.Type, customError: Error? = nil, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) throws -> T {
|
||||||
|
do {
|
||||||
|
let decoder: JSONDecoder = JSONDecoder()
|
||||||
|
decoder.userInfo = [ OpenGroupAPI.Dependencies.userInfoKey: dependencies ]
|
||||||
|
|
||||||
|
return try decoder.decode(type, from: self)
|
||||||
|
}
|
||||||
|
catch let error {
|
||||||
|
throw (customError ?? error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,34 +0,0 @@
|
||||||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Curve25519Kit
|
|
||||||
import SessionUtilitiesKit
|
|
||||||
import Sodium
|
|
||||||
|
|
||||||
public extension ECKeyPair {
|
|
||||||
func convert(to targetPrefix: IdPrefix, with otherKey: String, using sodium: Sodium = Sodium()) throws -> ECKeyPair? {
|
|
||||||
guard let publicKeyPrefix: IdPrefix = IdPrefix(with: hexEncodedPublicKey) else { return nil }
|
|
||||||
|
|
||||||
switch (publicKeyPrefix, targetPrefix) {
|
|
||||||
case (.standard, .blinded): // Only support standard -> blinded conversions
|
|
||||||
// TODO: Figure out why this is broken...
|
|
||||||
// guard let otherPubKeyData: Data = otherKey.data(using: .utf8) else { return nil }
|
|
||||||
guard let otherPubKeyData: Data = otherKey.dataFromHex() else { return nil }
|
|
||||||
guard let otherPubKeyHashBytes: Bytes = sodium.genericHash.hash(message: [UInt8](otherPubKeyData)) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
guard let blindedPublicKey: Sodium.SharedSecret = sodium.sharedSecret(otherPubKeyHashBytes, [UInt8](publicKey)) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
guard let blindedPrivateKey: Sodium.SharedSecret = sodium.sharedSecret(otherPubKeyHashBytes, [UInt8](privateKey)) else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return try BlindedECKeyPair(publicKeyData: blindedPublicKey, privateKeyData: blindedPrivateKey)
|
|
||||||
|
|
||||||
case (.standard, .standard): return self
|
|
||||||
case (.blinded, .blinded): return self
|
|
||||||
default: return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,21 +5,21 @@ import PromiseKit
|
||||||
import SessionSnodeKit
|
import SessionSnodeKit
|
||||||
|
|
||||||
extension Promise where T == Data {
|
extension Promise where T == Data {
|
||||||
func decoded<R: Decodable>(as type: R.Type, on queue: DispatchQueue? = nil, error: Error? = nil) -> Promise<R> {
|
func decoded<R: Decodable>(as type: R.Type, on queue: DispatchQueue? = nil, error: Error? = nil, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> Promise<R> {
|
||||||
self.map(on: queue) { data -> R in
|
self.map(on: queue) { data -> R in
|
||||||
try data.decoded(as: type, customError: error)
|
try data.decoded(as: type, customError: error, using: dependencies)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Promise where T == (OnionRequestResponseInfoType, Data?) {
|
extension Promise where T == (OnionRequestResponseInfoType, Data?) {
|
||||||
func decoded<R: Decodable>(as type: R.Type, on queue: DispatchQueue? = nil, error: Error? = nil) -> Promise<(OnionRequestResponseInfoType, R)> {
|
func decoded<R: Decodable>(as type: R.Type, on queue: DispatchQueue? = nil, error: Error? = nil, using dependencies: OpenGroupAPI.Dependencies = OpenGroupAPI.Dependencies()) -> Promise<(OnionRequestResponseInfoType, R)> {
|
||||||
self.map(on: queue) { responseInfo, maybeData -> (OnionRequestResponseInfoType, R) in
|
self.map(on: queue) { responseInfo, maybeData -> (OnionRequestResponseInfoType, R) in
|
||||||
guard let data: Data = maybeData else {
|
guard let data: Data = maybeData else {
|
||||||
throw OpenGroupAPI.Error.parsingFailed
|
throw OpenGroupAPI.Error.parsingFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
return (responseInfo, try data.decoded(as: type, customError: error))
|
return (responseInfo, try data.decoded(as: type, customError: error, using: dependencies))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,14 +41,13 @@ extension Sign {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Sodium {
|
extension Sodium {
|
||||||
public typealias SharedSecret = Data
|
|
||||||
|
|
||||||
private static let scalarLength: Int = Int(crypto_core_ed25519_scalarbytes()) // 32
|
private static let scalarLength: Int = Int(crypto_core_ed25519_scalarbytes()) // 32
|
||||||
private static let noClampLength: Int = Int(crypto_scalarmult_ed25519_bytes()) // 32
|
private static let noClampLength: Int = Int(crypto_scalarmult_ed25519_bytes()) // 32
|
||||||
private static let scalarMultLength: Int = Int(crypto_scalarmult_bytes()) // 32
|
private static let scalarMultLength: Int = Int(crypto_scalarmult_bytes()) // 32
|
||||||
private static let publicKeyLength: Int = Int(crypto_scalarmult_bytes()) // 32
|
private static let publicKeyLength: Int = Int(crypto_scalarmult_bytes()) // 32
|
||||||
private static let secretKeyLength: Int = Int(crypto_sign_secretkeybytes()) // 64
|
private static let secretKeyLength: Int = Int(crypto_sign_secretkeybytes()) // 64
|
||||||
|
|
||||||
|
/// Constructs a "blinded" key pair (`ka, kA`) based on an open group server `publicKey` and an ed25519 `keyPair`
|
||||||
public func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? {
|
public func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? {
|
||||||
guard edKeyPair.publicKey.count == Sodium.publicKeyLength && edKeyPair.secretKey.count == Sodium.secretKeyLength else {
|
guard edKeyPair.publicKey.count == Sodium.publicKeyLength && edKeyPair.secretKey.count == Sodium.secretKeyLength else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -171,7 +170,8 @@ extension Sodium {
|
||||||
return (Data(bytes: sig_RPtr, count: Sodium.noClampLength).bytes + Data(bytes: sig_sPtr, count: Sodium.scalarLength).bytes)
|
return (Data(bytes: sig_RPtr, count: Sodium.noClampLength).bytes + Data(bytes: sig_sPtr, count: Sodium.scalarLength).bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sharedEdSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> SharedSecret? {
|
// TODO: Determine if we still need this? (To generate the `kB` value for the `/inbox` API????)
|
||||||
|
public func sharedEdSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Bytes? {
|
||||||
let sharedSecretPtr: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Sodium.noClampLength)
|
let sharedSecretPtr: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Sodium.noClampLength)
|
||||||
let result = secondKeyBytes.withUnsafeBytes { (secondKeyPtr: UnsafeRawBufferPointer) -> Int32 in
|
let result = secondKeyBytes.withUnsafeBytes { (secondKeyPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||||
return firstKeyBytes.withUnsafeBytes { (firstKeyPtr: UnsafeRawBufferPointer) -> Int32 in
|
return firstKeyBytes.withUnsafeBytes { (firstKeyPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||||
|
@ -188,33 +188,7 @@ extension Sodium {
|
||||||
|
|
||||||
guard result == 0 else { return nil }
|
guard result == 0 else { return nil }
|
||||||
|
|
||||||
return Data(bytes: sharedSecretPtr, count: Sodium.scalarMultLength)
|
return Data(bytes: sharedSecretPtr, count: Sodium.scalarMultLength).bytes
|
||||||
}
|
|
||||||
|
|
||||||
public func sharedSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> SharedSecret? {
|
|
||||||
guard firstKeyBytes.count == Sodium.publicKeyLength && secondKeyBytes.count == Sodium.publicKeyLength else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let sharedSecretPtr: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Sodium.scalarMultLength)
|
|
||||||
let result = secondKeyBytes.withUnsafeBytes { (secondKeyPtr: UnsafeRawBufferPointer) -> Int32 in
|
|
||||||
return firstKeyBytes.withUnsafeBytes { (firstKeyPtr: UnsafeRawBufferPointer) -> Int32 in
|
|
||||||
guard let firstKeyBaseAddress: UnsafePointer<UInt8> = firstKeyPtr.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
guard let secondKeyBaseAddress: UnsafePointer<UInt8> = secondKeyPtr.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
//crypto_sign_ed25519_publickeybytes
|
|
||||||
//crypto_scalarmult_curve25519(<#T##q: UnsafeMutablePointer<UInt8>##UnsafeMutablePointer<UInt8>#>, <#T##n: UnsafePointer<UInt8>##UnsafePointer<UInt8>#>, <#T##p: UnsafePointer<UInt8>##UnsafePointer<UInt8>#>)
|
|
||||||
return crypto_scalarmult(sharedSecretPtr, firstKeyBaseAddress, secondKeyBaseAddress)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard result == 0 else { return nil }
|
|
||||||
|
|
||||||
return Data(bytes: sharedSecretPtr, count: Sodium.scalarMultLength)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,15 +67,28 @@ class OpenGroupAPITests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
var testStorage: TestStorage!
|
var testStorage: TestStorage!
|
||||||
|
var testSodium: TestSodium!
|
||||||
|
var testAeadXChaCha20Poly1305Ietf: TestAeadXChaCha20Poly1305Ietf!
|
||||||
|
var testGenericHash: TestGenericHash!
|
||||||
|
var testSign: TestSign!
|
||||||
var dependencies: OpenGroupAPI.Dependencies!
|
var dependencies: OpenGroupAPI.Dependencies!
|
||||||
|
|
||||||
// MARK: - Configuration
|
// MARK: - Configuration
|
||||||
|
|
||||||
override func setUpWithError() throws {
|
override func setUpWithError() throws {
|
||||||
testStorage = TestStorage()
|
testStorage = TestStorage()
|
||||||
|
testSodium = TestSodium()
|
||||||
|
testAeadXChaCha20Poly1305Ietf = TestAeadXChaCha20Poly1305Ietf()
|
||||||
|
testGenericHash = TestGenericHash()
|
||||||
|
testSign = TestSign()
|
||||||
dependencies = OpenGroupAPI.Dependencies(
|
dependencies = OpenGroupAPI.Dependencies(
|
||||||
api: TestApi.self,
|
api: TestApi.self,
|
||||||
storage: testStorage,
|
storage: testStorage,
|
||||||
|
sodium: testSodium,
|
||||||
|
aeadXChaCha20Poly1305Ietf: testAeadXChaCha20Poly1305Ietf,
|
||||||
|
sign: testSign,
|
||||||
|
genericHash: testGenericHash,
|
||||||
|
ed25519: TestEd25519.self,
|
||||||
nonceGenerator: TestNonceGenerator(),
|
nonceGenerator: TestNonceGenerator(),
|
||||||
date: Date(timeIntervalSince1970: 1234567890)
|
date: Date(timeIntervalSince1970: 1234567890)
|
||||||
)
|
)
|
||||||
|
@ -100,6 +113,18 @@ class OpenGroupAPITests: XCTestCase {
|
||||||
publicKeyData: Data.data(fromHex: "7aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d")!,
|
publicKeyData: Data.data(fromHex: "7aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d")!,
|
||||||
privateKeyData: Data.data(fromHex: "881132ee03dbd2da065aa4c94f96081f62142dc8011d1b7a00de83e4aab38ce4")!
|
privateKeyData: Data.data(fromHex: "881132ee03dbd2da065aa4c94f96081f62142dc8011d1b7a00de83e4aab38ce4")!
|
||||||
)
|
)
|
||||||
|
testStorage.mockData[.userEdKeyPair] = Box.KeyPair(
|
||||||
|
publicKey: Data.data(fromHex: "7aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d")!.bytes,
|
||||||
|
secretKey: Data.data(fromHex: "881132ee03dbd2da065aa4c94f96081f62142dc8011d1b7a00de83e4aab38ce4881132ee03dbd2da065aa4c94f96081f62142dc8011d1b7a00de83e4aab38ce4")!.bytes
|
||||||
|
)
|
||||||
|
|
||||||
|
testGenericHash.mockData[.hashOutputLength] = []
|
||||||
|
testSodium.mockData[.blindedKeyPair] = Box.KeyPair(
|
||||||
|
publicKey: Data.data(fromHex: "7aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d")!.bytes,
|
||||||
|
secretKey: Data.data(fromHex: "881132ee03dbd2da065aa4c94f96081f62142dc8011d1b7a00de83e4aab38ce4881132ee03dbd2da065aa4c94f96081f62142dc8011d1b7a00de83e4aab38ce4")!.bytes
|
||||||
|
)
|
||||||
|
testSodium.mockData[.sogsSignature] = "TestSogsSignature".bytes
|
||||||
|
testSign.mockData[.signature] = "TestSignature".bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tearDownWithError() throws {
|
override func tearDownWithError() throws {
|
||||||
|
@ -415,12 +440,16 @@ class OpenGroupAPITests: XCTestCase {
|
||||||
|
|
||||||
// MARK: - Authentication
|
// MARK: - Authentication
|
||||||
|
|
||||||
func testItSignsTheRequestCorrectly() throws {
|
func testItSignsTheUnblindedRequestCorrectly() throws {
|
||||||
class LocalTestApi: TestApi {
|
class LocalTestApi: TestApi {
|
||||||
override class var mockResponse: Data? {
|
override class var mockResponse: Data? {
|
||||||
return try! JSONEncoder().encode([OpenGroupAPI.Room]())
|
return try! JSONEncoder().encode([OpenGroupAPI.Room]())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server(
|
||||||
|
name: "testServer",
|
||||||
|
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: [])
|
||||||
|
)
|
||||||
dependencies = dependencies.with(api: LocalTestApi.self)
|
dependencies = dependencies.with(api: LocalTestApi.self)
|
||||||
|
|
||||||
var response: (OnionRequestResponseInfoType, [OpenGroupAPI.Room])? = nil
|
var response: (OnionRequestResponseInfoType, [OpenGroupAPI.Room])? = nil
|
||||||
|
@ -445,14 +474,74 @@ class OpenGroupAPITests: XCTestCase {
|
||||||
expect(requestData?.server).to(equal("testServer"))
|
expect(requestData?.server).to(equal("testServer"))
|
||||||
expect(requestData?.publicKey).to(equal("7aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d"))
|
expect(requestData?.publicKey).to(equal("7aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d"))
|
||||||
expect(requestData?.headers).to(haveCount(4))
|
expect(requestData?.headers).to(haveCount(4))
|
||||||
expect(requestData?.headers[Header.sogsPubKey.rawValue]).to(equal("057aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d"))
|
expect(requestData?.headers[Header.sogsPubKey.rawValue]).to(equal("007aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d"))
|
||||||
expect(requestData?.headers[Header.sogsTimestamp.rawValue]).to(equal("1234567890"))
|
expect(requestData?.headers[Header.sogsTimestamp.rawValue]).to(equal("1234567890"))
|
||||||
expect(requestData?.headers[Header.sogsNonce.rawValue]).to(equal("pK6YRtQApl4NhECGizF0Cg=="))
|
expect(requestData?.headers[Header.sogsNonce.rawValue]).to(equal("pK6YRtQApl4NhECGizF0Cg=="))
|
||||||
expect(requestData?.headers[Header.sogsSignature.rawValue]).to(equal("fxqLy5ZDWCsLQpwLw0Dax+4xe7cG2vPRk1NlHORIm0DPd3o9UA24KLZY"))
|
expect(requestData?.headers[Header.sogsSignature.rawValue]).to(equal("TestSignature".bytes.toBase64()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testItSignsTheBlindedRequestCorrectly() throws {
|
||||||
|
class LocalTestApi: TestApi {
|
||||||
|
override class var mockResponse: Data? {
|
||||||
|
return try! JSONEncoder().encode([OpenGroupAPI.Room]())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server(
|
||||||
|
name: "testServer",
|
||||||
|
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs, .blinding], missing: [])
|
||||||
|
)
|
||||||
|
dependencies = dependencies.with(api: LocalTestApi.self)
|
||||||
|
|
||||||
|
var response: (OnionRequestResponseInfoType, [OpenGroupAPI.Room])? = nil
|
||||||
|
var error: Error? = nil
|
||||||
|
|
||||||
|
OpenGroupAPI.rooms(for: "testServer", using: dependencies)
|
||||||
|
.get { result in response = result }
|
||||||
|
.catch { requestError in error = requestError }
|
||||||
|
.retainUntilComplete()
|
||||||
|
|
||||||
|
expect(response)
|
||||||
|
.toEventuallyNot(
|
||||||
|
beNil(),
|
||||||
|
timeout: .milliseconds(100)
|
||||||
|
)
|
||||||
|
expect(error?.localizedDescription).to(beNil())
|
||||||
|
|
||||||
|
// Validate signature headers
|
||||||
|
let requestData: TestApi.RequestData? = (response?.0 as? TestResponseInfo)?.requestData
|
||||||
|
expect(requestData?.urlString).to(equal("testServer/rooms"))
|
||||||
|
expect(requestData?.httpMethod).to(equal("GET"))
|
||||||
|
expect(requestData?.server).to(equal("testServer"))
|
||||||
|
expect(requestData?.publicKey).to(equal("7aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d"))
|
||||||
|
expect(requestData?.headers).to(haveCount(4))
|
||||||
|
expect(requestData?.headers[Header.sogsPubKey.rawValue]).to(equal("157aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d"))
|
||||||
|
expect(requestData?.headers[Header.sogsTimestamp.rawValue]).to(equal("1234567890"))
|
||||||
|
expect(requestData?.headers[Header.sogsNonce.rawValue]).to(equal("pK6YRtQApl4NhECGizF0Cg=="))
|
||||||
|
expect(requestData?.headers[Header.sogsSignature.rawValue]).to(equal("TestSogsSignature".bytes.toBase64()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testItFailsToSignIfThereIsNoUserEdKeyPair() throws {
|
||||||
|
testStorage.mockData[.userEdKeyPair] = nil
|
||||||
|
|
||||||
|
var response: Any? = nil
|
||||||
|
var error: Error? = nil
|
||||||
|
|
||||||
|
OpenGroupAPI.rooms(for: "testServer", using: dependencies)
|
||||||
|
.get { result in response = result }
|
||||||
|
.catch { requestError in error = requestError }
|
||||||
|
.retainUntilComplete()
|
||||||
|
|
||||||
|
expect(error?.localizedDescription)
|
||||||
|
.toEventually(
|
||||||
|
equal(OpenGroupAPI.Error.signingFailed.localizedDescription),
|
||||||
|
timeout: .milliseconds(100)
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(response).to(beNil())
|
||||||
}
|
}
|
||||||
|
|
||||||
func testItFailsToSignIfTheServerPublicKeyIsInvalid() throws {
|
func testItFailsToSignIfTheServerPublicKeyIsInvalid() throws {
|
||||||
testStorage.mockData[.openGroupPublicKeys] = ["testServer": ""]
|
testStorage.mockData[.openGroupPublicKeys] = [:]
|
||||||
|
|
||||||
var response: Any? = nil
|
var response: Any? = nil
|
||||||
var error: Error? = nil
|
var error: Error? = nil
|
||||||
|
@ -464,44 +553,32 @@ class OpenGroupAPITests: XCTestCase {
|
||||||
|
|
||||||
expect(error?.localizedDescription)
|
expect(error?.localizedDescription)
|
||||||
.toEventually(
|
.toEventually(
|
||||||
equal(OpenGroupAPI.Error.signingFailed.localizedDescription),
|
equal(OpenGroupAPI.Error.noPublicKey.localizedDescription),
|
||||||
timeout: .milliseconds(100)
|
timeout: .milliseconds(100)
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(response).to(beNil())
|
expect(response).to(beNil())
|
||||||
}
|
}
|
||||||
|
|
||||||
func testItFailsToSignIfThereIsNoUserKeyPair() throws {
|
func testItFailsToSignIfBlindedAndTheBlindedKeyDoesNotGetGenerated() throws {
|
||||||
testStorage.mockData[.userKeyPair] = nil
|
|
||||||
|
|
||||||
var response: Any? = nil
|
|
||||||
var error: Error? = nil
|
|
||||||
|
|
||||||
OpenGroupAPI.rooms(for: "testServer", using: dependencies)
|
|
||||||
.get { result in response = result }
|
|
||||||
.catch { requestError in error = requestError }
|
|
||||||
.retainUntilComplete()
|
|
||||||
|
|
||||||
expect(error?.localizedDescription)
|
|
||||||
.toEventually(
|
|
||||||
equal(OpenGroupAPI.Error.signingFailed.localizedDescription),
|
|
||||||
timeout: .milliseconds(100)
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(response).to(beNil())
|
|
||||||
}
|
|
||||||
|
|
||||||
func testItFailsToSignIfTheSharedSecretDoesNotGetGenerated() throws {
|
|
||||||
class InvalidSodium: SodiumType {
|
class InvalidSodium: SodiumType {
|
||||||
func getGenericHash() -> GenericHashType { return Sodium().genericHash }
|
func getGenericHash() -> GenericHashType { return Sodium().genericHash }
|
||||||
func sharedSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Sodium.SharedSecret? { return nil }
|
|
||||||
func sharedEdSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Sodium.SharedSecret? { return nil }
|
|
||||||
func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return Sodium().aead.xchacha20poly1305ietf }
|
func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return Sodium().aead.xchacha20poly1305ietf }
|
||||||
|
func getSign() -> SignType { return Sodium().sign }
|
||||||
|
|
||||||
func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? {
|
func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func sogsSignature(message: Bytes, secretKey: Bytes, blindedSecretKey ka: Bytes, blindedPublicKey kA: Bytes) -> Bytes? {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sharedEdSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Bytes? { return nil }
|
||||||
}
|
}
|
||||||
|
testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server(
|
||||||
|
name: "testServer",
|
||||||
|
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs, .blinding], missing: [])
|
||||||
|
)
|
||||||
dependencies = dependencies.with(sodium: InvalidSodium())
|
dependencies = dependencies.with(sodium: InvalidSodium())
|
||||||
|
|
||||||
var response: Any? = nil
|
var response: Any? = nil
|
||||||
|
@ -521,15 +598,29 @@ class OpenGroupAPITests: XCTestCase {
|
||||||
expect(response).to(beNil())
|
expect(response).to(beNil())
|
||||||
}
|
}
|
||||||
|
|
||||||
func testItFailsToSignIfTheIntermediateHashDoesNotGetGenerated() throws {
|
func testItFailsToSignIfBlindedAndTheSogsSignatureDoesNotGetGenerated() throws {
|
||||||
class InvalidGenericHash: GenericHashType {
|
class InvalidSodium: SodiumType {
|
||||||
func hash(message: Bytes, key: Bytes?) -> Bytes? { return nil }
|
func getGenericHash() -> GenericHashType { return Sodium().genericHash }
|
||||||
func hashSaltPersonal(message: Bytes, outputLength: Int, key: Bytes?, salt: Bytes, personal: Bytes) -> Bytes? {
|
func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return Sodium().aead.xchacha20poly1305ietf }
|
||||||
|
func getSign() -> SignType { return Sodium().sign }
|
||||||
|
|
||||||
|
func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? {
|
||||||
|
return Box.KeyPair(
|
||||||
|
publicKey: Data.data(fromHex: "7aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d")!.bytes,
|
||||||
|
secretKey: Data.data(fromHex: "881132ee03dbd2da065aa4c94f96081f62142dc8011d1b7a00de83e4aab38ce4881132ee03dbd2da065aa4c94f96081f62142dc8011d1b7a00de83e4aab38ce4")!.bytes
|
||||||
|
)
|
||||||
|
}
|
||||||
|
func sogsSignature(message: Bytes, secretKey: Bytes, blindedSecretKey ka: Bytes, blindedPublicKey kA: Bytes) -> Bytes? {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sharedEdSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Bytes? { return nil }
|
||||||
}
|
}
|
||||||
|
testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server(
|
||||||
dependencies = dependencies.with(genericHash: InvalidGenericHash())
|
name: "testServer",
|
||||||
|
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs, .blinding], missing: [])
|
||||||
|
)
|
||||||
|
dependencies = dependencies.with(sodium: InvalidSodium())
|
||||||
|
|
||||||
var response: Any? = nil
|
var response: Any? = nil
|
||||||
var error: Error? = nil
|
var error: Error? = nil
|
||||||
|
@ -548,22 +639,16 @@ class OpenGroupAPITests: XCTestCase {
|
||||||
expect(response).to(beNil())
|
expect(response).to(beNil())
|
||||||
}
|
}
|
||||||
|
|
||||||
func testItFailsToSignIfTheSecretHashDoesNotGetGenerated() throws {
|
func testItFailsToSignIfUnblindedAndTheSignatureDoesNotGetGenerated() throws {
|
||||||
class InvalidSecondGenericHash: GenericHashType {
|
class InvalidSign: SignType {
|
||||||
static var didSucceedOnce: Bool = false
|
func signature(message: Bytes, secretKey: Bytes) -> Bytes? { return nil }
|
||||||
|
func verify(message: Bytes, publicKey: Bytes, signature: Bytes) -> Bool { return false }
|
||||||
func hash(message: Bytes, key: Bytes?) -> Bytes? { return nil }
|
|
||||||
func hashSaltPersonal(message: Bytes, outputLength: Int, key: Bytes?, salt: Bytes, personal: Bytes) -> Bytes? {
|
|
||||||
if !InvalidSecondGenericHash.didSucceedOnce {
|
|
||||||
InvalidSecondGenericHash.didSucceedOnce = true
|
|
||||||
return Data().bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server(
|
||||||
dependencies = dependencies.with(genericHash: InvalidSecondGenericHash())
|
name: "testServer",
|
||||||
|
capabilities: OpenGroupAPI.Capabilities(capabilities: [.sogs], missing: [])
|
||||||
|
)
|
||||||
|
dependencies = dependencies.with(sign: InvalidSign())
|
||||||
|
|
||||||
var response: Any? = nil
|
var response: Any? = nil
|
||||||
var error: Error? = nil
|
var error: Error? = nil
|
||||||
|
|
|
@ -7,3 +7,9 @@ protocol Mockable {
|
||||||
|
|
||||||
var mockData: [Key: Any] { get }
|
var mockData: [Key: Any] { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protocol StaticMockable {
|
||||||
|
associatedtype Key: Hashable
|
||||||
|
|
||||||
|
static var mockData: [Key: Any] { get }
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import PromiseKit
|
||||||
|
import Sodium
|
||||||
|
|
||||||
|
@testable import SessionMessagingKit
|
||||||
|
|
||||||
|
class TestAeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType, Mockable {
|
||||||
|
// MARK: - Mockable
|
||||||
|
|
||||||
|
enum DataKey: Hashable {
|
||||||
|
case encrypt
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias Key = DataKey
|
||||||
|
|
||||||
|
var mockData: [DataKey: Any] = [:]
|
||||||
|
|
||||||
|
// MARK: - SignType
|
||||||
|
|
||||||
|
func encrypt(message: Bytes, secretKey: Aead.XChaCha20Poly1305Ietf.Key, additionalData: Bytes?) -> (authenticatedCipherText: Bytes, nonce: Aead.XChaCha20Poly1305Ietf.Nonce)? {
|
||||||
|
return (mockData[.encrypt] as? (authenticatedCipherText: Bytes, nonce: Aead.XChaCha20Poly1305Ietf.Nonce))
|
||||||
|
}
|
||||||
|
}
|
25
SessionMessagingKitTests/_TestUtilities/TestEd25519.swift
Normal file
25
SessionMessagingKitTests/_TestUtilities/TestEd25519.swift
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import PromiseKit
|
||||||
|
import Sodium
|
||||||
|
|
||||||
|
@testable import SessionMessagingKit
|
||||||
|
|
||||||
|
class TestEd25519: Ed25519Type, StaticMockable {
|
||||||
|
// MARK: - Mockable
|
||||||
|
|
||||||
|
enum DataKey: Hashable {
|
||||||
|
case verifySignature(signature: Data, publicKey: Data, data: Data) // TODO: Test the uniqueness of this
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias Key = DataKey
|
||||||
|
|
||||||
|
static var mockData: [DataKey: Any] = [:]
|
||||||
|
|
||||||
|
// MARK: - SignType
|
||||||
|
|
||||||
|
static func verifySignature(_ signature: Data, publicKey: Data, data: Data) throws -> Bool {
|
||||||
|
return (mockData[.verifySignature(signature: signature, publicKey: publicKey, data: data)] as! Bool)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import PromiseKit
|
||||||
|
import Sodium
|
||||||
|
|
||||||
|
@testable import SessionMessagingKit
|
||||||
|
|
||||||
|
class TestGenericHash: GenericHashType, Mockable {
|
||||||
|
// MARK: - Mockable
|
||||||
|
|
||||||
|
enum DataKey: Hashable {
|
||||||
|
case hash
|
||||||
|
case hashOutputLength
|
||||||
|
case hashSaltPersonal
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias Key = DataKey
|
||||||
|
|
||||||
|
var mockData: [DataKey: Any] = [:]
|
||||||
|
|
||||||
|
// MARK: - SignType
|
||||||
|
|
||||||
|
func hash(message: Bytes, key: Bytes?) -> Bytes? {
|
||||||
|
return (mockData[.hash] as? Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hash(message: Bytes, outputLength: Int) -> Bytes? {
|
||||||
|
return (mockData[.hashOutputLength] as? Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashSaltPersonal(message: Bytes, outputLength: Int, key: Bytes?, salt: Bytes, personal: Bytes) -> Bytes? {
|
||||||
|
return (mockData[.hashSaltPersonal] as? Bytes)
|
||||||
|
}
|
||||||
|
}
|
30
SessionMessagingKitTests/_TestUtilities/TestSign.swift
Normal file
30
SessionMessagingKitTests/_TestUtilities/TestSign.swift
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import PromiseKit
|
||||||
|
import Sodium
|
||||||
|
|
||||||
|
@testable import SessionMessagingKit
|
||||||
|
|
||||||
|
class TestSign: SignType, Mockable {
|
||||||
|
// MARK: - Mockable
|
||||||
|
|
||||||
|
enum DataKey: Hashable {
|
||||||
|
case signature
|
||||||
|
case verify
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias Key = DataKey
|
||||||
|
|
||||||
|
var mockData: [DataKey: Any] = [:]
|
||||||
|
|
||||||
|
// MARK: - SignType
|
||||||
|
|
||||||
|
func signature(message: Bytes, secretKey: Bytes) -> Bytes? {
|
||||||
|
return (mockData[.signature] as? Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func verify(message: Bytes, publicKey: Bytes, signature: Bytes) -> Bool {
|
||||||
|
return (mockData[.verify] as! Bool)
|
||||||
|
}
|
||||||
|
}
|
46
SessionMessagingKitTests/_TestUtilities/TestSodium.swift
Normal file
46
SessionMessagingKitTests/_TestUtilities/TestSodium.swift
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import PromiseKit
|
||||||
|
import Sodium
|
||||||
|
|
||||||
|
@testable import SessionMessagingKit
|
||||||
|
|
||||||
|
class TestSodium: SodiumType, Mockable {
|
||||||
|
// MARK: - Mockable
|
||||||
|
|
||||||
|
enum DataKey: Hashable {
|
||||||
|
case genericHash
|
||||||
|
case aeadXChaCha20Poly1305Ietf
|
||||||
|
case sign
|
||||||
|
case blindedKeyPair
|
||||||
|
case sogsSignature
|
||||||
|
case sharedEdSecret
|
||||||
|
}
|
||||||
|
|
||||||
|
typealias Key = DataKey
|
||||||
|
|
||||||
|
var mockData: [DataKey: Any] = [:]
|
||||||
|
|
||||||
|
// MARK: - SodiumType
|
||||||
|
|
||||||
|
func getGenericHash() -> GenericHashType { return (mockData[.genericHash] as! GenericHashType) }
|
||||||
|
|
||||||
|
func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType {
|
||||||
|
return (mockData[.aeadXChaCha20Poly1305Ietf] as! AeadXChaCha20Poly1305IetfType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSign() -> SignType { return (mockData[.sign] as! SignType) }
|
||||||
|
|
||||||
|
func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? {
|
||||||
|
return (mockData[.blindedKeyPair] as? Box.KeyPair)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sogsSignature(message: Bytes, secretKey: Bytes, blindedSecretKey ka: Bytes, blindedPublicKey kA: Bytes) -> Bytes? {
|
||||||
|
return (mockData[.sogsSignature] as? Bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sharedEdSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Bytes? {
|
||||||
|
return (mockData[.sharedEdSecret] as? Bytes)
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable {
|
||||||
case allOpenGroups
|
case allOpenGroups
|
||||||
case openGroupPublicKeys
|
case openGroupPublicKeys
|
||||||
case userKeyPair
|
case userKeyPair
|
||||||
|
case userEdKeyPair
|
||||||
case openGroup
|
case openGroup
|
||||||
case openGroupServer
|
case openGroupServer
|
||||||
case openGroupImage
|
case openGroupImage
|
||||||
|
@ -43,7 +44,7 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable {
|
||||||
|
|
||||||
func getUserPublicKey() -> String? { return nil }
|
func getUserPublicKey() -> String? { return nil }
|
||||||
func getUserKeyPair() -> ECKeyPair? { return (mockData[.userKeyPair] as? ECKeyPair) }
|
func getUserKeyPair() -> ECKeyPair? { return (mockData[.userKeyPair] as? ECKeyPair) }
|
||||||
func getUserED25519KeyPair() -> Box.KeyPair? { return nil }
|
func getUserED25519KeyPair() -> Box.KeyPair? { return (mockData[.userEdKeyPair] as? Box.KeyPair) }
|
||||||
func getUser() -> Contact? { return nil }
|
func getUser() -> Contact? { return nil }
|
||||||
func getAllContacts() -> Set<Contact> { return Set() }
|
func getAllContacts() -> Set<Contact> { return Set() }
|
||||||
|
|
||||||
|
|
|
@ -33,16 +33,3 @@ public extension Data {
|
||||||
return result as NSData
|
return result as NSData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Decoding
|
|
||||||
|
|
||||||
public extension Data {
|
|
||||||
func decoded<T: Decodable>(as type: T.Type, customError: Error? = nil) throws -> T {
|
|
||||||
do {
|
|
||||||
return try JSONDecoder().decode(type, from: self)
|
|
||||||
}
|
|
||||||
catch let error {
|
|
||||||
throw (customError ?? error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue