@ -40,11 +40,13 @@ public final class SharedSenderKeysImplementation : NSObject {
public enum RatchetingError : LocalizedError {
case loadingFailed ( groupPublicKey : String , senderPublicKey : String )
case messageKeyMissing ( targetKeyIndex : UInt , groupPublicKey : String , senderPublicKey : String )
case generic
public var errorDescription : String ? {
switch self {
case . loadingFailed ( let groupPublicKey , let senderPublicKey ) : return " Couldn't get ratchet for closed group with public key: \( groupPublicKey ) , sender public key: \( senderPublicKey ) . "
case . messageKeyMissing ( let targetKeyIndex , let groupPublicKey , let senderPublicKey ) : return " Couldn't find message key for old key index: \( targetKeyIndex ) , public key: \( groupPublicKey ) , sender public key: \( senderPublicKey ) . "
case . generic : return " An error occurred "
}
}
}
@ -66,7 +68,8 @@ public final class SharedSenderKeysImplementation : NSObject {
let nextMessageKey = try HMAC ( key : Data ( hex : ratchet . chainKey ) . bytes , variant : . sha256 ) . authenticate ( [ UInt8 ( 1 ) ] )
let nextChainKey = try HMAC ( key : Data ( hex : ratchet . chainKey ) . bytes , variant : . sha256 ) . authenticate ( [ UInt8 ( 2 ) ] )
let nextKeyIndex = ratchet . keyIndex + 1
return ClosedGroupRatchet ( chainKey : nextChainKey . toHexString ( ) , keyIndex : nextKeyIndex , messageKeys : [ nextMessageKey . toHexString ( ) ] )
let messageKeys = ratchet . messageKeys + [ nextMessageKey . toHexString ( ) ]
return ClosedGroupRatchet ( chainKey : nextChainKey . toHexString ( ) , keyIndex : nextKeyIndex , messageKeys : messageKeys )
}
// / - N o t e : S y n c . D o n ' t c a l l f r o m t h e m a i n t h r e a d .
@ -110,19 +113,16 @@ public final class SharedSenderKeysImplementation : NSObject {
return ratchet
} else {
var currentKeyIndex = ratchet . keyIndex
var current = ratchet
var messageKeys : [ String ] = [ ]
var result = ratchet
while currentKeyIndex < targetKeyIndex {
do {
current = try step ( current )
messageKeys += current . messageKeys
currentKeyIndex = current . keyIndex
result = try step ( result )
currentKeyIndex = result . keyIndex
} catch {
print ( " [Loki] Couldn't step ratchet due to error: \( error ) . " )
throw error
}
}
let result = ClosedGroupRatchet ( chainKey : current . chainKey , keyIndex : current . keyIndex , messageKeys : messageKeys ) // I n c l u d e s a n y s k i p p e d m e s s a g e k e y s
let collection : Storage . ClosedGroupRatchetCollectionType = ( isRetry ) ? . old : . current
Storage . setClosedGroupRatchet ( for : groupPublicKey , senderPublicKey : senderPublicKey , ratchet : result , in : collection , using : transaction )
return result
@ -182,20 +182,31 @@ public final class SharedSenderKeysImplementation : NSObject {
let iv = ivAndCiphertext [ 0. . < Int ( SharedSenderKeysImplementation . ivSize ) ]
let ciphertext = ivAndCiphertext [ Int ( SharedSenderKeysImplementation . ivSize ) . . . ]
let gcm = GCM ( iv : iv . bytes , tagLength : Int ( SharedSenderKeysImplementation . gcmTagSize ) , mode : . combined )
guard let messageKey = ratchet . messageKeys . last else {
let messageKeys = ratchet . messageKeys
let lastNMessageKeys : [ String ]
if messageKeys . count > 16 { // P i c k a n a r b i t r a r y n u m b e r o f m e s s a g e k e y s t o t r y ; t h i s h e l p s r e s o l v e i s s u e s c a u s e d b y m e s s a g e s a r r i v i n g o u t o f o r d e r
lastNMessageKeys = [ String ] ( messageKeys [ messageKeys . index ( messageKeys . endIndex , offsetBy : - 16 ) . . < messageKeys . endIndex ] )
} else {
lastNMessageKeys = messageKeys
}
guard ! lastNMessageKeys . isEmpty else {
throw RatchetingError . messageKeyMissing ( targetKeyIndex : keyIndex , groupPublicKey : groupPublicKey , senderPublicKey : senderPublicKey )
}
let aes = try AES ( key : Data ( hex : messageKey ) . bytes , blockMode : gcm , padding : . noPadding )
do {
return Data ( try aes . decrypt ( ciphertext . bytes ) )
} catch {
if ! isRetry {
return try decrypt ( ivAndCiphertext , for : groupPublicKey , senderPublicKey : senderPublicKey , keyIndex : keyIndex , using : transaction , isRetry : true )
} else {
ClosedGroupsProtocol . requestSenderKey ( for : groupPublicKey , senderPublicKey : senderPublicKey , using : transaction )
throw error
var error : Error ?
for messageKey in lastNMessageKeys . reversed ( ) { // R e v e r s e d b e c a u s e m o s t l i k e l y t h e l a s t o n e i s t h e o n e w e n e e d
let aes = try AES ( key : Data ( hex : messageKey ) . bytes , blockMode : gcm , padding : . noPadding )
do {
return Data ( try aes . decrypt ( ciphertext . bytes ) )
} catch ( let e ) {
error = e
}
}
if ! isRetry {
return try decrypt ( ivAndCiphertext , for : groupPublicKey , senderPublicKey : senderPublicKey , keyIndex : keyIndex , using : transaction , isRetry : true )
} else {
ClosedGroupsProtocol . requestSenderKey ( for : groupPublicKey , senderPublicKey : senderPublicKey , using : transaction )
throw error ? ? RatchetingError . generic
}
}
@objc public func isClosedGroup ( _ publicKey : String ) -> Bool {