ios/BitwardenWatchApp/Services/CryptoService.swift
2024-01-16 06:46:13 -07:00

103 lines
2.9 KiB
Swift

import CryptoKit
import Foundation
public class CryptoService {
static let ENCRYPTION_KEY: String = "encryptionKey"
private(set) var key: SymmetricKey?
init() {
key = loadKey()
}
func encrypt(_ plainText: String?) -> Data? {
guard let plainText, let key else {
return nil
}
let nonce = randomData(lengthInBytes: 12)
let plainData = plainText.data(using: .utf8)
let sealedData = try! AES.GCM.seal(plainData!, using: key, nonce: AES.GCM.Nonce(data: nonce))
return sealedData.combined
}
func decrypt<T: Decodable>(_ combinedEncryptedData: Data, _ type: T.Type) -> T? {
guard let key else {
return nil
}
let sealedBox = try! AES.GCM.SealedBox(combined: combinedEncryptedData)
let decryptedData = try! AES.GCM.open(sealedBox, using: key)
do {
let item = try JSONDecoder().decode(type, from: decryptedData)
return item
} catch {
Log.e("Fail to decode item for keychain: \(error)")
return nil
}
}
func decrypt(_ combinedEncryptedData: Data) -> Data? {
guard let key else {
return nil
}
let sealedBox = try! AES.GCM.SealedBox(combined: combinedEncryptedData)
let decryptedData = try! AES.GCM.open(sealedBox, using: key)
return decryptedData
}
func loadKey() -> SymmetricKey {
if let encKey = KeychainHelper.standard.read(CryptoService.ENCRYPTION_KEY) {
return SymmetricKey(data: encKey)
}
// First time so we need to generate the key
let newKey = SymmetricKey(size: .bits256)
let keyData = newKey.withUnsafeBytes { body in
Data(Array(body))
}
KeychainHelper.standard.save(keyData, CryptoService.ENCRYPTION_KEY)
return newKey
}
func randomData(lengthInBytes: Int) -> Data {
var data = Data(count: lengthInBytes)
_ = data.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, lengthInBytes, $0.baseAddress!)
}
return data
}
static func clearKey() {
KeychainHelper.standard.delete(CryptoService.ENCRYPTION_KEY)
}
}
public extension Data {
init?(hexString: String) {
let len = hexString.count / 2
var data = Data(capacity: len)
var i = hexString.startIndex
for _ in 0 ..< len {
let j = hexString.index(i, offsetBy: 2)
let bytes = hexString[i ..< j]
if var num = UInt8(bytes, radix: 16) {
data.append(&num, count: 1)
} else {
return nil
}
i = j
}
self = data
}
/// Hexadecimal string representation of `Data` object.
var hexadecimal: String {
map { String(format: "%02x", $0) }
.joined()
}
}