1 /* Copyright (c) 1998,2003-2006,2008 Apple Inc.
5 * Verify proper detection of basicConstraints.cA
8 #include <utilLib/common.h>
9 #include <utilLib/cspwrap.h>
10 #include <security_cdsa_utils/cuFileIo.h>
11 #include <clAppUtils/CertBuilderApp.h>
12 #include <clAppUtils/clutils.h>
13 #include <clAppUtils/timeStr.h>
14 #include <clAppUtils/tpUtils.h>
15 #include <security_cdsa_utils/cuPrintCert.h>
19 #include <Security/cssm.h>
20 #include <Security/x509defs.h>
21 #include <Security/oidsattr.h>
22 #include <Security/oidscert.h>
23 #include <Security/oidsalg.h>
24 #include <Security/certextensions.h>
25 #include <Security/cssmapple.h>
28 /* define nonzero to build on Puma */
31 /* default key and signature algorithm */
32 #define SIG_ALG_DEFAULT CSSM_ALGID_SHA1WithRSA
33 #define KEY_ALG_DEFAULT CSSM_ALGID_RSA
35 #define NUM_CERTS_DEF 5 /* default is random from 2 to this */
36 #define NUM_LOOPS_DEF 100
40 void cssmPerror(const char *how
, CSSM_RETURN error
);
42 #endif /* PUMA_BUILD */
44 static void usage(char **argv
)
46 printf("Usage: %s [options]\n", argv
[0]);
48 printf(" n=numCerts (default=random from 2 to %d)\n", NUM_CERTS_DEF
);
49 printf(" a=alg where alg is s(RSA/SHA1), 5(RSA/MD5), f(FEE/MD5), "
50 "F(FEE/SHA1), e(ECDSA), E(ANSI/ECDSA), 6(ECDSA/SHA256)\n");
51 printf(" k=keySizeInBits\n");
52 printf(" c (dump certs on error)\n");
53 printf(" l=numLoops (default = %d)\n", NUM_LOOPS_DEF
);
58 const CSSM_DATA
*certs
,
63 for(i
=0; i
<numCerts
; i
++) {
64 printf("======== cert %d ========\n", i
);
65 printCert(certs
[i
].Data
, certs
[i
].Length
, CSSM_FALSE
);
71 const CSSM_DATA
*certs
,
77 for(i
=0; i
<numCerts
; i
++) {
78 sprintf(fileName
, "caVerifyCert%u.cer", i
);
79 writeFile(fileName
, certs
[i
].Data
, certs
[i
].Length
);
80 printf("....wrote %lu bytes to %s\n", certs
[i
].Length
, fileName
);
85 * Generate a cert chain using specified key pairs and extensions.
86 * The last cert in the chain (certs[numCerts-1]) is a root cert,
89 static CSSM_RETURN
tpGenCertsExten(
90 CSSM_CSP_HANDLE cspHand
,
91 CSSM_CL_HANDLE clHand
,
92 CSSM_ALGORITHMS sigAlg
, /* CSSM_ALGID_SHA1WithRSA, etc. */
95 const char *nameBase
, /* C string */
96 CSSM_KEY_PTR pubKeys
, /* array of public keys */
97 CSSM_KEY_PTR privKeys
, /* array of private keys */
98 CSSM_X509_EXTENSION
**extensions
,
99 unsigned *numExtensions
,
100 CSSM_DATA_PTR certs
, /* array of certs RETURNED here */
101 const char *notBeforeStr
, /* from genTimeAtNowPlus() */
102 const char *notAfterStr
) /* from genTimeAtNowPlus() */
107 CSSM_X509_NAME
*issuerName
= NULL
;
108 CSSM_X509_NAME
*subjectName
= NULL
;
109 CSSM_X509_TIME
*notBefore
; // UTC-style "not before" time
110 CSSM_X509_TIME
*notAfter
; // UTC-style "not after" time
111 CSSM_DATA_PTR rawCert
= NULL
; // from CSSM_CL_CertCreateTemplate
112 CSSM_DATA signedCert
; // from CSSM_CL_CertSign
114 CSSM_KEY_PTR signerKey
; // signs the cert
115 CSSM_CC_HANDLE signContext
;
117 CSSM_DATA_PTR thisCert
; // ptr into certs[]
120 nameOid
.oid
= &CSSMOID_OrganizationName
; // const
121 nameOid
.string
= nameStr
;
123 /* main loop - once per keypair/cert - starting at end/root */
124 for(dex
=numCerts
-1; dex
>=0; dex
--) {
125 thisCert
= &certs
[dex
];
127 thisCert
->Data
= NULL
;
128 thisCert
->Length
= 0;
130 sprintf(nameStr
, "%s%04d", nameBase
, dex
);
131 if(issuerName
== NULL
) {
132 /* last (root) cert - subject same as issuer */
133 issuerName
= CB_BuildX509Name(&nameOid
, 1);
135 signerKey
= &privKeys
[dex
];
138 /* previous subject becomes current issuer */
139 CB_FreeX509Name(issuerName
);
140 issuerName
= subjectName
;
141 signerKey
= &privKeys
[dex
+1];
143 subjectName
= CB_BuildX509Name(&nameOid
, 1);
144 if((subjectName
== NULL
) || (issuerName
== NULL
)) {
145 printf("Error creating X509Names\n");
146 crtn
= CSSMERR_CSSM_MEMORY_ERROR
;
151 * not before/after in Y2k-compliant generalized time format.
152 * These come preformatted from our caller.
154 notBefore
= CB_BuildX509Time(0, notBeforeStr
);
155 notAfter
= CB_BuildX509Time(0, notAfterStr
);
158 * Cook up cert template
159 * Note serial number would be app-specified in real world
161 rawCert
= CB_MakeCertTemplate(clHand
,
162 0x12345 + dex
, // serial number
169 NULL
, // subj unique ID
170 NULL
, // issuer unique ID
171 extensions
[dex
], // extensions
172 numExtensions
[dex
]); // numExtensions
174 if(rawCert
== NULL
) {
175 crtn
= CSSM_ERRCODE_INTERNAL_ERROR
;
179 /* Free the stuff we allocd to get here */
180 CB_FreeX509Time(notBefore
);
181 CB_FreeX509Time(notAfter
);
183 /**** sign the cert ****/
184 /* 1. get a signing context */
185 crtn
= CSSM_CSP_CreateSignatureContext(cspHand
,
187 NULL
, // no passphrase for now
191 printError("CreateSignatureContext", crtn
);
195 /* 2. use CL to sign the cert */
196 signedCert
.Data
= NULL
;
197 signedCert
.Length
= 0;
198 crtn
= CSSM_CL_CertSign(clHand
,
200 rawCert
, // CertToBeSigned
201 NULL
, // SignScope per spec
202 0, // ScopeSize per spec
205 printError("CSSM_CL_CertSign", crtn
);
209 /* 3. delete signing context */
210 crtn
= CSSM_DeleteContext(signContext
);
212 printError("CSSM_DeleteContext", crtn
);
217 * CSSM_CL_CertSign() returned us a mallocd CSSM_DATA. Copy
218 * its fields to caller's cert.
220 certs
[dex
] = signedCert
;
222 /* and the raw unsigned cert as well */
223 appFreeCssmData(rawCert
, CSSM_TRUE
);
228 if(issuerName
!= NULL
) {
229 CB_FreeX509Name(issuerName
);
231 if(subjectName
!= NULL
) {
232 CB_FreeX509Name(subjectName
);
238 CSSM_CSP_HANDLE cspHand
, // CSP handle
239 CSSM_CL_HANDLE clHand
, // CL handle
240 CSSM_TP_HANDLE tpHand
, // TP handle
241 unsigned numCerts
, // >= 2
242 CSSM_KEY_PTR pubKeys
,
243 CSSM_KEY_PTR privKeys
,
244 CSSM_ALGORITHMS sigAlg
,
245 CSSM_BOOL expectFail
,
250 CSSM_X509_EXTENSION
**extens
;
252 char *notBeforeStr
= genTimeAtNowPlus(0);
253 char *notAfterStr
= genTimeAtNowPlus(10000);
255 CE_BasicConstraints
*bc
;
256 CSSM_X509_EXTENSION
*thisExten
;
257 CE_BasicConstraints
*thisBc
;
258 const char *failMode
= "not set - internal error";
260 unsigned badCertDex
= 0;
262 certs
= (CSSM_DATA_PTR
)malloc(sizeof(CSSM_DATA
) * numCerts
);
263 memset(certs
, 0, sizeof(CSSM_DATA
) * numCerts
);
266 * For now just zero or one extension per cert - basicConstraints.
267 * Eventually we'll want to test keyUsage as well.
269 extens
= (CSSM_X509_EXTENSION
**)malloc(sizeof(CSSM_X509_EXTENSION
*) * numCerts
);
270 memset(extens
, 0, sizeof(CSSM_X509_EXTENSION
*) * numCerts
);
271 numExtens
= (unsigned *)malloc(sizeof(unsigned) * numCerts
);
272 bc
= (CE_BasicConstraints
*)malloc(sizeof(CE_BasicConstraints
) * numCerts
);
275 * Set up all extensions for success
277 for(certDex
=0; certDex
<numCerts
; certDex
++) {
278 extens
[certDex
] = (CSSM_X509_EXTENSION
*)malloc(sizeof(CSSM_X509_EXTENSION
));
279 numExtens
[certDex
] = 1;
280 thisExten
= extens
[certDex
];
281 thisBc
= &bc
[certDex
];
283 thisExten
->extnId
= CSSMOID_BasicConstraints
;
284 thisExten
->critical
= CSSM_TRUE
;
285 thisExten
->format
= CSSM_X509_DATAFORMAT_PARSED
;
286 thisExten
->value
.parsedValue
= thisBc
;
287 thisExten
->BERvalue
.Data
= NULL
;
288 thisExten
->BERvalue
.Length
= 0;
291 /* leaf - flip coin to determine presence of basicConstraints */
292 int coin
= genRand(1,2);
294 /* basicConstraints, !cA */
295 thisBc
->cA
= CSSM_FALSE
;
296 thisBc
->pathLenConstraintPresent
= CSSM_FALSE
;
297 thisBc
->pathLenConstraint
= 0;
300 /* !basicConstraints, !cA by default */
301 numExtens
[certDex
] = 0;
304 else if(certDex
== (numCerts
-1)) {
305 /* root - flip coin to determine presence of basicConstraints */
306 int coin
= genRand(1,2);
308 /* basicConstraints, cA */
309 thisBc
->cA
= CSSM_TRUE
;
310 /* flip coin to determine present of pathLenConstraint */
313 thisBc
->pathLenConstraintPresent
= CSSM_FALSE
;
314 thisBc
->pathLenConstraint
= 0;
317 thisBc
->pathLenConstraintPresent
= CSSM_TRUE
;
318 thisBc
->pathLenConstraint
= genRand(certDex
-1, numCerts
+1);
322 /* !basicConstraints, cA by default */
323 numExtens
[certDex
] = 0;
327 /* intermediate = cA required */
328 thisBc
->cA
= CSSM_TRUE
;
329 /* flip coin to determine presence of pathLenConstraint */
330 int coin
= genRand(1,2);
332 thisBc
->pathLenConstraintPresent
= CSSM_FALSE
;
333 thisBc
->pathLenConstraint
= 0;
336 thisBc
->pathLenConstraintPresent
= CSSM_TRUE
;
337 thisBc
->pathLenConstraint
= genRand(certDex
-1, numCerts
+1);
343 /* introduce a failure */
345 /* only possible failure is explicit !cA in root */
346 /* don't assume presence of BC exten */
348 thisExten
= extens
[badCertDex
];
349 thisBc
= &bc
[badCertDex
];
350 thisBc
->cA
= CSSM_FALSE
;
351 thisBc
->pathLenConstraintPresent
= CSSM_FALSE
;
352 bc
->pathLenConstraint
= 0;
353 numExtens
[badCertDex
] = 1;
354 failMode
= "Explicit !cA in root";
357 /* roll the dice to select an intermediate cert */
358 badCertDex
= genRand(1, numCerts
-2);
359 thisExten
= extens
[badCertDex
];
360 if((thisExten
== NULL
) || (numExtens
[badCertDex
] == 0)) {
361 printf("***INTERNAL SCREWUP\n");
364 thisBc
= &bc
[badCertDex
];
368 * -- no BasicConstraints
370 * -- bad pathLenConstraint
372 int die
= genRand(1,3);
374 (badCertDex
!= 1)) { // last cA doesn't need pathLenConstraint
375 thisBc
->pathLenConstraintPresent
= CSSM_TRUE
;
376 thisBc
->pathLenConstraint
= badCertDex
- 2; // one short
377 failMode
= "Short pathLenConstraint";
380 thisBc
->cA
= CSSM_FALSE
;
381 failMode
= "Explicit !cA in intermediate";
385 numExtens
[badCertDex
] = 0;
386 failMode
= "No BasicConstraints in intermediate";
390 if(!quiet
&& expectFail
) {
391 printf(" ...bad cert at index %d: %s\n", badCertDex
, failMode
);
394 /* here we go - create cert chain */
395 crtn
= tpGenCertsExten(cspHand
,
399 "caVerify", // nameBase
408 printError("tpGenCertsExten", crtn
);
409 return crtn
; // and leak like crazy
413 memset(&cgrp
, 0, sizeof(CSSM_CERTGROUP
));
414 cgrp
.NumCerts
= numCerts
;
416 cgrp
.CertGroupType
= CSSM_CERTGROUP_ENCODED_CERT
;
419 cgrp
.CertGroupType
= CSSM_CERTGROUP_DATA
;
420 #endif /* PUMA_BUILD */
421 cgrp
.CertType
= CSSM_CERT_X_509v3
;
422 cgrp
.CertEncoding
= CSSM_CERT_ENCODING_DER
;
423 cgrp
.GroupList
.CertList
= certs
;
426 crtn
= tpCertGroupVerify(tpHand
,
430 &CSSMOID_APPLE_X509_BASIC
, // SSL requires built-in root match
432 /* pass in OUR ROOT as anchors */
433 (CSSM_DATA_PTR
)&certs
[numCerts
-1], // anchorCerts
435 CSSM_TP_STOP_ON_POLICY
,
436 CSSM_FALSE
, // allowExpired
440 crtn
= tpCertGroupVerify(tpHand
,
444 &CSSMOID_APPLE_TP_SSL
, // may want to parameterize this
445 NULL
, // fieldOpts for server name
446 NULL
, // actionDataPtr for allow expired
449 /* pass in OUR ROOT as anchors */
450 (CSSM_DATA_PTR
)&certs
[numCerts
-1], // anchorCerts
452 CSSM_TP_STOP_ON_POLICY
,
455 #endif /* PUMA_BUILD */
457 if(crtn
!= CSSMERR_TP_VERIFY_ACTION_FAILED
) {
458 cssmPerror("***Expected error TP_VERIFY_ACTION_FAILED; got ", crtn
);
459 printf(" Expected failure due to %s\n", failMode
);
461 showCerts(certs
, numCerts
);
462 writeCerts(certs
, numCerts
);
464 return testError(quiet
);
468 cssmPerror("Unexpected failure on tpCertGroupVerify", crtn
);
470 showCerts(certs
, numCerts
);
472 return testError(quiet
);
479 int main(int argc
, char **argv
)
481 CSSM_CL_HANDLE clHand
; // CL handle
482 CSSM_CSP_HANDLE cspHand
; // CSP handle
483 CSSM_TP_HANDLE tpHand
;
484 CSSM_KEY_PTR pubKeys
;
485 CSSM_KEY_PTR privKeys
;
492 /* user-spec'd variables */
493 CSSM_ALGORITHMS keyAlg
= KEY_ALG_DEFAULT
;
494 CSSM_ALGORITHMS sigAlg
= SIG_ALG_DEFAULT
;
495 uint32 keySizeInBits
= CSP_KEY_SIZE_DEFAULT
;
496 unsigned numCerts
= 0; // means random per loop
497 unsigned numLoops
= NUM_LOOPS_DEF
;
498 CSSM_BOOL quiet
= CSSM_FALSE
;
499 CSSM_BOOL dumpCerts
= CSSM_FALSE
;
501 for(arg
=1; arg
<argc
; arg
++) {
502 switch(argv
[arg
][0]) {
504 if((argv
[arg
][1] == '\0') || (argv
[arg
][2] == '\0')) {
507 switch(argv
[arg
][2]) {
509 keyAlg
= CSSM_ALGID_RSA
;
510 sigAlg
= CSSM_ALGID_SHA1WithRSA
;
513 keyAlg
= CSSM_ALGID_RSA
;
514 sigAlg
= CSSM_ALGID_MD5WithRSA
;
517 keyAlg
= CSSM_ALGID_FEE
;
518 sigAlg
= CSSM_ALGID_FEE_MD5
;
521 keyAlg
= CSSM_ALGID_FEE
;
522 sigAlg
= CSSM_ALGID_FEE_SHA1
;
525 keyAlg
= CSSM_ALGID_FEE
;
526 sigAlg
= CSSM_ALGID_SHA1WithECDSA
;
529 keyAlg
= CSSM_ALGID_ECDSA
;
530 sigAlg
= CSSM_ALGID_SHA1WithECDSA
;
533 keyAlg
= CSSM_ALGID_ECDSA
;
534 sigAlg
= CSSM_ALGID_SHA256WithECDSA
;
541 keySizeInBits
= atoi(&argv
[arg
][2]);
544 numLoops
= atoi(&argv
[arg
][2]);
547 numCerts
= atoi(&argv
[arg
][2]);
550 dumpCerts
= CSSM_TRUE
;
560 /* connect to CL, TP, and CSP */
561 clHand
= clStartup();
565 tpHand
= tpStartup();
569 cspHand
= cspStartup();
575 maxCerts
= NUM_CERTS_DEF
; // random, this is the max $
578 maxCerts
= numCerts
; // user-specd
581 printf("Starting caVerify; args: ");
582 for(unsigned i
=1; i
<(unsigned)argc
; i
++) {
583 printf("%s ", argv
[i
]);
587 /* cook up maxCerts key pairs */
589 printf("generating key pairs...\n");
591 pubKeys
= (CSSM_KEY_PTR
)malloc(sizeof(CSSM_KEY
) * maxCerts
);
592 privKeys
= (CSSM_KEY_PTR
)malloc(sizeof(CSSM_KEY
) * maxCerts
);
593 for(certDex
=0; certDex
<maxCerts
; certDex
++) {
594 crtn
= cspGenKeyPair(cspHand
,
600 CSSM_FALSE
, // pubIsRef - should work both ways, but not yet
602 CSSM_KEYBLOB_RAW_FORMAT_NONE
,
604 CSSM_TRUE
, // privIsRef - doesn't matter
606 CSSM_KEYBLOB_RAW_FORMAT_NONE
,
609 printError("cspGenKeyPair", crtn
);
610 printf("***error generating key pair. Aborting.\n");
615 for(loopNum
=0; loopNum
<numLoops
; loopNum
++) {
616 unsigned thisNumCerts
;
618 /* random: num certs and whether this loop is to test a failure */
620 thisNumCerts
= numCerts
;
623 thisNumCerts
= genRand(2, NUM_CERTS_DEF
);
625 int coin
= genRand(1,2);
626 CSSM_BOOL expectFail
= (coin
== 1) ? CSSM_TRUE
: CSSM_FALSE
;
628 printf("...loop %d numCerts %u expectFail %s\n", loopNum
,
629 thisNumCerts
, expectFail
? "TRUE" : "FALSE");