Added more unit tests
Fixed a possible divide by zero error Cleaned up some of the id blinding methods (ie. removing handling for impossible error states) Added unit tests for the new Sodium methods (used for id blinding) Added unit tests for some of the shared code Added unit tests for the MessageSender+Encryption extension functions Added unit tests for the MessageReceiver+Decryption extension functions Updated the unit test key constants to be consistent with the SOGS auth-example keys for consistency
This commit is contained in:
parent
2851d5e8c7
commit
c44256b1d6
|
@ -788,6 +788,15 @@
|
|||
FD078E6027E2BB36000769AF /* MockIdentityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD078E5F27E2BB36000769AF /* MockIdentityManager.swift */; };
|
||||
FD0BA51B27CD88EC00CC6805 /* BlindedIdMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0BA51A27CD88EC00CC6805 /* BlindedIdMapping.swift */; };
|
||||
FD0BA51D27CDC34600CC6805 /* SOGSV4Migration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD0BA51C27CDC34600CC6805 /* SOGSV4Migration.swift */; };
|
||||
FD3C905C27E3FBEF00CD579F /* BatchRequestInfoSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C905B27E3FBEF00CD579F /* BatchRequestInfoSpec.swift */; };
|
||||
FD3C906027E410F700CD579F /* FileUploadResponseSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C905F27E410F700CD579F /* FileUploadResponseSpec.swift */; };
|
||||
FD3C906227E411AF00CD579F /* HeaderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C906127E411AF00CD579F /* HeaderSpec.swift */; };
|
||||
FD3C906427E4122F00CD579F /* RequestSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C906327E4122F00CD579F /* RequestSpec.swift */; };
|
||||
FD3C906727E416AF00CD579F /* BlindedIdMappingSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C906627E416AF00CD579F /* BlindedIdMappingSpec.swift */; };
|
||||
FD3C906A27E417CE00CD579F /* SodiumUtilitiesSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C906927E417CE00CD579F /* SodiumUtilitiesSpec.swift */; };
|
||||
FD3C906D27E43C4B00CD579F /* MessageSenderEncryptionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C906C27E43C4B00CD579F /* MessageSenderEncryptionSpec.swift */; };
|
||||
FD3C906F27E43E8700CD579F /* MockBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C906E27E43E8700CD579F /* MockBox.swift */; };
|
||||
FD3C907127E445E500CD579F /* MessageReceiverDecryptionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD3C907027E445E500CD579F /* MessageReceiverDecryptionSpec.swift */; };
|
||||
FD5D200F27AA2B6000FEA984 /* MessageRequestResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D200E27AA2B6000FEA984 /* MessageRequestResponse.swift */; };
|
||||
FD5D201127AA331F00FEA984 /* ConfigurationMessage+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D201027AA331F00FEA984 /* ConfigurationMessage+Convenience.swift */; };
|
||||
FD5D201E27B0D87C00FEA984 /* SessionId.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD5D201D27B0D87C00FEA984 /* SessionId.swift */; };
|
||||
|
@ -1938,6 +1947,15 @@
|
|||
FD078E5F27E2BB36000769AF /* MockIdentityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockIdentityManager.swift; sourceTree = "<group>"; };
|
||||
FD0BA51A27CD88EC00CC6805 /* BlindedIdMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlindedIdMapping.swift; sourceTree = "<group>"; };
|
||||
FD0BA51C27CDC34600CC6805 /* SOGSV4Migration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SOGSV4Migration.swift; sourceTree = "<group>"; };
|
||||
FD3C905B27E3FBEF00CD579F /* BatchRequestInfoSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatchRequestInfoSpec.swift; sourceTree = "<group>"; };
|
||||
FD3C905F27E410F700CD579F /* FileUploadResponseSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUploadResponseSpec.swift; sourceTree = "<group>"; };
|
||||
FD3C906127E411AF00CD579F /* HeaderSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderSpec.swift; sourceTree = "<group>"; };
|
||||
FD3C906327E4122F00CD579F /* RequestSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestSpec.swift; sourceTree = "<group>"; };
|
||||
FD3C906627E416AF00CD579F /* BlindedIdMappingSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlindedIdMappingSpec.swift; sourceTree = "<group>"; };
|
||||
FD3C906927E417CE00CD579F /* SodiumUtilitiesSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SodiumUtilitiesSpec.swift; sourceTree = "<group>"; };
|
||||
FD3C906C27E43C4B00CD579F /* MessageSenderEncryptionSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageSenderEncryptionSpec.swift; sourceTree = "<group>"; };
|
||||
FD3C906E27E43E8700CD579F /* MockBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockBox.swift; sourceTree = "<group>"; };
|
||||
FD3C907027E445E500CD579F /* MessageReceiverDecryptionSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageReceiverDecryptionSpec.swift; sourceTree = "<group>"; };
|
||||
FD5D200E27AA2B6000FEA984 /* MessageRequestResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestResponse.swift; sourceTree = "<group>"; };
|
||||
FD5D201027AA331F00FEA984 /* ConfigurationMessage+Convenience.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ConfigurationMessage+Convenience.swift"; sourceTree = "<group>"; };
|
||||
FD5D201D27B0D87C00FEA984 /* SessionId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionId.swift; sourceTree = "<group>"; };
|
||||
|
@ -3884,6 +3902,49 @@
|
|||
path = Session;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FD3C905D27E410DB00CD579F /* Common Networking */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FD3C905E27E410EE00CD579F /* Models */,
|
||||
FD3C906127E411AF00CD579F /* HeaderSpec.swift */,
|
||||
FD3C906327E4122F00CD579F /* RequestSpec.swift */,
|
||||
);
|
||||
path = "Common Networking";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FD3C905E27E410EE00CD579F /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FD3C905F27E410F700CD579F /* FileUploadResponseSpec.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FD3C906527E416A200CD579F /* Contacts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FD3C906627E416AF00CD579F /* BlindedIdMappingSpec.swift */,
|
||||
);
|
||||
path = Contacts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FD3C906827E417B100CD579F /* Utilities */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FD3C906927E417CE00CD579F /* SodiumUtilitiesSpec.swift */,
|
||||
);
|
||||
path = Utilities;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FD3C906B27E43C2400CD579F /* Sending & Receiving */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FD3C906C27E43C4B00CD579F /* MessageSenderEncryptionSpec.swift */,
|
||||
FD3C907027E445E500CD579F /* MessageReceiverDecryptionSpec.swift */,
|
||||
);
|
||||
path = "Sending & Receiving";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FD659ABE27A7648200F12C02 /* Message Requests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -3924,6 +3985,7 @@
|
|||
children = (
|
||||
FD83B9C227CF33F7005E1583 /* ServerSpec.swift */,
|
||||
FD83B9C427CF3E2A005E1583 /* OpenGroupSpec.swift */,
|
||||
FD3C905B27E3FBEF00CD579F /* BatchRequestInfoSpec.swift */,
|
||||
FD83B9C627CF3F10005E1583 /* CapabilitiesSpec.swift */,
|
||||
FDC2908627D7047F005DAE71 /* RoomSpec.swift */,
|
||||
FDC2908827D70656005DAE71 /* RoomPollInfoSpec.swift */,
|
||||
|
@ -4038,7 +4100,11 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
FDC4389B27BA01E300C60D73 /* _TestUtilities */,
|
||||
FD3C905D27E410DB00CD579F /* Common Networking */,
|
||||
FD3C906527E416A200CD579F /* Contacts */,
|
||||
FD3C906B27E43C2400CD579F /* Sending & Receiving */,
|
||||
FDC4389827BA001800C60D73 /* Open Groups */,
|
||||
FD3C906827E417B100CD579F /* Utilities */,
|
||||
);
|
||||
path = SessionMessagingKitTests;
|
||||
sourceTree = "<group>";
|
||||
|
@ -4061,9 +4127,10 @@
|
|||
FD078E5F27E2BB36000769AF /* MockIdentityManager.swift */,
|
||||
FDC4389C27BA01F000C60D73 /* MockStorage.swift */,
|
||||
FD859EF327C2F49200510D0C /* MockSodium.swift */,
|
||||
FD3C906E27E43E8700CD579F /* MockBox.swift */,
|
||||
FD859EF927C2F5C500510D0C /* MockGenericHash.swift */,
|
||||
FD859EF527C2F52C00510D0C /* MockSign.swift */,
|
||||
FD859EF727C2F58900510D0C /* MockAeadXChaCha20Poly1305Ietf.swift */,
|
||||
FD859EF927C2F5C500510D0C /* MockGenericHash.swift */,
|
||||
FD859EFB27C2F60700510D0C /* MockEd25519.swift */,
|
||||
FD078E5927E29F09000769AF /* MockNonce16Generator.swift */,
|
||||
FD078E5B27E29F78000769AF /* MockNonce24Generator.swift */,
|
||||
|
@ -5673,20 +5740,25 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
FDC290AC27DB0B1C005DAE71 /* MockedExtensions.swift in Sources */,
|
||||
FD3C905C27E3FBEF00CD579F /* BatchRequestInfoSpec.swift in Sources */,
|
||||
FD078E5827E1B831000769AF /* TestIncomingMessage.swift in Sources */,
|
||||
FD859EFA27C2F5C500510D0C /* MockGenericHash.swift in Sources */,
|
||||
FDC2909427D710B4005DAE71 /* SOGSEndpointSpec.swift in Sources */,
|
||||
FDC290AF27DFEE97005DAE71 /* TestTransaction.swift in Sources */,
|
||||
FDC290B327DFF9F5005DAE71 /* TestOnionRequestAPI.swift in Sources */,
|
||||
FDC2909127D709CA005DAE71 /* SOGSMessageSpec.swift in Sources */,
|
||||
FD3C906A27E417CE00CD579F /* SodiumUtilitiesSpec.swift in Sources */,
|
||||
FD078E6027E2BB36000769AF /* MockIdentityManager.swift in Sources */,
|
||||
FD3C907127E445E500CD579F /* MessageReceiverDecryptionSpec.swift in Sources */,
|
||||
FDC2909627D71252005DAE71 /* SOGSErrorSpec.swift in Sources */,
|
||||
FDC2908727D7047F005DAE71 /* RoomSpec.swift in Sources */,
|
||||
FD078E4F27E175F1000769AF /* DependencyExtensions.swift in Sources */,
|
||||
FDC2909C27D713D2005DAE71 /* SodiumProtocolsSpec.swift in Sources */,
|
||||
FD3C906027E410F700CD579F /* FileUploadResponseSpec.swift in Sources */,
|
||||
FD83B9C327CF33F7005E1583 /* ServerSpec.swift in Sources */,
|
||||
FD83B9C727CF3F10005E1583 /* CapabilitiesSpec.swift in Sources */,
|
||||
FDC2909A27D71376005DAE71 /* NonceGeneratorSpec.swift in Sources */,
|
||||
FD3C906427E4122F00CD579F /* RequestSpec.swift in Sources */,
|
||||
FD078E4827E02561000769AF /* CommonMockedExtensions.swift in Sources */,
|
||||
FDC290A027D85826005DAE71 /* TestContactThread.swift in Sources */,
|
||||
FD859EF827C2F58900510D0C /* MockAeadXChaCha20Poly1305Ietf.swift in Sources */,
|
||||
|
@ -5697,20 +5769,24 @@
|
|||
FD859EFC27C2F60700510D0C /* MockEd25519.swift in Sources */,
|
||||
FDC290A627D860CE005DAE71 /* Mock.swift in Sources */,
|
||||
FD83B9C027CF2294005E1583 /* TestConstants.swift in Sources */,
|
||||
FD3C906F27E43E8700CD579F /* MockBox.swift in Sources */,
|
||||
FDC4389A27BA002500C60D73 /* OpenGroupAPISpec.swift in Sources */,
|
||||
FD83B9C527CF3E2A005E1583 /* OpenGroupSpec.swift in Sources */,
|
||||
FDC2908B27D707F3005DAE71 /* SendMessageRequestSpec.swift in Sources */,
|
||||
FDC290A827D9B46D005DAE71 /* NimbleExtensions.swift in Sources */,
|
||||
FD3C906227E411AF00CD579F /* HeaderSpec.swift in Sources */,
|
||||
FDC290B727E00FDB005DAE71 /* TestGroupThread.swift in Sources */,
|
||||
FDC2908F27D70938005DAE71 /* SendDirectMessageRequestSpec.swift in Sources */,
|
||||
FDC438BD27BB2AB400C60D73 /* Mockable.swift in Sources */,
|
||||
FD859EF627C2F52C00510D0C /* MockSign.swift in Sources */,
|
||||
FDC2908927D70656005DAE71 /* RoomPollInfoSpec.swift in Sources */,
|
||||
FD078E5A27E29F09000769AF /* MockNonce16Generator.swift in Sources */,
|
||||
FD3C906D27E43C4B00CD579F /* MessageSenderEncryptionSpec.swift in Sources */,
|
||||
FDC2908D27D70905005DAE71 /* UpdateMessageRequestSpec.swift in Sources */,
|
||||
FDC290A227D85890005DAE71 /* TestInteraction.swift in Sources */,
|
||||
FD078E5427E197CA000769AF /* OpenGroupManagerSpec.swift in Sources */,
|
||||
FDC4389D27BA01F000C60D73 /* MockStorage.swift in Sources */,
|
||||
FD3C906727E416AF00CD579F /* BlindedIdMappingSpec.swift in Sources */,
|
||||
FD83B9D227D59495005E1583 /* MockUserDefaults.swift in Sources */,
|
||||
FD078E5C27E29F78000769AF /* MockNonce24Generator.swift in Sources */,
|
||||
);
|
||||
|
|
|
@ -96,3 +96,5 @@ struct Request<T: Encodable, Endpoint: EndpointType> {
|
|||
return urlRequest
|
||||
}
|
||||
}
|
||||
|
||||
extension Request: Equatable where T: Equatable {}
|
||||
|
|
|
@ -21,12 +21,6 @@ public protocol OGMCacheType {
|
|||
func getTimeSinceLastOpen(using dependencies: Dependencies) -> TimeInterval
|
||||
}
|
||||
|
||||
extension OGMCacheType {
|
||||
func getTimeSinceLastOpen() -> TimeInterval {
|
||||
return getTimeSinceLastOpen(using: Dependencies())
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - OpenGroupManager
|
||||
|
||||
@objc(SNOpenGroupManager)
|
||||
|
@ -49,7 +43,7 @@ public final class OpenGroupManager: NSObject {
|
|||
public var timeSinceLastPoll: [String: TimeInterval] = [:]
|
||||
|
||||
fileprivate var _timeSinceLastOpen: TimeInterval?
|
||||
public func getTimeSinceLastOpen(using dependencies: Dependencies = Dependencies()) -> TimeInterval {
|
||||
public func getTimeSinceLastOpen(using dependencies: Dependencies) -> TimeInterval {
|
||||
if let storedTimeSinceLastOpen: TimeInterval = _timeSinceLastOpen {
|
||||
return storedTimeSinceLastOpen
|
||||
}
|
||||
|
@ -702,9 +696,10 @@ extension OpenGroupManager {
|
|||
identityManager: IdentityManagerProtocol? = nil,
|
||||
storage: SessionMessagingKitStorageProtocol? = nil,
|
||||
sodium: SodiumType? = nil,
|
||||
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
|
||||
sign: SignType? = nil,
|
||||
box: BoxType? = nil,
|
||||
genericHash: GenericHashType? = nil,
|
||||
sign: SignType? = nil,
|
||||
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
|
||||
ed25519: Ed25519Type? = nil,
|
||||
nonceGenerator16: NonceGenerator16ByteType? = nil,
|
||||
nonceGenerator24: NonceGenerator24ByteType? = nil,
|
||||
|
@ -718,9 +713,10 @@ extension OpenGroupManager {
|
|||
identityManager: identityManager,
|
||||
storage: storage,
|
||||
sodium: sodium,
|
||||
aeadXChaCha20Poly1305Ietf: aeadXChaCha20Poly1305Ietf,
|
||||
sign: sign,
|
||||
box: box,
|
||||
genericHash: genericHash,
|
||||
sign: sign,
|
||||
aeadXChaCha20Poly1305Ietf: aeadXChaCha20Poly1305Ietf,
|
||||
ed25519: ed25519,
|
||||
nonceGenerator16: nonceGenerator16,
|
||||
nonceGenerator24: nonceGenerator24,
|
||||
|
|
|
@ -5,18 +5,19 @@ import Sodium
|
|||
import Curve25519Kit
|
||||
|
||||
public protocol SodiumType {
|
||||
func getBox() -> BoxType
|
||||
func getGenericHash() -> GenericHashType
|
||||
func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType
|
||||
func getSign() -> SignType
|
||||
func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType
|
||||
|
||||
func generateBlindingFactor(serverPublicKey: String) -> Bytes?
|
||||
func generateBlindingFactor(serverPublicKey: String, genericHash: GenericHashType) -> Bytes?
|
||||
func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair?
|
||||
func sogsSignature(message: Bytes, secretKey: Bytes, blindedSecretKey ka: Bytes, blindedPublicKey kA: Bytes) -> Bytes?
|
||||
|
||||
func combineKeys(lhsKeyBytes: Bytes, rhsKeyBytes: Bytes) -> Bytes?
|
||||
func sharedBlindedEncryptionKey(secretKey a: Bytes, otherBlindedPublicKey: Bytes, fromBlindedPublicKey kA: Bytes, toBlindedPublicKey kB: Bytes, genericHash: GenericHashType) -> Bytes?
|
||||
|
||||
func sessionId(_ sessionId: String, matchesBlindedId blindedSessionId: String, serverPublicKey: String) -> Bool
|
||||
func sessionId(_ sessionId: String, matchesBlindedId blindedSessionId: String, serverPublicKey: String, genericHash: GenericHashType) -> Bool
|
||||
}
|
||||
|
||||
public protocol AeadXChaCha20Poly1305IetfType {
|
||||
|
@ -32,12 +33,9 @@ public protocol Ed25519Type {
|
|||
func verifySignature(_ signature: Data, publicKey: Data, data: Data) throws -> Bool
|
||||
}
|
||||
|
||||
public protocol SignType {
|
||||
var PublicKeyBytes: Int { get }
|
||||
|
||||
func toX25519(ed25519PublicKey: Bytes) -> Bytes?
|
||||
func signature(message: Bytes, secretKey: Bytes) -> Bytes?
|
||||
func verify(message: Bytes, publicKey: Bytes, signature: Bytes) -> Bool
|
||||
public protocol BoxType {
|
||||
func seal(message: Bytes, recipientPublicKey: Bytes) -> Bytes?
|
||||
func open(anonymousCipherText: Bytes, recipientPublicKey: Bytes, recipientSecretKey: Bytes) -> Bytes?
|
||||
}
|
||||
|
||||
public protocol GenericHashType {
|
||||
|
@ -46,8 +44,25 @@ public protocol GenericHashType {
|
|||
func hashSaltPersonal(message: Bytes, outputLength: Int, key: Bytes?, salt: Bytes, personal: Bytes) -> Bytes?
|
||||
}
|
||||
|
||||
public protocol SignType {
|
||||
var Bytes: Int { get }
|
||||
var PublicKeyBytes: Int { get }
|
||||
|
||||
func toX25519(ed25519PublicKey: Bytes) -> Bytes?
|
||||
func signature(message: Bytes, secretKey: Bytes) -> Bytes?
|
||||
func verify(message: Bytes, publicKey: Bytes, signature: Bytes) -> Bool
|
||||
}
|
||||
|
||||
// MARK: - Default Values
|
||||
|
||||
extension GenericHashType {
|
||||
func hash(message: Bytes) -> Bytes? { return hash(message: message, key: nil) }
|
||||
|
||||
func hashSaltPersonal(message: Bytes, outputLength: Int, salt: Bytes, personal: Bytes) -> Bytes? {
|
||||
return hashSaltPersonal(message: message, outputLength: outputLength, key: nil, salt: salt, personal: personal)
|
||||
}
|
||||
}
|
||||
|
||||
extension AeadXChaCha20Poly1305IetfType {
|
||||
func encrypt(message: Bytes, secretKey: Bytes, nonce: Bytes) -> Bytes? {
|
||||
return encrypt(message: message, secretKey: secretKey, nonce: nonce, additionalData: nil)
|
||||
|
@ -58,17 +73,10 @@ extension AeadXChaCha20Poly1305IetfType {
|
|||
}
|
||||
}
|
||||
|
||||
extension GenericHashType {
|
||||
func hash(message: Bytes) -> Bytes? { return hash(message: message, key: nil) }
|
||||
|
||||
func hashSaltPersonal(message: Bytes, outputLength: Int, salt: Bytes, personal: Bytes) -> Bytes? {
|
||||
return hashSaltPersonal(message: message, outputLength: outputLength, key: nil, salt: salt, personal: personal)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Conformance
|
||||
|
||||
extension Sodium: SodiumType {
|
||||
public func getBox() -> BoxType { return box }
|
||||
public func getGenericHash() -> GenericHashType { return genericHash }
|
||||
public func getSign() -> SignType { return sign }
|
||||
public func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return aead.xchacha20poly1305ietf }
|
||||
|
@ -78,9 +86,10 @@ extension Sodium: SodiumType {
|
|||
}
|
||||
}
|
||||
|
||||
extension Aead.XChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType {}
|
||||
extension Sign: SignType {}
|
||||
extension Box: BoxType {}
|
||||
extension GenericHash: GenericHashType {}
|
||||
extension Sign: SignType {}
|
||||
extension Aead.XChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType {}
|
||||
|
||||
struct Ed25519Wrapper: Ed25519Type {
|
||||
func sign(data: Bytes, keyPair: ECKeyPair) throws -> Bytes? {
|
||||
|
|
|
@ -3,27 +3,40 @@ import SessionUtilitiesKit
|
|||
import Sodium
|
||||
|
||||
extension MessageReceiver {
|
||||
|
||||
internal static func decryptWithSessionProtocol(ciphertext: Data, using x25519KeyPair: ECKeyPair) throws -> (plaintext: Data, senderX25519PublicKey: String) {
|
||||
internal static func decryptWithSessionProtocol(ciphertext: Data, using x25519KeyPair: ECKeyPair, dependencies: Dependencies = Dependencies()) throws -> (plaintext: Data, senderX25519PublicKey: String) {
|
||||
let recipientX25519PrivateKey = x25519KeyPair.privateKey
|
||||
let recipientX25519PublicKey = Data(hex: x25519KeyPair.hexEncodedPublicKey.removingIdPrefixIfNeeded())
|
||||
let sodium = Sodium()
|
||||
let signatureSize = sodium.sign.Bytes
|
||||
let ed25519PublicKeySize = sodium.sign.PublicKeyBytes
|
||||
let signatureSize = dependencies.sign.Bytes
|
||||
let ed25519PublicKeySize = dependencies.sign.PublicKeyBytes
|
||||
|
||||
// 1. ) Decrypt the message
|
||||
guard let plaintextWithMetadata = sodium.box.open(anonymousCipherText: Bytes(ciphertext), recipientPublicKey: Box.PublicKey(Bytes(recipientX25519PublicKey)),
|
||||
recipientSecretKey: Bytes(recipientX25519PrivateKey)), plaintextWithMetadata.count > (signatureSize + ed25519PublicKeySize) else { throw Error.decryptionFailed }
|
||||
guard
|
||||
let plaintextWithMetadata = dependencies.box.open(
|
||||
anonymousCipherText: Bytes(ciphertext),
|
||||
recipientPublicKey: Box.PublicKey(Bytes(recipientX25519PublicKey)),
|
||||
recipientSecretKey: Bytes(recipientX25519PrivateKey)
|
||||
),
|
||||
plaintextWithMetadata.count > (signatureSize + ed25519PublicKeySize)
|
||||
else {
|
||||
throw Error.decryptionFailed
|
||||
}
|
||||
|
||||
// 2. ) Get the message parts
|
||||
let signature = Bytes(plaintextWithMetadata[plaintextWithMetadata.count - signatureSize ..< plaintextWithMetadata.count])
|
||||
let senderED25519PublicKey = Bytes(plaintextWithMetadata[plaintextWithMetadata.count - (signatureSize + ed25519PublicKeySize) ..< plaintextWithMetadata.count - signatureSize])
|
||||
let plaintext = Bytes(plaintextWithMetadata[0..<plaintextWithMetadata.count - (signatureSize + ed25519PublicKeySize)])
|
||||
|
||||
// 3. ) Verify the signature
|
||||
let verificationData = plaintext + senderED25519PublicKey + recipientX25519PublicKey
|
||||
let isValid = sodium.sign.verify(message: verificationData, publicKey: senderED25519PublicKey, signature: signature)
|
||||
guard isValid else { throw Error.invalidSignature }
|
||||
guard dependencies.sign.verify(message: verificationData, publicKey: senderED25519PublicKey, signature: signature) else {
|
||||
throw Error.invalidSignature
|
||||
}
|
||||
|
||||
// 4. ) Get the sender's X25519 public key
|
||||
guard let senderX25519PublicKey = sodium.sign.toX25519(ed25519PublicKey: senderED25519PublicKey) else { throw Error.decryptionFailed }
|
||||
guard let senderX25519PublicKey = dependencies.sign.toX25519(ed25519PublicKey: senderED25519PublicKey) else {
|
||||
throw Error.decryptionFailed
|
||||
}
|
||||
|
||||
return (Data(plaintext), SessionId(.standard, publicKey: senderX25519PublicKey).hexString)
|
||||
}
|
||||
|
||||
|
@ -72,7 +85,7 @@ extension MessageReceiver {
|
|||
])
|
||||
|
||||
/// Verify that the inner sender_edpk (A) yields the same outer kA we got with the message
|
||||
guard let blindingFactor: Bytes = dependencies.sodium.generateBlindingFactor(serverPublicKey: openGroupPublicKey) else {
|
||||
guard let blindingFactor: Bytes = dependencies.sodium.generateBlindingFactor(serverPublicKey: openGroupPublicKey, genericHash: dependencies.genericHash) else {
|
||||
throw Error.invalidSignature
|
||||
}
|
||||
guard let sharedSecret: Bytes = dependencies.sodium.combineKeys(lhsKeyBytes: blindingFactor, rhsKeyBytes: sender_edpk) else {
|
||||
|
|
|
@ -18,7 +18,7 @@ extension MessageReceiver {
|
|||
case let message as ExpirationTimerUpdate: handleExpirationTimerUpdate(message, using: transaction)
|
||||
case let message as ConfigurationMessage: handleConfigurationMessage(message, using: transaction)
|
||||
case let message as UnsendRequest: handleUnsendRequest(message, using: transaction)
|
||||
case let message as MessageRequestResponse: handleMessageRequestResponse(message, using: transaction)
|
||||
case let message as MessageRequestResponse: handleMessageRequestResponse(message, using: transaction, dependencies: dependencies)
|
||||
case let message as VisibleMessage: try handleVisibleMessage(message, associatedWithProto: proto, openGroupID: openGroupID, isBackgroundPoll: isBackgroundPoll, using: transaction, dependencies: dependencies)
|
||||
default: fatalError()
|
||||
}
|
||||
|
@ -839,7 +839,7 @@ extension MessageReceiver {
|
|||
}
|
||||
}
|
||||
|
||||
public static func handleMessageRequestResponse(_ message: MessageRequestResponse, using transaction: Any) {
|
||||
public static func handleMessageRequestResponse(_ message: MessageRequestResponse, using transaction: Any, dependencies: Dependencies) {
|
||||
let userPublicKey = getUserHexEncodedPublicKey()
|
||||
var hadBlindedContact: Bool = false
|
||||
var blindedThreadIds: [String] = []
|
||||
|
@ -870,7 +870,7 @@ extension MessageReceiver {
|
|||
|
||||
// If the sessionId matches the blindedId then this thread needs to be converted to an un-blinded thread
|
||||
guard let serverPublicKey: String = blindedThread.originalOpenGroupPublicKey else { continue }
|
||||
guard Sodium().sessionId(senderId, matchesBlindedId: blindedId, serverPublicKey: serverPublicKey) else { continue }
|
||||
guard dependencies.sodium.sessionId(senderId, matchesBlindedId: blindedId, serverPublicKey: serverPublicKey, genericHash: dependencies.genericHash) else { continue }
|
||||
guard let blindedThreadId: String = blindedThread.uniqueId else { continue }
|
||||
guard let view: YapDatabaseAutoViewTransaction = transaction.ext(TSMessageDatabaseViewExtensionName) as? YapDatabaseAutoViewTransaction else {
|
||||
continue
|
||||
|
@ -878,7 +878,7 @@ extension MessageReceiver {
|
|||
|
||||
// Cache the mapping
|
||||
let mapping: BlindedIdMapping = BlindedIdMapping(blindedId: blindedId, sessionId: senderId, serverPublicKey: serverPublicKey)
|
||||
Storage.shared.cacheBlindedIdMapping(mapping, using: transaction)
|
||||
dependencies.storage.cacheBlindedIdMapping(mapping, using: transaction)
|
||||
|
||||
// Flag that we had a blinded contact and add the `blindedThreadId` to an array so we can remove
|
||||
// them at the end of processing
|
||||
|
|
|
@ -2,22 +2,20 @@ import SessionUtilitiesKit
|
|||
import Sodium
|
||||
|
||||
extension MessageSender {
|
||||
|
||||
internal static func encryptWithSessionProtocol(_ plaintext: Data, for recipientHexEncodedX25519PublicKey: String) throws -> Data {
|
||||
guard let userED25519KeyPair = SNMessagingKitConfiguration.shared.storage.getUserED25519KeyPair() else {
|
||||
internal static func encryptWithSessionProtocol(_ plaintext: Data, for recipientHexEncodedX25519PublicKey: String, using dependencies: Dependencies = Dependencies()) throws -> Data {
|
||||
guard let userED25519KeyPair = dependencies.storage.getUserED25519KeyPair() else {
|
||||
throw Error.noUserED25519KeyPair
|
||||
}
|
||||
|
||||
let recipientX25519PublicKey = Data(hex: recipientHexEncodedX25519PublicKey.removingIdPrefixIfNeeded())
|
||||
let sodium = Sodium()
|
||||
|
||||
let verificationData = plaintext + Data(userED25519KeyPair.publicKey) + recipientX25519PublicKey
|
||||
guard let signature = sodium.sign.signature(message: Bytes(verificationData), secretKey: userED25519KeyPair.secretKey) else {
|
||||
guard let signature = dependencies.sign.signature(message: Bytes(verificationData), secretKey: userED25519KeyPair.secretKey) else {
|
||||
throw Error.signingFailed
|
||||
}
|
||||
|
||||
let plaintextWithMetadata = plaintext + Data(userED25519KeyPair.publicKey) + Data(signature)
|
||||
guard let ciphertext = sodium.box.seal(message: Bytes(plaintextWithMetadata), recipientPublicKey: Bytes(recipientX25519PublicKey)) else {
|
||||
guard let ciphertext = dependencies.box.seal(message: Bytes(plaintextWithMetadata), recipientPublicKey: Bytes(recipientX25519PublicKey)) else {
|
||||
throw Error.encryptionFailed
|
||||
}
|
||||
|
||||
|
@ -26,7 +24,7 @@ extension MessageSender {
|
|||
|
||||
internal static func encryptWithSessionBlindingProtocol(_ plaintext: Data, for recipientBlindedId: String, openGroupPublicKey: String, using dependencies: Dependencies = Dependencies()) throws -> Data {
|
||||
guard SessionId.Prefix(from: recipientBlindedId) == .blinded else { throw Error.signingFailed }
|
||||
guard let userEd25519KeyPair = SNMessagingKitConfiguration.shared.storage.getUserED25519KeyPair() else {
|
||||
guard let userEd25519KeyPair = dependencies.storage.getUserED25519KeyPair() else {
|
||||
throw Error.noUserED25519KeyPair
|
||||
}
|
||||
guard let blindedKeyPair = dependencies.sodium.blindedKeyPair(serverPublicKey: openGroupPublicKey, edKeyPair: userEd25519KeyPair, genericHash: dependencies.genericHash) else {
|
||||
|
|
|
@ -73,7 +73,7 @@ public enum ContactUtilities {
|
|||
|
||||
// Then we try loop through all approved contact threads to see if one of those contacts can be blinded to match
|
||||
ContactUtilities.enumerateApprovedContactThreads(using: transaction) { contactThread, contact, stop in
|
||||
guard dependencies.sodium.sessionId(contact.sessionID, matchesBlindedId: blindedId, serverPublicKey: serverPublicKey) else {
|
||||
guard dependencies.sodium.sessionId(contact.sessionID, matchesBlindedId: blindedId, serverPublicKey: serverPublicKey, genericHash: dependencies.genericHash) else {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -91,7 +91,7 @@ public enum ContactUtilities {
|
|||
// a thread with this contact in a different SOGS and had cached the mapping)
|
||||
dependencies.storage.enumerateBlindedIdMapping(using: transaction) { mapping, stop in
|
||||
guard mapping.serverPublicKey != serverPublicKey else { return }
|
||||
guard dependencies.sodium.sessionId(mapping.sessionId, matchesBlindedId: blindedId, serverPublicKey: serverPublicKey) else {
|
||||
guard dependencies.sodium.sessionId(mapping.sessionId, matchesBlindedId: blindedId, serverPublicKey: serverPublicKey, genericHash: dependencies.genericHash) else {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -30,10 +30,16 @@ public class Dependencies {
|
|||
set { _sodium = newValue }
|
||||
}
|
||||
|
||||
internal var _aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType?
|
||||
public var aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType {
|
||||
get { Dependencies.getValueSettingIfNull(&_aeadXChaCha20Poly1305Ietf) { sodium.getAeadXChaCha20Poly1305Ietf() } }
|
||||
set { _aeadXChaCha20Poly1305Ietf = newValue }
|
||||
internal var _box: BoxType?
|
||||
public var box: BoxType {
|
||||
get { Dependencies.getValueSettingIfNull(&_box) { sodium.getBox() } }
|
||||
set { _box = newValue }
|
||||
}
|
||||
|
||||
internal var _genericHash: GenericHashType?
|
||||
public var genericHash: GenericHashType {
|
||||
get { Dependencies.getValueSettingIfNull(&_genericHash) { sodium.getGenericHash() } }
|
||||
set { _genericHash = newValue }
|
||||
}
|
||||
|
||||
internal var _sign: SignType?
|
||||
|
@ -42,10 +48,10 @@ public class Dependencies {
|
|||
set { _sign = newValue }
|
||||
}
|
||||
|
||||
internal var _genericHash: GenericHashType?
|
||||
public var genericHash: GenericHashType {
|
||||
get { Dependencies.getValueSettingIfNull(&_genericHash) { sodium.getGenericHash() } }
|
||||
set { _genericHash = newValue }
|
||||
internal var _aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType?
|
||||
public var aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType {
|
||||
get { Dependencies.getValueSettingIfNull(&_aeadXChaCha20Poly1305Ietf) { sodium.getAeadXChaCha20Poly1305Ietf() } }
|
||||
set { _aeadXChaCha20Poly1305Ietf = newValue }
|
||||
}
|
||||
|
||||
internal var _ed25519: Ed25519Type?
|
||||
|
@ -85,9 +91,10 @@ public class Dependencies {
|
|||
identityManager: IdentityManagerProtocol? = nil,
|
||||
storage: SessionMessagingKitStorageProtocol? = nil,
|
||||
sodium: SodiumType? = nil,
|
||||
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
|
||||
sign: SignType? = nil,
|
||||
box: BoxType? = nil,
|
||||
genericHash: GenericHashType? = nil,
|
||||
sign: SignType? = nil,
|
||||
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
|
||||
ed25519: Ed25519Type? = nil,
|
||||
nonceGenerator16: NonceGenerator16ByteType? = nil,
|
||||
nonceGenerator24: NonceGenerator24ByteType? = nil,
|
||||
|
@ -98,9 +105,10 @@ public class Dependencies {
|
|||
_identityManager = identityManager
|
||||
_storage = storage
|
||||
_sodium = sodium
|
||||
_aeadXChaCha20Poly1305Ietf = aeadXChaCha20Poly1305Ietf
|
||||
_sign = sign
|
||||
_box = box
|
||||
_genericHash = genericHash
|
||||
_sign = sign
|
||||
_aeadXChaCha20Poly1305Ietf = aeadXChaCha20Poly1305Ietf
|
||||
_ed25519 = ed25519
|
||||
_nonceGenerator16 = nonceGenerator16
|
||||
_nonceGenerator24 = nonceGenerator24
|
||||
|
|
|
@ -41,6 +41,15 @@ extension Sign {
|
|||
}
|
||||
}
|
||||
|
||||
/// These extenion methods are used to generate a sign "blinded" messages
|
||||
///
|
||||
/// According to the Swift engineers the only situation when `UnsafeRawBufferPointer.baseAddress` is nil is when it's an
|
||||
/// empty collection; as such our guard cases wihch return `-1` when unwrapping this value should never be hit and we can ignore
|
||||
/// them as possible results.
|
||||
///
|
||||
/// For more information see:
|
||||
/// https://forums.swift.org/t/when-is-unsafemutablebufferpointer-baseaddress-nil/32136/5
|
||||
/// https://github.com/apple/swift-evolution/blob/master/proposals/0055-optional-unsafe-pointers.md#unsafebufferpointer
|
||||
extension Sodium {
|
||||
private static let scalarLength: Int = Int(crypto_core_ed25519_scalarbytes()) // 32
|
||||
private static let noClampLength: Int = Int(crypto_scalarmult_ed25519_bytes()) // 32
|
||||
|
@ -49,7 +58,7 @@ extension Sodium {
|
|||
private static let secretKeyLength: Int = Int(crypto_sign_secretkeybytes()) // 64
|
||||
|
||||
/// 64-byte blake2b hash then reduce to get the blinding factor
|
||||
public func generateBlindingFactor(serverPublicKey: String) -> Bytes? {
|
||||
public func generateBlindingFactor(serverPublicKey: String, genericHash: GenericHashType) -> Bytes? {
|
||||
/// k = salt.crypto_core_ed25519_scalar_reduce(blake2b(server_pk, digest_size=64).digest())
|
||||
guard let serverPubKeyData: Data = serverPublicKey.dataFromHex() else { return nil }
|
||||
guard let serverPublicKeyHashBytes: Bytes = genericHash.hash(message: [UInt8](serverPubKeyData), outputLength: 64) else {
|
||||
|
@ -59,18 +68,15 @@ extension Sodium {
|
|||
/// Reduce the server public key into an ed25519 scalar (`k`)
|
||||
let kPtr: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Sodium.scalarLength)
|
||||
|
||||
let kResult = serverPublicKeyHashBytes.withUnsafeBytes { (serverPublicKeyHashPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
_ = serverPublicKeyHashBytes.withUnsafeBytes { (serverPublicKeyHashPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
guard let serverPublicKeyHashBaseAddress: UnsafePointer<UInt8> = serverPublicKeyHashPtr.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return -1
|
||||
return -1 // Impossible case (refer to comments at top of extension)
|
||||
}
|
||||
|
||||
crypto_core_ed25519_scalar_reduce(kPtr, serverPublicKeyHashBaseAddress)
|
||||
return 0
|
||||
}
|
||||
|
||||
/// Ensure the above worked
|
||||
guard kResult == 0 else { return nil }
|
||||
|
||||
return Data(bytes: kPtr, count: Sodium.scalarLength).bytes
|
||||
}
|
||||
|
||||
|
@ -78,21 +84,20 @@ extension Sodium {
|
|||
/// convert to an *x* secret key, which seems wrong--but isn't because converted keys use the
|
||||
/// same secret scalar secret (and so this is just the most convenient way to get 'a' out of
|
||||
/// a sodium Ed25519 secret key)
|
||||
private func generatePrivateKeyScalar(secretKey: Bytes) -> Bytes? {
|
||||
func generatePrivateKeyScalar(secretKey: Bytes) -> Bytes {
|
||||
/// a = s.to_curve25519_private_key().encode()
|
||||
let aPtr: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Sodium.scalarMultLength)
|
||||
|
||||
let aResult = secretKey.withUnsafeBytes { (secretKeyPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
/// Looks like the `crypto_sign_ed25519_sk_to_curve25519` function can't actually fail so no need to verify the result
|
||||
/// See: https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_sign/ed25519/ref10/keypair.c#L70
|
||||
_ = secretKey.withUnsafeBytes { (secretKeyPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
guard let secretKeyBaseAddress: UnsafePointer<UInt8> = secretKeyPtr.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return -1
|
||||
return -1 // Impossible case (refer to comments at top of extension)
|
||||
}
|
||||
|
||||
return crypto_sign_ed25519_sk_to_curve25519(aPtr, secretKeyBaseAddress)
|
||||
}
|
||||
|
||||
/// Ensure the above worked
|
||||
guard aResult == 0 else { return nil }
|
||||
|
||||
return Data(bytes: aPtr, count: Sodium.scalarMultLength).bytes
|
||||
}
|
||||
|
||||
|
@ -101,20 +106,22 @@ extension Sodium {
|
|||
guard edKeyPair.publicKey.count == Sodium.publicKeyLength && edKeyPair.secretKey.count == Sodium.secretKeyLength else {
|
||||
return nil
|
||||
}
|
||||
guard let kBytes: Bytes = generateBlindingFactor(serverPublicKey: serverPublicKey) else { return nil }
|
||||
guard let aBytes: Bytes = generatePrivateKeyScalar(secretKey: edKeyPair.secretKey) else { return nil }
|
||||
guard let kBytes: Bytes = generateBlindingFactor(serverPublicKey: serverPublicKey, genericHash: genericHash) else {
|
||||
return nil
|
||||
}
|
||||
let aBytes: Bytes = generatePrivateKeyScalar(secretKey: edKeyPair.secretKey)
|
||||
|
||||
/// Generate the blinded key pair `ka`, `kA`
|
||||
let kaPtr: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Sodium.secretKeyLength)
|
||||
let kAPtr: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Sodium.publicKeyLength)
|
||||
|
||||
let kaResult = aBytes.withUnsafeBytes { (aPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
_ = aBytes.withUnsafeBytes { (aPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
return kBytes.withUnsafeBytes { (kPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
guard let kBaseAddress: UnsafePointer<UInt8> = kPtr.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return -1
|
||||
return -1 // Impossible case (refer to comments at top of extension)
|
||||
}
|
||||
guard let aBaseAddress: UnsafePointer<UInt8> = aPtr.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return -1
|
||||
return -1 // Impossible case (refer to comments at top of extension)
|
||||
}
|
||||
|
||||
crypto_core_ed25519_scalar_mul(kaPtr, kBaseAddress, aBaseAddress)
|
||||
|
@ -122,9 +129,6 @@ extension Sodium {
|
|||
}
|
||||
}
|
||||
|
||||
/// Ensure the above worked
|
||||
guard kaResult == 0 else { return nil }
|
||||
|
||||
guard crypto_scalarmult_ed25519_base_noclamp(kAPtr, kaPtr) == 0 else { return nil }
|
||||
|
||||
return Box.KeyPair(
|
||||
|
@ -144,18 +148,15 @@ extension Sodium {
|
|||
let combinedHashBytes: Bytes = (H_rh + kA + message).sha512()
|
||||
let rPtr: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Sodium.scalarLength)
|
||||
|
||||
let rResult = combinedHashBytes.withUnsafeBytes { (combinedHashPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
_ = combinedHashBytes.withUnsafeBytes { (combinedHashPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
guard let combinedHashBaseAddress: UnsafePointer<UInt8> = combinedHashPtr.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return -1
|
||||
return -1 // Impossible case (refer to comments at top of extension)
|
||||
}
|
||||
|
||||
crypto_core_ed25519_scalar_reduce(rPtr, combinedHashBaseAddress)
|
||||
return 0
|
||||
}
|
||||
|
||||
/// Ensure the above worked
|
||||
guard rResult == 0 else { return nil }
|
||||
|
||||
/// sig_R = salt.crypto_scalarmult_ed25519_base_noclamp(r)
|
||||
let sig_RPtr: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Sodium.noClampLength)
|
||||
guard crypto_scalarmult_ed25519_base_noclamp(sig_RPtr, rPtr) == 0 else { return nil }
|
||||
|
@ -165,25 +166,22 @@ extension Sodium {
|
|||
let HRAMHashBytes: Bytes = (sig_RBytes + kA + message).sha512()
|
||||
let HRAMPtr: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Sodium.scalarLength)
|
||||
|
||||
let HRAMResult = HRAMHashBytes.withUnsafeBytes { (HRAMHashPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
_ = HRAMHashBytes.withUnsafeBytes { (HRAMHashPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
guard let HRAMHashBaseAddress: UnsafePointer<UInt8> = HRAMHashPtr.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return -1
|
||||
return -1 // Impossible case (refer to comments at top of extension)
|
||||
}
|
||||
|
||||
crypto_core_ed25519_scalar_reduce(HRAMPtr, HRAMHashBaseAddress)
|
||||
return 0
|
||||
}
|
||||
|
||||
/// Ensure the above worked
|
||||
guard HRAMResult == 0 else { return nil }
|
||||
|
||||
/// sig_s = salt.crypto_core_ed25519_scalar_add(r, salt.crypto_core_ed25519_scalar_mul(HRAM, ka))
|
||||
let sig_sMulPtr: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Sodium.scalarLength)
|
||||
let sig_sPtr: UnsafeMutablePointer<UInt8> = UnsafeMutablePointer<UInt8>.allocate(capacity: Sodium.scalarLength)
|
||||
|
||||
let sig_sResult = ka.withUnsafeBytes { (kaPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
_ = ka.withUnsafeBytes { (kaPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
guard let kaBaseAddress: UnsafePointer<UInt8> = kaPtr.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return -1
|
||||
return -1 // Impossible case (refer to comments at top of extension)
|
||||
}
|
||||
|
||||
crypto_core_ed25519_scalar_mul(sig_sMulPtr, HRAMPtr, kaBaseAddress)
|
||||
|
@ -191,8 +189,6 @@ extension Sodium {
|
|||
return 0
|
||||
}
|
||||
|
||||
guard sig_sResult == 0 else { return nil }
|
||||
|
||||
/// full_sig = sig_R + sig_s
|
||||
return (Data(bytes: sig_RPtr, count: Sodium.noClampLength).bytes + Data(bytes: sig_sPtr, count: Sodium.scalarLength).bytes)
|
||||
}
|
||||
|
@ -204,10 +200,10 @@ extension Sodium {
|
|||
let result = rhsKeyBytes.withUnsafeBytes { (rhsKeyBytesPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
return lhsKeyBytes.withUnsafeBytes { (lhsKeyBytesPtr: UnsafeRawBufferPointer) -> Int32 in
|
||||
guard let lhsKeyBytesBaseAddress: UnsafePointer<UInt8> = lhsKeyBytesPtr.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return -1
|
||||
return -1 // Impossible case (refer to comments at top of extension)
|
||||
}
|
||||
guard let rhsKeyBytesBaseAddress: UnsafePointer<UInt8> = rhsKeyBytesPtr.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
|
||||
return -1
|
||||
return -1 // Impossible case (refer to comments at top of extension)
|
||||
}
|
||||
|
||||
return crypto_scalarmult_ed25519_noclamp(combinedPtr, lhsKeyBytesBaseAddress, rhsKeyBytesBaseAddress)
|
||||
|
@ -228,18 +224,23 @@ extension Sodium {
|
|||
///
|
||||
/// BLAKE2b(b kA || kA || kB)
|
||||
public func sharedBlindedEncryptionKey(secretKey: Bytes, otherBlindedPublicKey: Bytes, fromBlindedPublicKey kA: Bytes, toBlindedPublicKey kB: Bytes, genericHash: GenericHashType) -> Bytes? {
|
||||
guard let aBytes: Bytes = generatePrivateKeyScalar(secretKey: secretKey) else { return nil }
|
||||
guard let combinedKeyBytes: Bytes = combineKeys(lhsKeyBytes: aBytes, rhsKeyBytes: otherBlindedPublicKey) else { return nil }
|
||||
let aBytes: Bytes = generatePrivateKeyScalar(secretKey: secretKey)
|
||||
|
||||
guard let combinedKeyBytes: Bytes = combineKeys(lhsKeyBytes: aBytes, rhsKeyBytes: otherBlindedPublicKey) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return genericHash.hash(message: (combinedKeyBytes + kA + kB), outputLength: 32)
|
||||
}
|
||||
|
||||
/// This method should be used to check if a users standard sessionId matches a blinded one
|
||||
public func sessionId(_ standardSessionId: String, matchesBlindedId blindedSessionId: String, serverPublicKey: String) -> Bool {
|
||||
public func sessionId(_ standardSessionId: String, matchesBlindedId blindedSessionId: String, serverPublicKey: String, genericHash: GenericHashType) -> Bool {
|
||||
// Only support generating blinded keys for standard session ids
|
||||
guard let sessionId: SessionId = SessionId(from: standardSessionId), sessionId.prefix == .standard else { return false }
|
||||
guard let blindedId: SessionId = SessionId(from: blindedSessionId), blindedId.prefix == .blinded else { return false }
|
||||
guard let kBytes: Bytes = generateBlindingFactor(serverPublicKey: serverPublicKey) else { return false }
|
||||
guard let kBytes: Bytes = generateBlindingFactor(serverPublicKey: serverPublicKey, genericHash: genericHash) else {
|
||||
return false
|
||||
}
|
||||
|
||||
/// From the session id (ignoring 05 prefix) we have two possible ed25519 pubkeys; the first is the positive (which is what
|
||||
/// Signal's XEd25519 conversion always uses)
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class HeaderSpec: QuickSpec {
|
||||
// MARK: - Spec
|
||||
|
||||
override func spec() {
|
||||
describe("a Dictionary of Header to String values") {
|
||||
it("can be converted into a dictionary of String to String values") {
|
||||
expect([Header.authorization: "test"].toHTTPHeaders()).to(equal(["Authorization": "test"]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class FileUploadResponseSpec: QuickSpec {
|
||||
// MARK: - Spec
|
||||
|
||||
override func spec() {
|
||||
describe("a FileUploadResponse") {
|
||||
context("when decoding") {
|
||||
it("handles a string id value") {
|
||||
let jsonData: Data = "{\"id\":\"123\"}".data(using: .utf8)!
|
||||
let response: FileUploadResponse? = try? JSONDecoder().decode(FileUploadResponse.self, from: jsonData)
|
||||
|
||||
expect(response?.id).to(equal("123"))
|
||||
}
|
||||
|
||||
it("handles an int id value") {
|
||||
let jsonData: Data = "{\"id\":124}".data(using: .utf8)!
|
||||
let response: FileUploadResponse? = try? JSONDecoder().decode(FileUploadResponse.self, from: jsonData)
|
||||
|
||||
expect(response?.id).to(equal("124"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class RequestSpec: QuickSpec {
|
||||
struct TestType: Codable, Equatable {
|
||||
let stringValue: String
|
||||
}
|
||||
|
||||
// MARK: - Spec
|
||||
|
||||
override func spec() {
|
||||
describe("a Request") {
|
||||
it("is initialized with the correct default values") {
|
||||
let request: Request<NoBody, OpenGroupAPI.Endpoint> = Request(
|
||||
server: "testServer",
|
||||
endpoint: .batch
|
||||
)
|
||||
|
||||
expect(request.method.rawValue).to(equal("GET"))
|
||||
expect(request.queryParameters).to(equal([:]))
|
||||
expect(request.headers).to(equal([:]))
|
||||
expect(request.body).to(beNil())
|
||||
}
|
||||
|
||||
context("when generating a URL") {
|
||||
it("adds a leading forward slash to the endpoint path") {
|
||||
let request: Request<NoBody, OpenGroupAPI.Endpoint> = Request(
|
||||
server: "testServer",
|
||||
endpoint: .batch
|
||||
)
|
||||
|
||||
expect(request.urlPathAndParamsString).to(equal("/batch"))
|
||||
}
|
||||
|
||||
it("creates a valid URL with no query parameters") {
|
||||
let request: Request<NoBody, OpenGroupAPI.Endpoint> = Request(
|
||||
server: "testServer",
|
||||
endpoint: .batch
|
||||
)
|
||||
|
||||
expect(request.urlPathAndParamsString).to(equal("/batch"))
|
||||
}
|
||||
|
||||
it("creates a valid URL when query parameters are provided") {
|
||||
let request: Request<NoBody, OpenGroupAPI.Endpoint> = Request(
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
queryParameters: [
|
||||
.limit: "123"
|
||||
]
|
||||
)
|
||||
|
||||
expect(request.urlPathAndParamsString).to(equal("/batch?limit=123"))
|
||||
}
|
||||
}
|
||||
|
||||
context("when generating a URLRequest") {
|
||||
it("sets all the values correctly") {
|
||||
let request: Request<NoBody, OpenGroupAPI.Endpoint> = Request(
|
||||
method: .delete,
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
headers: [
|
||||
.authorization: "test"
|
||||
]
|
||||
)
|
||||
let urlRequest: URLRequest? = try? request.generateUrlRequest()
|
||||
|
||||
expect(urlRequest?.httpMethod).to(equal("DELETE"))
|
||||
expect(urlRequest?.allHTTPHeaderFields).to(equal(["Authorization": "test"]))
|
||||
expect(urlRequest?.httpBody).to(beNil())
|
||||
}
|
||||
|
||||
it("throws an error if the URL is invalid") {
|
||||
let request: Request<NoBody, OpenGroupAPI.Endpoint> = Request(
|
||||
server: "testServer",
|
||||
endpoint: .roomPollInfo("!!%%", 123)
|
||||
)
|
||||
|
||||
expect {
|
||||
try request.generateUrlRequest()
|
||||
}
|
||||
.to(throwError(HTTP.Error.invalidURL))
|
||||
}
|
||||
|
||||
context("with a base64 string body") {
|
||||
it("successfully encodes the body") {
|
||||
let request: Request<String, OpenGroupAPI.Endpoint> = Request(
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
body: "TestMessage".data(using: .utf8)!.base64EncodedString()
|
||||
)
|
||||
|
||||
let urlRequest: URLRequest? = try? request.generateUrlRequest()
|
||||
let requestBody: Data? = Data(base64Encoded: urlRequest?.httpBody?.base64EncodedString() ?? "")
|
||||
let requestBodyString: String? = String(data: requestBody ?? Data(), encoding: .utf8)
|
||||
|
||||
expect(requestBodyString).to(equal("TestMessage"))
|
||||
}
|
||||
|
||||
it("throws an error if the body is not base64 encoded") {
|
||||
let request: Request<String, OpenGroupAPI.Endpoint> = Request(
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
body: "TestMessage"
|
||||
)
|
||||
|
||||
expect {
|
||||
try request.generateUrlRequest()
|
||||
}
|
||||
.to(throwError(HTTP.Error.parsingFailed))
|
||||
}
|
||||
}
|
||||
|
||||
context("with a byte body") {
|
||||
it("successfully encodes the body") {
|
||||
let request: Request<[UInt8], OpenGroupAPI.Endpoint> = Request(
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
body: [1, 2, 3]
|
||||
)
|
||||
|
||||
let urlRequest: URLRequest? = try? request.generateUrlRequest()
|
||||
|
||||
expect(urlRequest?.httpBody?.bytes).to(equal([1, 2, 3]))
|
||||
}
|
||||
}
|
||||
|
||||
context("with a JSON body") {
|
||||
it("successfully encodes the body") {
|
||||
let request: Request<TestType, OpenGroupAPI.Endpoint> = Request(
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
body: TestType(stringValue: "test")
|
||||
)
|
||||
|
||||
let urlRequest: URLRequest? = try? request.generateUrlRequest()
|
||||
let requestBody: TestType? = try? JSONDecoder().decode(
|
||||
TestType.self,
|
||||
from: urlRequest?.httpBody ?? Data()
|
||||
)
|
||||
|
||||
expect(requestBody).to(equal(TestType(stringValue: "test")))
|
||||
}
|
||||
|
||||
it("successfully encodes no body") {
|
||||
let request: Request<NoBody, OpenGroupAPI.Endpoint> = Request(
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
body: nil
|
||||
)
|
||||
|
||||
expect {
|
||||
try request.generateUrlRequest()
|
||||
}.toNot(throwError())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class BlindedIdMappingSpec: QuickSpec {
|
||||
// MARK: - Spec
|
||||
|
||||
override func spec() {
|
||||
describe("a BlindedIdMapping") {
|
||||
context("when initializing") {
|
||||
it("sets the values correctly") {
|
||||
let mapping: BlindedIdMapping = BlindedIdMapping(
|
||||
blindedId: "testBlindedId",
|
||||
sessionId: "testSessionId",
|
||||
serverPublicKey: "testPublicKey"
|
||||
)
|
||||
|
||||
expect(mapping.blindedId).to(equal("testBlindedId"))
|
||||
expect(mapping.sessionId).to(equal("testSessionId"))
|
||||
expect(mapping.serverPublicKey).to(equal("testPublicKey"))
|
||||
}
|
||||
}
|
||||
|
||||
context("when NSCoding") {
|
||||
// Note: Unit testing NSCoder is horrible so we won't do it properly - wait until we refactor it to Codable
|
||||
it("successfully encodes and decodes") {
|
||||
let mappingToEncode: BlindedIdMapping = BlindedIdMapping(
|
||||
blindedId: "testBlindedId",
|
||||
sessionId: "testSessionId",
|
||||
serverPublicKey: "testPublicKey"
|
||||
)
|
||||
let encodedData: Data = try! NSKeyedArchiver.archivedData(withRootObject: mappingToEncode, requiringSecureCoding: false)
|
||||
let mapping: BlindedIdMapping? = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(encodedData) as? BlindedIdMapping
|
||||
|
||||
expect(mapping).toNot(beNil())
|
||||
expect(mapping?.blindedId).to(equal("testBlindedId"))
|
||||
expect(mapping?.sessionId).to(equal("testSessionId"))
|
||||
expect(mapping?.serverPublicKey).to(equal("testPublicKey"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,385 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import SessionSnodeKit
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
import AVFoundation
|
||||
|
||||
class BatchRequestInfoSpec: QuickSpec {
|
||||
struct TestType: Codable, Equatable {
|
||||
let stringValue: String
|
||||
}
|
||||
|
||||
// MARK: - Spec
|
||||
|
||||
override func spec() {
|
||||
// MARK: - BatchSubRequest
|
||||
|
||||
describe("a BatchSubRequest") {
|
||||
var subRequest: OpenGroupAPI.BatchSubRequest!
|
||||
|
||||
context("when initializing") {
|
||||
it("sets the headers to nil if there aren't any") {
|
||||
subRequest = OpenGroupAPI.BatchSubRequest(
|
||||
request: Request<NoBody, OpenGroupAPI.Endpoint>(
|
||||
server: "testServer",
|
||||
endpoint: .batch
|
||||
)
|
||||
)
|
||||
|
||||
expect(subRequest.headers).to(beNil())
|
||||
}
|
||||
|
||||
it("converts the headers to HTTP headers") {
|
||||
subRequest = OpenGroupAPI.BatchSubRequest(
|
||||
request: Request<NoBody, OpenGroupAPI.Endpoint>(
|
||||
method: .get,
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
queryParameters: [:],
|
||||
headers: [.authorization: "testAuth"],
|
||||
body: nil
|
||||
)
|
||||
)
|
||||
|
||||
expect(subRequest.headers).to(equal(["Authorization": "testAuth"]))
|
||||
}
|
||||
}
|
||||
|
||||
context("when encoding") {
|
||||
it("successfully encodes a string body") {
|
||||
subRequest = OpenGroupAPI.BatchSubRequest(
|
||||
request: Request<String, OpenGroupAPI.Endpoint>(
|
||||
method: .get,
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
queryParameters: [:],
|
||||
headers: [:],
|
||||
body: "testBody"
|
||||
)
|
||||
)
|
||||
let subRequestData: Data = try! JSONEncoder().encode(subRequest)
|
||||
let subRequestString: String? = String(data: subRequestData, encoding: .utf8)
|
||||
|
||||
expect(subRequestString)
|
||||
.to(equal("{\"path\":\"\\/batch\",\"method\":\"GET\",\"b64\":\"testBody\"}"))
|
||||
}
|
||||
|
||||
it("successfully encodes a byte body") {
|
||||
subRequest = OpenGroupAPI.BatchSubRequest(
|
||||
request: Request<[UInt8], OpenGroupAPI.Endpoint>(
|
||||
method: .get,
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
queryParameters: [:],
|
||||
headers: [:],
|
||||
body: [1, 2, 3]
|
||||
)
|
||||
)
|
||||
let subRequestData: Data = try! JSONEncoder().encode(subRequest)
|
||||
let subRequestString: String? = String(data: subRequestData, encoding: .utf8)
|
||||
|
||||
expect(subRequestString)
|
||||
.to(equal("{\"path\":\"\\/batch\",\"method\":\"GET\",\"bytes\":[1,2,3]}"))
|
||||
}
|
||||
|
||||
it("successfully encodes a JSON body") {
|
||||
subRequest = OpenGroupAPI.BatchSubRequest(
|
||||
request: Request<TestType, OpenGroupAPI.Endpoint>(
|
||||
method: .get,
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
queryParameters: [:],
|
||||
headers: [:],
|
||||
body: TestType(stringValue: "testValue")
|
||||
)
|
||||
)
|
||||
let subRequestData: Data = try! JSONEncoder().encode(subRequest)
|
||||
let subRequestString: String? = String(data: subRequestData, encoding: .utf8)
|
||||
|
||||
expect(subRequestString)
|
||||
.to(equal("{\"path\":\"\\/batch\",\"method\":\"GET\",\"json\":{\"stringValue\":\"testValue\"}}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - BatchSubResponse<T>
|
||||
|
||||
describe("a BatchSubResponse<T>") {
|
||||
context("when decoding") {
|
||||
it("decodes correctly") {
|
||||
let jsonString: String = """
|
||||
{
|
||||
"code": 200,
|
||||
"headers": {
|
||||
"testKey": "testValue"
|
||||
},
|
||||
"body": {
|
||||
"stringValue": "testValue"
|
||||
}
|
||||
}
|
||||
"""
|
||||
let subResponse: OpenGroupAPI.BatchSubResponse<TestType>? = try? JSONDecoder().decode(
|
||||
OpenGroupAPI.BatchSubResponse<TestType>.self,
|
||||
from: jsonString.data(using: .utf8)!
|
||||
)
|
||||
|
||||
expect(subResponse).toNot(beNil())
|
||||
expect(subResponse?.body).toNot(beNil())
|
||||
}
|
||||
|
||||
it("decodes with invalid body data") {
|
||||
let jsonString: String = """
|
||||
{
|
||||
"code": 200,
|
||||
"headers": {
|
||||
"testKey": "testValue"
|
||||
},
|
||||
"body": "Hello!!!"
|
||||
}
|
||||
"""
|
||||
let subResponse: OpenGroupAPI.BatchSubResponse<TestType>? = try? JSONDecoder().decode(
|
||||
OpenGroupAPI.BatchSubResponse<TestType>.self,
|
||||
from: jsonString.data(using: .utf8)!
|
||||
)
|
||||
|
||||
expect(subResponse).toNot(beNil())
|
||||
}
|
||||
|
||||
it("flags invalid body data as invalid") {
|
||||
let jsonString: String = """
|
||||
{
|
||||
"code": 200,
|
||||
"headers": {
|
||||
"testKey": "testValue"
|
||||
},
|
||||
"body": "Hello!!!"
|
||||
}
|
||||
"""
|
||||
let subResponse: OpenGroupAPI.BatchSubResponse<TestType>? = try? JSONDecoder().decode(
|
||||
OpenGroupAPI.BatchSubResponse<TestType>.self,
|
||||
from: jsonString.data(using: .utf8)!
|
||||
)
|
||||
|
||||
expect(subResponse).toNot(beNil())
|
||||
expect(subResponse?.body).to(beNil())
|
||||
expect(subResponse?.failedToParseBody).to(beTrue())
|
||||
}
|
||||
|
||||
it("does not flag a missing or invalid optional body as invalid") {
|
||||
let jsonString: String = """
|
||||
{
|
||||
"code": 200,
|
||||
"headers": {
|
||||
"testKey": "testValue"
|
||||
},
|
||||
}
|
||||
"""
|
||||
let subResponse: OpenGroupAPI.BatchSubResponse<TestType?>? = try? JSONDecoder().decode(
|
||||
OpenGroupAPI.BatchSubResponse<TestType?>.self,
|
||||
from: jsonString.data(using: .utf8)!
|
||||
)
|
||||
|
||||
expect(subResponse).toNot(beNil())
|
||||
expect(subResponse?.body).to(beNil())
|
||||
expect(subResponse?.failedToParseBody).to(beFalse())
|
||||
}
|
||||
|
||||
it("does not flag a NoResponse body as invalid") {
|
||||
let jsonString: String = """
|
||||
{
|
||||
"code": 200,
|
||||
"headers": {
|
||||
"testKey": "testValue"
|
||||
},
|
||||
}
|
||||
"""
|
||||
let subResponse: OpenGroupAPI.BatchSubResponse<NoResponse>? = try? JSONDecoder().decode(
|
||||
OpenGroupAPI.BatchSubResponse<NoResponse>.self,
|
||||
from: jsonString.data(using: .utf8)!
|
||||
)
|
||||
|
||||
expect(subResponse).toNot(beNil())
|
||||
expect(subResponse?.body).to(beNil())
|
||||
expect(subResponse?.failedToParseBody).to(beFalse())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - BatchRequestInfo<T, R>
|
||||
|
||||
describe("a BatchRequestInfo<T, R>") {
|
||||
var request: Request<TestType, OpenGroupAPI.Endpoint>!
|
||||
|
||||
beforeEach {
|
||||
request = Request(
|
||||
method: .get,
|
||||
server: "testServer",
|
||||
endpoint: .batch,
|
||||
queryParameters: [:],
|
||||
headers: [:],
|
||||
body: TestType(stringValue: "testValue")
|
||||
)
|
||||
}
|
||||
|
||||
it("initializes correctly when given a request") {
|
||||
let requestInfo: OpenGroupAPI.BatchRequestInfo<TestType> = OpenGroupAPI.BatchRequestInfo(
|
||||
request: request
|
||||
)
|
||||
|
||||
expect(requestInfo.request).to(equal(request))
|
||||
expect(requestInfo.responseType == OpenGroupAPI.BatchSubResponse<NoResponse>.self).to(beTrue())
|
||||
}
|
||||
|
||||
it("initializes correctly when given a request and a response type") {
|
||||
let requestInfo: OpenGroupAPI.BatchRequestInfo<TestType> = OpenGroupAPI.BatchRequestInfo(
|
||||
request: request,
|
||||
responseType: TestType.self
|
||||
)
|
||||
|
||||
expect(requestInfo.request).to(equal(request))
|
||||
expect(requestInfo.responseType == OpenGroupAPI.BatchSubResponse<TestType>.self).to(beTrue())
|
||||
}
|
||||
|
||||
it("exposes the endpoint correctly") {
|
||||
let requestInfo: OpenGroupAPI.BatchRequestInfo<TestType> = OpenGroupAPI.BatchRequestInfo(
|
||||
request: request
|
||||
)
|
||||
|
||||
expect(requestInfo.endpoint.path).to(equal(request.endpoint.path))
|
||||
}
|
||||
|
||||
it("generates a sub request correctly") {
|
||||
let requestInfo: OpenGroupAPI.BatchRequestInfo<TestType> = OpenGroupAPI.BatchRequestInfo(
|
||||
request: request
|
||||
)
|
||||
let subRequest: OpenGroupAPI.BatchSubRequest = requestInfo.toSubRequest()
|
||||
|
||||
expect(subRequest.method).to(equal(request.method))
|
||||
expect(subRequest.path).to(equal(request.urlPathAndParamsString))
|
||||
expect(subRequest.headers).to(beNil())
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Convenience
|
||||
// MARK: --Decodable
|
||||
|
||||
describe("a Decodable") {
|
||||
it("decodes correctly") {
|
||||
let jsonData: Data = "{\"stringValue\":\"testValue\"}".data(using: .utf8)!
|
||||
let result: TestType? = try? TestType.decoded(from: jsonData)
|
||||
|
||||
expect(result).to(equal(TestType(stringValue: "testValue")))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - --Promise
|
||||
|
||||
describe("an (OnionRequestResponseInfoType, Data?) Promise") {
|
||||
var responseInfo: OnionRequestResponseInfoType!
|
||||
var capabilities: OpenGroupAPI.Capabilities!
|
||||
var pinnedMessage: OpenGroupAPI.PinnedMessage!
|
||||
var data: Data!
|
||||
|
||||
beforeEach {
|
||||
responseInfo = OnionRequestAPI.ResponseInfo(code: 200, headers: [:])
|
||||
capabilities = OpenGroupAPI.Capabilities(capabilities: [], missing: nil)
|
||||
pinnedMessage = OpenGroupAPI.PinnedMessage(id: 1, pinnedAt: 123, pinnedBy: "test")
|
||||
data = """
|
||||
[\([
|
||||
try! JSONEncoder().encode(
|
||||
OpenGroupAPI.BatchSubResponse(
|
||||
code: 200,
|
||||
headers: [:],
|
||||
body: capabilities,
|
||||
failedToParseBody: false
|
||||
)
|
||||
),
|
||||
try! JSONEncoder().encode(
|
||||
OpenGroupAPI.BatchSubResponse(
|
||||
code: 200,
|
||||
headers: [:],
|
||||
body: pinnedMessage,
|
||||
failedToParseBody: false
|
||||
)
|
||||
)
|
||||
]
|
||||
.map { String(data: $0, encoding: .utf8)! }
|
||||
.joined(separator: ","))]
|
||||
""".data(using: .utf8)!
|
||||
}
|
||||
|
||||
it("decodes valid data correctly") {
|
||||
let result = Promise.value((responseInfo, data))
|
||||
.decoded(as: [
|
||||
OpenGroupAPI.BatchSubResponse<OpenGroupAPI.Capabilities>.self,
|
||||
OpenGroupAPI.BatchSubResponse<OpenGroupAPI.PinnedMessage>.self
|
||||
])
|
||||
|
||||
expect(result.value).toNot(beNil())
|
||||
expect((result.value?[0].1 as? OpenGroupAPI.BatchSubResponse<OpenGroupAPI.Capabilities>)?.body)
|
||||
.to(equal(capabilities))
|
||||
expect((result.value?[1].1 as? OpenGroupAPI.BatchSubResponse<OpenGroupAPI.PinnedMessage>)?.body)
|
||||
.to(equal(pinnedMessage))
|
||||
}
|
||||
|
||||
it("fails if there is no data") {
|
||||
let result = Promise.value((responseInfo, nil)).decoded(as: [])
|
||||
|
||||
expect(result.error?.localizedDescription).to(equal(HTTP.Error.parsingFailed.localizedDescription))
|
||||
}
|
||||
|
||||
it("fails if the data is not JSON") {
|
||||
let result = Promise.value((responseInfo, Data([1, 2, 3]))).decoded(as: [])
|
||||
|
||||
expect(result.error?.localizedDescription).to(equal(HTTP.Error.parsingFailed.localizedDescription))
|
||||
}
|
||||
|
||||
it("fails if the data is not a JSON array") {
|
||||
let result = Promise.value((responseInfo, "{}".data(using: .utf8))).decoded(as: [])
|
||||
|
||||
expect(result.error?.localizedDescription).to(equal(HTTP.Error.parsingFailed.localizedDescription))
|
||||
}
|
||||
|
||||
it("fails if the JSON array does not have the same number of items as the expected types") {
|
||||
let result = Promise.value((responseInfo, data))
|
||||
.decoded(as: [
|
||||
OpenGroupAPI.BatchSubResponse<OpenGroupAPI.Capabilities>.self,
|
||||
OpenGroupAPI.BatchSubResponse<OpenGroupAPI.PinnedMessage>.self,
|
||||
OpenGroupAPI.BatchSubResponse<OpenGroupAPI.PinnedMessage>.self
|
||||
])
|
||||
|
||||
expect(result.error?.localizedDescription).to(equal(HTTP.Error.parsingFailed.localizedDescription))
|
||||
}
|
||||
|
||||
it("fails if one of the JSON array values fails to decode") {
|
||||
data = """
|
||||
[\([
|
||||
try! JSONEncoder().encode(
|
||||
OpenGroupAPI.BatchSubResponse(
|
||||
code: 200,
|
||||
headers: [:],
|
||||
body: capabilities,
|
||||
failedToParseBody: false
|
||||
)
|
||||
)
|
||||
]
|
||||
.map { String(data: $0, encoding: .utf8)! }
|
||||
.joined(separator: ",")),{"test": "test"}]
|
||||
""".data(using: .utf8)!
|
||||
let result = Promise.value((responseInfo, data))
|
||||
.decoded(as: [
|
||||
OpenGroupAPI.BatchSubResponse<OpenGroupAPI.Capabilities>.self,
|
||||
OpenGroupAPI.BatchSubResponse<OpenGroupAPI.PinnedMessage>.self
|
||||
])
|
||||
|
||||
expect(result.error?.localizedDescription).to(equal(HTTP.Error.parsingFailed.localizedDescription))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ class OpenGroupSpec: QuickSpec {
|
|||
}
|
||||
|
||||
context("when NSCoding") {
|
||||
// Note: Unit testing NSCoder is horrible so we won't do it - wait until we refactor it to Codable
|
||||
// Note: Unit testing NSCoder is horrible so we won't do it properly - wait until we refactor it to Codable
|
||||
it("successfully encodes and decodes") {
|
||||
let openGroupToEncode: OpenGroup = OpenGroup(
|
||||
server: "server",
|
||||
|
|
|
@ -24,7 +24,7 @@ class ServerSpec: QuickSpec {
|
|||
}
|
||||
|
||||
context("when NSCoding") {
|
||||
// Note: Unit testing NSCoder is horrible so we won't do it - wait until we refactor it to Codable
|
||||
// Note: Unit testing NSCoder is horrible so we won't do it properly - wait until we refactor it to Codable
|
||||
it("successfully encodes and decodes") {
|
||||
let serverToEncode: OpenGroupAPI.Server = OpenGroupAPI.Server(
|
||||
name: "test",
|
||||
|
|
|
@ -43,9 +43,9 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
onionApi: TestOnionRequestAPI.self,
|
||||
storage: mockStorage,
|
||||
sodium: mockSodium,
|
||||
aeadXChaCha20Poly1305Ietf: mockAeadXChaCha20Poly1305Ietf,
|
||||
sign: mockSign,
|
||||
genericHash: mockGenericHash,
|
||||
sign: mockSign,
|
||||
aeadXChaCha20Poly1305Ietf: mockAeadXChaCha20Poly1305Ietf,
|
||||
ed25519: mockEd25519,
|
||||
nonceGenerator16: mockNonce16Generator,
|
||||
nonceGenerator24: mockNonce24Generator,
|
||||
|
@ -229,7 +229,7 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
expect(requestData?.urlString).to(equal("testServer/batch"))
|
||||
expect(requestData?.httpMethod).to(equal("POST"))
|
||||
expect(requestData?.server).to(equal("testServer"))
|
||||
expect(requestData?.publicKey).to(equal("7aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d"))
|
||||
expect(requestData?.publicKey).to(equal("88672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"))
|
||||
}
|
||||
|
||||
it("retrieves recent messages if there was no last message") {
|
||||
|
@ -2663,10 +2663,10 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
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?.publicKey).to(equal("88672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"))
|
||||
expect(requestData?.headers).to(haveCount(4))
|
||||
expect(requestData?.headers[Header.sogsPubKey.rawValue])
|
||||
.to(equal("007aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d"))
|
||||
.to(equal("0088672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"))
|
||||
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("TestSignature".bytes.toBase64()))
|
||||
|
@ -2720,9 +2720,9 @@ class OpenGroupAPISpec: QuickSpec {
|
|||
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?.publicKey).to(equal("88672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"))
|
||||
expect(requestData?.headers).to(haveCount(4))
|
||||
expect(requestData?.headers[Header.sogsPubKey.rawValue]).to(equal("157aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d"))
|
||||
expect(requestData?.headers[Header.sogsPubKey.rawValue]).to(equal("1588672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"))
|
||||
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()))
|
||||
|
|
|
@ -114,9 +114,9 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
identityManager: mockIdentityManager,
|
||||
storage: mockStorage,
|
||||
sodium: mockSodium,
|
||||
aeadXChaCha20Poly1305Ietf: mockAeadXChaCha20Poly1305Ietf,
|
||||
sign: mockSign,
|
||||
genericHash: mockGenericHash,
|
||||
sign: mockSign,
|
||||
aeadXChaCha20Poly1305Ietf: mockAeadXChaCha20Poly1305Ietf,
|
||||
ed25519: MockEd25519(),
|
||||
nonceGenerator16: mockNonce16Generator,
|
||||
nonceGenerator24: mockNonce24Generator,
|
||||
|
@ -1600,7 +1600,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
|
||||
expect(didComplete).toEventually(beTrue(), timeout: .milliseconds(50))
|
||||
expect(mockStorage)
|
||||
.to(call(matchingParameters: true) {
|
||||
.toEventually(call(matchingParameters: true) {
|
||||
$0.setOpenGroup(
|
||||
OpenGroup(
|
||||
server: "testServer",
|
||||
|
@ -1620,6 +1620,11 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
beNil(),
|
||||
timeout: .milliseconds(50)
|
||||
)
|
||||
expect(mockOGMCache)
|
||||
.toEventually(
|
||||
call(.exactly(times: 1)) { $0.groupImagePromises },
|
||||
timeout: .milliseconds(50)
|
||||
)
|
||||
expect(testGroupThread.numSaveCalls)
|
||||
.toEventually(
|
||||
equal(2), // Call to save the open group and then to save the image
|
||||
|
@ -2121,7 +2126,7 @@ class OpenGroupManagerSpec: QuickSpec {
|
|||
}
|
||||
.thenReturn([])
|
||||
mockSodium
|
||||
.when { $0.generateBlindingFactor(serverPublicKey: any()) }
|
||||
.when { $0.generateBlindingFactor(serverPublicKey: any(), genericHash: mockGenericHash) }
|
||||
.thenReturn([])
|
||||
mockAeadXChaCha20Poly1305Ietf
|
||||
.when {
|
||||
|
|
|
@ -0,0 +1,500 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Sodium
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class MessageReceiverDecryptionSpec: QuickSpec {
|
||||
// MARK: - Spec
|
||||
|
||||
override func spec() {
|
||||
var mockStorage: MockStorage!
|
||||
var mockSodium: MockSodium!
|
||||
var mockBox: MockBox!
|
||||
var mockGenericHash: MockGenericHash!
|
||||
var mockSign: MockSign!
|
||||
var mockAeadXChaCha: MockAeadXChaCha20Poly1305Ietf!
|
||||
var mockNonce24Generator: MockNonce24Generator!
|
||||
var dependencies: Dependencies!
|
||||
|
||||
describe("a MessageReceiver") {
|
||||
beforeEach {
|
||||
mockStorage = MockStorage()
|
||||
mockSodium = MockSodium()
|
||||
mockBox = MockBox()
|
||||
mockGenericHash = MockGenericHash()
|
||||
mockSign = MockSign()
|
||||
mockAeadXChaCha = MockAeadXChaCha20Poly1305Ietf()
|
||||
mockNonce24Generator = MockNonce24Generator()
|
||||
|
||||
mockAeadXChaCha
|
||||
.when { $0.encrypt(message: anyArray(), secretKey: anyArray(), nonce: anyArray()) }
|
||||
.thenReturn(nil)
|
||||
|
||||
dependencies = Dependencies(
|
||||
storage: mockStorage,
|
||||
sodium: mockSodium,
|
||||
box: mockBox,
|
||||
genericHash: mockGenericHash,
|
||||
sign: mockSign,
|
||||
aeadXChaCha20Poly1305Ietf: mockAeadXChaCha,
|
||||
nonceGenerator24: mockNonce24Generator
|
||||
)
|
||||
|
||||
mockStorage
|
||||
.when { $0.getUserED25519KeyPair() }
|
||||
.thenReturn(
|
||||
Box.KeyPair(
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
)
|
||||
)
|
||||
mockBox
|
||||
.when {
|
||||
$0.open(
|
||||
anonymousCipherText: anyArray(),
|
||||
recipientPublicKey: anyArray(),
|
||||
recipientSecretKey: anyArray()
|
||||
)
|
||||
}
|
||||
.thenReturn([UInt8](repeating: 0, count: 100))
|
||||
mockSodium
|
||||
.when { $0.blindedKeyPair(serverPublicKey: any(), edKeyPair: any(), genericHash: mockGenericHash) }
|
||||
.thenReturn(
|
||||
Box.KeyPair(
|
||||
publicKey: Data(hex: TestConstants.blindedPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
)
|
||||
)
|
||||
mockSodium
|
||||
.when {
|
||||
$0.sharedBlindedEncryptionKey(
|
||||
secretKey: anyArray(),
|
||||
otherBlindedPublicKey: anyArray(),
|
||||
fromBlindedPublicKey: anyArray(),
|
||||
toBlindedPublicKey: anyArray(),
|
||||
genericHash: mockGenericHash
|
||||
)
|
||||
}
|
||||
.thenReturn([])
|
||||
mockSodium
|
||||
.when { $0.generateBlindingFactor(serverPublicKey: any(), genericHash: mockGenericHash) }
|
||||
.thenReturn([])
|
||||
mockSodium
|
||||
.when { $0.combineKeys(lhsKeyBytes: anyArray(), rhsKeyBytes: anyArray()) }
|
||||
.thenReturn(Data(hex: TestConstants.blindedPublicKey).bytes)
|
||||
mockSign
|
||||
.when { $0.toX25519(ed25519PublicKey: anyArray()) }
|
||||
.thenReturn(Data(hex: TestConstants.publicKey).bytes)
|
||||
mockSign
|
||||
.when { $0.verify(message: anyArray(), publicKey: anyArray(), signature: anyArray()) }
|
||||
.thenReturn(true)
|
||||
mockAeadXChaCha
|
||||
.when { $0.decrypt(authenticatedCipherText: anyArray(), secretKey: anyArray(), nonce: anyArray()) }
|
||||
.thenReturn("TestMessage".data(using: .utf8)!.bytes + [UInt8](repeating: 0, count: 32))
|
||||
mockNonce24Generator
|
||||
.when { $0.nonce() }
|
||||
.thenReturn(Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!.bytes)
|
||||
}
|
||||
|
||||
context("when decrypting with the session protocol") {
|
||||
it("successfully decrypts a message") {
|
||||
let result = try? MessageReceiver.decryptWithSessionProtocol(
|
||||
ciphertext: Data(
|
||||
base64Encoded: "SRP0eBUWh4ez6ppWjUs5/Wph5fhnPRgB5zsWWnTz+FBAw/YI3oS2pDpIfyetMTbU" +
|
||||
"sFMhE5G4PbRtQFey1hsxLl221Qivc3ayaX2Mm/X89Dl8e45BC+Lb/KU9EdesxIK4pVgYXs9XrMtX3v8" +
|
||||
"dt0eBaXneOBfr7qB8pHwwMZjtkOu1ED07T9nszgbWabBphUfWXe2U9K3PTRisSCI="
|
||||
)!,
|
||||
using: try! ECKeyPair(
|
||||
publicKeyData: Data.data(fromHex: TestConstants.publicKey)!,
|
||||
privateKeyData: Data.data(fromHex: TestConstants.privateKey)!
|
||||
),
|
||||
dependencies: Dependencies()
|
||||
)
|
||||
|
||||
expect(String(data: (result?.plaintext ?? Data()), encoding: .utf8)).to(equal("TestMessage"))
|
||||
expect(result?.senderX25519PublicKey)
|
||||
.to(equal("0588672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"))
|
||||
}
|
||||
|
||||
it("throws an error if it cannot open the message") {
|
||||
mockBox
|
||||
.when {
|
||||
$0.open(
|
||||
anonymousCipherText: anyArray(),
|
||||
recipientPublicKey: anyArray(),
|
||||
recipientSecretKey: anyArray()
|
||||
)
|
||||
}
|
||||
.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageReceiver.decryptWithSessionProtocol(
|
||||
ciphertext: "TestMessage".data(using: .utf8)!,
|
||||
using: try! ECKeyPair(
|
||||
publicKeyData: Data.data(fromHex: TestConstants.publicKey)!,
|
||||
privateKeyData: Data.data(fromHex: TestConstants.privateKey)!
|
||||
),
|
||||
dependencies: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageReceiver.Error.decryptionFailed))
|
||||
}
|
||||
|
||||
it("throws an error if the open message is too short") {
|
||||
mockBox
|
||||
.when {
|
||||
$0.open(
|
||||
anonymousCipherText: anyArray(),
|
||||
recipientPublicKey: anyArray(),
|
||||
recipientSecretKey: anyArray()
|
||||
)
|
||||
}
|
||||
.thenReturn([1, 2, 3])
|
||||
|
||||
expect {
|
||||
try MessageReceiver.decryptWithSessionProtocol(
|
||||
ciphertext: "TestMessage".data(using: .utf8)!,
|
||||
using: try! ECKeyPair(
|
||||
publicKeyData: Data.data(fromHex: TestConstants.publicKey)!,
|
||||
privateKeyData: Data.data(fromHex: TestConstants.privateKey)!
|
||||
),
|
||||
dependencies: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageReceiver.Error.decryptionFailed))
|
||||
}
|
||||
|
||||
it("throws an error if it cannot verify the message") {
|
||||
mockSign
|
||||
.when { $0.verify(message: anyArray(), publicKey: anyArray(), signature: anyArray()) }
|
||||
.thenReturn(false)
|
||||
|
||||
expect {
|
||||
try MessageReceiver.decryptWithSessionProtocol(
|
||||
ciphertext: "TestMessage".data(using: .utf8)!,
|
||||
using: try! ECKeyPair(
|
||||
publicKeyData: Data.data(fromHex: TestConstants.publicKey)!,
|
||||
privateKeyData: Data.data(fromHex: TestConstants.privateKey)!
|
||||
),
|
||||
dependencies: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageReceiver.Error.invalidSignature))
|
||||
}
|
||||
|
||||
it("throws an error if it cannot get the senders x25519 public key") {
|
||||
mockSign.when { $0.toX25519(ed25519PublicKey: anyArray()) }.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageReceiver.decryptWithSessionProtocol(
|
||||
ciphertext: "TestMessage".data(using: .utf8)!,
|
||||
using: try! ECKeyPair(
|
||||
publicKeyData: Data.data(fromHex: TestConstants.publicKey)!,
|
||||
privateKeyData: Data.data(fromHex: TestConstants.privateKey)!
|
||||
),
|
||||
dependencies: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageReceiver.Error.decryptionFailed))
|
||||
}
|
||||
}
|
||||
|
||||
context("when decrypting with the blinded session protocol") {
|
||||
it("successfully decrypts a message") {
|
||||
let result = try? MessageReceiver.decryptWithSessionBlindingProtocol(
|
||||
data: Data(
|
||||
hex: "00db16b6687382811d69875a5376f66acad9c49fe5e26bcf770c7e6e9c230299" +
|
||||
"f61b315299dd1fa700dd7f34305c0465af9e64dc791d7f4123f1eeafa5b4d48b3ade4" +
|
||||
"f4b2a2764762e5a2c7900f254bd91633b43"
|
||||
),
|
||||
isOutgoing: true,
|
||||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
),
|
||||
using: Dependencies()
|
||||
)
|
||||
|
||||
expect(String(data: (result?.plaintext ?? Data()), encoding: .utf8)).to(equal("TestMessage"))
|
||||
expect(result?.senderX25519PublicKey)
|
||||
.to(equal("0588672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"))
|
||||
}
|
||||
|
||||
it("successfully decrypts a mocked incoming message") {
|
||||
let result = try? MessageReceiver.decryptWithSessionBlindingProtocol(
|
||||
data: (
|
||||
Data([0]) +
|
||||
"TestMessage".data(using: .utf8)! +
|
||||
Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!
|
||||
),
|
||||
isOutgoing: false,
|
||||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
expect(String(data: (result?.plaintext ?? Data()), encoding: .utf8)).to(equal("TestMessage"))
|
||||
expect(result?.senderX25519PublicKey)
|
||||
.to(equal("0588672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"))
|
||||
}
|
||||
|
||||
it("throws an error if the data is too short") {
|
||||
expect {
|
||||
try MessageReceiver.decryptWithSessionBlindingProtocol(
|
||||
data: Data([1, 2, 3]),
|
||||
isOutgoing: true,
|
||||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageReceiver.Error.decryptionFailed))
|
||||
}
|
||||
|
||||
it("throws an error if it cannot get the blinded keyPair") {
|
||||
mockSodium
|
||||
.when { $0.blindedKeyPair(serverPublicKey: any(), edKeyPair: any(), genericHash: mockGenericHash) }
|
||||
.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageReceiver.decryptWithSessionBlindingProtocol(
|
||||
data: (
|
||||
Data([0]) +
|
||||
"TestMessage".data(using: .utf8)! +
|
||||
Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!
|
||||
),
|
||||
isOutgoing: true,
|
||||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageReceiver.Error.decryptionFailed))
|
||||
}
|
||||
|
||||
it("throws an error if it cannot get the decryption key") {
|
||||
mockSodium
|
||||
.when {
|
||||
$0.sharedBlindedEncryptionKey(
|
||||
secretKey: anyArray(),
|
||||
otherBlindedPublicKey: anyArray(),
|
||||
fromBlindedPublicKey: anyArray(),
|
||||
toBlindedPublicKey: anyArray(),
|
||||
genericHash: mockGenericHash
|
||||
)
|
||||
}
|
||||
.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageReceiver.decryptWithSessionBlindingProtocol(
|
||||
data: (
|
||||
Data([0]) +
|
||||
"TestMessage".data(using: .utf8)! +
|
||||
Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!
|
||||
),
|
||||
isOutgoing: true,
|
||||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageReceiver.Error.decryptionFailed))
|
||||
}
|
||||
|
||||
it("throws an error if the data version is not 0") {
|
||||
expect {
|
||||
try MessageReceiver.decryptWithSessionBlindingProtocol(
|
||||
data: (
|
||||
Data([1]) +
|
||||
"TestMessage".data(using: .utf8)! +
|
||||
Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!
|
||||
),
|
||||
isOutgoing: true,
|
||||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageReceiver.Error.decryptionFailed))
|
||||
}
|
||||
|
||||
it("throws an error if it cannot decrypt the data") {
|
||||
mockAeadXChaCha
|
||||
.when { $0.decrypt(authenticatedCipherText: anyArray(), secretKey: anyArray(), nonce: anyArray()) }
|
||||
.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageReceiver.decryptWithSessionBlindingProtocol(
|
||||
data: (
|
||||
Data([0]) +
|
||||
"TestMessage".data(using: .utf8)! +
|
||||
Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!
|
||||
),
|
||||
isOutgoing: true,
|
||||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageReceiver.Error.decryptionFailed))
|
||||
}
|
||||
|
||||
it("throws an error if the inner bytes are too short") {
|
||||
mockAeadXChaCha
|
||||
.when { $0.decrypt(authenticatedCipherText: anyArray(), secretKey: anyArray(), nonce: anyArray()) }
|
||||
.thenReturn([1, 2, 3])
|
||||
|
||||
expect {
|
||||
try MessageReceiver.decryptWithSessionBlindingProtocol(
|
||||
data: (
|
||||
Data([0]) +
|
||||
"TestMessage".data(using: .utf8)! +
|
||||
Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!
|
||||
),
|
||||
isOutgoing: true,
|
||||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageReceiver.Error.decryptionFailed))
|
||||
}
|
||||
|
||||
it("throws an error if it cannot generate the blinding factor") {
|
||||
mockSodium
|
||||
.when { $0.generateBlindingFactor(serverPublicKey: any(), genericHash: mockGenericHash) }
|
||||
.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageReceiver.decryptWithSessionBlindingProtocol(
|
||||
data: (
|
||||
Data([0]) +
|
||||
"TestMessage".data(using: .utf8)! +
|
||||
Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!
|
||||
),
|
||||
isOutgoing: true,
|
||||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageReceiver.Error.invalidSignature))
|
||||
}
|
||||
|
||||
it("throws an error if it cannot generate the combined key") {
|
||||
mockSodium
|
||||
.when { $0.combineKeys(lhsKeyBytes: anyArray(), rhsKeyBytes: anyArray()) }
|
||||
.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageReceiver.decryptWithSessionBlindingProtocol(
|
||||
data: (
|
||||
Data([0]) +
|
||||
"TestMessage".data(using: .utf8)! +
|
||||
Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!
|
||||
),
|
||||
isOutgoing: true,
|
||||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageReceiver.Error.invalidSignature))
|
||||
}
|
||||
|
||||
it("throws an error if the combined key does not match kA") {
|
||||
mockSodium
|
||||
.when { $0.combineKeys(lhsKeyBytes: anyArray(), rhsKeyBytes: anyArray()) }
|
||||
.thenReturn(Data(hex: TestConstants.publicKey).bytes)
|
||||
|
||||
expect {
|
||||
try MessageReceiver.decryptWithSessionBlindingProtocol(
|
||||
data: (
|
||||
Data([0]) +
|
||||
"TestMessage".data(using: .utf8)! +
|
||||
Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!
|
||||
),
|
||||
isOutgoing: true,
|
||||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageReceiver.Error.invalidSignature))
|
||||
}
|
||||
|
||||
it("throws an error if it cannot get the senders x25519 public key") {
|
||||
mockSign
|
||||
.when { $0.toX25519(ed25519PublicKey: anyArray()) }
|
||||
.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageReceiver.decryptWithSessionBlindingProtocol(
|
||||
data: (
|
||||
Data([0]) +
|
||||
"TestMessage".data(using: .utf8)! +
|
||||
Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!
|
||||
),
|
||||
isOutgoing: true,
|
||||
otherBlindedPublicKey: "15\(TestConstants.blindedPublicKey)",
|
||||
with: TestConstants.serverPublicKey,
|
||||
userEd25519KeyPair: Box.KeyPair(
|
||||
publicKey: Data.data(fromHex: TestConstants.edPublicKey)!.bytes,
|
||||
secretKey: Data.data(fromHex: TestConstants.edSecretKey)!.bytes
|
||||
),
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageReceiver.Error.decryptionFailed))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,272 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Sodium
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class MessageSenderEncryptionSpec: QuickSpec {
|
||||
// MARK: - Spec
|
||||
|
||||
override func spec() {
|
||||
var mockStorage: MockStorage!
|
||||
var mockBox: MockBox!
|
||||
var mockSign: MockSign!
|
||||
var mockNonce24Generator: MockNonce24Generator!
|
||||
var dependencies: Dependencies!
|
||||
|
||||
describe("a MessageSender") {
|
||||
beforeEach {
|
||||
mockStorage = MockStorage()
|
||||
mockBox = MockBox()
|
||||
mockSign = MockSign()
|
||||
mockNonce24Generator = MockNonce24Generator()
|
||||
|
||||
dependencies = Dependencies(
|
||||
storage: mockStorage,
|
||||
box: mockBox,
|
||||
sign: mockSign,
|
||||
nonceGenerator24: mockNonce24Generator
|
||||
)
|
||||
|
||||
mockStorage.when { $0.getUserED25519KeyPair() }
|
||||
.thenReturn(
|
||||
Box.KeyPair(
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
)
|
||||
)
|
||||
mockNonce24Generator
|
||||
.when { $0.nonce() }
|
||||
.thenReturn(Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!.bytes)
|
||||
}
|
||||
|
||||
context("when encrypting with the session protocol") {
|
||||
beforeEach {
|
||||
mockBox.when { $0.seal(message: anyArray(), recipientPublicKey: anyArray()) }.thenReturn([1, 2, 3])
|
||||
mockSign.when { $0.signature(message: anyArray(), secretKey: anyArray()) }.thenReturn([])
|
||||
}
|
||||
|
||||
it("can encrypt correctly") {
|
||||
let result = try? MessageSender.encryptWithSessionProtocol(
|
||||
"TestMessage".data(using: .utf8)!,
|
||||
for: "05\(TestConstants.publicKey)",
|
||||
using: Dependencies(storage: mockStorage)
|
||||
)
|
||||
|
||||
// Note: A Nonce is used for this so we can't compare the exact value when not mocked
|
||||
expect(result).toNot(beNil())
|
||||
expect(result?.count).to(equal(155))
|
||||
}
|
||||
|
||||
it("returns the correct value when mocked") {
|
||||
let result = try? MessageSender.encryptWithSessionProtocol(
|
||||
"TestMessage".data(using: .utf8)!,
|
||||
for: "05\(TestConstants.publicKey)",
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
expect(result?.bytes).to(equal([1, 2, 3]))
|
||||
}
|
||||
|
||||
it("throws an error if there is no ed25519 keyPair") {
|
||||
mockStorage.when { $0.getUserED25519KeyPair() }.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageSender.encryptWithSessionProtocol(
|
||||
"TestMessage".data(using: .utf8)!,
|
||||
for: "05\(TestConstants.publicKey)",
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageSender.Error.noUserED25519KeyPair))
|
||||
}
|
||||
|
||||
it("throws an error if the signature generation fails") {
|
||||
mockSign.when { $0.signature(message: anyArray(), secretKey: anyArray()) }.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageSender.encryptWithSessionProtocol(
|
||||
"TestMessage".data(using: .utf8)!,
|
||||
for: "05\(TestConstants.publicKey)",
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageSender.Error.signingFailed))
|
||||
}
|
||||
|
||||
it("throws an error if the encryption fails") {
|
||||
mockBox.when { $0.seal(message: anyArray(), recipientPublicKey: anyArray()) }.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageSender.encryptWithSessionProtocol(
|
||||
"TestMessage".data(using: .utf8)!,
|
||||
for: "05\(TestConstants.publicKey)",
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageSender.Error.encryptionFailed))
|
||||
}
|
||||
}
|
||||
|
||||
context("when encrypting with the blinded session protocol") {
|
||||
it("successfully encrypts") {
|
||||
let result = try? MessageSender.encryptWithSessionBlindingProtocol(
|
||||
"TestMessage".data(using: .utf8)!,
|
||||
for: "15\(TestConstants.blindedPublicKey)",
|
||||
openGroupPublicKey: TestConstants.serverPublicKey,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
expect(result?.toHexString())
|
||||
.to(equal(
|
||||
"00db16b6687382811d69875a5376f66acad9c49fe5e26bcf770c7e6e9c230299" +
|
||||
"f61b315299dd1fa700dd7f34305c0465af9e64dc791d7f4123f1eeafa5b4d48b" +
|
||||
"3ade4f4b2a2764762e5a2c7900f254bd91633b43"
|
||||
))
|
||||
}
|
||||
|
||||
it("includes a version at the start of the encrypted value") {
|
||||
let result = try? MessageSender.encryptWithSessionBlindingProtocol(
|
||||
"TestMessage".data(using: .utf8)!,
|
||||
for: "15\(TestConstants.blindedPublicKey)",
|
||||
openGroupPublicKey: TestConstants.serverPublicKey,
|
||||
using: dependencies
|
||||
)
|
||||
|
||||
expect(result?.toHexString().prefix(2)).to(equal("00"))
|
||||
}
|
||||
|
||||
it("includes the nonce at the end of the encrypted value") {
|
||||
let maybeResult = try? MessageSender.encryptWithSessionBlindingProtocol(
|
||||
"TestMessage".data(using: .utf8)!,
|
||||
for: "15\(TestConstants.blindedPublicKey)",
|
||||
openGroupPublicKey: TestConstants.serverPublicKey,
|
||||
using: dependencies
|
||||
)
|
||||
let result: [UInt8] = (maybeResult?.bytes ?? [])
|
||||
let nonceBytes: [UInt8] = Array(result[max(0, (result.count - 24))..<result.count])
|
||||
|
||||
expect(Data(nonceBytes).base64EncodedString())
|
||||
.to(equal("pbTUizreT0sqJ2R2LloseQDyVL2RYztD"))
|
||||
}
|
||||
|
||||
it("throws an error if the recipient isn't a blinded id") {
|
||||
expect {
|
||||
try MessageSender.encryptWithSessionBlindingProtocol(
|
||||
"TestMessage".data(using: .utf8)!,
|
||||
for: "05\(TestConstants.publicKey)",
|
||||
openGroupPublicKey: TestConstants.serverPublicKey,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageSender.Error.signingFailed))
|
||||
}
|
||||
|
||||
it("throws an error if there is no ed25519 keyPair") {
|
||||
mockStorage.when { $0.getUserED25519KeyPair() }.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageSender.encryptWithSessionBlindingProtocol(
|
||||
"TestMessage".data(using: .utf8)!,
|
||||
for: "15\(TestConstants.blindedPublicKey)",
|
||||
openGroupPublicKey: TestConstants.serverPublicKey,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageSender.Error.noUserED25519KeyPair))
|
||||
}
|
||||
|
||||
it("throws an error if it fails to generate a blinded keyPair") {
|
||||
let mockSodium: MockSodium = MockSodium()
|
||||
let mockGenericHash: MockGenericHash = MockGenericHash()
|
||||
dependencies = dependencies.with(sodium: mockSodium, genericHash: mockGenericHash)
|
||||
|
||||
mockSodium
|
||||
.when {
|
||||
$0.blindedKeyPair(
|
||||
serverPublicKey: any(),
|
||||
edKeyPair: any(),
|
||||
genericHash: mockGenericHash
|
||||
)
|
||||
}
|
||||
.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageSender.encryptWithSessionBlindingProtocol(
|
||||
"TestMessage".data(using: .utf8)!,
|
||||
for: "15\(TestConstants.blindedPublicKey)",
|
||||
openGroupPublicKey: TestConstants.serverPublicKey,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageSender.Error.signingFailed))
|
||||
}
|
||||
|
||||
it("throws an error if it fails to generate an encryption key") {
|
||||
let mockSodium: MockSodium = MockSodium()
|
||||
let mockGenericHash: MockGenericHash = MockGenericHash()
|
||||
dependencies = dependencies.with(sodium: mockSodium, genericHash: mockGenericHash)
|
||||
|
||||
mockSodium
|
||||
.when {
|
||||
$0.blindedKeyPair(
|
||||
serverPublicKey: any(),
|
||||
edKeyPair: any(),
|
||||
genericHash: mockGenericHash
|
||||
)
|
||||
}
|
||||
.thenReturn(
|
||||
Box.KeyPair(
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
)
|
||||
)
|
||||
mockSodium
|
||||
.when {
|
||||
$0.sharedBlindedEncryptionKey(
|
||||
secretKey: anyArray(),
|
||||
otherBlindedPublicKey: anyArray(),
|
||||
fromBlindedPublicKey: anyArray(),
|
||||
toBlindedPublicKey: anyArray(),
|
||||
genericHash: mockGenericHash
|
||||
)
|
||||
}
|
||||
.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageSender.encryptWithSessionBlindingProtocol(
|
||||
"TestMessage".data(using: .utf8)!,
|
||||
for: "15\(TestConstants.blindedPublicKey)",
|
||||
openGroupPublicKey: TestConstants.serverPublicKey,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageSender.Error.signingFailed))
|
||||
}
|
||||
|
||||
it("throws an error if it fails to encrypt") {
|
||||
let mockAeadXChaCha: MockAeadXChaCha20Poly1305Ietf = MockAeadXChaCha20Poly1305Ietf()
|
||||
dependencies = dependencies.with(aeadXChaCha20Poly1305Ietf: mockAeadXChaCha)
|
||||
|
||||
mockAeadXChaCha
|
||||
.when { $0.encrypt(message: anyArray(), secretKey: anyArray(), nonce: anyArray()) }
|
||||
.thenReturn(nil)
|
||||
|
||||
expect {
|
||||
try MessageSender.encryptWithSessionBlindingProtocol(
|
||||
"TestMessage".data(using: .utf8)!,
|
||||
for: "15\(TestConstants.blindedPublicKey)",
|
||||
openGroupPublicKey: TestConstants.serverPublicKey,
|
||||
using: dependencies
|
||||
)
|
||||
}
|
||||
.to(throwError(MessageSender.Error.encryptionFailed))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,351 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import Sodium
|
||||
|
||||
import Quick
|
||||
import Nimble
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class SodiumUtilitiesSpec: QuickSpec {
|
||||
// MARK: - Spec
|
||||
|
||||
override func spec() {
|
||||
// MARK: - Sign
|
||||
|
||||
describe("an extended Sign") {
|
||||
var sign: Sign!
|
||||
|
||||
beforeEach {
|
||||
sign = Sodium().sign
|
||||
}
|
||||
|
||||
it("can convert an ed25519 public key into an x25519 public key") {
|
||||
let result = sign.toX25519(ed25519PublicKey: TestConstants.edPublicKey.bytes)
|
||||
|
||||
expect(result?.toHexString())
|
||||
.to(equal("95ffb559d4e804e9b414a5178454c426f616b4a61089b217b41165dbb7c9fe2d"))
|
||||
}
|
||||
|
||||
it("can convert an ed25519 private key into an x25519 private key") {
|
||||
let result = sign.toX25519(ed25519SecretKey: TestConstants.edSecretKey.bytes)
|
||||
|
||||
expect(result?.toHexString())
|
||||
.to(equal("c83f9a1479b103c275d2db2d6c199fdc6f589b29b742f6405e01cc5a9a1d135d"))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Sodium
|
||||
|
||||
describe("an extended Sodium") {
|
||||
var sodium: Sodium!
|
||||
var genericHash: GenericHashType!
|
||||
|
||||
beforeEach {
|
||||
sodium = Sodium()
|
||||
genericHash = sodium.genericHash
|
||||
}
|
||||
|
||||
context("when generating a blinding factor") {
|
||||
it("successfully generates a blinding factor") {
|
||||
let result = sodium.generateBlindingFactor(
|
||||
serverPublicKey: TestConstants.serverPublicKey,
|
||||
genericHash: genericHash
|
||||
)
|
||||
|
||||
expect(result?.toHexString())
|
||||
.to(equal("84e3eb75028a9b73fec031b7448e322a68ca6485fad81ab1bead56f759ebeb0f"))
|
||||
}
|
||||
|
||||
it("fails if the serverPublicKey is not a hex string") {
|
||||
let result = sodium.generateBlindingFactor(
|
||||
serverPublicKey: "Test",
|
||||
genericHash: genericHash
|
||||
)
|
||||
|
||||
expect(result).to(beNil())
|
||||
}
|
||||
|
||||
it("fails if it cannot hash the serverPublicKey bytes") {
|
||||
genericHash = MockGenericHash()
|
||||
(genericHash as? MockGenericHash)?
|
||||
.when { $0.hash(message: anyArray(), outputLength: any()) }
|
||||
.thenReturn(nil)
|
||||
|
||||
let result = sodium.generateBlindingFactor(
|
||||
serverPublicKey: TestConstants.serverPublicKey,
|
||||
genericHash: genericHash
|
||||
)
|
||||
|
||||
expect(result).to(beNil())
|
||||
}
|
||||
}
|
||||
|
||||
context("when generating a blinded key pair") {
|
||||
it("successfully generates a blinded key pair") {
|
||||
let result = sodium.blindedKeyPair(
|
||||
serverPublicKey: TestConstants.serverPublicKey,
|
||||
edKeyPair: Box.KeyPair(
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
genericHash: genericHash
|
||||
)
|
||||
|
||||
// Note: The first 64 characters of the secretKey are consistent but the chars after that always differ
|
||||
expect(result?.publicKey.toHexString()).to(equal(TestConstants.blindedPublicKey))
|
||||
expect(String(result?.secretKey.toHexString().prefix(64) ?? ""))
|
||||
.to(equal("16663322d6b684e1c9dcc02b9e8642c3affd3bc431a9ea9e63dbbac88ce7a305"))
|
||||
}
|
||||
|
||||
it("fails if the edKeyPair public key length wrong") {
|
||||
let result = sodium.blindedKeyPair(
|
||||
serverPublicKey: TestConstants.serverPublicKey,
|
||||
edKeyPair: Box.KeyPair(
|
||||
publicKey: Data(hex: String(TestConstants.edPublicKey.prefix(4))).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
genericHash: genericHash
|
||||
)
|
||||
|
||||
expect(result).to(beNil())
|
||||
}
|
||||
|
||||
it("fails if the edKeyPair secret key length wrong") {
|
||||
let result = sodium.blindedKeyPair(
|
||||
serverPublicKey: TestConstants.serverPublicKey,
|
||||
edKeyPair: Box.KeyPair(
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: String(TestConstants.edSecretKey.prefix(4))).bytes
|
||||
),
|
||||
genericHash: genericHash
|
||||
)
|
||||
|
||||
expect(result).to(beNil())
|
||||
}
|
||||
|
||||
it("fails if it cannot generate a blinding factor") {
|
||||
let result = sodium.blindedKeyPair(
|
||||
serverPublicKey: "Test",
|
||||
edKeyPair: Box.KeyPair(
|
||||
publicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes
|
||||
),
|
||||
genericHash: genericHash
|
||||
)
|
||||
|
||||
expect(result).to(beNil())
|
||||
}
|
||||
}
|
||||
|
||||
context("when generating a sogsSignature") {
|
||||
it("generates a correct signature") {
|
||||
let result = sodium.sogsSignature(
|
||||
message: "TestMessage".bytes,
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes,
|
||||
blindedSecretKey: Data(hex: "44d82cc15c0a5056825cae7520b6b52d000a23eb0c5ed94c4be2d9dc41d2d409").bytes,
|
||||
blindedPublicKey: Data(hex: "0bb7815abb6ba5142865895f3e5286c0527ba4d31dbb75c53ce95e91ffe025a2").bytes
|
||||
)
|
||||
|
||||
expect(result?.toHexString())
|
||||
.to(equal(
|
||||
"dcc086abdd2a740d9260b008fb37e12aa0ff47bd2bd9e177bbbec37fd46705a9" +
|
||||
"072ce747bda66c788c3775cdd7ad60ad15a478e0886779aad5d795fd7bf8350d"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
context("when combining keys") {
|
||||
it("generates a correct combined key") {
|
||||
let result = sodium.combineKeys(
|
||||
lhsKeyBytes: Data(hex: TestConstants.edSecretKey).bytes,
|
||||
rhsKeyBytes: Data(hex: TestConstants.edPublicKey).bytes
|
||||
)
|
||||
|
||||
expect(result?.toHexString())
|
||||
.to(equal("1159b5d0fcfba21228eb2121a0f59712fa8276fc6e5547ff519685a40b9819e6"))
|
||||
}
|
||||
|
||||
it("fails if the scalar multiplication fails") {
|
||||
let result = sodium.combineKeys(
|
||||
lhsKeyBytes: sodium.generatePrivateKeyScalar(secretKey: Data(hex: TestConstants.edSecretKey).bytes),
|
||||
rhsKeyBytes: Data(hex: TestConstants.publicKey).bytes
|
||||
)
|
||||
|
||||
expect(result).to(beNil())
|
||||
}
|
||||
}
|
||||
|
||||
context("when creating a shared blinded encryption key") {
|
||||
it("generates a correct combined key") {
|
||||
let result = sodium.sharedBlindedEncryptionKey(
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes,
|
||||
otherBlindedPublicKey: Data(hex: TestConstants.blindedPublicKey).bytes,
|
||||
fromBlindedPublicKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
toBlindedPublicKey: Data(hex: TestConstants.blindedPublicKey).bytes,
|
||||
genericHash: genericHash
|
||||
)
|
||||
|
||||
expect(result?.toHexString())
|
||||
.to(equal("388ee09e4c356b91f1cce5cc0aa0cf59e8e8cade69af61685d09c2d2731bc99e"))
|
||||
}
|
||||
|
||||
it("fails if the scalar multiplication fails") {
|
||||
let result = sodium.sharedBlindedEncryptionKey(
|
||||
secretKey: Data(hex: TestConstants.edSecretKey).bytes,
|
||||
otherBlindedPublicKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
fromBlindedPublicKey: Data(hex: TestConstants.edPublicKey).bytes,
|
||||
toBlindedPublicKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
genericHash: genericHash
|
||||
)
|
||||
|
||||
expect(result?.toHexString()).to(beNil())
|
||||
}
|
||||
}
|
||||
|
||||
context("when checking if a session id matches a blinded id") {
|
||||
it("returns true when they match") {
|
||||
let result = sodium.sessionId(
|
||||
"05\(TestConstants.publicKey)",
|
||||
matchesBlindedId: "15\(TestConstants.blindedPublicKey)",
|
||||
serverPublicKey: TestConstants.serverPublicKey,
|
||||
genericHash: genericHash
|
||||
)
|
||||
|
||||
expect(result).to(beTrue())
|
||||
}
|
||||
|
||||
it("returns false if given an invalid session id") {
|
||||
let result = sodium.sessionId(
|
||||
"AB\(TestConstants.publicKey)",
|
||||
matchesBlindedId: "15\(TestConstants.blindedPublicKey)",
|
||||
serverPublicKey: TestConstants.serverPublicKey,
|
||||
genericHash: genericHash
|
||||
)
|
||||
|
||||
expect(result).to(beFalse())
|
||||
}
|
||||
|
||||
it("returns false if given an invalid blinded id") {
|
||||
let result = sodium.sessionId(
|
||||
"05\(TestConstants.publicKey)",
|
||||
matchesBlindedId: "AB\(TestConstants.blindedPublicKey)",
|
||||
serverPublicKey: TestConstants.serverPublicKey,
|
||||
genericHash: genericHash
|
||||
)
|
||||
|
||||
expect(result).to(beFalse())
|
||||
}
|
||||
|
||||
it("returns false if it fails to generate the blinding factor") {
|
||||
let result = sodium.sessionId(
|
||||
"05\(TestConstants.publicKey)",
|
||||
matchesBlindedId: "15\(TestConstants.blindedPublicKey)",
|
||||
serverPublicKey: "Test",
|
||||
genericHash: genericHash
|
||||
)
|
||||
|
||||
expect(result).to(beFalse())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - GenericHash
|
||||
|
||||
describe("an extended GenericHash") {
|
||||
var genericHash: GenericHashType!
|
||||
|
||||
beforeEach {
|
||||
genericHash = Sodium().genericHash
|
||||
}
|
||||
|
||||
context("when generating a hash with salt and personal values") {
|
||||
it("generates a hash correctly") {
|
||||
let result = genericHash.hashSaltPersonal(
|
||||
message: "TestMessage".bytes,
|
||||
outputLength: 32,
|
||||
key: "Key".bytes,
|
||||
salt: "Salt".bytes,
|
||||
personal: "Personal".bytes
|
||||
)
|
||||
|
||||
expect(result).toNot(beNil())
|
||||
expect(result?.count).to(equal(32))
|
||||
}
|
||||
|
||||
it("generates a hash correctly with no key") {
|
||||
let result = genericHash.hashSaltPersonal(
|
||||
message: "TestMessage".bytes,
|
||||
outputLength: 32,
|
||||
key: nil,
|
||||
salt: "Salt".bytes,
|
||||
personal: "Personal".bytes
|
||||
)
|
||||
|
||||
expect(result).toNot(beNil())
|
||||
expect(result?.count).to(equal(32))
|
||||
}
|
||||
|
||||
it("fails if given invalid options") {
|
||||
let result = genericHash.hashSaltPersonal(
|
||||
message: "TestMessage".bytes,
|
||||
outputLength: 65, // Max of 64
|
||||
key: "Key".bytes,
|
||||
salt: "Salt".bytes,
|
||||
personal: "Personal".bytes
|
||||
)
|
||||
|
||||
expect(result).to(beNil())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AeadXChaCha20Poly1305IetfType
|
||||
|
||||
describe("an extended AeadXChaCha20Poly1305IetfType") {
|
||||
var aeadXchacha20poly1305ietf: AeadXChaCha20Poly1305IetfType!
|
||||
|
||||
beforeEach {
|
||||
aeadXchacha20poly1305ietf = Sodium().aead.xchacha20poly1305ietf
|
||||
}
|
||||
|
||||
context("when encrypting") {
|
||||
it("encrypts correctly") {
|
||||
let result = aeadXchacha20poly1305ietf.encrypt(
|
||||
message: "TestMessage".bytes,
|
||||
secretKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
nonce: "TestNonce".bytes,
|
||||
additionalData: nil
|
||||
)
|
||||
|
||||
expect(result).toNot(beNil())
|
||||
expect(result?.count).to(equal(27))
|
||||
}
|
||||
|
||||
it("encrypts correctly with additional data") {
|
||||
let result = aeadXchacha20poly1305ietf.encrypt(
|
||||
message: "TestMessage".bytes,
|
||||
secretKey: Data(hex: TestConstants.publicKey).bytes,
|
||||
nonce: "TestNonce".bytes,
|
||||
additionalData: "TestData".bytes
|
||||
)
|
||||
|
||||
expect(result).toNot(beNil())
|
||||
expect(result?.count).to(equal(27))
|
||||
}
|
||||
|
||||
it("fails if given an invalid key") {
|
||||
let result = aeadXchacha20poly1305ietf.encrypt(
|
||||
message: "TestMessage".bytes,
|
||||
secretKey: "TestKey".bytes,
|
||||
nonce: "TestNonce".bytes,
|
||||
additionalData: "TestData".bytes
|
||||
)
|
||||
|
||||
expect(result).to(beNil())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,9 +11,10 @@ extension Dependencies {
|
|||
identityManager: IdentityManagerProtocol? = nil,
|
||||
storage: SessionMessagingKitStorageProtocol? = nil,
|
||||
sodium: SodiumType? = nil,
|
||||
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
|
||||
sign: SignType? = nil,
|
||||
box: BoxType? = nil,
|
||||
genericHash: GenericHashType? = nil,
|
||||
sign: SignType? = nil,
|
||||
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
|
||||
ed25519: Ed25519Type? = nil,
|
||||
nonceGenerator16: NonceGenerator16ByteType? = nil,
|
||||
nonceGenerator24: NonceGenerator24ByteType? = nil,
|
||||
|
@ -25,9 +26,10 @@ extension Dependencies {
|
|||
identityManager: (identityManager ?? self._identityManager),
|
||||
storage: (storage ?? self._storage),
|
||||
sodium: (sodium ?? self._sodium),
|
||||
aeadXChaCha20Poly1305Ietf: (aeadXChaCha20Poly1305Ietf ?? self._aeadXChaCha20Poly1305Ietf),
|
||||
sign: (sign ?? self._sign),
|
||||
box: (box ?? self._box),
|
||||
genericHash: (genericHash ?? self._genericHash),
|
||||
sign: (sign ?? self._sign),
|
||||
aeadXChaCha20Poly1305Ietf: (aeadXChaCha20Poly1305Ietf ?? self._aeadXChaCha20Poly1305Ietf),
|
||||
ed25519: (ed25519 ?? self._ed25519),
|
||||
nonceGenerator16: (nonceGenerator16 ?? self._nonceGenerator16),
|
||||
nonceGenerator24: (nonceGenerator24 ?? self._nonceGenerator24),
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import PromiseKit
|
||||
import Sodium
|
||||
|
||||
@testable import SessionMessagingKit
|
||||
|
||||
class MockBox: Mock<BoxType>, BoxType {
|
||||
func seal(message: Bytes, recipientPublicKey: Bytes) -> Bytes? {
|
||||
return accept(args: [message, recipientPublicKey]) as? Bytes
|
||||
}
|
||||
|
||||
func open(anonymousCipherText: Bytes, recipientPublicKey: Bytes, recipientSecretKey: Bytes) -> Bytes? {
|
||||
return accept(args: [anonymousCipherText, recipientPublicKey, recipientSecretKey]) as? Bytes
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import Sodium
|
|||
@testable import SessionMessagingKit
|
||||
|
||||
class MockSign: Mock<SignType>, SignType {
|
||||
var Bytes: Int = 64
|
||||
var PublicKeyBytes: Int = 32
|
||||
|
||||
func signature(message: Bytes, secretKey: Bytes) -> Bytes? {
|
||||
|
|
|
@ -7,12 +7,13 @@ import Sodium
|
|||
@testable import SessionMessagingKit
|
||||
|
||||
class MockSodium: Mock<SodiumType>, SodiumType {
|
||||
func getBox() -> BoxType { return accept() as! BoxType }
|
||||
func getGenericHash() -> GenericHashType { return accept() as! GenericHashType }
|
||||
func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return accept() as! AeadXChaCha20Poly1305IetfType }
|
||||
func getSign() -> SignType { return accept() as! SignType }
|
||||
func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return accept() as! AeadXChaCha20Poly1305IetfType }
|
||||
|
||||
func generateBlindingFactor(serverPublicKey: String) -> Bytes? {
|
||||
return accept(args: [serverPublicKey]) as? Bytes
|
||||
func generateBlindingFactor(serverPublicKey: String, genericHash: GenericHashType) -> Bytes? {
|
||||
return accept(args: [serverPublicKey, genericHash]) as? Bytes
|
||||
}
|
||||
|
||||
func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? {
|
||||
|
@ -31,7 +32,7 @@ class MockSodium: Mock<SodiumType>, SodiumType {
|
|||
return accept(args: [a, otherBlindedPublicKey, kA, kB, genericHash]) as? Bytes
|
||||
}
|
||||
|
||||
func sessionId(_ sessionId: String, matchesBlindedId blindedSessionId: String, serverPublicKey: String) -> Bool {
|
||||
return accept(args: [sessionId, blindedSessionId, serverPublicKey]) as! Bool
|
||||
func sessionId(_ sessionId: String, matchesBlindedId blindedSessionId: String, serverPublicKey: String, genericHash: GenericHashType) -> Bool {
|
||||
return accept(args: [sessionId, blindedSessionId, serverPublicKey, genericHash]) as! Bool
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,9 +12,10 @@ extension OpenGroupManager.OGMDependencies {
|
|||
identityManager: IdentityManagerProtocol? = nil,
|
||||
storage: SessionMessagingKitStorageProtocol? = nil,
|
||||
sodium: SodiumType? = nil,
|
||||
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
|
||||
sign: SignType? = nil,
|
||||
box: BoxType? = nil,
|
||||
genericHash: GenericHashType? = nil,
|
||||
sign: SignType? = nil,
|
||||
aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil,
|
||||
ed25519: Ed25519Type? = nil,
|
||||
nonceGenerator16: NonceGenerator16ByteType? = nil,
|
||||
nonceGenerator24: NonceGenerator24ByteType? = nil,
|
||||
|
@ -27,9 +28,10 @@ extension OpenGroupManager.OGMDependencies {
|
|||
identityManager: (identityManager ?? self._identityManager),
|
||||
storage: (storage ?? self._storage),
|
||||
sodium: (sodium ?? self._sodium),
|
||||
aeadXChaCha20Poly1305Ietf: (aeadXChaCha20Poly1305Ietf ?? self._aeadXChaCha20Poly1305Ietf),
|
||||
sign: (sign ?? self._sign),
|
||||
box: (box ?? self._box),
|
||||
genericHash: (genericHash ?? self._genericHash),
|
||||
sign: (sign ?? self._sign),
|
||||
aeadXChaCha20Poly1305Ietf: (aeadXChaCha20Poly1305Ietf ?? self._aeadXChaCha20Poly1305Ietf),
|
||||
ed25519: (ed25519 ?? self._ed25519),
|
||||
nonceGenerator16: (nonceGenerator16 ?? self._nonceGenerator16),
|
||||
nonceGenerator24: (nonceGenerator24 ?? self._nonceGenerator24),
|
||||
|
|
|
@ -11,6 +11,7 @@ extension String {
|
|||
.map { index -> String in String(chars[index]) + String(chars[index + 1]) }
|
||||
.compactMap { (str: String) -> UInt8? in UInt8(str, radix: 16) }
|
||||
|
||||
guard bytes.count > 0 else { return nil }
|
||||
guard (self.count / bytes.count) == 2 else { return nil }
|
||||
|
||||
return Data(bytes)
|
||||
|
|
|
@ -42,11 +42,11 @@ class SessionIdSpec: QuickSpec {
|
|||
|
||||
it("generates the correct hex string") {
|
||||
expect(SessionId(.unblinded, publicKey: Data(hex: TestConstants.publicKey).bytes).hexString)
|
||||
.to(equal("007aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d"))
|
||||
.to(equal("0088672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"))
|
||||
expect(SessionId(.standard, publicKey: Data(hex: TestConstants.publicKey).bytes).hexString)
|
||||
.to(equal("057aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d"))
|
||||
.to(equal("0588672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"))
|
||||
expect(SessionId(.blinded, publicKey: Data(hex: TestConstants.publicKey).bytes).hexString)
|
||||
.to(equal("157aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d"))
|
||||
.to(equal("1588672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,12 @@
|
|||
import Foundation
|
||||
|
||||
enum TestConstants {
|
||||
// Test Private key, not actually used (from here https://www.notion.so/oxen/SOGS-Authentication-dc64cc846cb24b2abbf7dd4bfd74abbb)
|
||||
static let publicKey: String = "7aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d"
|
||||
static let privateKey: String = "881132ee03dbd2da065aa4c94f96081f62142dc8011d1b7a00de83e4aab38ce4"
|
||||
static let edSecretKey: String = "881132ee03dbd2da065aa4c94f96081f62142dc8011d1b7a00de83e4aab38ce4881132ee03dbd2da065aa4c94f96081f62142dc8011d1b7a00de83e4aab38ce4"
|
||||
// Test keys (from here https://github.com/jagerman/session-pysogs/blob/docs/contrib/auth-example.py)
|
||||
static let publicKey: String = "88672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"
|
||||
static let privateKey: String = "30d796c1ddb4dc455fd998a98aa275c247494a9a7bde9c1fee86ae45cd585241"
|
||||
static let edKeySeed: String = "c010d89eccbaf5d1c6d19df766c6eedf965d4a28a56f87c9fc819edb59896dd9"
|
||||
static let edPublicKey: String = "bac6e71efd7dfa4a83c98ed24f254ab2c267f9ccdb172a5280a0444ad24e89cc"
|
||||
static let edSecretKey: String = "c010d89eccbaf5d1c6d19df766c6eedf965d4a28a56f87c9fc819edb59896dd9bac6e71efd7dfa4a83c98ed24f254ab2c267f9ccdb172a5280a0444ad24e89cc"
|
||||
static let blindedPublicKey: String = "98932d4bccbe595a8789d7eb1629cefc483a0eaddc7e20e8fe5c771efafd9af5"
|
||||
static let serverPublicKey: String = "c3b3c6f32f0ab5a57f853cc4f30f5da7fda5624b0c77b3fb0829de562ada081d"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue