Progress towards CMake on Mac
[WebKit-https.git] / Source / WebCore / crypto / mac / SerializedCryptoKeyWrapMac.mm
1 /*
2  * Copyright (C) 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "SerializedCryptoKeyWrap.h"
28
29 #if ENABLE(SUBTLE_CRYPTO)
30
31 #include "CommonCryptoUtilities.h"
32 #include "LocalizedStrings.h"
33 #include <CommonCrypto/CommonSymmetricKeywrap.h>
34 #include <crt_externs.h>
35 #include <wtf/text/Base64.h>
36 #include <wtf/text/CString.h>
37 #include <wtf/CryptographicUtilities.h>
38 #include <wtf/RetainPtr.h>
39
40 #define USE_KEYCHAIN_ACCESS_CONTROL_LISTS (!PLATFORM(IOS))
41
42 namespace WebCore {
43
44 const NSUInteger currentSerializationVersion = 1;
45
46 const NSString* versionKey = @"version";
47 const NSString* wrappedKEKKey = @"wrappedKEK";
48 const NSString* encryptedKeyKey = @"encryptedKey";
49 const NSString* tagKey = @"tag";
50
51 const size_t masterKeySizeInBytes = 16;
52
53 inline Vector<uint8_t> vectorFromNSData(NSData* data)
54 {
55     Vector<uint8_t> result;
56     result.append((const uint8_t*)[data bytes], [data length]);
57     return result;
58 }
59
60 static NSString* masterKeyAccountNameForCurrentApplication()
61 {
62 #if PLATFORM(IOS)
63     NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
64 #else
65     NSString *bundleIdentifier = [[NSRunningApplication currentApplication] bundleIdentifier];
66 #endif
67     if (!bundleIdentifier)
68         bundleIdentifier = [NSString stringWithCString:*_NSGetProgname() encoding:NSASCIIStringEncoding];
69     return [NSString stringWithFormat:@"com.apple.WebKit.WebCrypto.master+%@", bundleIdentifier];
70 }
71
72 static bool createAndStoreMasterKey(Vector<uint8_t>& masterKeyData)
73 {
74     masterKeyData.resize(masterKeySizeInBytes);
75     CCRandomCopyBytes(kCCRandomDefault, masterKeyData.data(), masterKeyData.size());
76
77 #if PLATFORM(IOS)
78     NSBundle *mainBundle = [NSBundle mainBundle];
79     NSString *applicationName = [mainBundle objectForInfoDictionaryKey:@"CFBundleDisplayName"];
80     if (!applicationName)
81         applicationName = [mainBundle objectForInfoDictionaryKey:(NSString *)kCFBundleNameKey];
82     if (!applicationName)
83         applicationName = [mainBundle bundleIdentifier];
84     NSString *localizedItemName = webCryptoMasterKeyKeychainLabel(applicationName);
85 #else
86     NSString *localizedItemName = webCryptoMasterKeyKeychainLabel([[NSRunningApplication currentApplication] localizedName]);
87 #endif
88
89     OSStatus status;
90
91 #if USE(KEYCHAIN_ACCESS_CONTROL_LISTS)
92     SecAccessRef accessRef;
93     status = SecAccessCreate((CFStringRef)localizedItemName, nullptr, &accessRef);
94     if (status) {
95         WTFLogAlways("Cannot create a security access object for storing WebCrypto master key, error %d", (int)status);
96         return false;
97     }
98     RetainPtr<SecAccessRef> access = adoptCF(accessRef);
99
100     RetainPtr<CFArrayRef> acls = adoptCF(SecAccessCopyMatchingACLList(accessRef, kSecACLAuthorizationExportClear));
101     SecACLRef acl = (SecACLRef)CFArrayGetValueAtIndex(acls.get(), 0);
102
103     SecTrustedApplicationRef trustedAppRef;
104     status = SecTrustedApplicationCreateFromPath(0, &trustedAppRef);
105     if (status) {
106         WTFLogAlways("Cannot create a trusted application object for storing WebCrypto master key, error %d", (int)status);
107         return false;
108     }
109     RetainPtr<SecTrustedApplicationRef> trustedApp = adoptCF(trustedAppRef);
110
111     status = SecACLSetContents(acl, (CFArrayRef)@[ (id)trustedApp.get() ], (CFStringRef)localizedItemName, kSecKeychainPromptRequirePassphase);
112     if (status) {
113         WTFLogAlways("Cannot set ACL for WebCrypto master key, error %d", (int)status);
114         return false;
115     }
116 #endif
117
118     Vector<char> base64EncodedMasterKeyData;
119     base64Encode(masterKeyData, base64EncodedMasterKeyData);
120
121     // Cannot use kSecClassKey because of <rdar://problem/16068207>.
122     NSDictionary *attributes = @{
123         (id)kSecClass : (id)kSecClassGenericPassword,
124         (id)kSecAttrSynchronizable : @NO,
125 #if USE(KEYCHAIN_ACCESS_CONTROL_LISTS)
126         (id)kSecAttrAccess : (id)access.get(),
127 #endif
128         (id)kSecAttrComment : webCryptoMasterKeyKeychainComment(),
129         (id)kSecAttrLabel : localizedItemName,
130         (id)kSecAttrAccount : masterKeyAccountNameForCurrentApplication(),
131         (id)kSecValueData : [NSData dataWithBytes:base64EncodedMasterKeyData.data() length:base64EncodedMasterKeyData.size()],
132     };
133
134     status = SecItemAdd((CFDictionaryRef)attributes, nullptr);
135     if (status) {
136         WTFLogAlways("Cannot store WebCrypto master key, error %d", (int)status);
137         return false;
138     }
139     return true;
140 }
141
142 static bool findMasterKey(Vector<uint8_t>& masterKeyData)
143 {
144     NSDictionary *query = @{
145         (id)kSecClass : (id)kSecClassGenericPassword,
146         (id)kSecAttrAccount : masterKeyAccountNameForCurrentApplication(),
147         (id)kSecReturnData : @YES,
148     };
149
150     CFDataRef keyDataRef;
151     OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef*)&keyDataRef);
152     if (status) {
153         if (status != errSecItemNotFound && status != errSecUserCanceled)
154             WTFLogAlways("Could not find WebCrypto master key in Keychain, error %d", (int)status);
155         return false;
156     }
157     RetainPtr<CFDataRef> keyData = adoptCF(keyDataRef);
158
159     Vector<uint8_t> base64EncodedMasterKeyData = vectorFromNSData((NSData *)keyData.get());
160     return base64Decode((const char*)base64EncodedMasterKeyData.data(), base64EncodedMasterKeyData.size(), masterKeyData);
161 }
162
163 bool getDefaultWebCryptoMasterKey(Vector<uint8_t>& masterKey)
164 {
165     if (!findMasterKey(masterKey) && !createAndStoreMasterKey(masterKey))
166         return false;
167     RELEASE_ASSERT(masterKey.size() == masterKeySizeInBytes);
168     return true;
169 }
170
171 bool wrapSerializedCryptoKey(const Vector<uint8_t>& masterKey, const Vector<uint8_t>& key, Vector<uint8_t>& result)
172 {
173     Vector<uint8_t> kek(16);
174     CCRandomCopyBytes(kCCRandomDefault, kek.data(), kek.size());
175
176     Vector<uint8_t> wrappedKEK(CCSymmetricWrappedSize(kCCWRAPAES, kek.size()));
177
178     size_t wrappedKEKSize = wrappedKEK.size();
179     CCCryptorStatus status = CCSymmetricKeyWrap(kCCWRAPAES, CCrfc3394_iv, CCrfc3394_ivLen, masterKey.data(), masterKey.size(), kek.data(), kek.size(), wrappedKEK.data(), &wrappedKEKSize);
180     if (status != kCCSuccess)
181         return false;
182
183     wrappedKEK.shrink(wrappedKEKSize);
184
185     Vector<uint8_t> encryptedKey(key.size());
186     size_t tagLength = 16;
187     uint8_t tag[tagLength];
188
189     status = CCCryptorGCM(kCCEncrypt, kCCAlgorithmAES128, kek.data(), kek.size(),
190         nullptr, 0, // iv
191         nullptr, 0, // auth data
192         key.data(), key.size(),
193         encryptedKey.data(),
194         tag, &tagLength);
195
196     if (status != kCCSuccess)
197         return false;
198     RELEASE_ASSERT(tagLength == 16);
199
200     auto dictionary = @{
201         versionKey: [NSNumber numberWithUnsignedInteger:currentSerializationVersion],
202         wrappedKEKKey: [NSData dataWithBytes:wrappedKEK.data() length:wrappedKEK.size()],
203         encryptedKeyKey: [NSData dataWithBytes:encryptedKey.data() length:encryptedKey.size()],
204         tagKey: [NSData dataWithBytes:tag length:tagLength]
205     };
206
207     NSData* serialization = [NSPropertyListSerialization dataWithPropertyList:dictionary format:NSPropertyListBinaryFormat_v1_0 options:0 error:nullptr];
208     if (!serialization)
209         return false;
210
211     result = vectorFromNSData(serialization);
212     return true;
213 }
214
215 bool unwrapSerializedCryptoKey(const Vector<uint8_t>& masterKey, const Vector<uint8_t>& wrappedKey, Vector<uint8_t>& key)
216 {
217     NSDictionary* dictionary = [NSPropertyListSerialization propertyListWithData:[NSData dataWithBytesNoCopy:(void*)wrappedKey.data() length:wrappedKey.size() freeWhenDone:NO] options:0 format:nullptr error:nullptr];
218     if (!dictionary)
219         return false;
220
221     id versionObject = [dictionary objectForKey:versionKey];
222     if (![versionObject isKindOfClass:[NSNumber class]])
223         return false;
224     if ([versionObject unsignedIntegerValue] > currentSerializationVersion)
225         return false;
226
227     id wrappedKEKObject = [dictionary objectForKey:wrappedKEKKey];
228     if (![wrappedKEKObject isKindOfClass:[NSData class]])
229         return false;
230     Vector<uint8_t> wrappedKEK = vectorFromNSData(wrappedKEKObject);
231
232     id encryptedKeyObject = [dictionary objectForKey:encryptedKeyKey];
233     if (![encryptedKeyObject isKindOfClass:[NSData class]])
234         return false;
235     Vector<uint8_t> encryptedKey = vectorFromNSData(encryptedKeyObject);
236
237     id tagObject = [dictionary objectForKey:tagKey];
238     if (![tagObject isKindOfClass:[NSData class]])
239         return false;
240     Vector<uint8_t> tag = vectorFromNSData(tagObject);
241     if (tag.size() != 16)
242         return false;
243
244     Vector<uint8_t> kek(CCSymmetricUnwrappedSize(kCCWRAPAES, wrappedKEK.size()));
245     size_t kekSize = kek.size();
246     CCCryptorStatus status = CCSymmetricKeyUnwrap(kCCWRAPAES, CCrfc3394_iv, CCrfc3394_ivLen, masterKey.data(), masterKey.size(), wrappedKEK.data(), wrappedKEK.size(), kek.data(), &kekSize);
247     if (status != kCCSuccess)
248         return false;
249     kek.shrink(kekSize);
250
251     size_t tagLength = 16;
252     uint8_t actualTag[tagLength];
253
254     key.resize(encryptedKey.size());
255     status = CCCryptorGCM(kCCDecrypt, kCCAlgorithmAES128, kek.data(), kek.size(),
256         nullptr, 0, // iv
257         nullptr, 0, // auth data
258         encryptedKey.data(), encryptedKey.size(),
259         key.data(),
260         actualTag, &tagLength);
261
262     if (status != kCCSuccess)
263         return false;
264     RELEASE_ASSERT(tagLength == 16);
265
266     if (constantTimeMemcmp(tag.data(), actualTag, tagLength))
267         return false;
268
269     return true;
270 }
271
272 }
273
274 #endif // ENABLE(SUBTLE_CRYPTO)