mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
731c45f2b1
r=rrelyea
1409 lines
43 KiB
C
1409 lines
43 KiB
C
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is the Netscape security libraries.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1994-2000
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
/*
|
|
* Support routines for PKCS7 implementation, none of which are exported.
|
|
* This file should only contain things that are needed by both the
|
|
* encoding/creation side *and* the decoding/decryption side. Anything
|
|
* else should be static routines in the appropriate file.
|
|
*
|
|
* $Id: p7local.c,v 1.13 2008/05/30 03:39:46 nelson%bolyard.com Exp $
|
|
*/
|
|
|
|
#include "p7local.h"
|
|
|
|
#include "cryptohi.h"
|
|
#include "secasn1.h"
|
|
#include "secoid.h"
|
|
#include "secitem.h"
|
|
#include "pk11func.h"
|
|
#include "secpkcs5.h"
|
|
#include "secerr.h"
|
|
|
|
/*
|
|
* -------------------------------------------------------------------
|
|
* Cipher stuff.
|
|
*/
|
|
|
|
typedef SECStatus (*sec_pkcs7_cipher_function) (void *,
|
|
unsigned char *,
|
|
unsigned *,
|
|
unsigned int,
|
|
const unsigned char *,
|
|
unsigned int);
|
|
typedef SECStatus (*sec_pkcs7_cipher_destroy) (void *, PRBool);
|
|
|
|
#define BLOCK_SIZE 4096
|
|
|
|
struct sec_pkcs7_cipher_object {
|
|
void *cx;
|
|
sec_pkcs7_cipher_function doit;
|
|
sec_pkcs7_cipher_destroy destroy;
|
|
PRBool encrypt;
|
|
int block_size;
|
|
int pad_size;
|
|
int pending_count;
|
|
unsigned char pending_buf[BLOCK_SIZE];
|
|
};
|
|
|
|
SEC_ASN1_MKSUB(CERT_IssuerAndSNTemplate)
|
|
SEC_ASN1_MKSUB(CERT_SetOfSignedCrlTemplate)
|
|
SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
|
|
SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
|
|
SEC_ASN1_MKSUB(SEC_SetOfAnyTemplate)
|
|
|
|
/*
|
|
* Create a cipher object to do decryption, based on the given bulk
|
|
* encryption key and algorithm identifier (which may include an iv).
|
|
*
|
|
* XXX This interface, or one similar, would be really nice available
|
|
* in general... I tried to keep the pkcs7-specific stuff (mostly
|
|
* having to do with padding) out of here.
|
|
*
|
|
* XXX Once both are working, it might be nice to combine this and the
|
|
* function below (for starting up encryption) into one routine, and just
|
|
* have two simple cover functions which call it.
|
|
*/
|
|
sec_PKCS7CipherObject *
|
|
sec_PKCS7CreateDecryptObject (PK11SymKey *key, SECAlgorithmID *algid)
|
|
{
|
|
sec_PKCS7CipherObject *result;
|
|
SECOidTag algtag;
|
|
void *ciphercx;
|
|
CK_MECHANISM_TYPE cryptoMechType;
|
|
SECItem *param;
|
|
PK11SlotInfo *slot;
|
|
|
|
result = (struct sec_pkcs7_cipher_object*)
|
|
PORT_ZAlloc (sizeof(struct sec_pkcs7_cipher_object));
|
|
if (result == NULL)
|
|
return NULL;
|
|
|
|
ciphercx = NULL;
|
|
algtag = SECOID_GetAlgorithmTag (algid);
|
|
|
|
if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
|
|
SECItem *pwitem;
|
|
|
|
pwitem = (SECItem *)PK11_GetSymKeyUserData(key);
|
|
if (!pwitem) {
|
|
PORT_Free(result);
|
|
return NULL;
|
|
}
|
|
|
|
cryptoMechType = PK11_GetPBECryptoMechanism(algid, ¶m, pwitem);
|
|
if (cryptoMechType == CKM_INVALID_MECHANISM) {
|
|
PORT_Free(result);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
cryptoMechType = PK11_AlgtagToMechanism(algtag);
|
|
param = PK11_ParamFromAlgid(algid);
|
|
if (param == NULL) {
|
|
PORT_Free(result);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
result->pad_size = PK11_GetBlockSize(cryptoMechType, param);
|
|
slot = PK11_GetSlotFromKey(key);
|
|
result->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : result->pad_size;
|
|
PK11_FreeSlot(slot);
|
|
ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_DECRYPT,
|
|
key, param);
|
|
SECITEM_FreeItem(param,PR_TRUE);
|
|
if (ciphercx == NULL) {
|
|
PORT_Free (result);
|
|
return NULL;
|
|
}
|
|
|
|
result->cx = ciphercx;
|
|
result->doit = (sec_pkcs7_cipher_function) PK11_CipherOp;
|
|
result->destroy = (sec_pkcs7_cipher_destroy) PK11_DestroyContext;
|
|
result->encrypt = PR_FALSE;
|
|
result->pending_count = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Create a cipher object to do encryption, based on the given bulk
|
|
* encryption key and algorithm tag. Fill in the algorithm identifier
|
|
* (which may include an iv) appropriately.
|
|
*
|
|
* XXX This interface, or one similar, would be really nice available
|
|
* in general... I tried to keep the pkcs7-specific stuff (mostly
|
|
* having to do with padding) out of here.
|
|
*
|
|
* XXX Once both are working, it might be nice to combine this and the
|
|
* function above (for starting up decryption) into one routine, and just
|
|
* have two simple cover functions which call it.
|
|
*/
|
|
sec_PKCS7CipherObject *
|
|
sec_PKCS7CreateEncryptObject (PRArenaPool *poolp, PK11SymKey *key,
|
|
SECOidTag algtag, SECAlgorithmID *algid)
|
|
{
|
|
sec_PKCS7CipherObject *result;
|
|
void *ciphercx;
|
|
SECItem *param;
|
|
SECStatus rv;
|
|
CK_MECHANISM_TYPE cryptoMechType;
|
|
PRBool needToEncodeAlgid = PR_FALSE;
|
|
PK11SlotInfo *slot;
|
|
|
|
result = (struct sec_pkcs7_cipher_object*)
|
|
PORT_ZAlloc (sizeof(struct sec_pkcs7_cipher_object));
|
|
if (result == NULL)
|
|
return NULL;
|
|
|
|
ciphercx = NULL;
|
|
if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
|
|
SECItem *pwitem;
|
|
|
|
pwitem = (SECItem *)PK11_GetSymKeyUserData(key);
|
|
if (!pwitem) {
|
|
PORT_Free(result);
|
|
return NULL;
|
|
}
|
|
|
|
cryptoMechType = PK11_GetPBECryptoMechanism(algid, ¶m, pwitem);
|
|
if (cryptoMechType == CKM_INVALID_MECHANISM) {
|
|
PORT_Free(result);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
cryptoMechType = PK11_AlgtagToMechanism(algtag);
|
|
param = PK11_GenerateNewParam(cryptoMechType, key);
|
|
if (param == NULL) {
|
|
PORT_Free(result);
|
|
return NULL;
|
|
}
|
|
needToEncodeAlgid = PR_TRUE;
|
|
}
|
|
|
|
result->pad_size = PK11_GetBlockSize(cryptoMechType,param);
|
|
slot = PK11_GetSlotFromKey(key);
|
|
result->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : result->pad_size;
|
|
PK11_FreeSlot(slot);
|
|
ciphercx = PK11_CreateContextBySymKey(cryptoMechType, CKA_ENCRYPT,
|
|
key, param);
|
|
if (ciphercx == NULL) {
|
|
PORT_Free (result);
|
|
SECITEM_FreeItem(param,PR_TRUE);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* These are placed after the CreateContextBySymKey() because some
|
|
* mechanisms have to generate their IVs from their card (i.e. FORTEZZA).
|
|
* Don't move it from here.
|
|
*/
|
|
if (needToEncodeAlgid) {
|
|
rv = PK11_ParamToAlgid(algtag,param,poolp,algid);
|
|
if(rv != SECSuccess) {
|
|
PORT_Free (result);
|
|
SECITEM_FreeItem(param,PR_TRUE);
|
|
return NULL;
|
|
}
|
|
}
|
|
SECITEM_FreeItem(param,PR_TRUE);
|
|
|
|
result->cx = ciphercx;
|
|
result->doit = (sec_pkcs7_cipher_function) PK11_CipherOp;
|
|
result->destroy = (sec_pkcs7_cipher_destroy) PK11_DestroyContext;
|
|
result->encrypt = PR_TRUE;
|
|
result->pending_count = 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/*
|
|
* Destroy the cipher object.
|
|
*/
|
|
static void
|
|
sec_pkcs7_destroy_cipher (sec_PKCS7CipherObject *obj)
|
|
{
|
|
(* obj->destroy) (obj->cx, PR_TRUE);
|
|
PORT_Free (obj);
|
|
}
|
|
|
|
void
|
|
sec_PKCS7DestroyDecryptObject (sec_PKCS7CipherObject *obj)
|
|
{
|
|
PORT_Assert (obj != NULL);
|
|
if (obj == NULL)
|
|
return;
|
|
PORT_Assert (! obj->encrypt);
|
|
sec_pkcs7_destroy_cipher (obj);
|
|
}
|
|
|
|
void
|
|
sec_PKCS7DestroyEncryptObject (sec_PKCS7CipherObject *obj)
|
|
{
|
|
PORT_Assert (obj != NULL);
|
|
if (obj == NULL)
|
|
return;
|
|
PORT_Assert (obj->encrypt);
|
|
sec_pkcs7_destroy_cipher (obj);
|
|
}
|
|
|
|
|
|
/*
|
|
* XXX I think all of the following lengths should be longs instead
|
|
* of ints, but our current crypto interface uses ints, so I did too.
|
|
*/
|
|
|
|
|
|
/*
|
|
* What will be the output length of the next call to decrypt?
|
|
* Result can be used to perform memory allocations. Note that the amount
|
|
* is exactly accurate only when not doing a block cipher or when final
|
|
* is false, otherwise it is an upper bound on the amount because until
|
|
* we see the data we do not know how many padding bytes there are
|
|
* (always between 1 and bsize).
|
|
*
|
|
* Note that this can return zero, which does not mean that the decrypt
|
|
* operation can be skipped! (It simply means that there are not enough
|
|
* bytes to make up an entire block; the bytes will be reserved until
|
|
* there are enough to encrypt/decrypt at least one block.) However,
|
|
* if zero is returned it *does* mean that no output buffer need be
|
|
* passed in to the subsequent decrypt operation, as no output bytes
|
|
* will be stored.
|
|
*/
|
|
unsigned int
|
|
sec_PKCS7DecryptLength (sec_PKCS7CipherObject *obj, unsigned int input_len,
|
|
PRBool final)
|
|
{
|
|
int blocks, block_size;
|
|
|
|
PORT_Assert (! obj->encrypt);
|
|
|
|
block_size = obj->block_size;
|
|
|
|
/*
|
|
* If this is not a block cipher, then we always have the same
|
|
* number of output bytes as we had input bytes.
|
|
*/
|
|
if (block_size == 0)
|
|
return input_len;
|
|
|
|
/*
|
|
* On the final call, we will always use up all of the pending
|
|
* bytes plus all of the input bytes, *but*, there will be padding
|
|
* at the end and we cannot predict how many bytes of padding we
|
|
* will end up removing. The amount given here is actually known
|
|
* to be at least 1 byte too long (because we know we will have
|
|
* at least 1 byte of padding), but seemed clearer/better to me.
|
|
*/
|
|
if (final)
|
|
return obj->pending_count + input_len;
|
|
|
|
/*
|
|
* Okay, this amount is exactly what we will output on the
|
|
* next cipher operation. We will always hang onto the last
|
|
* 1 - block_size bytes for non-final operations. That is,
|
|
* we will do as many complete blocks as we can *except* the
|
|
* last block (complete or partial). (This is because until
|
|
* we know we are at the end, we cannot know when to interpret
|
|
* and removing the padding byte(s), which are guaranteed to
|
|
* be there.)
|
|
*/
|
|
blocks = (obj->pending_count + input_len - 1) / block_size;
|
|
return blocks * block_size;
|
|
}
|
|
|
|
/*
|
|
* What will be the output length of the next call to encrypt?
|
|
* Result can be used to perform memory allocations.
|
|
*
|
|
* Note that this can return zero, which does not mean that the encrypt
|
|
* operation can be skipped! (It simply means that there are not enough
|
|
* bytes to make up an entire block; the bytes will be reserved until
|
|
* there are enough to encrypt/decrypt at least one block.) However,
|
|
* if zero is returned it *does* mean that no output buffer need be
|
|
* passed in to the subsequent encrypt operation, as no output bytes
|
|
* will be stored.
|
|
*/
|
|
unsigned int
|
|
sec_PKCS7EncryptLength (sec_PKCS7CipherObject *obj, unsigned int input_len,
|
|
PRBool final)
|
|
{
|
|
int blocks, block_size;
|
|
int pad_size;
|
|
|
|
PORT_Assert (obj->encrypt);
|
|
|
|
block_size = obj->block_size;
|
|
pad_size = obj->pad_size;
|
|
|
|
/*
|
|
* If this is not a block cipher, then we always have the same
|
|
* number of output bytes as we had input bytes.
|
|
*/
|
|
if (block_size == 0)
|
|
return input_len;
|
|
|
|
/*
|
|
* On the final call, we only send out what we need for
|
|
* remaining bytes plus the padding. (There is always padding,
|
|
* so even if we have an exact number of blocks as input, we
|
|
* will add another full block that is just padding.)
|
|
*/
|
|
if (final) {
|
|
if (pad_size == 0) {
|
|
return obj->pending_count + input_len;
|
|
} else {
|
|
blocks = (obj->pending_count + input_len) / pad_size;
|
|
blocks++;
|
|
return blocks*pad_size;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now, count the number of complete blocks of data we have.
|
|
*/
|
|
blocks = (obj->pending_count + input_len) / block_size;
|
|
|
|
|
|
return blocks * block_size;
|
|
}
|
|
|
|
|
|
/*
|
|
* Decrypt a given length of input buffer (starting at "input" and
|
|
* containing "input_len" bytes), placing the decrypted bytes in
|
|
* "output" and storing the output length in "*output_len_p".
|
|
* "obj" is the return value from sec_PKCS7CreateDecryptObject.
|
|
* When "final" is true, this is the last of the data to be decrypted.
|
|
*
|
|
* This is much more complicated than it sounds when the cipher is
|
|
* a block-type, meaning that the decryption function will only
|
|
* operate on whole blocks. But our caller is operating stream-wise,
|
|
* and can pass in any number of bytes. So we need to keep track
|
|
* of block boundaries. We save excess bytes between calls in "obj".
|
|
* We also need to determine which bytes are padding, and remove
|
|
* them from the output. We can only do this step when we know we
|
|
* have the final block of data. PKCS #7 specifies that the padding
|
|
* used for a block cipher is a string of bytes, each of whose value is
|
|
* the same as the length of the padding, and that all data is padded.
|
|
* (Even data that starts out with an exact multiple of blocks gets
|
|
* added to it another block, all of which is padding.)
|
|
*/
|
|
SECStatus
|
|
sec_PKCS7Decrypt (sec_PKCS7CipherObject *obj, unsigned char *output,
|
|
unsigned int *output_len_p, unsigned int max_output_len,
|
|
const unsigned char *input, unsigned int input_len,
|
|
PRBool final)
|
|
{
|
|
int blocks, bsize, pcount, padsize;
|
|
unsigned int max_needed, ifraglen, ofraglen, output_len;
|
|
unsigned char *pbuf;
|
|
SECStatus rv;
|
|
|
|
PORT_Assert (! obj->encrypt);
|
|
|
|
/*
|
|
* Check that we have enough room for the output. Our caller should
|
|
* already handle this; failure is really an internal error (i.e. bug).
|
|
*/
|
|
max_needed = sec_PKCS7DecryptLength (obj, input_len, final);
|
|
PORT_Assert (max_output_len >= max_needed);
|
|
if (max_output_len < max_needed) {
|
|
/* PORT_SetError (XXX); */
|
|
return SECFailure;
|
|
}
|
|
|
|
/*
|
|
* hardware encryption does not like small decryption sizes here, so we
|
|
* allow both blocking and padding.
|
|
*/
|
|
bsize = obj->block_size;
|
|
padsize = obj->pad_size;
|
|
|
|
/*
|
|
* When no blocking or padding work to do, we can simply call the
|
|
* cipher function and we are done.
|
|
*/
|
|
if (bsize == 0) {
|
|
return (* obj->doit) (obj->cx, output, output_len_p, max_output_len,
|
|
input, input_len);
|
|
}
|
|
|
|
pcount = obj->pending_count;
|
|
pbuf = obj->pending_buf;
|
|
|
|
output_len = 0;
|
|
|
|
if (pcount) {
|
|
/*
|
|
* Try to fill in an entire block, starting with the bytes
|
|
* we already have saved away.
|
|
*/
|
|
while (input_len && pcount < bsize) {
|
|
pbuf[pcount++] = *input++;
|
|
input_len--;
|
|
}
|
|
/*
|
|
* If we have at most a whole block and this is not our last call,
|
|
* then we are done for now. (We do not try to decrypt a lone
|
|
* single block because we cannot interpret the padding bytes
|
|
* until we know we are handling the very last block of all input.)
|
|
*/
|
|
if (input_len == 0 && !final) {
|
|
obj->pending_count = pcount;
|
|
if (output_len_p)
|
|
*output_len_p = 0;
|
|
return SECSuccess;
|
|
}
|
|
/*
|
|
* Given the logic above, we expect to have a full block by now.
|
|
* If we do not, there is something wrong, either with our own
|
|
* logic or with (length of) the data given to us.
|
|
*/
|
|
PORT_Assert ((padsize == 0) || (pcount % padsize) == 0);
|
|
if ((padsize != 0) && (pcount % padsize) != 0) {
|
|
PORT_Assert (final);
|
|
PORT_SetError (SEC_ERROR_BAD_DATA);
|
|
return SECFailure;
|
|
}
|
|
/*
|
|
* Decrypt the block.
|
|
*/
|
|
rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
|
|
pbuf, pcount);
|
|
if (rv != SECSuccess)
|
|
return rv;
|
|
|
|
/*
|
|
* For now anyway, all of our ciphers have the same number of
|
|
* bytes of output as they do input. If this ever becomes untrue,
|
|
* then sec_PKCS7DecryptLength needs to be made smarter!
|
|
*/
|
|
PORT_Assert (ofraglen == pcount);
|
|
|
|
/*
|
|
* Account for the bytes now in output.
|
|
*/
|
|
max_output_len -= ofraglen;
|
|
output_len += ofraglen;
|
|
output += ofraglen;
|
|
}
|
|
|
|
/*
|
|
* If this is our last call, we expect to have an exact number of
|
|
* blocks left to be decrypted; we will decrypt them all.
|
|
*
|
|
* If not our last call, we always save between 1 and bsize bytes
|
|
* until next time. (We must do this because we cannot be sure
|
|
* that none of the decrypted bytes are padding bytes until we
|
|
* have at least another whole block of data. You cannot tell by
|
|
* looking -- the data could be anything -- you can only tell by
|
|
* context, knowing you are looking at the last block.) We could
|
|
* decrypt a whole block now but it is easier if we just treat it
|
|
* the same way we treat partial block bytes.
|
|
*/
|
|
if (final) {
|
|
if (padsize) {
|
|
blocks = input_len / padsize;
|
|
ifraglen = blocks * padsize;
|
|
} else ifraglen = input_len;
|
|
PORT_Assert (ifraglen == input_len);
|
|
|
|
if (ifraglen != input_len) {
|
|
PORT_SetError (SEC_ERROR_BAD_DATA);
|
|
return SECFailure;
|
|
}
|
|
} else {
|
|
blocks = (input_len - 1) / bsize;
|
|
ifraglen = blocks * bsize;
|
|
PORT_Assert (ifraglen < input_len);
|
|
|
|
pcount = input_len - ifraglen;
|
|
PORT_Memcpy (pbuf, input + ifraglen, pcount);
|
|
obj->pending_count = pcount;
|
|
}
|
|
|
|
if (ifraglen) {
|
|
rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
|
|
input, ifraglen);
|
|
if (rv != SECSuccess)
|
|
return rv;
|
|
|
|
/*
|
|
* For now anyway, all of our ciphers have the same number of
|
|
* bytes of output as they do input. If this ever becomes untrue,
|
|
* then sec_PKCS7DecryptLength needs to be made smarter!
|
|
*/
|
|
PORT_Assert (ifraglen == ofraglen);
|
|
if (ifraglen != ofraglen) {
|
|
PORT_SetError (SEC_ERROR_BAD_DATA);
|
|
return SECFailure;
|
|
}
|
|
|
|
output_len += ofraglen;
|
|
} else {
|
|
ofraglen = 0;
|
|
}
|
|
|
|
/*
|
|
* If we just did our very last block, "remove" the padding by
|
|
* adjusting the output length.
|
|
*/
|
|
if (final && (padsize != 0)) {
|
|
unsigned int padlen = *(output + ofraglen - 1);
|
|
if (padlen == 0 || padlen > padsize) {
|
|
PORT_SetError (SEC_ERROR_BAD_DATA);
|
|
return SECFailure;
|
|
}
|
|
output_len -= padlen;
|
|
}
|
|
|
|
PORT_Assert (output_len_p != NULL || output_len == 0);
|
|
if (output_len_p != NULL)
|
|
*output_len_p = output_len;
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
/*
|
|
* Encrypt a given length of input buffer (starting at "input" and
|
|
* containing "input_len" bytes), placing the encrypted bytes in
|
|
* "output" and storing the output length in "*output_len_p".
|
|
* "obj" is the return value from sec_PKCS7CreateEncryptObject.
|
|
* When "final" is true, this is the last of the data to be encrypted.
|
|
*
|
|
* This is much more complicated than it sounds when the cipher is
|
|
* a block-type, meaning that the encryption function will only
|
|
* operate on whole blocks. But our caller is operating stream-wise,
|
|
* and can pass in any number of bytes. So we need to keep track
|
|
* of block boundaries. We save excess bytes between calls in "obj".
|
|
* We also need to add padding bytes at the end. PKCS #7 specifies
|
|
* that the padding used for a block cipher is a string of bytes,
|
|
* each of whose value is the same as the length of the padding,
|
|
* and that all data is padded. (Even data that starts out with
|
|
* an exact multiple of blocks gets added to it another block,
|
|
* all of which is padding.)
|
|
*
|
|
* XXX I would kind of like to combine this with the function above
|
|
* which does decryption, since they have a lot in common. But the
|
|
* tricky parts about padding and filling blocks would be much
|
|
* harder to read that way, so I left them separate. At least for
|
|
* now until it is clear that they are right.
|
|
*/
|
|
SECStatus
|
|
sec_PKCS7Encrypt (sec_PKCS7CipherObject *obj, unsigned char *output,
|
|
unsigned int *output_len_p, unsigned int max_output_len,
|
|
const unsigned char *input, unsigned int input_len,
|
|
PRBool final)
|
|
{
|
|
int blocks, bsize, padlen, pcount, padsize;
|
|
unsigned int max_needed, ifraglen, ofraglen, output_len;
|
|
unsigned char *pbuf;
|
|
SECStatus rv;
|
|
|
|
PORT_Assert (obj->encrypt);
|
|
|
|
/*
|
|
* Check that we have enough room for the output. Our caller should
|
|
* already handle this; failure is really an internal error (i.e. bug).
|
|
*/
|
|
max_needed = sec_PKCS7EncryptLength (obj, input_len, final);
|
|
PORT_Assert (max_output_len >= max_needed);
|
|
if (max_output_len < max_needed) {
|
|
/* PORT_SetError (XXX); */
|
|
return SECFailure;
|
|
}
|
|
|
|
bsize = obj->block_size;
|
|
padsize = obj->pad_size;
|
|
|
|
/*
|
|
* When no blocking and padding work to do, we can simply call the
|
|
* cipher function and we are done.
|
|
*/
|
|
if (bsize == 0) {
|
|
return (* obj->doit) (obj->cx, output, output_len_p, max_output_len,
|
|
input, input_len);
|
|
}
|
|
|
|
pcount = obj->pending_count;
|
|
pbuf = obj->pending_buf;
|
|
|
|
output_len = 0;
|
|
|
|
if (pcount) {
|
|
/*
|
|
* Try to fill in an entire block, starting with the bytes
|
|
* we already have saved away.
|
|
*/
|
|
while (input_len && pcount < bsize) {
|
|
pbuf[pcount++] = *input++;
|
|
input_len--;
|
|
}
|
|
/*
|
|
* If we do not have a full block and we know we will be
|
|
* called again, then we are done for now.
|
|
*/
|
|
if (pcount < bsize && !final) {
|
|
obj->pending_count = pcount;
|
|
if (output_len_p != NULL)
|
|
*output_len_p = 0;
|
|
return SECSuccess;
|
|
}
|
|
/*
|
|
* If we have a whole block available, encrypt it.
|
|
*/
|
|
if ((padsize == 0) || (pcount % padsize) == 0) {
|
|
rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
|
|
pbuf, pcount);
|
|
if (rv != SECSuccess)
|
|
return rv;
|
|
|
|
/*
|
|
* For now anyway, all of our ciphers have the same number of
|
|
* bytes of output as they do input. If this ever becomes untrue,
|
|
* then sec_PKCS7EncryptLength needs to be made smarter!
|
|
*/
|
|
PORT_Assert (ofraglen == pcount);
|
|
|
|
/*
|
|
* Account for the bytes now in output.
|
|
*/
|
|
max_output_len -= ofraglen;
|
|
output_len += ofraglen;
|
|
output += ofraglen;
|
|
|
|
pcount = 0;
|
|
}
|
|
}
|
|
|
|
if (input_len) {
|
|
PORT_Assert (pcount == 0);
|
|
|
|
blocks = input_len / bsize;
|
|
ifraglen = blocks * bsize;
|
|
|
|
if (ifraglen) {
|
|
rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
|
|
input, ifraglen);
|
|
if (rv != SECSuccess)
|
|
return rv;
|
|
|
|
/*
|
|
* For now anyway, all of our ciphers have the same number of
|
|
* bytes of output as they do input. If this ever becomes untrue,
|
|
* then sec_PKCS7EncryptLength needs to be made smarter!
|
|
*/
|
|
PORT_Assert (ifraglen == ofraglen);
|
|
|
|
max_output_len -= ofraglen;
|
|
output_len += ofraglen;
|
|
output += ofraglen;
|
|
}
|
|
|
|
pcount = input_len - ifraglen;
|
|
PORT_Assert (pcount < bsize);
|
|
if (pcount)
|
|
PORT_Memcpy (pbuf, input + ifraglen, pcount);
|
|
}
|
|
|
|
if (final) {
|
|
padlen = padsize - (pcount % padsize);
|
|
PORT_Memset (pbuf + pcount, padlen, padlen);
|
|
rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
|
|
pbuf, pcount+padlen);
|
|
if (rv != SECSuccess)
|
|
return rv;
|
|
|
|
/*
|
|
* For now anyway, all of our ciphers have the same number of
|
|
* bytes of output as they do input. If this ever becomes untrue,
|
|
* then sec_PKCS7EncryptLength needs to be made smarter!
|
|
*/
|
|
PORT_Assert (ofraglen == (pcount+padlen));
|
|
output_len += ofraglen;
|
|
} else {
|
|
obj->pending_count = pcount;
|
|
}
|
|
|
|
PORT_Assert (output_len_p != NULL || output_len == 0);
|
|
if (output_len_p != NULL)
|
|
*output_len_p = output_len;
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
/*
|
|
* End of cipher stuff.
|
|
* -------------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
/*
|
|
* -------------------------------------------------------------------
|
|
* XXX The following Attribute stuff really belongs elsewhere.
|
|
* The Attribute type is *not* part of pkcs7 but rather X.501.
|
|
* But for now, since PKCS7 is the only customer of attributes,
|
|
* we define them here. Once there is a use outside of PKCS7,
|
|
* then change the attribute types and functions from internal
|
|
* to external naming convention, and move them elsewhere!
|
|
*/
|
|
|
|
/*
|
|
* Look through a set of attributes and find one that matches the
|
|
* specified object ID. If "only" is true, then make sure that
|
|
* there is not more than one attribute of the same type. Otherwise,
|
|
* just return the first one found. (XXX Does anybody really want
|
|
* that first-found behavior? It was like that when I found it...)
|
|
*/
|
|
SEC_PKCS7Attribute *
|
|
sec_PKCS7FindAttribute (SEC_PKCS7Attribute **attrs, SECOidTag oidtag,
|
|
PRBool only)
|
|
{
|
|
SECOidData *oid;
|
|
SEC_PKCS7Attribute *attr1, *attr2;
|
|
|
|
if (attrs == NULL)
|
|
return NULL;
|
|
|
|
oid = SECOID_FindOIDByTag(oidtag);
|
|
if (oid == NULL)
|
|
return NULL;
|
|
|
|
while ((attr1 = *attrs++) != NULL) {
|
|
if (attr1->type.len == oid->oid.len && PORT_Memcmp (attr1->type.data,
|
|
oid->oid.data,
|
|
oid->oid.len) == 0)
|
|
break;
|
|
}
|
|
|
|
if (attr1 == NULL)
|
|
return NULL;
|
|
|
|
if (!only)
|
|
return attr1;
|
|
|
|
while ((attr2 = *attrs++) != NULL) {
|
|
if (attr2->type.len == oid->oid.len && PORT_Memcmp (attr2->type.data,
|
|
oid->oid.data,
|
|
oid->oid.len) == 0)
|
|
break;
|
|
}
|
|
|
|
if (attr2 != NULL)
|
|
return NULL;
|
|
|
|
return attr1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Return the single attribute value, doing some sanity checking first:
|
|
* - Multiple values are *not* expected.
|
|
* - Empty values are *not* expected.
|
|
*/
|
|
SECItem *
|
|
sec_PKCS7AttributeValue(SEC_PKCS7Attribute *attr)
|
|
{
|
|
SECItem *value;
|
|
|
|
if (attr == NULL)
|
|
return NULL;
|
|
|
|
value = attr->values[0];
|
|
|
|
if (value == NULL || value->data == NULL || value->len == 0)
|
|
return NULL;
|
|
|
|
if (attr->values[1] != NULL)
|
|
return NULL;
|
|
|
|
return value;
|
|
}
|
|
|
|
static const SEC_ASN1Template *
|
|
sec_attr_choose_attr_value_template(void *src_or_dest, PRBool encoding)
|
|
{
|
|
const SEC_ASN1Template *theTemplate;
|
|
|
|
SEC_PKCS7Attribute *attribute;
|
|
SECOidData *oiddata;
|
|
PRBool encoded;
|
|
|
|
PORT_Assert (src_or_dest != NULL);
|
|
if (src_or_dest == NULL)
|
|
return NULL;
|
|
|
|
attribute = (SEC_PKCS7Attribute*)src_or_dest;
|
|
|
|
if (encoding && attribute->encoded)
|
|
return SEC_ASN1_GET(SEC_AnyTemplate);
|
|
|
|
oiddata = attribute->typeTag;
|
|
if (oiddata == NULL) {
|
|
oiddata = SECOID_FindOID(&attribute->type);
|
|
attribute->typeTag = oiddata;
|
|
}
|
|
|
|
if (oiddata == NULL) {
|
|
encoded = PR_TRUE;
|
|
theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
|
|
} else {
|
|
switch (oiddata->offset) {
|
|
default:
|
|
encoded = PR_TRUE;
|
|
theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
|
|
break;
|
|
case SEC_OID_PKCS9_EMAIL_ADDRESS:
|
|
case SEC_OID_RFC1274_MAIL:
|
|
case SEC_OID_PKCS9_UNSTRUCTURED_NAME:
|
|
encoded = PR_FALSE;
|
|
theTemplate = SEC_ASN1_GET(SEC_IA5StringTemplate);
|
|
break;
|
|
case SEC_OID_PKCS9_CONTENT_TYPE:
|
|
encoded = PR_FALSE;
|
|
theTemplate = SEC_ASN1_GET(SEC_ObjectIDTemplate);
|
|
break;
|
|
case SEC_OID_PKCS9_MESSAGE_DIGEST:
|
|
encoded = PR_FALSE;
|
|
theTemplate = SEC_ASN1_GET(SEC_OctetStringTemplate);
|
|
break;
|
|
case SEC_OID_PKCS9_SIGNING_TIME:
|
|
encoded = PR_FALSE;
|
|
theTemplate = SEC_ASN1_GET(CERT_TimeChoiceTemplate);
|
|
break;
|
|
/* XXX Want other types here, too */
|
|
}
|
|
}
|
|
|
|
if (encoding) {
|
|
/*
|
|
* If we are encoding and we think we have an already-encoded value,
|
|
* then the code which initialized this attribute should have set
|
|
* the "encoded" property to true (and we would have returned early,
|
|
* up above). No devastating error, but that code should be fixed.
|
|
* (It could indicate that the resulting encoded bytes are wrong.)
|
|
*/
|
|
PORT_Assert (!encoded);
|
|
} else {
|
|
/*
|
|
* We are decoding; record whether the resulting value is
|
|
* still encoded or not.
|
|
*/
|
|
attribute->encoded = encoded;
|
|
}
|
|
return theTemplate;
|
|
}
|
|
|
|
static const SEC_ASN1TemplateChooserPtr sec_attr_chooser
|
|
= sec_attr_choose_attr_value_template;
|
|
|
|
static const SEC_ASN1Template sec_pkcs7_attribute_template[] = {
|
|
{ SEC_ASN1_SEQUENCE,
|
|
0, NULL, sizeof(SEC_PKCS7Attribute) },
|
|
{ SEC_ASN1_OBJECT_ID,
|
|
offsetof(SEC_PKCS7Attribute,type) },
|
|
{ SEC_ASN1_DYNAMIC | SEC_ASN1_SET_OF,
|
|
offsetof(SEC_PKCS7Attribute,values),
|
|
&sec_attr_chooser },
|
|
{ 0 }
|
|
};
|
|
|
|
static const SEC_ASN1Template sec_pkcs7_set_of_attribute_template[] = {
|
|
{ SEC_ASN1_SET_OF, 0, sec_pkcs7_attribute_template },
|
|
};
|
|
|
|
/*
|
|
* If you are wondering why this routine does not reorder the attributes
|
|
* first, and might be tempted to make it do so, see the comment by the
|
|
* call to ReorderAttributes in p7encode.c. (Or, see who else calls this
|
|
* and think long and hard about the implications of making it always
|
|
* do the reordering.)
|
|
*/
|
|
SECItem *
|
|
sec_PKCS7EncodeAttributes (PRArenaPool *poolp, SECItem *dest, void *src)
|
|
{
|
|
return SEC_ASN1EncodeItem (poolp, dest, src,
|
|
sec_pkcs7_set_of_attribute_template);
|
|
}
|
|
|
|
/*
|
|
* Make sure that the order of the attributes guarantees valid DER
|
|
* (which must be in lexigraphically ascending order for a SET OF);
|
|
* if reordering is necessary it will be done in place (in attrs).
|
|
*/
|
|
SECStatus
|
|
sec_PKCS7ReorderAttributes (SEC_PKCS7Attribute **attrs)
|
|
{
|
|
PRArenaPool *poolp;
|
|
int num_attrs, i, pass, besti;
|
|
unsigned int j;
|
|
SECItem **enc_attrs;
|
|
SEC_PKCS7Attribute **new_attrs;
|
|
|
|
/*
|
|
* I think we should not be called with NULL. But if we are,
|
|
* call it a success anyway, because the order *is* okay.
|
|
*/
|
|
PORT_Assert (attrs != NULL);
|
|
if (attrs == NULL)
|
|
return SECSuccess;
|
|
|
|
/*
|
|
* Count how many attributes we are dealing with here.
|
|
*/
|
|
num_attrs = 0;
|
|
while (attrs[num_attrs] != NULL)
|
|
num_attrs++;
|
|
|
|
/*
|
|
* Again, I think we should have some attributes here.
|
|
* But if we do not, or if there is only one, then call it
|
|
* a success because it also already has a fine order.
|
|
*/
|
|
PORT_Assert (num_attrs);
|
|
if (num_attrs == 0 || num_attrs == 1)
|
|
return SECSuccess;
|
|
|
|
/*
|
|
* Allocate an arena for us to work with, so it is easy to
|
|
* clean up all of the memory (fairly small pieces, really).
|
|
*/
|
|
poolp = PORT_NewArena (1024); /* XXX what is right value? */
|
|
if (poolp == NULL)
|
|
return SECFailure; /* no memory; nothing we can do... */
|
|
|
|
/*
|
|
* Allocate arrays to hold the individual encodings which we will use
|
|
* for comparisons and the reordered attributes as they are sorted.
|
|
*/
|
|
enc_attrs=(SECItem**)PORT_ArenaZAlloc(poolp, num_attrs*sizeof(SECItem *));
|
|
new_attrs = (SEC_PKCS7Attribute**)PORT_ArenaZAlloc (poolp,
|
|
num_attrs * sizeof(SEC_PKCS7Attribute *));
|
|
if (enc_attrs == NULL || new_attrs == NULL) {
|
|
PORT_FreeArena (poolp, PR_FALSE);
|
|
return SECFailure;
|
|
}
|
|
|
|
/*
|
|
* DER encode each individual attribute.
|
|
*/
|
|
for (i = 0; i < num_attrs; i++) {
|
|
enc_attrs[i] = SEC_ASN1EncodeItem (poolp, NULL, attrs[i],
|
|
sec_pkcs7_attribute_template);
|
|
if (enc_attrs[i] == NULL) {
|
|
PORT_FreeArena (poolp, PR_FALSE);
|
|
return SECFailure;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now compare and sort them; this is not the most efficient sorting
|
|
* method, but it is just fine for the problem at hand, because the
|
|
* number of attributes is (always) going to be small.
|
|
*/
|
|
for (pass = 0; pass < num_attrs; pass++) {
|
|
/*
|
|
* Find the first not-yet-accepted attribute. (Once one is
|
|
* sorted into the other array, it is cleared from enc_attrs.)
|
|
*/
|
|
for (i = 0; i < num_attrs; i++) {
|
|
if (enc_attrs[i] != NULL)
|
|
break;
|
|
}
|
|
PORT_Assert (i < num_attrs);
|
|
besti = i;
|
|
|
|
/*
|
|
* Find the lowest (lexigraphically) encoding. One that is
|
|
* shorter than all the rest is known to be "less" because each
|
|
* attribute is of the same type (a SEQUENCE) and so thus the
|
|
* first octet of each is the same, and the second octet is
|
|
* the length (or the length of the length with the high bit
|
|
* set, followed by the length, which also works out to always
|
|
* order the shorter first). Two (or more) that have the
|
|
* same length need to be compared byte by byte until a mismatch
|
|
* is found.
|
|
*/
|
|
for (i = besti + 1; i < num_attrs; i++) {
|
|
if (enc_attrs[i] == NULL) /* slot already handled */
|
|
continue;
|
|
|
|
if (enc_attrs[i]->len != enc_attrs[besti]->len) {
|
|
if (enc_attrs[i]->len < enc_attrs[besti]->len)
|
|
besti = i;
|
|
continue;
|
|
}
|
|
|
|
for (j = 0; j < enc_attrs[i]->len; j++) {
|
|
if (enc_attrs[i]->data[j] < enc_attrs[besti]->data[j]) {
|
|
besti = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* For this not to be true, we would have to have encountered
|
|
* two *identical* attributes, which I think we should not see.
|
|
* So assert if it happens, but even if it does, let it go
|
|
* through; the ordering of the two does not matter.
|
|
*/
|
|
PORT_Assert (j < enc_attrs[i]->len);
|
|
}
|
|
|
|
/*
|
|
* Now we have found the next-lowest one; copy it over and
|
|
* remove it from enc_attrs.
|
|
*/
|
|
new_attrs[pass] = attrs[besti];
|
|
enc_attrs[besti] = NULL;
|
|
}
|
|
|
|
/*
|
|
* Now new_attrs has the attributes in the order we want;
|
|
* copy them back into the attrs array we started with.
|
|
*/
|
|
for (i = 0; i < num_attrs; i++)
|
|
attrs[i] = new_attrs[i];
|
|
|
|
PORT_FreeArena (poolp, PR_FALSE);
|
|
return SECSuccess;
|
|
}
|
|
|
|
/*
|
|
* End of attribute stuff.
|
|
* -------------------------------------------------------------------
|
|
*/
|
|
|
|
|
|
/*
|
|
* Templates and stuff. Keep these at the end of the file.
|
|
*/
|
|
|
|
/* forward declaration */
|
|
static const SEC_ASN1Template *
|
|
sec_pkcs7_choose_content_template(void *src_or_dest, PRBool encoding);
|
|
|
|
static const SEC_ASN1TemplateChooserPtr sec_pkcs7_chooser
|
|
= sec_pkcs7_choose_content_template;
|
|
|
|
const SEC_ASN1Template sec_PKCS7ContentInfoTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
|
|
0, NULL, sizeof(SEC_PKCS7ContentInfo) },
|
|
{ SEC_ASN1_OBJECT_ID,
|
|
offsetof(SEC_PKCS7ContentInfo,contentType) },
|
|
{ SEC_ASN1_OPTIONAL | SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM
|
|
| SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
|
|
offsetof(SEC_PKCS7ContentInfo,content),
|
|
&sec_pkcs7_chooser },
|
|
{ 0 }
|
|
};
|
|
|
|
/* XXX These names should change from external to internal convention. */
|
|
|
|
static const SEC_ASN1Template SEC_PKCS7SignerInfoTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE,
|
|
0, NULL, sizeof(SEC_PKCS7SignerInfo) },
|
|
{ SEC_ASN1_INTEGER,
|
|
offsetof(SEC_PKCS7SignerInfo,version) },
|
|
{ SEC_ASN1_POINTER | SEC_ASN1_XTRN,
|
|
offsetof(SEC_PKCS7SignerInfo,issuerAndSN),
|
|
SEC_ASN1_SUB(CERT_IssuerAndSNTemplate) },
|
|
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN,
|
|
offsetof(SEC_PKCS7SignerInfo,digestAlg),
|
|
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
|
|
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
|
|
offsetof(SEC_PKCS7SignerInfo,authAttr),
|
|
sec_pkcs7_set_of_attribute_template },
|
|
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN,
|
|
offsetof(SEC_PKCS7SignerInfo,digestEncAlg),
|
|
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
|
|
{ SEC_ASN1_OCTET_STRING,
|
|
offsetof(SEC_PKCS7SignerInfo,encDigest) },
|
|
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
|
|
offsetof(SEC_PKCS7SignerInfo,unAuthAttr),
|
|
sec_pkcs7_set_of_attribute_template },
|
|
{ 0 }
|
|
};
|
|
|
|
static const SEC_ASN1Template SEC_PKCS7SignedDataTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
|
|
0, NULL, sizeof(SEC_PKCS7SignedData) },
|
|
{ SEC_ASN1_INTEGER,
|
|
offsetof(SEC_PKCS7SignedData,version) },
|
|
{ SEC_ASN1_SET_OF | SEC_ASN1_XTRN,
|
|
offsetof(SEC_PKCS7SignedData,digestAlgorithms),
|
|
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
|
|
{ SEC_ASN1_INLINE,
|
|
offsetof(SEC_PKCS7SignedData,contentInfo),
|
|
sec_PKCS7ContentInfoTemplate },
|
|
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
|
|
SEC_ASN1_XTRN | 0,
|
|
offsetof(SEC_PKCS7SignedData,rawCerts),
|
|
SEC_ASN1_SUB(SEC_SetOfAnyTemplate) },
|
|
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
|
|
SEC_ASN1_XTRN | 1,
|
|
offsetof(SEC_PKCS7SignedData,crls),
|
|
SEC_ASN1_SUB(CERT_SetOfSignedCrlTemplate) },
|
|
{ SEC_ASN1_SET_OF,
|
|
offsetof(SEC_PKCS7SignedData,signerInfos),
|
|
SEC_PKCS7SignerInfoTemplate },
|
|
{ 0 }
|
|
};
|
|
|
|
static const SEC_ASN1Template SEC_PointerToPKCS7SignedDataTemplate[] = {
|
|
{ SEC_ASN1_POINTER, 0, SEC_PKCS7SignedDataTemplate }
|
|
};
|
|
|
|
static const SEC_ASN1Template SEC_PKCS7RecipientInfoTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE,
|
|
0, NULL, sizeof(SEC_PKCS7RecipientInfo) },
|
|
{ SEC_ASN1_INTEGER,
|
|
offsetof(SEC_PKCS7RecipientInfo,version) },
|
|
{ SEC_ASN1_POINTER | SEC_ASN1_XTRN,
|
|
offsetof(SEC_PKCS7RecipientInfo,issuerAndSN),
|
|
SEC_ASN1_SUB(CERT_IssuerAndSNTemplate) },
|
|
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN,
|
|
offsetof(SEC_PKCS7RecipientInfo,keyEncAlg),
|
|
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
|
|
{ SEC_ASN1_OCTET_STRING,
|
|
offsetof(SEC_PKCS7RecipientInfo,encKey) },
|
|
{ 0 }
|
|
};
|
|
|
|
static const SEC_ASN1Template SEC_PKCS7EncryptedContentInfoTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
|
|
0, NULL, sizeof(SEC_PKCS7EncryptedContentInfo) },
|
|
{ SEC_ASN1_OBJECT_ID,
|
|
offsetof(SEC_PKCS7EncryptedContentInfo,contentType) },
|
|
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN,
|
|
offsetof(SEC_PKCS7EncryptedContentInfo,contentEncAlg),
|
|
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
|
|
{ SEC_ASN1_OPTIONAL | SEC_ASN1_MAY_STREAM | SEC_ASN1_CONTEXT_SPECIFIC |
|
|
SEC_ASN1_XTRN | 0,
|
|
offsetof(SEC_PKCS7EncryptedContentInfo,encContent),
|
|
SEC_ASN1_SUB(SEC_OctetStringTemplate) },
|
|
{ 0 }
|
|
};
|
|
|
|
static const SEC_ASN1Template SEC_PKCS7EnvelopedDataTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
|
|
0, NULL, sizeof(SEC_PKCS7EnvelopedData) },
|
|
{ SEC_ASN1_INTEGER,
|
|
offsetof(SEC_PKCS7EnvelopedData,version) },
|
|
{ SEC_ASN1_SET_OF,
|
|
offsetof(SEC_PKCS7EnvelopedData,recipientInfos),
|
|
SEC_PKCS7RecipientInfoTemplate },
|
|
{ SEC_ASN1_INLINE,
|
|
offsetof(SEC_PKCS7EnvelopedData,encContentInfo),
|
|
SEC_PKCS7EncryptedContentInfoTemplate },
|
|
{ 0 }
|
|
};
|
|
|
|
static const SEC_ASN1Template SEC_PointerToPKCS7EnvelopedDataTemplate[] = {
|
|
{ SEC_ASN1_POINTER, 0, SEC_PKCS7EnvelopedDataTemplate }
|
|
};
|
|
|
|
static const SEC_ASN1Template SEC_PKCS7SignedAndEnvelopedDataTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
|
|
0, NULL, sizeof(SEC_PKCS7SignedAndEnvelopedData) },
|
|
{ SEC_ASN1_INTEGER,
|
|
offsetof(SEC_PKCS7SignedAndEnvelopedData,version) },
|
|
{ SEC_ASN1_SET_OF,
|
|
offsetof(SEC_PKCS7SignedAndEnvelopedData,recipientInfos),
|
|
SEC_PKCS7RecipientInfoTemplate },
|
|
{ SEC_ASN1_SET_OF | SEC_ASN1_XTRN,
|
|
offsetof(SEC_PKCS7SignedAndEnvelopedData,digestAlgorithms),
|
|
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
|
|
{ SEC_ASN1_INLINE,
|
|
offsetof(SEC_PKCS7SignedAndEnvelopedData,encContentInfo),
|
|
SEC_PKCS7EncryptedContentInfoTemplate },
|
|
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
|
|
SEC_ASN1_XTRN | 0,
|
|
offsetof(SEC_PKCS7SignedAndEnvelopedData,rawCerts),
|
|
SEC_ASN1_SUB(SEC_SetOfAnyTemplate) },
|
|
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
|
|
SEC_ASN1_XTRN | 1,
|
|
offsetof(SEC_PKCS7SignedAndEnvelopedData,crls),
|
|
SEC_ASN1_SUB(CERT_SetOfSignedCrlTemplate) },
|
|
{ SEC_ASN1_SET_OF,
|
|
offsetof(SEC_PKCS7SignedAndEnvelopedData,signerInfos),
|
|
SEC_PKCS7SignerInfoTemplate },
|
|
{ 0 }
|
|
};
|
|
|
|
static const SEC_ASN1Template
|
|
SEC_PointerToPKCS7SignedAndEnvelopedDataTemplate[] = {
|
|
{ SEC_ASN1_POINTER, 0, SEC_PKCS7SignedAndEnvelopedDataTemplate }
|
|
};
|
|
|
|
static const SEC_ASN1Template SEC_PKCS7DigestedDataTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
|
|
0, NULL, sizeof(SEC_PKCS7DigestedData) },
|
|
{ SEC_ASN1_INTEGER,
|
|
offsetof(SEC_PKCS7DigestedData,version) },
|
|
{ SEC_ASN1_INLINE | SEC_ASN1_XTRN,
|
|
offsetof(SEC_PKCS7DigestedData,digestAlg),
|
|
SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
|
|
{ SEC_ASN1_INLINE,
|
|
offsetof(SEC_PKCS7DigestedData,contentInfo),
|
|
sec_PKCS7ContentInfoTemplate },
|
|
{ SEC_ASN1_OCTET_STRING,
|
|
offsetof(SEC_PKCS7DigestedData,digest) },
|
|
{ 0 }
|
|
};
|
|
|
|
static const SEC_ASN1Template SEC_PointerToPKCS7DigestedDataTemplate[] = {
|
|
{ SEC_ASN1_POINTER, 0, SEC_PKCS7DigestedDataTemplate }
|
|
};
|
|
|
|
static const SEC_ASN1Template SEC_PKCS7EncryptedDataTemplate[] = {
|
|
{ SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
|
|
0, NULL, sizeof(SEC_PKCS7EncryptedData) },
|
|
{ SEC_ASN1_INTEGER,
|
|
offsetof(SEC_PKCS7EncryptedData,version) },
|
|
{ SEC_ASN1_INLINE,
|
|
offsetof(SEC_PKCS7EncryptedData,encContentInfo),
|
|
SEC_PKCS7EncryptedContentInfoTemplate },
|
|
{ 0 }
|
|
};
|
|
|
|
static const SEC_ASN1Template SEC_PointerToPKCS7EncryptedDataTemplate[] = {
|
|
{ SEC_ASN1_POINTER, 0, SEC_PKCS7EncryptedDataTemplate }
|
|
};
|
|
|
|
const SEC_ASN1Template SEC_SMIMEKEAParamTemplateSkipjack[] = {
|
|
{ SEC_ASN1_SEQUENCE,
|
|
0, NULL, sizeof(SEC_PKCS7SMIMEKEAParameters) },
|
|
{ SEC_ASN1_OCTET_STRING /* | SEC_ASN1_OPTIONAL */,
|
|
offsetof(SEC_PKCS7SMIMEKEAParameters,originatorKEAKey) },
|
|
{ SEC_ASN1_OCTET_STRING,
|
|
offsetof(SEC_PKCS7SMIMEKEAParameters,originatorRA) },
|
|
{ 0 }
|
|
};
|
|
|
|
const SEC_ASN1Template SEC_SMIMEKEAParamTemplateNoSkipjack[] = {
|
|
{ SEC_ASN1_SEQUENCE,
|
|
0, NULL, sizeof(SEC_PKCS7SMIMEKEAParameters) },
|
|
{ SEC_ASN1_OCTET_STRING /* | SEC_ASN1_OPTIONAL */,
|
|
offsetof(SEC_PKCS7SMIMEKEAParameters,originatorKEAKey) },
|
|
{ SEC_ASN1_OCTET_STRING,
|
|
offsetof(SEC_PKCS7SMIMEKEAParameters,originatorRA) },
|
|
{ SEC_ASN1_OCTET_STRING | SEC_ASN1_OPTIONAL ,
|
|
offsetof(SEC_PKCS7SMIMEKEAParameters,nonSkipjackIV) },
|
|
{ 0 }
|
|
};
|
|
|
|
const SEC_ASN1Template SEC_SMIMEKEAParamTemplateAllParams[] = {
|
|
{ SEC_ASN1_SEQUENCE,
|
|
0, NULL, sizeof(SEC_PKCS7SMIMEKEAParameters) },
|
|
{ SEC_ASN1_OCTET_STRING /* | SEC_ASN1_OPTIONAL */,
|
|
offsetof(SEC_PKCS7SMIMEKEAParameters,originatorKEAKey) },
|
|
{ SEC_ASN1_OCTET_STRING,
|
|
offsetof(SEC_PKCS7SMIMEKEAParameters,originatorRA) },
|
|
{ SEC_ASN1_OCTET_STRING | SEC_ASN1_OPTIONAL ,
|
|
offsetof(SEC_PKCS7SMIMEKEAParameters,nonSkipjackIV) },
|
|
{ SEC_ASN1_OCTET_STRING | SEC_ASN1_OPTIONAL ,
|
|
offsetof(SEC_PKCS7SMIMEKEAParameters,bulkKeySize) },
|
|
{ 0 }
|
|
};
|
|
|
|
const SEC_ASN1Template*
|
|
sec_pkcs7_get_kea_template(SECKEATemplateSelector whichTemplate)
|
|
{
|
|
const SEC_ASN1Template *returnVal = NULL;
|
|
|
|
switch(whichTemplate)
|
|
{
|
|
case SECKEAUsesNonSkipjack:
|
|
returnVal = SEC_SMIMEKEAParamTemplateNoSkipjack;
|
|
break;
|
|
case SECKEAUsesSkipjack:
|
|
returnVal = SEC_SMIMEKEAParamTemplateSkipjack;
|
|
break;
|
|
case SECKEAUsesNonSkipjackWithPaddedEncKey:
|
|
default:
|
|
returnVal = SEC_SMIMEKEAParamTemplateAllParams;
|
|
break;
|
|
}
|
|
return returnVal;
|
|
}
|
|
|
|
static const SEC_ASN1Template *
|
|
sec_pkcs7_choose_content_template(void *src_or_dest, PRBool encoding)
|
|
{
|
|
const SEC_ASN1Template *theTemplate;
|
|
SEC_PKCS7ContentInfo *cinfo;
|
|
SECOidTag kind;
|
|
|
|
PORT_Assert (src_or_dest != NULL);
|
|
if (src_or_dest == NULL)
|
|
return NULL;
|
|
|
|
cinfo = (SEC_PKCS7ContentInfo*)src_or_dest;
|
|
kind = SEC_PKCS7ContentType (cinfo);
|
|
switch (kind) {
|
|
default:
|
|
theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate);
|
|
break;
|
|
case SEC_OID_PKCS7_DATA:
|
|
theTemplate = SEC_ASN1_GET(SEC_PointerToOctetStringTemplate);
|
|
break;
|
|
case SEC_OID_PKCS7_SIGNED_DATA:
|
|
theTemplate = SEC_PointerToPKCS7SignedDataTemplate;
|
|
break;
|
|
case SEC_OID_PKCS7_ENVELOPED_DATA:
|
|
theTemplate = SEC_PointerToPKCS7EnvelopedDataTemplate;
|
|
break;
|
|
case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
|
|
theTemplate = SEC_PointerToPKCS7SignedAndEnvelopedDataTemplate;
|
|
break;
|
|
case SEC_OID_PKCS7_DIGESTED_DATA:
|
|
theTemplate = SEC_PointerToPKCS7DigestedDataTemplate;
|
|
break;
|
|
case SEC_OID_PKCS7_ENCRYPTED_DATA:
|
|
theTemplate = SEC_PointerToPKCS7EncryptedDataTemplate;
|
|
break;
|
|
}
|
|
return theTemplate;
|
|
}
|
|
|
|
/*
|
|
* End of templates. Do not add stuff after this; put new code
|
|
* up above the start of the template definitions.
|
|
*/
|