ed1da0a51e4de46f52dd45e9f17d00ee1a034f64
[WebKit-https.git] / WebKit / WebCoreSupport.subproj / WebNewKeyGeneration.c
1 /*
2  *  WebNewKeyGeneration.c
3  *  WebKit
4  *
5  *  Created by Chris Blumenberg on Mon Aug 23 2004.
6  *  Copyright (c) 2003 Apple Computer. All rights reserved.
7  *
8  */
9
10 #import <WebKit/WebNewKeyGeneration.h>
11
12 #ifdef USE_NEW_KEY_GENERATION
13
14 #import <Security/asn1Templates.h>
15 #import <Security/SecAsn1Coder.h>
16 #import <Security/secasn1t.h>
17 #import <Security/Security.h>
18
19 /*
20  * Netscape Certifiate Sequence is defined by Netscape as a PKCS7
21  * ContentInfo with a contentType of netscape-cert-sequence and a content
22  * consisting of a sequence of certificates.
23  *
24  * For simplicity - i.e., to avoid the general purpose ContentInfo
25  * polymorphism - we'll just hard-code this particular type right here.
26  *
27  * Inside the ContentInfo is an array of standard X509 certificates.
28  * We don't need to parse the certs themselves so they remain as
29  * opaque data blobs.
30  */
31 typedef struct {
32   CSSM_OID              contentType;            // netscape-cert-sequence
33   CSSM_DATA             **certs;
34 } NetscapeCertSequence;
35
36 extern const SecAsn1Template NetscapeCertSequenceTemplate[];
37
38 /*
39  * Public key/challenge, to send to CA.
40  *
41  * PublicKeyAndChallenge ::= SEQUENCE {
42  *
43  * ???\200?     spki SubjectPublicKeyInfo,
44  *      challenge IA5STRING
45  * }
46  *
47  * SignedPublicKeyAndChallenge ::= SEQUENCE {
48  *              publicKeyAndChallenge PublicKeyAndChallenge,
49  *              signatureAlgorithm AlgorithmIdentifier,
50  *              signature BIT STRING
51  * }
52  */
53 typedef struct {
54   CSSM_X509_SUBJECT_PUBLIC_KEY_INFO     spki;
55   CSSM_DATA                                                     challenge;      // ASCII
56 } PublicKeyAndChallenge;
57
58 typedef struct {
59   PublicKeyAndChallenge                         pubKeyAndChallenge;
60   CSSM_X509_ALGORITHM_IDENTIFIER                algId;
61   CSSM_DATA                                                     signature; // length in BITS
62 } SignedPublicKeyAndChallenge;
63
64 extern const SecAsn1Template PublicKeyAndChallengeTemplate[];
65 extern const SecAsn1Template SignedPublicKeyAndChallengeTemplate[];
66
67
68 #import <WebKit/WebAssertions.h>
69
70 #import <Security/keyTemplates.h>
71 #import <Security/SecKeyPriv.h>                /* Security.framework SPI */
72
73 #import <security_cdsa_utils/cuEnc64.h>
74
75 /* hard coded params, some of which may come from the user in real life */
76 #define GNR_KEY_ALG                     CSSM_ALGID_RSA
77 #define GNR_SIG_ALG                     CSSM_ALGID_MD5WithRSA
78 #define GNR_SIG_ALGOID                  CSSMOID_MD5WithRSA
79
80 const SecAsn1Template NetscapeCertSequenceTemplate[] = {
81 { SEC_ASN1_SEQUENCE,
82     0, NULL, sizeof(NetscapeCertSequence) },
83 { SEC_ASN1_OBJECT_ID,
84     offsetof(NetscapeCertSequence, contentType), 0, 0 },
85 { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | 
86     SEC_ASN1_CONTEXT_SPECIFIC | 0 , 
87     offsetof(NetscapeCertSequence, certs),
88     kSecAsn1SequenceOfAnyTemplate, 0 },
89 { 0, 0, 0, 0 }
90 };
91
92 const SecAsn1Template PublicKeyAndChallengeTemplate[] = {
93     { SEC_ASN1_SEQUENCE,
94         0, NULL, sizeof(PublicKeyAndChallenge) },
95     { SEC_ASN1_INLINE,
96         offsetof(PublicKeyAndChallenge, spki),
97         kSecAsn1SubjectPublicKeyInfoTemplate, 0},
98     { SEC_ASN1_INLINE,
99         offsetof(PublicKeyAndChallenge, challenge),
100         kSecAsn1IA5StringTemplate, 0 },
101     { 0, 0, 0, 0}
102 };
103
104 const SecAsn1Template SignedPublicKeyAndChallengeTemplate[] = {
105     { SEC_ASN1_SEQUENCE,
106         0, NULL, sizeof(SignedPublicKeyAndChallenge) },
107     { SEC_ASN1_INLINE,
108         offsetof(SignedPublicKeyAndChallenge, pubKeyAndChallenge),
109         PublicKeyAndChallengeTemplate, 0 },
110     { SEC_ASN1_INLINE,
111         offsetof(SignedPublicKeyAndChallenge, algId),
112         kSecAsn1AlgorithmIDTemplate, 0 },
113     { SEC_ASN1_BIT_STRING,
114         offsetof(SignedPublicKeyAndChallenge, signature), 0, 0 },
115     { 0, 0, 0, 0 }
116 };
117
118 void gnrNullAlgParams(CSSM_X509_ALGORITHM_IDENTIFIER *algId);
119 CSSM_RETURN gnrSign(CSSM_CSP_HANDLE             cspHand,
120                     const CSSM_DATA             *plainText,
121                     SecKeyRef                   privKey,
122                     CSSM_ALGORITHMS             sigAlg,         // e.g., CSSM_ALGID_SHA1WithRSA
123                     CSSM_DATA                   *sig);
124 unsigned nssArraySize(const void **array);
125 bool addCertificateToKeychainFromData(const unsigned char *certData,
126                                       unsigned certDataLen,
127                                       unsigned certNum);
128
129 /*
130  * Given a context specified via a CSSM_CC_HANDLE, add a new
131  * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
132  * AttributeLength, and an untyped pointer.
133  *
134  * This is currently used to add a second CSSM_KEY attribute when performing
135  * ops with algorithm CSSM_ALGID_FEED and CSSM_ALGID_FEECFILE.
136  */
137 static CSSM_RETURN gnrAddContextAttribute(CSSM_CC_HANDLE CCHandle,
138                                           uint32 AttributeType,
139                                           uint32 AttributeLength,
140                                           const void *AttributePtr)
141 {
142     CSSM_CONTEXT_ATTRIBUTE              newAttr;        
143     CSSM_RETURN                                 crtn;
144     
145     newAttr.AttributeType     = AttributeType;
146     newAttr.AttributeLength   = AttributeLength;
147     newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
148     crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
149     if(crtn) {
150         ERROR("CSSM_UpdateContextAttributes", crtn);
151     }
152     return crtn;
153 }
154
155 /*
156  * Given a public key as a SecKeyRef, obtain the key material in
157  * SubjectPublicKeyInfo format. This entails a NULL wrap to format
158  * in CSSM_KEYBLOB_RAW_FORMAT_X509 form. Caller must eventually
159  * free the returned key via CSSM_FreeKey().
160  */
161 static OSStatus gnrGetSubjPubKey(
162                                  CSSM_CSP_HANDLE        cspHand,
163                                  SecKeyRef secKey,
164                                  CSSM_KEY_PTR subjPubKey)               // RETURNED
165 {
166     CSSM_CC_HANDLE              ccHand;
167     CSSM_RETURN                 crtn;
168     CSSM_ACCESS_CREDENTIALS     creds;
169     const CSSM_KEY              *refPubKey;
170     OSStatus                    ortn;
171     
172     /* Get public key in CSSM form */
173     ortn = SecKeyGetCSSMKey(secKey, &refPubKey);
174     if(ortn) {
175         ERROR("SecKeyGetCSSMKey", ortn);
176         return ortn;
177     }
178     
179     /* NULL wrap via CSPDL */
180     memset(subjPubKey, 0, sizeof(CSSM_KEY));
181     memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
182     crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
183                                            CSSM_ALGID_NONE,
184                                            CSSM_ALGMODE_NONE,
185                                            &creds,                              // passPhrase
186                                            NULL,                                // wrapping key
187                                            NULL,                                // init vector
188                                            CSSM_PADDING_NONE,   // Padding
189                                            0,                                   // Params
190                                            &ccHand);
191     if(crtn) {
192         ERROR("gnrGetSubjPubKey CSSM_CSP_CreateSymmetricContext", 
193                      crtn);
194         return crtn;
195     }
196     
197     /*
198      * Specify X509 format' that is NOT the default for RSA (PKCS1 is)
199      */
200     crtn = gnrAddContextAttribute(ccHand,
201                                   CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT,
202                                   sizeof(uint32),
203                                   (void *)CSSM_KEYBLOB_RAW_FORMAT_X509);
204     if(crtn) {
205         ERROR("gnrAddContextAttribute", crtn);
206         goto errOut;
207     }
208     
209     crtn = CSSM_WrapKey(ccHand,
210                         &creds,
211                         refPubKey,
212                         NULL,                   // DescriptiveData
213                         subjPubKey);
214     if(crtn) {
215         ERROR("CSSM_WrapKey", crtn);
216     }
217 errOut:
218         CSSM_DeleteContext(ccHand);
219     return crtn;
220 }
221
222 /* 
223 * Set up a encoded NULL for CSSM_X509_ALGORITHM_IDENTIFIER.parameters.
224  */
225 void gnrNullAlgParams(CSSM_X509_ALGORITHM_IDENTIFIER *algId)
226 {
227     static const uint8 encNull[2] = { SEC_ASN1_NULL, 0 };
228     algId->parameters.Data = (uint8 *)encNull;
229     algId->parameters.Length = 2;
230 }
231
232 /*
233  * Sign specified plaintext. Caller must free signature data via
234  * gnrFreeCssmData().
235  */
236 CSSM_RETURN gnrSign(CSSM_CSP_HANDLE             cspHand,
237                     const CSSM_DATA             *plainText,
238                     SecKeyRef                   privKey,
239                     CSSM_ALGORITHMS             sigAlg,         // e.g., CSSM_ALGID_SHA1WithRSA
240                     CSSM_DATA                   *sig)           // allocated by CSP and RETURNED
241 {
242     CSSM_CC_HANDLE              ccHand;
243     CSSM_RETURN                 crtn;
244     const CSSM_KEY              *refPrivKey;
245     OSStatus                    ortn;
246     const CSSM_ACCESS_CREDENTIALS *creds;
247     
248     /* Get private key in CSSM form */
249     ortn = SecKeyGetCSSMKey(privKey, &refPrivKey);
250     if(ortn) {
251         ERROR("SecKeyGetCSSMKey", ortn);
252         return ortn;
253     }
254     
255     /* Get appropriate access credentials */
256     ortn = SecKeyGetCredentials(privKey,
257                                 CSSM_ACL_AUTHORIZATION_SIGN,
258                                 kSecCredentialTypeDefault,
259                                 &creds);
260     if(ortn) {
261         ERROR("SecKeyGetCredentials", ortn);
262         return ortn;
263     }
264     
265     /* cook up signature context */
266     crtn = CSSM_CSP_CreateSignatureContext(cspHand,
267                                            sigAlg,
268                                            creds,       
269                                            refPrivKey,
270                                            &ccHand);
271     if(crtn) {
272         ERROR("CSSM_CSP_CreateSignatureContext", ortn);
273         return crtn;
274     }
275     
276     /* go for it */
277     sig->Data = NULL;
278     sig->Length = 0;
279     crtn = CSSM_SignData(ccHand,
280                          plainText,
281                          1,
282                          CSSM_ALGID_NONE,
283                          sig);
284     if(crtn) {
285         ERROR("CSSM_SignData", ortn);
286     }
287     CSSM_DeleteContext(ccHand);
288     return crtn;
289 }
290
291 /*
292  * Free data mallocd on app's behalf by a CSSM module.
293  */
294 static void gnrFreeCssmData(
295                             CSSM_HANDLE         modHand,
296                             CSSM_DATA           *cdata)
297 {
298     CSSM_API_MEMORY_FUNCS memFuncs;
299     CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(modHand, &memFuncs);
300     if(crtn) {
301         ERROR("CSSM_GetAPIMemoryFunctions", crtn);
302         /* oh well, leak and continue */
303     }
304     else {
305         memFuncs.free_func(cdata->Data, memFuncs.AllocRef);
306     }
307     return;
308 }
309
310 unsigned nssArraySize(const void **array)
311 {
312     unsigned count = 0;
313     if (array) {
314         while (*array++) {
315             count++;
316         }
317     }
318     return count;
319 }
320
321 CFStringRef signedPublicKeyAndChallengeString(unsigned keySize, CFStringRef challenge, CFStringRef keyDescription)
322 {
323     OSStatus            ortn;
324     CSSM_RETURN         crtn;
325     SecKeyRef           pubKey = NULL;
326     SecKeyRef           privKey = NULL;
327     CSSM_KEY            subjectPubKey;
328     bool                freeSubjPubKey = false;
329     CSSM_CSP_HANDLE     cspHand;
330     SecAsn1CoderRef     coder = NULL;
331     SignedPublicKeyAndChallenge spkc;
332     PublicKeyAndChallenge               *pkc = &spkc.pubKeyAndChallenge;
333     /* DER encoded spkc.pubKeyAndChallenge and spkc */
334     CSSM_DATA           encodedPkc = {0, NULL};         
335     CSSM_DATA           encodedSpkc = {0, NULL};
336     CSSM_DATA           signature = {0, NULL};
337     unsigned char       *spkcB64 = NULL;                // base64 encoded encodedSpkc
338     unsigned            spkcB64Len;
339     SecAccessRef        accessRef;
340     CFArrayRef          acls;
341     SecACLRef           acl;
342     CFStringRef         result = NULL;
343     
344     ortn = SecAccessCreate(keyDescription, NULL, &accessRef);
345     if (ortn) {
346         ERROR("***SecAccessCreate %d", ortn);
347         goto errOut;
348     }
349     ortn = SecAccessCopySelectedACLList(accessRef, CSSM_ACL_AUTHORIZATION_DECRYPT, &acls);
350     if (ortn) {
351         ERROR("***SecAccessCopySelectedACLList %d", ortn);
352         goto errOut;
353     }
354     acl = (SecACLRef)CFArrayGetValueAtIndex(acls, 0);
355     CFRelease(acls);
356     ortn = SecACLSetSimpleContents(acl, NULL, keyDescription, NULL);
357     if (ortn) {
358         ERROR("***SecACLSetSimpleContents %d", ortn);
359         goto errOut;
360     }
361     
362     // Cook up a key pair, just use any old params for now
363     ortn = SecKeyCreatePair(nil,                                        // in default KC
364                             GNR_KEY_ALG,                                // normally spec'd by user
365                             keySize,                                    // key size, ditto
366                             0,                                          // ContextHandle
367                             CSSM_KEYUSE_ANY,                            // might want to restrict this
368                             CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE | 
369                             CSSM_KEYATTR_RETURN_REF,                    // pub attrs
370                             CSSM_KEYUSE_ANY,                            // might want to restrict this
371                             CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_RETURN_REF |
372                             CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE,
373                             accessRef,
374                             &pubKey,
375                             &privKey);
376     if (ortn != noErr) {
377         ERROR("***SecKeyCreatePair %d", ortn);
378         goto errOut;
379     }
380     
381     /* get handle of CSPDL for crypto ops */
382     ortn = SecKeyGetCSPHandle(privKey, &cspHand);
383     if (ortn != noErr) {
384         ERROR("***SecKeyGetCSPHandle", ortn);
385         goto errOut;
386     }
387     
388     /*
389      * Get the public key in encoded SubjectPublicKeyInfo form.
390      */
391     ortn = gnrGetSubjPubKey(cspHand, pubKey, &subjectPubKey);
392     if (ortn != noErr) {
393         goto errOut;
394     }
395     freeSubjPubKey = true;
396     
397     ortn = SecAsn1CoderCreate(&coder);
398     if (ortn != noErr) {
399         ERROR("***SecAsn1CoderCreate", ortn);
400         goto errOut;
401     }
402     
403     /*
404      * Cook up PublicKeyAndChallenge and DER-encode it.
405      * First, DER-decode the key's SubjectPublicKeyInfo.
406      */
407     memset(&spkc, 0, sizeof(spkc));
408     
409     ortn = SecAsn1DecodeData(coder, &subjectPubKey.KeyData, kSecAsn1SubjectPublicKeyInfoTemplate, &pkc->spki);
410     if (ortn != noErr) {
411         /* should never happen */
412         ERROR("***Error decoding subject public key info\n");
413         goto errOut;
414     }
415     
416     pkc->challenge.Length = CFStringGetLength(challenge);
417     if (pkc->challenge.Length == 0) {
418         pkc->challenge.Length = 1;
419         pkc->challenge.Data = (uint8 *)strdup("\0");
420     } else {
421         pkc->challenge.Data = (uint8 *)malloc(pkc->challenge.Length + 1);
422         CFStringGetCString(challenge,  (char *)pkc->challenge.Data, pkc->challenge.Length + 1, kCFStringEncodingASCII);
423     }
424     ortn = SecAsn1EncodeItem(coder, pkc, PublicKeyAndChallengeTemplate, &encodedPkc);
425     if (ortn != noErr) {
426         /* should never happen */
427         ERROR("***Error encoding PublicKeyAndChallenge\n");
428         goto errOut;
429     }
430     
431     /*
432      * Sign the encoded PublicKeyAndChallenge.
433      */
434     crtn = gnrSign(cspHand, &encodedPkc, privKey, GNR_SIG_ALG, &signature);
435     if (crtn) {
436         goto errOut;
437     }
438     
439     /*
440      * Cook up SignedPublicKeyAndChallenge, DER-encode that. 
441      * The PublicKeyAndChallenge stays in place where we originally
442      * created it before we signed it. Now we just add the algId
443      * and the signature proper.
444      */
445     spkc.algId.algorithm = GNR_SIG_ALGOID;
446     gnrNullAlgParams(&spkc.algId);
447     spkc.signature = signature;
448     /* convert to BIT length */
449     spkc.signature.Length *= 8;
450     ortn = SecAsn1EncodeItem(coder, &spkc, SignedPublicKeyAndChallengeTemplate, &encodedSpkc);
451     if (ortn != noErr) {
452         /* should never happen */
453         ERROR("***Error encoding SignedPublicKeyAndChallenge\n");
454         goto errOut;
455     }
456     
457     /*
458      * Finally base64 the result and write that to outFile.
459      * cuEnc64() gives us a NULL-terminated string; we strip off the NULL.
460      */
461     spkcB64 = cuEnc64(encodedSpkc.Data, encodedSpkc.Length, &spkcB64Len);
462     if (spkcB64 == NULL) {
463         /* should never happen */
464         FATAL("***Error base64-encoding the result\n");
465         goto errOut;
466     }
467     
468 errOut:
469     if (coder != NULL) {
470         SecAsn1CoderRelease(coder);
471     }
472     if (freeSubjPubKey) {
473         CSSM_FreeKey(cspHand, NULL, &subjectPubKey, CSSM_FALSE);
474     }
475     if (signature.Data) {
476         gnrFreeCssmData(cspHand, &signature);
477     }
478     if (pubKey) {
479         CFRelease(pubKey);
480     }
481     if (privKey) {
482         CFRelease(privKey);
483     }
484     if (accessRef) {
485         CFRelease(accessRef);
486     }    
487     if (pkc->challenge.Data) {
488         free(pkc->challenge.Data);
489     }
490     if (spkcB64) {
491         result = CFStringCreateWithCString(NULL, (const char *)spkcB64, kCFStringEncodingASCII);
492         free(spkcB64);
493     }
494     return result;
495 }
496
497 /* 
498 * Per-cert processing, called for each cert we extract from the 
499  * incoming blob.
500  */
501 bool addCertificateToKeychainFromData(const unsigned char *certData,
502                                       unsigned certDataLen,
503                                       unsigned certNum)
504 {
505     CSSM_DATA cert = {certDataLen, (uint8 *)certData};
506     SecCertificateRef certRef;
507     
508     /* Make a SecCertificateRef */
509     OSStatus ortn = SecCertificateCreateFromData(&cert, 
510                                                  CSSM_CERT_X_509v3,
511                                                  CSSM_CERT_ENCODING_DER,
512                                                  &certRef);
513     if (ortn != noErr) {
514         ERROR("SecCertificateCreateFromData returned %d", (int)ortn);
515         return false;
516     }
517     
518     /* 
519         * Add it to default keychain.
520         * Many people will be surprised that this op works without
521         * the user having to unlock the keychain. 
522         */
523     ortn = SecCertificateAddToKeychain(certRef, nil);
524     
525     /* Free the cert in any case */
526     CFRelease(certRef);
527     switch(ortn) {
528         case noErr:
529             break;
530         case errSecDuplicateItem:
531             /* Not uncommon, definitely not an error */
532             ERROR("cert %u already present in keychain", certNum);
533             break;
534         default:
535             ERROR("SecCertificateAddToKeychain returned %d", (int)ortn);
536             return false;
537     }
538
539     return true;
540 }
541
542 WebCertificateParseResult addCertificatesToKeychainFromData(const void *bytes, unsigned length)
543 {   
544     WebCertificateParseResult result = WebCertificateParseResultFailed;
545
546     /* DER-decode, first as NetscapeCertSequence */
547     SecAsn1CoderRef coder = NULL;
548     NetscapeCertSequence certSeq;
549     OSErr ortn;
550     
551     ortn = SecAsn1CoderCreate(&coder);
552     if (ortn == noErr) {
553         memset(&certSeq, 0, sizeof(certSeq));
554         ortn = SecAsn1Decode(coder, bytes, length, NetscapeCertSequenceTemplate, &certSeq);
555         if (ortn == noErr) {
556             if (certSeq.contentType.Length == CSSMOID_PKCS7_SignedData.Length &&
557                 memcmp(certSeq.contentType.Data, CSSMOID_PKCS7_SignedData.Data, certSeq.contentType.Length) == 0) {
558                 return WebCertificateParseResultPKCS7;
559             }
560             /*
561              * Last cert is a root, which we do NOT want to add
562              * to the user's keychain.
563              */
564             unsigned numCerts = nssArraySize((const void **)certSeq.certs) - 1;
565             unsigned i;
566             for (i=0; i<numCerts; i++) {
567                 CSSM_DATA *cert = certSeq.certs[i];
568                 result = addCertificateToKeychainFromData(cert->Data, cert->Length, i) ? WebCertificateParseResultSucceeded : WebCertificateParseResultFailed;
569             } 
570         } else {
571             /*
572              * Didn't appear to be a NetscapeCertSequence; assume it's just 
573              * a cert. FIXME: Netscape spec says the blob might also be PKCS7
574              * format, which we're not handling here.
575              */
576             result = addCertificateToKeychainFromData(bytes, length, 0) ? WebCertificateParseResultSucceeded : WebCertificateParseResultFailed;
577         }
578     }
579     
580     if (coder != NULL) {
581         SecAsn1CoderRelease(coder);
582     }
583
584     return result;
585 }
586
587 #endif /* USE_NEW_KEY_GENERATION */