Signal-iOS/SignalServiceKit/SecureValueRecovery/SVRUtil.swift
2023-06-12 16:12:53 -07:00

67 lines
2.4 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
import LibSignalClient
/// Just a container for simple static helper methods on SVR
/// that can be shared with other classes (incl. objc classes).
@objc
public final class SVRUtil: NSObject {
@objc
public static func normalizePin(_ pin: String) -> String {
// Trim leading and trailing whitespace
var normalizedPin = pin.ows_stripped()
// If this pin contains only numerals, ensure they are arabic numerals.
if pin.digitsOnly() == normalizedPin { normalizedPin = normalizedPin.ensureArabicNumerals }
// NFKD unicode normalization.
return normalizedPin.decomposedStringWithCompatibilityMapping
}
internal enum Constants {
static let pinSaltLengthBytes: UInt = 16
}
internal static func deriveEncodedPINVerificationString(pin: String) throws -> String {
guard let pinData = normalizePin(pin).data(using: .utf8) else { throw SVR.SVRError.assertion }
return try LibSignalClient.hashLocalPin(pinData)
}
internal static func verifyPIN(
pin: String,
againstEncodedPINVerificationString encodedPINVerificationString: String
) -> Bool {
guard let pinData = normalizePin(pin).data(using: .utf8) else {
owsFailDebug("failed to determine pin data")
return false
}
do {
return try LibSignalClient.verifyLocalPin(pinData, againstEncodedHash: encodedPINVerificationString)
} catch {
owsFailDebug("Failed to validate encodedVerificationString with error: \(error)")
return false
}
}
internal static func deriveSVR1EncryptionKeyAndAccessKey(
pin: String,
backupId: Data
) throws -> (encryptionKey: Data, accessKey: Data) {
guard let pinData = normalizePin(pin).data(using: .utf8) else { throw SVR.SVRError.assertion }
guard backupId.count == 32 else { throw SVR.SVRError.assertion }
// From method documentation:
// Note: This should be used with SVR1 only. For SVR1, the salt should be the backup id.
// For SVR2 clients, use ``PinHash/init(pin:username:mrenclave:)`` which handles salt construction.
let pinHash = try LibSignalClient.PinHash(normalizedPin: pinData, salt: backupId)
return (encryptionKey: Data(pinHash.encryptionKey), accessKey: Data(pinHash.accessKey))
}
}