@@ -23,6 +23,8 @@ enum EncryptionKeyStoreError: Error, ErrorWithParameters {
23
23
case storageFailed( OSStatus )
24
24
case readFailed( OSStatus )
25
25
case deletionFailed( OSStatus )
26
+ case cannotTransformDataToString( OSStatus )
27
+ case cannotTransfrotmStringToBase64Data( OSStatus )
26
28
27
29
var errorParameters : [ String : String ] {
28
30
switch self {
@@ -32,6 +34,10 @@ enum EncryptionKeyStoreError: Error, ErrorWithParameters {
32
34
return [ Pixel . Parameters. keychainErrorCode: " \( status) " ]
33
35
case . deletionFailed( let status) :
34
36
return [ Pixel . Parameters. keychainErrorCode: " \( status) " ]
37
+ case . cannotTransformDataToString( let status) :
38
+ return [ Pixel . Parameters. keychainErrorCode: " \( status) " ]
39
+ case . cannotTransfrotmStringToBase64Data( let status) :
40
+ return [ Pixel . Parameters. keychainErrorCode: " \( status) " ]
35
41
}
36
42
}
37
43
}
@@ -45,6 +51,8 @@ final class EncryptionKeyStore: EncryptionKeyStoring {
45
51
static let encryptionKeyAccount = " com.duckduckgo.macos.browser "
46
52
#endif
47
53
static let encryptionKeyService = " DuckDuckGo Privacy Browser Data Encryption Key "
54
+
55
+ static let encryptionKeyServiceBase64 = " DuckDuckGo Privacy Browser Encryption Key v2 "
48
56
}
49
57
50
58
private let generator : EncryptionKeyGenerating
@@ -53,8 +61,7 @@ final class EncryptionKeyStore: EncryptionKeyStoring {
53
61
private var defaultKeychainQueryAttributes : [ String : Any ] {
54
62
return [
55
63
kSecClass: kSecClassGenericPassword,
56
- kSecAttrAccount: account,
57
- kSecUseDataProtectionKeychain: true
64
+ kSecAttrAccount: account
58
65
] as [ String : Any ]
59
66
}
60
67
@@ -70,25 +77,43 @@ final class EncryptionKeyStore: EncryptionKeyStoring {
70
77
// MARK: - Keychain
71
78
72
79
func store( key: SymmetricKey ) throws {
73
- var query = defaultKeychainQueryAttributes
74
- query [ kSecAttrService as String ] = Constants . encryptionKeyService
75
- query [ kSecAttrAccessible as String ] = kSecAttrAccessibleWhenUnlocked
76
- query [ kSecValueData as String ] = key. dataRepresentation
80
+ let attributes : [ String : Any ] = [
81
+ kSecClass as String : kSecClassGenericPassword,
82
+ kSecAttrAccount as String : account,
83
+ kSecValueData as String : key. dataRepresentation. base64EncodedString ( ) ,
84
+ kSecAttrService as String : Constants . encryptionKeyServiceBase64,
85
+ kSecAttrAccessible as String : kSecAttrAccessibleWhenUnlocked
86
+ ]
87
+
88
+ // Add the login item to the keychain
89
+ let status = SecItemAdd ( attributes as CFDictionary , nil )
90
+
91
+ guard status == errSecSuccess else {
92
+ throw EncryptionKeyStoreError . storageFailed ( status)
93
+ }
94
+ }
77
95
78
- let status = SecItemAdd ( query as CFDictionary , nil )
79
-
80
- guard status == errSecSuccess else {
81
- throw EncryptionKeyStoreError . storageFailed ( status)
96
+ func readKey( ) throws -> SymmetricKey {
97
+ /// Needed to change how we save the key
98
+ /// Checks if the base64 non iCloud key already exist
99
+ /// if so we return
100
+ if let key = try ? readKeyFromKeychain ( account: account, format: . base64) {
101
+ return key
102
+ }
103
+ /// If the base64 key does not exist we check if we have the legacy key
104
+ /// if so we store it as base64 local item key
105
+ if let key = try readKeyFromKeychain ( account: account, format: . raw) {
106
+ try store ( key: key)
82
107
}
83
- }
84
108
85
- func readKey( ) throws -> SymmetricKey {
86
- if let key = try readKeyFromKeychain ( account: account) {
109
+ /// We try again to retrieve the base64 non iCloud key
110
+ /// if so we return the key
111
+ /// otherwise we generate a new one and store it
112
+ if let key = try readKeyFromKeychain ( account: account, format: . base64) {
87
113
return key
88
114
} else {
89
115
let generatedKey = generator. randomKey ( )
90
116
try store ( key: generatedKey)
91
-
92
117
return generatedKey
93
118
}
94
119
}
@@ -105,11 +130,22 @@ final class EncryptionKeyStore: EncryptionKeyStoring {
105
130
106
131
// MARK: - Private
107
132
108
- private func readKeyFromKeychain( account: String ) throws -> SymmetricKey ? {
133
+ private enum KeyFormat {
134
+ case raw
135
+ case base64
136
+ }
137
+
138
+ private func readKeyFromKeychain( account: String , format: KeyFormat ) throws -> SymmetricKey ? {
109
139
var query = defaultKeychainQueryAttributes
110
140
query [ kSecReturnData as String ] = true
111
141
112
142
var item : CFTypeRef ?
143
+ switch format {
144
+ case . raw:
145
+ query [ kSecAttrService as String ] = Constants . encryptionKeyService
146
+ case . base64:
147
+ query [ kSecAttrService as String ] = Constants . encryptionKeyServiceBase64
148
+ }
113
149
let status = SecItemCopyMatching ( query as CFDictionary , & item)
114
150
115
151
switch status {
@@ -118,12 +154,25 @@ final class EncryptionKeyStore: EncryptionKeyStoring {
118
154
throw EncryptionKeyStoreError . readFailed ( status)
119
155
}
120
156
121
- return SymmetricKey ( data: data)
157
+ let finalData : Data
158
+ switch format {
159
+ case . raw:
160
+ finalData = data
161
+ case . base64:
162
+ guard let base64String = String ( data: data, encoding: . utf8) else {
163
+ throw EncryptionKeyStoreError . cannotTransformDataToString ( status)
164
+ }
165
+ guard let keyData = Data ( base64Encoded: base64String) else {
166
+ throw EncryptionKeyStoreError . cannotTransfrotmStringToBase64Data ( status)
167
+ }
168
+ finalData = keyData
169
+ }
170
+ print ( finalData. base64EncodedString ( ) )
171
+ return SymmetricKey ( data: finalData)
122
172
case errSecItemNotFound:
123
173
return nil
124
174
default :
125
175
throw EncryptionKeyStoreError . readFailed ( status)
126
176
}
127
177
}
128
-
129
178
}
0 commit comments