Roll src/third_party/WebKit 8121bc6:918aba1 (svn 188871:188878)
[chromium-blink-merge.git] / net / base / keygen_handler_mac.cc
blob63ea84751aa1d926cc7fb215edf03d5431567204
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/base/keygen_handler.h"
7 #include <Security/SecAsn1Coder.h>
8 #include <Security/SecAsn1Templates.h>
9 #include <Security/Security.h>
11 #include "base/base64.h"
12 #include "base/logging.h"
13 #include "base/mac/mac_logging.h"
14 #include "base/mac/scoped_cftyperef.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "base/synchronization/lock.h"
18 #include "crypto/cssm_init.h"
19 #include "crypto/mac_security_services_lock.h"
21 // These are in Security.framework but not declared in a public header.
22 extern const SecAsn1Template kSecAsn1AlgorithmIDTemplate[];
23 extern const SecAsn1Template kSecAsn1SubjectPublicKeyInfoTemplate[];
25 namespace net {
27 // Declarations of Netscape keygen cert structures for ASN.1 encoding:
29 struct PublicKeyAndChallenge {
30 CSSM_X509_SUBJECT_PUBLIC_KEY_INFO spki;
31 CSSM_DATA challenge_string;
34 // This is a copy of the built-in kSecAsn1IA5StringTemplate, but without the
35 // 'streamable' flag, which was causing bogus data to be written.
36 const SecAsn1Template kIA5StringTemplate[] = {
37 { SEC_ASN1_IA5_STRING, 0, NULL, sizeof(CSSM_DATA) }
40 static const SecAsn1Template kPublicKeyAndChallengeTemplate[] = {
42 SEC_ASN1_SEQUENCE,
44 NULL,
45 sizeof(PublicKeyAndChallenge)
48 SEC_ASN1_INLINE,
49 offsetof(PublicKeyAndChallenge, spki),
50 kSecAsn1SubjectPublicKeyInfoTemplate
53 SEC_ASN1_INLINE,
54 offsetof(PublicKeyAndChallenge, challenge_string),
55 kIA5StringTemplate
62 struct SignedPublicKeyAndChallenge {
63 PublicKeyAndChallenge pkac;
64 CSSM_X509_ALGORITHM_IDENTIFIER signature_algorithm;
65 CSSM_DATA signature;
68 static const SecAsn1Template kSignedPublicKeyAndChallengeTemplate[] = {
70 SEC_ASN1_SEQUENCE,
72 NULL,
73 sizeof(SignedPublicKeyAndChallenge)
76 SEC_ASN1_INLINE,
77 offsetof(SignedPublicKeyAndChallenge, pkac),
78 kPublicKeyAndChallengeTemplate
81 SEC_ASN1_INLINE,
82 offsetof(SignedPublicKeyAndChallenge, signature_algorithm),
83 kSecAsn1AlgorithmIDTemplate
86 SEC_ASN1_BIT_STRING,
87 offsetof(SignedPublicKeyAndChallenge, signature)
95 static OSStatus CreateRSAKeyPair(int size_in_bits,
96 SecAccessRef initial_access,
97 SecKeyRef* out_pub_key,
98 SecKeyRef* out_priv_key);
99 static OSStatus SignData(CSSM_DATA data,
100 SecKeyRef private_key,
101 CSSM_DATA* signature);
103 std::string KeygenHandler::GenKeyAndSignChallenge() {
104 std::string result;
105 OSStatus err;
106 SecAccessRef initial_access = NULL;
107 SecKeyRef public_key = NULL;
108 SecKeyRef private_key = NULL;
109 SecAsn1CoderRef coder = NULL;
110 CSSM_DATA signature = {0, NULL};
113 if (url_.has_host()) {
114 // TODO(davidben): Use something like "Key generated for
115 // example.com", but localize it.
116 base::ScopedCFTypeRef<CFStringRef> label(
117 base::SysUTF8ToCFStringRef(url_.host()));
118 // Create an initial access object to set the SecAccessRef. This
119 // sets a label on the Keychain dialogs. Pass NULL as the second
120 // argument to use the default trusted list; only allow the
121 // current application to access without user confirmation.
122 err = SecAccessCreate(label, NULL, &initial_access);
123 // If we fail, just continue without a label.
124 if (err)
125 crypto::LogCSSMError("SecAccessCreate", err);
128 // Create the key-pair.
129 err = CreateRSAKeyPair(key_size_in_bits_, initial_access,
130 &public_key, &private_key);
131 if (err)
132 goto failure;
134 // Get the public key data (DER sequence of modulus, exponent).
135 CFDataRef key_data = NULL;
136 err = SecKeychainItemExport(public_key, kSecFormatBSAFE, 0, NULL,
137 &key_data);
138 if (err) {
139 crypto::LogCSSMError("SecKeychainItemExpor", err);
140 goto failure;
142 base::ScopedCFTypeRef<CFDataRef> scoped_key_data(key_data);
144 // Create an ASN.1 encoder.
145 err = SecAsn1CoderCreate(&coder);
146 if (err) {
147 crypto::LogCSSMError("SecAsn1CoderCreate", err);
148 goto failure;
151 // Fill in and DER-encode the PublicKeyAndChallenge:
152 SignedPublicKeyAndChallenge spkac;
153 memset(&spkac, 0, sizeof(spkac));
154 spkac.pkac.spki.algorithm.algorithm = CSSMOID_RSA;
155 spkac.pkac.spki.subjectPublicKey.Length =
156 CFDataGetLength(key_data) * 8; // interpreted as a _bit_ count
157 spkac.pkac.spki.subjectPublicKey.Data =
158 const_cast<uint8_t*>(CFDataGetBytePtr(key_data));
159 spkac.pkac.challenge_string.Length = challenge_.length();
160 spkac.pkac.challenge_string.Data =
161 reinterpret_cast<uint8_t*>(const_cast<char*>(challenge_.data()));
163 CSSM_DATA encoded;
164 err = SecAsn1EncodeItem(coder, &spkac.pkac,
165 kPublicKeyAndChallengeTemplate, &encoded);
166 if (err) {
167 crypto::LogCSSMError("SecAsn1EncodeItem", err);
168 goto failure;
171 // Compute a signature of the result:
172 err = SignData(encoded, private_key, &signature);
173 if (err)
174 goto failure;
175 spkac.signature.Data = signature.Data;
176 spkac.signature.Length = signature.Length * 8; // a _bit_ count
177 spkac.signature_algorithm.algorithm = CSSMOID_MD5WithRSA;
178 // TODO(snej): MD5 is weak. Can we use SHA1 instead?
179 // See <https://bugzilla.mozilla.org/show_bug.cgi?id=549460>
181 // DER-encode the entire SignedPublicKeyAndChallenge:
182 err = SecAsn1EncodeItem(coder, &spkac,
183 kSignedPublicKeyAndChallengeTemplate, &encoded);
184 if (err) {
185 crypto::LogCSSMError("SecAsn1EncodeItem", err);
186 goto failure;
189 // Base64 encode the result.
190 std::string input(reinterpret_cast<char*>(encoded.Data), encoded.Length);
191 base::Base64Encode(input, &result);
194 failure:
195 if (err)
196 OSSTATUS_LOG(ERROR, err) << "SSL Keygen failed!";
197 else
198 VLOG(1) << "SSL Keygen succeeded! Output is: " << result;
200 // Remove keys from keychain if asked to during unit testing:
201 if (!stores_key_) {
202 if (public_key)
203 SecKeychainItemDelete(reinterpret_cast<SecKeychainItemRef>(public_key));
204 if (private_key)
205 SecKeychainItemDelete(reinterpret_cast<SecKeychainItemRef>(private_key));
208 // Clean up:
209 free(signature.Data);
210 if (coder)
211 SecAsn1CoderRelease(coder);
212 if (initial_access)
213 CFRelease(initial_access);
214 if (public_key)
215 CFRelease(public_key);
216 if (private_key)
217 CFRelease(private_key);
218 return result;
222 // Create an RSA key pair with size |size_in_bits|. |initial_access|
223 // is passed as the initial access control list in Keychain. The
224 // public and private keys are placed in |out_pub_key| and
225 // |out_priv_key|, respectively.
226 static OSStatus CreateRSAKeyPair(int size_in_bits,
227 SecAccessRef initial_access,
228 SecKeyRef* out_pub_key,
229 SecKeyRef* out_priv_key) {
230 OSStatus err;
231 SecKeychainRef keychain;
232 err = SecKeychainCopyDefault(&keychain);
233 if (err) {
234 crypto::LogCSSMError("SecKeychainCopyDefault", err);
235 return err;
237 base::ScopedCFTypeRef<SecKeychainRef> scoped_keychain(keychain);
239 base::AutoLock locked(crypto::GetMacSecurityServicesLock());
240 err = SecKeyCreatePair(
241 keychain,
242 CSSM_ALGID_RSA,
243 size_in_bits,
244 0LL,
245 // public key usage and attributes:
246 CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP,
247 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT,
248 // private key usage and attributes:
249 CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_SIGN | CSSM_KEYUSE_UNWRAP,
250 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT |
251 CSSM_KEYATTR_SENSITIVE,
252 initial_access,
253 out_pub_key, out_priv_key);
255 if (err)
256 crypto::LogCSSMError("SecKeyCreatePair", err);
257 return err;
260 static OSStatus CreateSignatureContext(SecKeyRef key,
261 CSSM_ALGORITHMS algorithm,
262 CSSM_CC_HANDLE* out_cc_handle) {
263 OSStatus err;
264 const CSSM_ACCESS_CREDENTIALS* credentials = NULL;
266 base::AutoLock locked(crypto::GetMacSecurityServicesLock());
267 err = SecKeyGetCredentials(key,
268 CSSM_ACL_AUTHORIZATION_SIGN,
269 kSecCredentialTypeDefault,
270 &credentials);
272 if (err) {
273 crypto::LogCSSMError("SecKeyGetCredentials", err);
274 return err;
277 CSSM_CSP_HANDLE csp_handle = 0;
279 base::AutoLock locked(crypto::GetMacSecurityServicesLock());
280 err = SecKeyGetCSPHandle(key, &csp_handle);
282 if (err) {
283 crypto::LogCSSMError("SecKeyGetCSPHandle", err);
284 return err;
287 const CSSM_KEY* cssm_key = NULL;
289 base::AutoLock locked(crypto::GetMacSecurityServicesLock());
290 err = SecKeyGetCSSMKey(key, &cssm_key);
292 if (err) {
293 crypto::LogCSSMError("SecKeyGetCSSMKey", err);
294 return err;
297 err = CSSM_CSP_CreateSignatureContext(csp_handle,
298 algorithm,
299 credentials,
300 cssm_key,
301 out_cc_handle);
302 if (err)
303 crypto::LogCSSMError("CSSM_CSP_CreateSignatureContext", err);
304 return err;
307 static OSStatus SignData(CSSM_DATA data,
308 SecKeyRef private_key,
309 CSSM_DATA* signature) {
310 CSSM_CC_HANDLE cc_handle;
311 OSStatus err = CreateSignatureContext(private_key,
312 CSSM_ALGID_MD5WithRSA,
313 &cc_handle);
314 if (err) {
315 crypto::LogCSSMError("CreateSignatureContext", err);
316 return err;
318 err = CSSM_SignData(cc_handle, &data, 1, CSSM_ALGID_NONE, signature);
319 if (err)
320 crypto::LogCSSMError("CSSM_SignData", err);
321 CSSM_DeleteContext(cc_handle);
322 return err;
325 } // namespace net