Use AES-GCM & AES-CTR from LibSignalClient

This commit is contained in:
Max Radermacher 2023-02-13 11:29:41 -08:00 committed by GitHub
parent db2678e86b
commit a47425f008
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 73 additions and 138 deletions

View File

@ -4,6 +4,7 @@
//
import Foundation
import LibSignalClient
import SignalArgon2
import SignalCoreKit
@ -495,39 +496,19 @@ public class KeyBackupService: KeyBackupServiceProtocol {
}
public func encrypt(keyType: KBS.DerivedKey, data: Data) throws -> Data {
guard let keyData = self.data(for: keyType), let key = OWSAES256Key(data: keyData) else {
guard let keyData = self.data(for: keyType) else {
owsFailDebug("missing derived key \(keyType)")
throw KBS.KBSError.assertion
}
guard let encryptedData = Cryptography.encryptAESGCMWithDataAndConcatenateResults(
plainTextData: data,
initializationVectorLength: kAESGCM256_DefaultIVLength,
key: key
) else {
owsFailDebug("Failed to encrypt data")
throw KBS.KBSError.assertion
}
return encryptedData
return try Aes256GcmEncryptedData.encrypt(data, key: keyData).concatenate()
}
public func decrypt(keyType: KBS.DerivedKey, encryptedData: Data) throws -> Data {
guard let keyData = self.data(for: keyType), let key = OWSAES256Key(data: keyData) else {
guard let keyData = self.data(for: keyType) else {
owsFailDebug("missing derived key \(keyType)")
throw KBS.KBSError.assertion
}
guard let data = Cryptography.decryptAESGCMConcatenatedData(
encryptedData: encryptedData,
initializationVectorLength: kAESGCM256_DefaultIVLength,
key: key
) else {
Logger.error("failed to decrypt data")
throw KBS.KBSError.assertion
}
return data
return try Aes256GcmEncryptedData(concatenated: encryptedData).decrypt(key: keyData)
}
public func deriveRegistrationLockToken() -> String? {
@ -942,21 +923,17 @@ public class KeyBackupService: KeyBackupServiceProtocol {
requestOption.set(on: requestBuilder)
let kbRequestData = try requestBuilder.buildSerializedData()
guard let encryptionResult = Cryptography.encryptAESGCM(
plainTextData: kbRequestData,
initializationVectorLength: kAESGCM256_DefaultIVLength,
additionalAuthenticatedData: remoteAttestation.requestId,
key: remoteAttestation.keys.clientKey
) else {
owsFailDebug("Failed to encrypt request data")
throw KBS.KBSError.assertion
}
let encryptionResult = try Aes256GcmEncryptedData.encrypt(
kbRequestData,
key: remoteAttestation.keys.clientKey.keyData,
associatedData: remoteAttestation.requestId
)
let request = OWSRequestFactory.kbsEnclaveRequest(
withRequestId: remoteAttestation.requestId,
data: encryptionResult.ciphertext,
cryptIv: encryptionResult.initializationVector,
cryptMac: encryptionResult.authTag,
cryptIv: encryptionResult.nonce,
cryptMac: encryptionResult.authenticationTag,
enclaveName: remoteAttestation.enclaveName,
authUsername: remoteAttestation.auth.username,
authPassword: remoteAttestation.auth.password,
@ -996,25 +973,14 @@ public class KeyBackupService: KeyBackupServiceProtocol {
}
let iv = try parser.requiredBase64EncodedData(key: "iv")
guard iv.count == 12 else {
owsFailDebug("iv is invalid")
throw KBS.KBSError.assertion
}
let mac = try parser.requiredBase64EncodedData(key: "mac")
guard mac.count == 16 else {
owsFailDebug("mac is invalid")
throw KBS.KBSError.assertion
}
guard let encryptionResult = Cryptography.decryptAESGCM(
withInitializationVector: iv,
ciphertext: data,
additionalAuthenticatedData: nil,
authTag: mac,
key: remoteAttestation.keys.serverKey
) else {
owsFailDebug("failed to decrypt KBS response")
let encryptionResult: Data
do {
let encryptedData = Aes256GcmEncryptedData(nonce: iv, ciphertext: data, authenticationTag: mac)
encryptionResult = try encryptedData.decrypt(key: remoteAttestation.keys.serverKey.keyData)
} catch {
owsFailDebug("failed to decrypt KBS response \(error)")
throw KBS.KBSError.assertion
}

View File

@ -4,6 +4,7 @@
//
import Foundation
import LibSignalClient
struct CDSRegisteredContact: Hashable {
let signalUuid: UUID
@ -70,14 +71,12 @@ class SGXContactDiscoveryOperation: ContactDiscoveryOperation {
guard let responseAttestion = respondingEnclaveAttestation?.value else {
throw ContactDiscoveryError.assertionError(description: "Invalid responding enclave for requestId: \(response.requestId)")
}
guard let plaintext = Cryptography.decryptAESGCM(
withInitializationVector: response.iv,
let plaintext = try Aes256GcmEncryptedData(
nonce: response.iv,
ciphertext: response.data,
additionalAuthenticatedData: nil,
authTag: response.mac,
key: responseAttestion.keys.serverKey) else {
throw ContactDiscoveryError.assertionError(description: "decryption failed")
}
authenticationTag: response.mac
).decrypt(key: responseAttestion.keys.serverKey.keyData)
// 16 bytes per UUID
let contactCount = UInt(e164sToLookup.count)
@ -123,38 +122,36 @@ class SGXContactDiscoveryOperation: ContactDiscoveryOperation {
let queryData = Data.join([noncePlainTextData, addressPlainTextData])
let key = OWSAES256Key.generateRandom()
guard let encryptionResult = Cryptography.encryptAESGCM(plainTextData: queryData,
initializationVectorLength: kAESGCM256_DefaultIVLength,
additionalAuthenticatedData: nil,
key: key) else {
throw ContactDiscoveryError.assertionError(description: "Encryption failure")
}
let encryptionResult = try Aes256GcmEncryptedData.encrypt(queryData, key: key.keyData)
assert(encryptionResult.ciphertext.count == e164sToLookup.count * 8 + 32)
let queryEnvelopes: [RemoteAttestation.CDSAttestation.Id: ContactDiscoveryService.IntersectionQuery.EnclaveEnvelope] = try remoteAttestations.mapValues { remoteAttestation in
guard let perEnclaveKey = Cryptography.encryptAESGCM(plainTextData: key.keyData,
initializationVectorLength: kAESGCM256_DefaultIVLength,
additionalAuthenticatedData: remoteAttestation.requestId,
key: remoteAttestation.keys.clientKey) else {
throw ContactDiscoveryError.assertionError(description: "failed to encrypt perEnclaveKey")
}
let perEnclaveKey = try Aes256GcmEncryptedData.encrypt(
key.keyData,
key: remoteAttestation.keys.clientKey.keyData,
associatedData: remoteAttestation.requestId
)
return ContactDiscoveryService.IntersectionQuery.EnclaveEnvelope(requestId: remoteAttestation.requestId,
data: perEnclaveKey.ciphertext,
iv: perEnclaveKey.initializationVector,
mac: perEnclaveKey.authTag)
return ContactDiscoveryService.IntersectionQuery.EnclaveEnvelope(
requestId: remoteAttestation.requestId,
data: perEnclaveKey.ciphertext,
iv: perEnclaveKey.nonce,
mac: perEnclaveKey.authenticationTag
)
}
guard let commitment = Cryptography.computeSHA256Digest(queryData) else {
throw ContactDiscoveryError.assertionError(description: "commitment was unexpectedly nil")
}
return ContactDiscoveryService.IntersectionQuery(addressCount: UInt(e164sToLookup.count),
commitment: commitment,
data: encryptionResult.ciphertext,
iv: encryptionResult.initializationVector,
mac: encryptionResult.authTag,
envelopes: queryEnvelopes)
return ContactDiscoveryService.IntersectionQuery(
addressCount: UInt(e164sToLookup.count),
commitment: commitment,
data: encryptionResult.ciphertext,
iv: encryptionResult.nonce,
mac: encryptionResult.authenticationTag,
envelopes: queryEnvelopes
)
}
class func uuidArray(from data: Data) -> [UUID] {

View File

@ -4,6 +4,7 @@
//
import Foundation
import LibSignalClient
import SignalCoreKit
@objc
@ -17,25 +18,15 @@ public class SMKUDAccessKey: NSObject {
@objc
public init(profileKey: Data) throws {
guard let aesGcmKey = OWSAES256Key(data: profileKey) else {
throw SMKError.assertionError(description: "Profile key is not valid AES GCM key.")
}
let cipher = try Aes256GcmEncryption(
key: profileKey,
nonce: Data(count: Aes256GcmEncryptedData.nonceLength),
associatedData: []
)
// We derive the "ud access key" from the private key by encrypting zeroes.
let emptyPlaintextLength = 16
let emptyPlaintext = Data(count: Int(emptyPlaintextLength))
let initializationVector = Data(count: Int(kAESGCM256_DefaultIVLength))
guard let keyData = Cryptography.encryptAESGCM(plainTextData: emptyPlaintext,
initializationVector: initializationVector,
additionalAuthenticatedData: nil,
key: aesGcmKey) else {
throw SMKError.assertionError(description: "Could not derive UD access key from profile key.")
}
guard keyData.ciphertext.count == SMKUDAccessKey.kUDAccessKeyLength else {
throw SMKError.assertionError(description: "\(SMKUDAccessKey.logTag()) key has invalid length")
}
self.keyData = keyData.ciphertext
var message = Data(count: Self.kUDAccessKeyLength)
try cipher.encrypt(&message)
self.keyData = message
}
@objc

View File

@ -398,13 +398,11 @@ fileprivate extension RemoteAttestation {
let quote = try RemoteAttestationQuote.parseQuote(from: quoteData)
guard let requestId = Cryptography.decryptAESGCM(withInitializationVector: encryptedRequestIv,
ciphertext: encryptedRequestId,
additionalAuthenticatedData: nil,
authTag: encryptedRequestTag,
key: keys.serverKey) else {
throw attestationError(reason: "failed to decrypt requestId")
}
let requestId = try Aes256GcmEncryptedData(
nonce: encryptedRequestIv,
ciphertext: encryptedRequestId,
authenticationTag: encryptedRequestTag
).decrypt(key: keys.serverKey.keyData)
try verifyServerQuote(quote, keys: keys, mrenclave: mrenclave)

View File

@ -3,8 +3,9 @@
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
import Curve25519Kit
import Foundation
import LibSignalClient
public enum DeviceNameError: Error {
case assertionFailure
@ -48,22 +49,15 @@ public class DeviceNames: NSObject {
let cipherKey = try computeCipherKey(masterSecret: masterSecret, syntheticIV: syntheticIV)
// cipher_text = AES-CTR(key=cipher_key, input=plaintext, counter=0)
//
var ciphertext = plaintextData
// An all-zeros IV corresponds to an AES CTR counter of zero.
let ciphertextIV = Data(count: Int(kAES256CTR_IVLength))
guard let ciphertextKey = OWSAES256Key(data: cipherKey) else {
owsFailDebug("Invalid cipher key.")
throw DeviceNameError.assertionFailure
}
guard let ciphertext: AES256CTREncryptionResult = Cryptography.encryptAESCTR(plaintextData: plaintextData, initializationVector: ciphertextIV, key: ciphertextKey) else {
owsFailDebug("Could not encrypt cipher text.")
throw DeviceNameError.assertionFailure
}
try Aes256Ctr32.process(&ciphertext, key: cipherKey, nonce: Data(count: Aes256Ctr32.nonceLength))
let keyData = (ephemeralKeyPair.publicKey as NSData).prependKeyType()
let protoBuilder = SignalIOSProtoDeviceName.builder(ephemeralPublic: keyData as Data,
syntheticIv: syntheticIV,
ciphertext: ciphertext.ciphertext)
let protoBuilder = SignalIOSProtoDeviceName.builder(
ephemeralPublic: ephemeralKeyPair.publicKey.prependKeyType(),
syntheticIv: syntheticIV,
ciphertext: ciphertext
)
return try protoBuilder.buildSerializedData()
}
@ -135,16 +129,12 @@ public class DeviceNames: NSObject {
let ephemeralPublic: Data
do {
ephemeralPublic = try (ephemeralPublicData as NSData).removeKeyType() as Data
ephemeralPublic = try ephemeralPublicData.removeKeyType()
} catch {
owsFailDebug("failed to remove key type")
throw DeviceNameError.invalidInput
}
guard ephemeralPublic.count > 0 else {
owsFailDebug("Invalid ephemeral public.")
throw DeviceNameError.assertionFailure
}
guard receivedSyntheticIV.count == syntheticIVLength else {
owsFailDebug("Invalid synthetic IV.")
throw DeviceNameError.assertionFailure
@ -168,15 +158,9 @@ public class DeviceNames: NSObject {
let cipherKey = try computeCipherKey(masterSecret: masterSecret, syntheticIV: receivedSyntheticIV)
// plaintext = AES-CTR(key=cipher_key, input=ciphertext, counter=0)
//
var plaintextData = ciphertext
// An all-zeros IV corresponds to an AES CTR counter of zero.
let ciphertextIV = Data(count: Int(kAES256CTR_IVLength))
guard let ciphertextKey = OWSAES256Key(data: cipherKey) else {
throw DeviceNameError.cryptError("Invalid cipher key.")
}
guard let plaintextData = Cryptography.decryptAESCTR(cipherText: ciphertext, initializationVector: ciphertextIV, key: ciphertextKey) else {
throw DeviceNameError.cryptError("Could not decrypt cipher text.")
}
try Aes256Ctr32.process(&plaintextData, key: cipherKey, nonce: Data(count: Aes256Ctr32.nonceLength))
// Verify the synthetic IV was correct.
// constant_time_compare(HmacSHA256(key=HmacSHA256(key=master_secret, input=auth), input=plaintext)[0:16], synthetic_iv) == true

View File

@ -4,6 +4,7 @@
//
import Foundation
import LibSignalClient
import Mantle
@objc
@ -143,13 +144,11 @@ public extension OWSUserProfile {
// MARK: - Encryption
class func encrypt(profileData: Data, profileKey: OWSAES256Key) -> Data? {
assert(profileKey.keyData.count == kAES256_KeyByteLength)
return Cryptography.encryptAESGCMProfileData(plainTextData: profileData, key: profileKey)
return try? Aes256GcmEncryptedData.encrypt(profileData, key: profileKey.keyData).concatenate()
}
class func decrypt(profileData: Data, profileKey: OWSAES256Key) -> Data? {
assert(profileKey.keyData.count == kAES256_KeyByteLength)
return Cryptography.decryptAESGCMProfileData(encryptedData: profileData, key: profileKey)
return try? Aes256GcmEncryptedData(concatenated: profileData).decrypt(key: profileKey.keyData)
}
class func decrypt(profileNameData: Data, profileKey: OWSAES256Key, address: SignalServiceAddress) -> PersonNameComponents? {