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