andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: #include "cert.h" andre@0: #include "certt.h" andre@0: #include "secder.h" andre@0: #include "key.h" andre@0: #include "secitem.h" andre@0: #include "secasn1.h" andre@0: #include "secerr.h" andre@0: andre@0: SEC_ASN1_MKSUB(SEC_AnyTemplate) andre@0: andre@0: const SEC_ASN1Template CERT_AttributeTemplate[] = { andre@0: { SEC_ASN1_SEQUENCE, andre@0: 0, NULL, sizeof(CERTAttribute) }, andre@0: { SEC_ASN1_OBJECT_ID, offsetof(CERTAttribute, attrType) }, andre@0: { SEC_ASN1_SET_OF | SEC_ASN1_XTRN, offsetof(CERTAttribute, attrValue), andre@0: SEC_ASN1_SUB(SEC_AnyTemplate) }, andre@0: { 0 } andre@0: }; andre@0: andre@0: const SEC_ASN1Template CERT_SetOfAttributeTemplate[] = { andre@0: { SEC_ASN1_SET_OF, 0, CERT_AttributeTemplate }, andre@0: }; andre@0: andre@0: const SEC_ASN1Template CERT_CertificateRequestTemplate[] = { andre@0: { SEC_ASN1_SEQUENCE, andre@0: 0, NULL, sizeof(CERTCertificateRequest) }, andre@0: { SEC_ASN1_INTEGER, andre@0: offsetof(CERTCertificateRequest,version) }, andre@0: { SEC_ASN1_INLINE, andre@0: offsetof(CERTCertificateRequest,subject), andre@0: CERT_NameTemplate }, andre@0: { SEC_ASN1_INLINE, andre@0: offsetof(CERTCertificateRequest,subjectPublicKeyInfo), andre@0: CERT_SubjectPublicKeyInfoTemplate }, andre@0: { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0, andre@0: offsetof(CERTCertificateRequest,attributes), andre@0: CERT_SetOfAttributeTemplate }, andre@0: { 0 } andre@0: }; andre@0: andre@0: SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CertificateRequestTemplate) andre@0: andre@0: CERTCertificate * andre@0: CERT_CreateCertificate(unsigned long serialNumber, andre@0: CERTName *issuer, andre@0: CERTValidity *validity, andre@0: CERTCertificateRequest *req) andre@0: { andre@0: CERTCertificate *c; andre@0: int rv; andre@0: PLArenaPool *arena; andre@0: andre@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); andre@0: andre@0: if ( !arena ) { andre@0: return(0); andre@0: } andre@0: andre@0: c = (CERTCertificate *)PORT_ArenaZAlloc(arena, sizeof(CERTCertificate)); andre@0: andre@0: if (!c) { andre@0: PORT_FreeArena(arena, PR_FALSE); andre@0: return 0; andre@0: } andre@0: andre@0: c->referenceCount = 1; andre@0: c->arena = arena; andre@0: andre@0: /* andre@0: * Default is a plain version 1. andre@0: * If extensions are added, it will get changed as appropriate. andre@0: */ andre@0: rv = DER_SetUInteger(arena, &c->version, SEC_CERTIFICATE_VERSION_1); andre@0: if (rv) goto loser; andre@0: andre@0: rv = DER_SetUInteger(arena, &c->serialNumber, serialNumber); andre@0: if (rv) goto loser; andre@0: andre@0: rv = CERT_CopyName(arena, &c->issuer, issuer); andre@0: if (rv) goto loser; andre@0: andre@0: rv = CERT_CopyValidity(arena, &c->validity, validity); andre@0: if (rv) goto loser; andre@0: andre@0: rv = CERT_CopyName(arena, &c->subject, &req->subject); andre@0: if (rv) goto loser; andre@0: rv = SECKEY_CopySubjectPublicKeyInfo(arena, &c->subjectPublicKeyInfo, andre@0: &req->subjectPublicKeyInfo); andre@0: if (rv) goto loser; andre@0: andre@0: return c; andre@0: andre@0: loser: andre@0: CERT_DestroyCertificate(c); andre@0: return 0; andre@0: } andre@0: andre@0: /************************************************************************/ andre@0: /* It's clear from the comments that the original author of this andre@0: * function expected the template for certificate requests to treat andre@0: * the attributes as a SET OF ANY. This function expected to be andre@0: * passed an array of SECItems each of which contained an already encoded andre@0: * Attribute. But the cert request template does not treat the andre@0: * Attributes as a SET OF ANY, and AFAIK never has. Instead the template andre@0: * encodes attributes as a SET OF xxxxxxx. That is, it expects to encode andre@0: * each of the Attributes, not have them pre-encoded. Consequently an andre@0: * array of SECItems containing encoded Attributes is of no value to this andre@0: * function. But we cannot change the signature of this public function. andre@0: * It must continue to take SECItems. andre@0: * andre@0: * I have recoded this function so that each SECItem contains an andre@0: * encoded cert extension. The encoded cert extensions form the list for the andre@0: * single attribute of the cert request. In this implementation there is at most andre@0: * one attribute and it is always of type SEC_OID_PKCS9_EXTENSION_REQUEST. andre@0: */ andre@0: andre@0: CERTCertificateRequest * andre@0: CERT_CreateCertificateRequest(CERTName *subject, andre@0: CERTSubjectPublicKeyInfo *spki, andre@0: SECItem **attributes) andre@0: { andre@0: CERTCertificateRequest *certreq; andre@0: PLArenaPool *arena; andre@0: CERTAttribute * attribute; andre@0: SECOidData * oidData; andre@0: SECStatus rv; andre@0: int i = 0; andre@0: andre@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); andre@0: if ( arena == NULL ) { andre@0: return NULL; andre@0: } andre@0: andre@0: certreq = PORT_ArenaZNew(arena, CERTCertificateRequest); andre@0: if (!certreq) { andre@0: PORT_FreeArena(arena, PR_FALSE); andre@0: return NULL; andre@0: } andre@0: /* below here it is safe to goto loser */ andre@0: andre@0: certreq->arena = arena; andre@0: andre@0: rv = DER_SetUInteger(arena, &certreq->version, andre@0: SEC_CERTIFICATE_REQUEST_VERSION); andre@0: if (rv != SECSuccess) andre@0: goto loser; andre@0: andre@0: rv = CERT_CopyName(arena, &certreq->subject, subject); andre@0: if (rv != SECSuccess) andre@0: goto loser; andre@0: andre@0: rv = SECKEY_CopySubjectPublicKeyInfo(arena, andre@0: &certreq->subjectPublicKeyInfo, andre@0: spki); andre@0: if (rv != SECSuccess) andre@0: goto loser; andre@0: andre@0: certreq->attributes = PORT_ArenaZNewArray(arena, CERTAttribute*, 2); andre@0: if(!certreq->attributes) andre@0: goto loser; andre@0: andre@0: /* Copy over attribute information */ andre@0: if (!attributes || !attributes[0]) { andre@0: /* andre@0: ** Invent empty attribute information. According to the andre@0: ** pkcs#10 spec, attributes has this ASN.1 type: andre@0: ** andre@0: ** attributes [0] IMPLICIT Attributes andre@0: ** andre@0: ** Which means, we should create a NULL terminated list andre@0: ** with the first entry being NULL; andre@0: */ andre@0: certreq->attributes[0] = NULL; andre@0: return certreq; andre@0: } andre@0: andre@0: /* allocate space for attributes */ andre@0: attribute = PORT_ArenaZNew(arena, CERTAttribute); andre@0: if (!attribute) andre@0: goto loser; andre@0: andre@0: oidData = SECOID_FindOIDByTag( SEC_OID_PKCS9_EXTENSION_REQUEST ); andre@0: PORT_Assert(oidData); andre@0: if (!oidData) andre@0: goto loser; andre@0: rv = SECITEM_CopyItem(arena, &attribute->attrType, &oidData->oid); andre@0: if (rv != SECSuccess) andre@0: goto loser; andre@0: andre@0: for (i = 0; attributes[i] != NULL ; i++) andre@0: ; andre@0: attribute->attrValue = PORT_ArenaZNewArray(arena, SECItem *, i+1); andre@0: if (!attribute->attrValue) andre@0: goto loser; andre@0: andre@0: /* copy attributes */ andre@0: for (i = 0; attributes[i]; i++) { andre@0: /* andre@0: ** Attributes are a SetOf Attribute which implies andre@0: ** lexigraphical ordering. It is assumes that the andre@0: ** attributes are passed in sorted. If we need to andre@0: ** add functionality to sort them, there is an andre@0: ** example in the PKCS 7 code. andre@0: */ andre@0: attribute->attrValue[i] = SECITEM_ArenaDupItem(arena, attributes[i]); andre@0: if(!attribute->attrValue[i]) andre@0: goto loser; andre@0: } andre@0: andre@0: certreq->attributes[0] = attribute; andre@0: andre@0: return certreq; andre@0: andre@0: loser: andre@0: CERT_DestroyCertificateRequest(certreq); andre@0: return NULL; andre@0: } andre@0: andre@0: void andre@0: CERT_DestroyCertificateRequest(CERTCertificateRequest *req) andre@0: { andre@0: if (req && req->arena) { andre@0: PORT_FreeArena(req->arena, PR_FALSE); andre@0: } andre@0: return; andre@0: } andre@0: andre@0: static void andre@0: setCRExt(void *o, CERTCertExtension **exts) andre@0: { andre@0: ((CERTCertificateRequest *)o)->attributes = (struct CERTAttributeStr **)exts; andre@0: } andre@0: andre@0: /* andre@0: ** Set up to start gathering cert extensions for a cert request. andre@0: ** The list is created as CertExtensions and converted to an andre@0: ** attribute list by CERT_FinishCRAttributes(). andre@0: */ andre@0: extern void *cert_StartExtensions(void *owner, PLArenaPool *ownerArena, andre@0: void (*setExts)(void *object, CERTCertExtension **exts)); andre@0: void * andre@0: CERT_StartCertificateRequestAttributes(CERTCertificateRequest *req) andre@0: { andre@0: return (cert_StartExtensions ((void *)req, req->arena, setCRExt)); andre@0: } andre@0: andre@0: /* andre@0: ** At entry req->attributes actually contains an list of cert extensions-- andre@0: ** req-attributes is overloaded until the list is DER encoded (the first andre@0: ** ...EncodeItem() below). andre@0: ** We turn this into an attribute list by encapsulating it andre@0: ** in a PKCS 10 Attribute structure andre@0: */ andre@0: SECStatus andre@0: CERT_FinishCertificateRequestAttributes(CERTCertificateRequest *req) andre@0: { SECItem *extlist; andre@0: SECOidData *oidrec; andre@0: CERTAttribute *attribute; andre@0: andre@0: if (!req || !req->arena) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return SECFailure; andre@0: } andre@0: if (req->attributes == NULL || req->attributes[0] == NULL) andre@0: return SECSuccess; andre@0: andre@0: extlist = SEC_ASN1EncodeItem(req->arena, NULL, &req->attributes, andre@0: SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate)); andre@0: if (extlist == NULL) andre@0: return(SECFailure); andre@0: andre@0: oidrec = SECOID_FindOIDByTag(SEC_OID_PKCS9_EXTENSION_REQUEST); andre@0: if (oidrec == NULL) andre@0: return SECFailure; andre@0: andre@0: /* now change the list of cert extensions into a list of attributes andre@0: */ andre@0: req->attributes = PORT_ArenaZNewArray(req->arena, CERTAttribute*, 2); andre@0: andre@0: attribute = PORT_ArenaZNew(req->arena, CERTAttribute); andre@0: andre@0: if (req->attributes == NULL || attribute == NULL || andre@0: SECITEM_CopyItem(req->arena, &attribute->attrType, &oidrec->oid) != 0) { andre@0: PORT_SetError(SEC_ERROR_NO_MEMORY); andre@0: return SECFailure; andre@0: } andre@0: attribute->attrValue = PORT_ArenaZNewArray(req->arena, SECItem*, 2); andre@0: andre@0: if (attribute->attrValue == NULL) andre@0: return SECFailure; andre@0: andre@0: attribute->attrValue[0] = extlist; andre@0: attribute->attrValue[1] = NULL; andre@0: req->attributes[0] = attribute; andre@0: req->attributes[1] = NULL; andre@0: andre@0: return SECSuccess; andre@0: } andre@0: andre@0: SECStatus andre@0: CERT_GetCertificateRequestExtensions(CERTCertificateRequest *req, andre@0: CERTCertExtension ***exts) andre@0: { andre@0: if (req == NULL || exts == NULL) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return SECFailure; andre@0: } andre@0: andre@0: if (req->attributes == NULL || *req->attributes == NULL) andre@0: return SECSuccess; andre@0: andre@0: if ((*req->attributes)->attrValue == NULL) { andre@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); andre@0: return SECFailure; andre@0: } andre@0: andre@0: return(SEC_ASN1DecodeItem(req->arena, exts, andre@0: SEC_ASN1_GET(CERT_SequenceOfCertExtensionTemplate), andre@0: (*req->attributes)->attrValue[0])); andre@0: }