mirror of
https://github.com/bitwarden/ios.git
synced 2025-12-11 13:54:06 -06:00
103 lines
2.9 KiB
Swift
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()
|
|
}
|
|
}
|