Signal-iOS/SignalServiceKit/MessageBackup/MessageBackupKeyMaterialImpl.swift
Harry bb083ca39c
Fold SignalCoreKit into SignalServiceKit
Co-authored-by: Adam Sharp <sharplet@signal.org>
2024-06-26 08:44:41 -07:00

123 lines
5.2 KiB
Swift

//
// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
import LibSignalClient
public struct MessageBackupKeyMaterialImpl: MessageBackupKeyMaterial {
private enum Constants {
static let MessageBackupIdInfoString = "20231003_Signal_Backups_GenerateBackupId"
static let MessageBackupIdLength = 16
static let MessageBackupEncryptionInfoString = "20231003_Signal_Backups_EncryptMessageBackup"
static let MessageBackupEncryptionDataLength = 64
static let MessageBackupPrivateKeyInfoString = "20231003_Signal_Backups_GenerateBackupIdKeyPair"
static let MessageBackupPrivateKeyDataLength = 32
}
private let svr: SecureValueRecovery
public init(svr: SecureValueRecovery) {
self.svr = svr
}
public func backupAuthRequestContext(localAci: Aci, tx: DBReadTransaction) throws -> BackupAuthCredentialRequestContext {
guard let backupKey = svr.data(for: .backupKey, transaction: tx) else {
throw MessageBackupKeyMaterialError.missingMasterKey
}
return BackupAuthCredentialRequestContext.create(backupKey: backupKey.rawData, aci: localAci.rawUUID)
}
public func backupID(localAci: Aci, tx: DBReadTransaction) throws -> Data {
guard let backupKey = svr.data(for: .backupKey, transaction: tx) else {
throw MessageBackupKeyMaterialError.missingMasterKey
}
guard let infoData = Constants.MessageBackupIdInfoString.data(using: .utf8) else {
owsFailDebug("Failed to encode data")
throw MessageBackupKeyMaterialError.invalidKeyInfo
}
let keyBytes = try hkdf(
outputLength: Constants.MessageBackupIdLength,
inputKeyMaterial: backupKey.rawData,
salt: localAci.serviceIdBinary,
info: infoData
)
return Data(keyBytes)
}
public func backupPrivateKey(localAci: Aci, tx: DBReadTransaction) throws -> PrivateKey {
guard let backupKey = svr.data(for: .backupKey, transaction: tx) else {
throw MessageBackupKeyMaterialError.missingMasterKey
}
guard let infoData = Constants.MessageBackupPrivateKeyInfoString.data(using: .utf8) else {
owsFailDebug("Failed to encode data")
throw MessageBackupKeyMaterialError.invalidKeyInfo
}
let privateKeyBytes = try hkdf(
outputLength: Constants.MessageBackupPrivateKeyDataLength,
inputKeyMaterial: backupKey.rawData,
salt: localAci.serviceIdBinary,
info: infoData
)
return try PrivateKey(privateKeyBytes)
}
public func messageBackupKey(localAci: Aci, tx: DBReadTransaction) throws -> MessageBackupKey {
guard let masterKey = svr.masterKeyDataForKeysSyncMessage(tx: tx) else {
throw MessageBackupKeyMaterialError.missingMasterKey
}
return try MessageBackupKey(masterKey: Array(masterKey), aci: localAci)
}
public func createEncryptingStreamTransform(localAci: Aci, tx: DBReadTransaction) throws -> EncryptingStreamTransform {
let (encryptionKey, _) = try buildEncryptionMaterial(localAci: localAci, tx: tx)
return try EncryptingStreamTransform(iv: Randomness.generateRandomBytes(16), encryptionKey: encryptionKey)
}
public func createDecryptingStreamTransform(localAci: Aci, tx: DBReadTransaction) throws -> DecryptingStreamTransform {
let (encryptionKey, _) = try buildEncryptionMaterial(localAci: localAci, tx: tx)
return try DecryptingStreamTransform(encryptionKey: encryptionKey)
}
public func createHmacGeneratingStreamTransform(localAci: Aci, tx: DBReadTransaction) throws -> HmacStreamTransform {
let (_, hmacKey) = try buildEncryptionMaterial(localAci: localAci, tx: tx)
return try HmacStreamTransform(hmacKey: hmacKey, operation: .generate)
}
public func createHmacValidatingStreamTransform(localAci: Aci, tx: DBReadTransaction) throws -> HmacStreamTransform {
let (_, hmacKey) = try buildEncryptionMaterial(localAci: localAci, tx: tx)
return try HmacStreamTransform(hmacKey: hmacKey, operation: .validate)
}
private func buildEncryptionMaterial(localAci: Aci, tx: DBReadTransaction) throws -> (encryptionKey: Data, hmacKey: Data) {
guard let backupKey = svr.data(for: .backupKey, transaction: tx) else {
throw MessageBackupKeyMaterialError.missingMasterKey
}
let backupId = try backupID(localAci: localAci, tx: tx)
guard let infoData = Constants.MessageBackupEncryptionInfoString.data(using: .utf8) else {
owsFailDebug("Failed to encode data")
throw MessageBackupKeyMaterialError.invalidKeyInfo
}
let keyBytes = try hkdf(
outputLength: Constants.MessageBackupEncryptionDataLength,
inputKeyMaterial: backupKey.rawData,
salt: backupId,
info: infoData
)
guard keyBytes.count == Constants.MessageBackupEncryptionDataLength else {
throw MessageBackupKeyMaterialError.invalidEncryptionKey
}
return (encryptionKey: Data(Array(keyBytes[32..<64])), hmacKey: Data(Array(keyBytes[0..<32])))
}
}