Skip to content

Commit

Permalink
WIP: Implement AddressBook encryption
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d authored and LukasKorba committed Oct 28, 2024
1 parent 3fe494a commit 21c539f
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import RemoteStorage
import Combine

import WalletStorage
import CryptoKit

extension AddressBookClient: DependencyKey {
private enum Constants {
Expand Down Expand Up @@ -157,14 +158,27 @@ extension AddressBookClient: DependencyKey {
}
)
}


/**
* Derives a one-time address book encryption key.
*
* At encryption time, the one-time property MUST be ensured by generating a
* random 32-byte salt.
*/
private static func deriveEncryptionKey(
addressBookKey: SymmetricKey,
salt: Data
) -> SymmetricKey {
assert(salt.count == 32)
return HKDF<SHA256>.deriveKey(inputKeyMaterial: addressBookKey, salt: salt, outputByteCount: 32)
}

private static func encryptContacts(_ abContacts: AddressBookContacts) throws -> Data {
@Dependency(\.walletStorage) var walletStorage

// TODO: str4d
// guard let encryptionKeys = try? walletStorage.exportAddressBookEncryptionKeys() else {
// throw AddressBookClient.AddressBookClientError.missingEncryptionKey
// }
guard let encryptionKeys = try? walletStorage.exportAddressBookEncryptionKeys() else {
throw AddressBookClient.AddressBookClientError.missingEncryptionKey
}

// here you have an array of all contacts
// you also have a key from the keychain
Expand All @@ -185,20 +199,36 @@ extension AddressBookClient: DependencyKey {
data.append(serializedContact)
}

return data
// Generate a fresh one-time sub-key for encrypting the address book.
var salt = SymmetricKey(size: SymmetricKeySize.bits256)
return try salt.withUnsafeBytes { salt in
var salt = Data(salt)
var subKey = deriveEncryptionKey(addressBookKey: encryptionKeys.key, salt: salt)

// Encrypt the serialized address book.
// CryptoKit encodes the SealedBox as `nonce || ciphertext || tag`.
var sealed = try ChaChaPoly.seal(data, using: subKey)

// Prepand the salt to the SealedBox so we can re-derive the sub-key.
return salt + sealed.combined
}
}

private static func decryptData(_ data: Data) throws -> AddressBookContacts {
private static func decryptData(_ encrypted: Data) throws -> AddressBookContacts {
@Dependency(\.walletStorage) var walletStorage

// TODO: str4d
// guard let encryptionKeys = try? walletStorage.exportAddressBookEncryptionKeys() else {
// throw AddressBookClient.AddressBookClientError.missingEncryptionKey
// }
guard let encryptionKeys = try? walletStorage.exportAddressBookEncryptionKeys() else {
throw AddressBookClient.AddressBookClientError.missingEncryptionKey
}

// Derive the sub-key for decrypting the address book.
var salt = encrypted.prefix(upTo: 32)
var subKey = deriveEncryptionKey(addressBookKey: encryptionKeys.key, salt: salt)

// Unseal the encrypted address book.
var sealed = try ChaChaPoly.SealedBox.init(combined: encrypted.suffix(from: 32))
var data = try ChaChaPoly.open(sealed, using: subKey)

// here you have the encrypted data from the cloud, the blob
// you also have a key from the keychain

var offset = 0

// Deserialize `version`
Expand Down
5 changes: 3 additions & 2 deletions modules/Sources/Models/AddressBookEncryptionKeys.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
//

import Foundation
import CryptoKit

/// Representation of the address book encryption keys
public struct AddressBookEncryptionKeys: Codable, Equatable {
public let key: String
public let key: SymmetricKey

public init(key: String) {
public init(key: SymmetricKey) {
self.key = key
}
}
Expand Down

0 comments on commit 21c539f

Please sign in to comment.