Re-land: C++ readability review
[chromium-blink-merge.git] / net / third_party / mozilla_security_manager / nsKeygenHandler.cpp
blobf239cc14c3d53554f258eee8a1c69e3c15f63118
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Vipul Gupta <vipul.gupta@sun.com>
25 * Douglas Stebila <douglas@stebila.ca>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 #include "net/third_party/mozilla_security_manager/nsKeygenHandler.h"
43 #include <pk11pub.h>
44 #include <prerror.h> // PR_GetError()
45 #include <secmod.h>
46 #include <secder.h> // DER_Encode()
47 #include <cryptohi.h> // SEC_DerSignData()
48 #include <keyhi.h> // SECKEY_CreateSubjectPublicKeyInfo()
50 #include "base/base64.h"
51 #include "base/logging.h"
52 #include "crypto/nss_util.h"
53 #include "url/gurl.h"
55 namespace {
57 // Template for creating the signed public key structure to be sent to the CA.
58 DERTemplate SECAlgorithmIDTemplate[] = {
59 { DER_SEQUENCE,
60 0, NULL, sizeof(SECAlgorithmID) },
61 { DER_OBJECT_ID,
62 offsetof(SECAlgorithmID, algorithm), },
63 { DER_OPTIONAL | DER_ANY,
64 offsetof(SECAlgorithmID, parameters), },
65 { 0, }
68 DERTemplate CERTSubjectPublicKeyInfoTemplate[] = {
69 { DER_SEQUENCE,
70 0, NULL, sizeof(CERTSubjectPublicKeyInfo) },
71 { DER_INLINE,
72 offsetof(CERTSubjectPublicKeyInfo, algorithm),
73 SECAlgorithmIDTemplate, },
74 { DER_BIT_STRING,
75 offsetof(CERTSubjectPublicKeyInfo, subjectPublicKey), },
76 { 0, }
79 DERTemplate CERTPublicKeyAndChallengeTemplate[] = {
80 { DER_SEQUENCE,
81 0, NULL, sizeof(CERTPublicKeyAndChallenge) },
82 { DER_ANY,
83 offsetof(CERTPublicKeyAndChallenge, spki), },
84 { DER_IA5_STRING,
85 offsetof(CERTPublicKeyAndChallenge, challenge), },
86 { 0, }
89 } // namespace
91 namespace mozilla_security_manager {
93 // This function is based on the nsKeygenFormProcessor::GetPublicKey function
94 // in mozilla/security/manager/ssl/src/nsKeygenHandler.cpp.
95 std::string GenKeyAndSignChallenge(int key_size_in_bits,
96 const std::string& challenge,
97 const GURL& url,
98 PK11SlotInfo* slot,
99 bool stores_key) {
100 // Key pair generation mechanism - only RSA is supported at present.
101 PRUint32 keyGenMechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; // from nss/pkcs11t.h
103 // Temporary structures used for generating the result
104 // in the right format.
105 PK11RSAGenParams rsaKeyGenParams; // Keygen parameters.
106 SECOidTag algTag; // used by SEC_DerSignData().
107 SECKEYPrivateKey *privateKey = NULL;
108 SECKEYPublicKey *publicKey = NULL;
109 CERTSubjectPublicKeyInfo *spkInfo = NULL;
110 PLArenaPool *arena = NULL;
111 SECStatus sec_rv =SECFailure;
112 SECItem spkiItem;
113 SECItem pkacItem;
114 SECItem signedItem;
115 CERTPublicKeyAndChallenge pkac;
116 void *keyGenParams;
117 bool isSuccess = true; // Set to false as soon as a step fails.
119 std::string result_blob; // the result.
121 switch (keyGenMechanism) {
122 case CKM_RSA_PKCS_KEY_PAIR_GEN:
123 rsaKeyGenParams.keySizeInBits = key_size_in_bits;
124 rsaKeyGenParams.pe = DEFAULT_RSA_KEYGEN_PE;
125 keyGenParams = &rsaKeyGenParams;
127 algTag = DEFAULT_RSA_KEYGEN_ALG;
128 break;
129 default:
130 // TODO(gauravsh): If we ever support other mechanisms,
131 // this can be changed.
132 LOG(ERROR) << "Only RSA keygen mechanism is supported";
133 isSuccess = false;
134 goto failure;
137 VLOG(1) << "Creating key pair...";
139 crypto::AutoNSSWriteLock lock;
140 privateKey = PK11_GenerateKeyPair(slot,
141 keyGenMechanism,
142 keyGenParams,
143 &publicKey,
144 PR_TRUE, // isPermanent?
145 PR_TRUE, // isSensitive?
146 NULL);
148 VLOG(1) << "done.";
150 if (!privateKey) {
151 LOG(ERROR) << "Generation of Keypair failed!";
152 isSuccess = false;
153 goto failure;
156 // Set friendly names for the keys.
157 if (url.has_host()) {
158 // TODO(davidben): Use something like "Key generated for
159 // example.com", but localize it.
160 const std::string& label = url.host();
162 crypto::AutoNSSWriteLock lock;
163 PK11_SetPublicKeyNickname(publicKey, label.c_str());
164 PK11_SetPrivateKeyNickname(privateKey, label.c_str());
168 // The CA expects the signed public key in a specific format
169 // Let's create that now.
171 // Create a subject public key info from the public key.
172 spkInfo = SECKEY_CreateSubjectPublicKeyInfo(publicKey);
173 if (!spkInfo) {
174 LOG(ERROR) << "Couldn't create SubjectPublicKeyInfo from public key";
175 isSuccess = false;
176 goto failure;
179 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
180 if (!arena) {
181 LOG(ERROR) << "PORT_NewArena: Couldn't allocate memory";
182 isSuccess = false;
183 goto failure;
186 // DER encode the whole subjectPublicKeyInfo.
187 sec_rv = DER_Encode(arena, &spkiItem, CERTSubjectPublicKeyInfoTemplate,
188 spkInfo);
189 if (SECSuccess != sec_rv) {
190 LOG(ERROR) << "Couldn't DER Encode subjectPublicKeyInfo";
191 isSuccess = false;
192 goto failure;
195 // Set up the PublicKeyAndChallenge data structure, then DER encode it.
196 pkac.spki = spkiItem;
197 pkac.challenge.type = siBuffer;
198 pkac.challenge.len = challenge.length();
199 pkac.challenge.data = (unsigned char *)challenge.data();
200 sec_rv = DER_Encode(arena, &pkacItem, CERTPublicKeyAndChallengeTemplate,
201 &pkac);
202 if (SECSuccess != sec_rv) {
203 LOG(ERROR) << "Couldn't DER Encode PublicKeyAndChallenge";
204 isSuccess = false;
205 goto failure;
208 // Sign the DER encoded PublicKeyAndChallenge.
209 sec_rv = SEC_DerSignData(arena, &signedItem, pkacItem.data, pkacItem.len,
210 privateKey, algTag);
211 if (SECSuccess != sec_rv) {
212 LOG(ERROR) << "Couldn't sign the DER encoded PublicKeyandChallenge";
213 isSuccess = false;
214 goto failure;
217 // Convert the signed public key and challenge into base64/ascii.
218 base::Base64Encode(
219 std::string(reinterpret_cast<char*>(signedItem.data), signedItem.len),
220 &result_blob);
222 failure:
223 if (!isSuccess) {
224 LOG(ERROR) << "SSL Keygen failed! (NSS error code " << PR_GetError() << ")";
225 } else {
226 VLOG(1) << "SSL Keygen succeeded!";
229 // Do cleanups
230 if (privateKey) {
231 // On successful keygen we need to keep the private key, of course,
232 // or we won't be able to use the client certificate.
233 if (!isSuccess || !stores_key) {
234 crypto::AutoNSSWriteLock lock;
235 PK11_DestroyTokenObject(privateKey->pkcs11Slot, privateKey->pkcs11ID);
237 SECKEY_DestroyPrivateKey(privateKey);
240 if (publicKey) {
241 if (!isSuccess || !stores_key) {
242 crypto::AutoNSSWriteLock lock;
243 PK11_DestroyTokenObject(publicKey->pkcs11Slot, publicKey->pkcs11ID);
245 SECKEY_DestroyPublicKey(publicKey);
247 if (spkInfo) {
248 SECKEY_DestroySubjectPublicKeyInfo(spkInfo);
250 if (arena) {
251 PORT_FreeArena(arena, PR_TRUE);
254 return (isSuccess ? result_blob : std::string());
257 } // namespace mozilla_security_manager