mirror of
https://github.com/cryb-to/cryb-to.git
synced 2024-12-30 00:11:08 +00:00
965 lines
32 KiB
C
965 lines
32 KiB
C
/* R_ENHANC.C - cryptographic enhancements for RSAREF
|
|
*/
|
|
|
|
/* Copyright (C) RSA Laboratories, a division of RSA Data Security,
|
|
Inc., created 1991. All rights reserved.
|
|
*/
|
|
|
|
#include "global.h"
|
|
#include "rsaref.h"
|
|
#include "r_random.h"
|
|
#include "rsa.h"
|
|
|
|
/* DigestInfo encoding is DIGEST_INFO_A, then 2 or 5 (for MD2/MD5),
|
|
then DIGEST_INFO_B, then 16-byte message digest.
|
|
*/
|
|
|
|
static unsigned char DIGEST_INFO_A[] = {
|
|
0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7,
|
|
0x0d, 0x02
|
|
};
|
|
#define DIGEST_INFO_A_LEN sizeof (DIGEST_INFO_A)
|
|
|
|
static unsigned char DIGEST_INFO_B[] = { 0x05, 0x00, 0x04, 0x10 };
|
|
#define DIGEST_INFO_B_LEN sizeof (DIGEST_INFO_B)
|
|
|
|
#define DIGEST_INFO_LEN (DIGEST_INFO_A_LEN + 1 + DIGEST_INFO_B_LEN + 16)
|
|
|
|
static unsigned char *PADDING[] = {
|
|
(unsigned char *)"", (unsigned char *)"\001", (unsigned char *)"\002\002",
|
|
(unsigned char *)"\003\003\003", (unsigned char *)"\004\004\004\004",
|
|
(unsigned char *)"\005\005\005\005\005",
|
|
(unsigned char *)"\006\006\006\006\006\006",
|
|
(unsigned char *)"\007\007\007\007\007\007\007",
|
|
(unsigned char *)"\010\010\010\010\010\010\010\010"
|
|
};
|
|
|
|
#define MAX_ENCRYPTED_KEY_LEN MAX_RSA_MODULUS_LEN
|
|
|
|
static void R_EncodeDigestInfo PROTO_LIST
|
|
((unsigned char *, int, unsigned char *));
|
|
static void EncryptPEMUpdateFinal PROTO_LIST
|
|
((R_ENVELOPE_CTX *, unsigned char *, unsigned int *, unsigned char *,
|
|
unsigned int));
|
|
static int DecryptPEMUpdateFinal PROTO_LIST
|
|
((R_ENVELOPE_CTX *, unsigned char *, unsigned int *, unsigned char *,
|
|
unsigned int));
|
|
static int CipherInit PROTO_LIST
|
|
((R_ENVELOPE_CTX *, int, unsigned char *, unsigned char *, int));
|
|
static void CipherUpdate PROTO_LIST
|
|
((R_ENVELOPE_CTX *, unsigned char *, unsigned char *, unsigned int));
|
|
static void CipherRestart PROTO_LIST ((R_ENVELOPE_CTX *));
|
|
|
|
int R_DigestInit (context, digestAlgorithm)
|
|
R_DIGEST_CTX *context; /* new context */
|
|
int digestAlgorithm; /* message-digest algorithm */
|
|
{
|
|
context->digestAlgorithm = digestAlgorithm;
|
|
|
|
switch (digestAlgorithm) {
|
|
case DA_MD2:
|
|
MD2Init (&context->context.md2);
|
|
break;
|
|
|
|
case DA_MD5:
|
|
MD5Init (&context->context.md5);
|
|
break;
|
|
|
|
default:
|
|
return (RE_DIGEST_ALGORITHM);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
int R_DigestUpdate (context, partIn, partInLen)
|
|
R_DIGEST_CTX *context; /* context */
|
|
unsigned char *partIn; /* next data part */
|
|
unsigned int partInLen; /* length of next data part */
|
|
{
|
|
if (context->digestAlgorithm == DA_MD2)
|
|
MD2Update (&context->context.md2, partIn, partInLen);
|
|
else
|
|
MD5Update (&context->context.md5, partIn, partInLen);
|
|
return (0);
|
|
}
|
|
|
|
int R_DigestFinal (context, digest, digestLen)
|
|
R_DIGEST_CTX *context; /* context */
|
|
unsigned char *digest; /* message digest */
|
|
unsigned int *digestLen; /* length of message digest */
|
|
{
|
|
*digestLen = 16;
|
|
if (context->digestAlgorithm == DA_MD2)
|
|
MD2Final (digest, &context->context.md2);
|
|
else
|
|
MD5Final (digest, &context->context.md5);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int R_SignInit (context, digestAlgorithm)
|
|
R_SIGNATURE_CTX *context; /* new context */
|
|
int digestAlgorithm; /* message-digest algorithm */
|
|
{
|
|
return (R_DigestInit (&context->digestContext, digestAlgorithm));
|
|
}
|
|
|
|
int R_SignUpdate (context, partIn, partInLen)
|
|
R_SIGNATURE_CTX *context; /* context */
|
|
unsigned char *partIn; /* next data part */
|
|
unsigned int partInLen; /* length of next data part */
|
|
{
|
|
return (R_DigestUpdate (&context->digestContext, partIn, partInLen));
|
|
}
|
|
|
|
int R_SignFinal (context, signature, signatureLen, privateKey)
|
|
R_SIGNATURE_CTX *context; /* context */
|
|
unsigned char *signature; /* signature */
|
|
unsigned int *signatureLen; /* length of signature */
|
|
R_RSA_PRIVATE_KEY *privateKey; /* signer's RSA private key */
|
|
{
|
|
int status;
|
|
unsigned char digest[MAX_DIGEST_LEN], digestInfo[DIGEST_INFO_LEN];
|
|
unsigned int digestLen;
|
|
|
|
do {
|
|
if ((status = R_DigestFinal (&context->digestContext, digest, &digestLen))
|
|
!= 0)
|
|
break;
|
|
|
|
R_EncodeDigestInfo
|
|
(digestInfo, context->digestContext.digestAlgorithm, digest);
|
|
|
|
if (RSAPrivateEncrypt
|
|
(signature, signatureLen, digestInfo, DIGEST_INFO_LEN, privateKey)
|
|
!= 0) {
|
|
status = RE_PRIVATE_KEY;
|
|
break;
|
|
}
|
|
|
|
/* Reset for another verification. Assume Init won't fail */
|
|
R_DigestInit
|
|
(&context->digestContext, context->digestContext.digestAlgorithm);
|
|
} while (0);
|
|
|
|
/* Zeroize potentially sensitive information.
|
|
*/
|
|
R_memset ((POINTER)digest, 0, sizeof (digest));
|
|
R_memset ((POINTER)digestInfo, 0, sizeof (digestInfo));
|
|
|
|
return (status);
|
|
}
|
|
|
|
int R_VerifyInit (context, digestAlgorithm)
|
|
R_SIGNATURE_CTX *context; /* new context */
|
|
int digestAlgorithm; /* message-digest algorithm */
|
|
{
|
|
return (R_DigestInit (&context->digestContext, digestAlgorithm));
|
|
}
|
|
|
|
int R_VerifyUpdate (context, partIn, partInLen)
|
|
R_SIGNATURE_CTX *context; /* context */
|
|
unsigned char *partIn; /* next data part */
|
|
unsigned int partInLen; /* length of next data part */
|
|
{
|
|
return (R_DigestUpdate (&context->digestContext, partIn, partInLen));
|
|
}
|
|
|
|
int R_VerifyFinal (context, signature, signatureLen, publicKey)
|
|
R_SIGNATURE_CTX *context; /* context */
|
|
unsigned char *signature; /* signature */
|
|
unsigned int signatureLen; /* length of signature */
|
|
R_RSA_PUBLIC_KEY *publicKey; /* signer's RSA public key */
|
|
{
|
|
int status;
|
|
unsigned char digest[MAX_DIGEST_LEN], digestInfo[DIGEST_INFO_LEN],
|
|
originalDigestInfo[MAX_SIGNATURE_LEN];
|
|
unsigned int originalDigestInfoLen, digestLen;
|
|
|
|
if (signatureLen > MAX_SIGNATURE_LEN)
|
|
return (RE_LEN);
|
|
|
|
status = 0;
|
|
do {
|
|
if ((status = R_DigestFinal (&context->digestContext, digest, &digestLen))
|
|
!= 0)
|
|
break;
|
|
|
|
R_EncodeDigestInfo
|
|
(digestInfo, context->digestContext.digestAlgorithm, digest);
|
|
|
|
if (RSAPublicDecrypt
|
|
(originalDigestInfo, &originalDigestInfoLen, signature, signatureLen,
|
|
publicKey) != 0) {
|
|
status = RE_PUBLIC_KEY;
|
|
break;
|
|
}
|
|
|
|
if ((originalDigestInfoLen != DIGEST_INFO_LEN) ||
|
|
(R_memcmp
|
|
((POINTER)originalDigestInfo, (POINTER)digestInfo,
|
|
DIGEST_INFO_LEN))) {
|
|
status = RE_SIGNATURE;
|
|
break;
|
|
}
|
|
|
|
/* Reset for another verification. Assume Init won't fail */
|
|
R_DigestInit
|
|
(&context->digestContext, context->digestContext.digestAlgorithm);
|
|
} while (0);
|
|
|
|
/* Zeroize potentially sensitive information.
|
|
*/
|
|
R_memset ((POINTER)digest, 0, sizeof (digest));
|
|
R_memset ((POINTER)digestInfo, 0, sizeof (digestInfo));
|
|
R_memset ((POINTER)originalDigestInfo, 0, sizeof (originalDigestInfo));
|
|
|
|
return (status);
|
|
}
|
|
|
|
/* Caller must ASCII recode the encrypted keys if desired.
|
|
*/
|
|
int R_SealInit
|
|
(context, encryptedKeys, encryptedKeyLens, iv, publicKeyCount, publicKeys,
|
|
encryptionAlgorithm, randomStruct)
|
|
R_ENVELOPE_CTX *context; /* new context */
|
|
unsigned char **encryptedKeys; /* encrypted keys */
|
|
unsigned int *encryptedKeyLens; /* lengths of encrypted keys */
|
|
unsigned char iv[8]; /* initialization vector */
|
|
unsigned int publicKeyCount; /* number of public keys */
|
|
R_RSA_PUBLIC_KEY **publicKeys; /* public keys */
|
|
int encryptionAlgorithm; /* data encryption algorithm */
|
|
R_RANDOM_STRUCT *randomStruct; /* random structure */
|
|
{
|
|
int status;
|
|
unsigned char key[24];
|
|
unsigned int keyLen, i;
|
|
|
|
do {
|
|
context->encryptionAlgorithm = encryptionAlgorithm;
|
|
|
|
keyLen = (encryptionAlgorithm == EA_DES_CBC) ? 8 : 24;
|
|
if ((status = R_GenerateBytes (key, keyLen, randomStruct)) != 0)
|
|
break;
|
|
if ((status = R_GenerateBytes (iv, 8, randomStruct)) != 0)
|
|
break;
|
|
|
|
if (encryptionAlgorithm == EA_DES_EDE2_CBC)
|
|
/* Make both E keys the same */
|
|
R_memcpy ((POINTER)(key + 16), (POINTER)key, 8);
|
|
|
|
if ((status = CipherInit (context, encryptionAlgorithm, key, iv, 1)) != 0)
|
|
break;
|
|
|
|
for (i = 0; i < publicKeyCount; ++i) {
|
|
if (RSAPublicEncrypt
|
|
(encryptedKeys[i], &encryptedKeyLens[i], key, keyLen,
|
|
publicKeys[i], randomStruct)) {
|
|
status = RE_PUBLIC_KEY;
|
|
break;
|
|
}
|
|
}
|
|
if (status != 0)
|
|
break;
|
|
|
|
context->bufferLen = 0;
|
|
} while (0);
|
|
|
|
/* Zeroize sensitive information.
|
|
*/
|
|
R_memset ((POINTER)key, 0, sizeof (key));
|
|
|
|
return (status);
|
|
}
|
|
|
|
/* Assume partOut buffer is at least partInLen + 7, since this may flush
|
|
buffered input.
|
|
*/
|
|
int R_SealUpdate (context, partOut, partOutLen, partIn, partInLen)
|
|
R_ENVELOPE_CTX *context; /* context */
|
|
unsigned char *partOut; /* next encrypted data part */
|
|
unsigned int *partOutLen; /* length of next encrypted data part */
|
|
unsigned char *partIn; /* next data part */
|
|
unsigned int partInLen; /* length of next data part */
|
|
{
|
|
unsigned int tempLen;
|
|
|
|
tempLen = 8 - context->bufferLen;
|
|
if (partInLen < tempLen) {
|
|
/* Just accumulate into buffer.
|
|
*/
|
|
R_memcpy
|
|
((POINTER)(context->buffer + context->bufferLen), (POINTER)partIn,
|
|
partInLen);
|
|
context->bufferLen += partInLen;
|
|
*partOutLen = 0;
|
|
return (0);
|
|
}
|
|
|
|
/* Fill the buffer and encrypt.
|
|
*/
|
|
R_memcpy
|
|
((POINTER)(context->buffer + context->bufferLen), (POINTER)partIn,
|
|
tempLen);
|
|
CipherUpdate (context, partOut, context->buffer, 8);
|
|
partIn += tempLen;
|
|
partInLen -= tempLen;
|
|
partOut += 8;
|
|
*partOutLen = 8;
|
|
|
|
/* Encrypt as many 8-byte blocks as possible.
|
|
*/
|
|
tempLen = 8 * (partInLen / 8);
|
|
CipherUpdate (context, partOut, partIn, tempLen);
|
|
partIn += tempLen;
|
|
partInLen -= tempLen;
|
|
*partOutLen += tempLen;
|
|
|
|
/* Length is now less than 8, so copy remainder to buffer.
|
|
*/
|
|
R_memcpy
|
|
((POINTER)context->buffer, (POINTER)partIn,
|
|
context->bufferLen = partInLen);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Assume partOut buffer is at least 8 bytes.
|
|
*/
|
|
int R_SealFinal (context, partOut, partOutLen)
|
|
R_ENVELOPE_CTX *context; /* context */
|
|
unsigned char *partOut; /* last encrypted data part */
|
|
unsigned int *partOutLen; /* length of last encrypted data part */
|
|
{
|
|
unsigned int padLen;
|
|
|
|
/* Pad and encrypt final block.
|
|
*/
|
|
padLen = 8 - context->bufferLen;
|
|
R_memset
|
|
((POINTER)(context->buffer + context->bufferLen), (int)padLen, padLen);
|
|
CipherUpdate (context, partOut, context->buffer, 8);
|
|
*partOutLen = 8;
|
|
|
|
/* Restart the context.
|
|
*/
|
|
CipherRestart (context);
|
|
context->bufferLen = 0;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Assume caller has already ASCII decoded the encryptedKey if necessary.
|
|
*/
|
|
int R_OpenInit
|
|
(context, encryptionAlgorithm, encryptedKey, encryptedKeyLen, iv, privateKey)
|
|
R_ENVELOPE_CTX *context; /* new context */
|
|
int encryptionAlgorithm; /* data encryption algorithm */
|
|
unsigned char *encryptedKey; /* encrypted data encryption key */
|
|
unsigned int encryptedKeyLen; /* length of encrypted key */
|
|
unsigned char iv[8]; /* initialization vector */
|
|
R_RSA_PRIVATE_KEY *privateKey; /* recipient's RSA private key */
|
|
{
|
|
int status;
|
|
unsigned char key[MAX_ENCRYPTED_KEY_LEN];
|
|
unsigned int keyLen;
|
|
|
|
if (encryptedKeyLen > MAX_ENCRYPTED_KEY_LEN)
|
|
return (RE_LEN);
|
|
|
|
do {
|
|
context->encryptionAlgorithm = encryptionAlgorithm;
|
|
|
|
if (RSAPrivateDecrypt
|
|
(key, &keyLen, encryptedKey, encryptedKeyLen, privateKey)) {
|
|
status = RE_PRIVATE_KEY;
|
|
break;
|
|
}
|
|
|
|
if (encryptionAlgorithm == EA_DES_CBC) {
|
|
if (keyLen != 8) {
|
|
status = RE_PRIVATE_KEY;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (keyLen != 24) {
|
|
status = RE_PRIVATE_KEY;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((status = CipherInit (context, encryptionAlgorithm, key, iv, 0)) != 0)
|
|
break;
|
|
|
|
context->bufferLen = 0;
|
|
} while (0);
|
|
|
|
/* Zeroize sensitive information.
|
|
*/
|
|
R_memset ((POINTER)key, 0, sizeof (key));
|
|
|
|
return (status);
|
|
}
|
|
|
|
/* Assume partOut buffer is at least partInLen + 7, since this may flush
|
|
buffered input. Always leaves at least one byte in buffer.
|
|
*/
|
|
int R_OpenUpdate (context, partOut, partOutLen, partIn, partInLen)
|
|
R_ENVELOPE_CTX *context; /* context */
|
|
unsigned char *partOut; /* next recovered data part */
|
|
unsigned int *partOutLen; /* length of next recovered data part */
|
|
unsigned char *partIn; /* next encrypted data part */
|
|
unsigned int partInLen; /* length of next encrypted data part */
|
|
{
|
|
unsigned int tempLen;
|
|
|
|
tempLen = 8 - context->bufferLen;
|
|
if (partInLen <= tempLen) {
|
|
/* Just accumulate into buffer.
|
|
*/
|
|
R_memcpy
|
|
((POINTER)(context->buffer + context->bufferLen), (POINTER)partIn,
|
|
partInLen);
|
|
context->bufferLen += partInLen;
|
|
*partOutLen = 0;
|
|
return (0);
|
|
}
|
|
|
|
/* Fill the buffer and decrypt. We know that there will be more left
|
|
in partIn after decrypting the buffer.
|
|
*/
|
|
R_memcpy
|
|
((POINTER)(context->buffer + context->bufferLen), (POINTER)partIn,
|
|
tempLen);
|
|
CipherUpdate (context, partOut, context->buffer, 8);
|
|
partIn += tempLen;
|
|
partInLen -= tempLen;
|
|
partOut += 8;
|
|
*partOutLen = 8;
|
|
|
|
/* Decrypt as many 8 byte blocks as possible, leaving at least one byte
|
|
in partIn.
|
|
*/
|
|
tempLen = 8 * ((partInLen - 1) / 8);
|
|
CipherUpdate (context, partOut, partIn, tempLen);
|
|
partIn += tempLen;
|
|
partInLen -= tempLen;
|
|
*partOutLen += tempLen;
|
|
|
|
/* Length is between 1 and 8, so copy into buffer.
|
|
*/
|
|
R_memcpy
|
|
((POINTER)context->buffer, (POINTER)partIn,
|
|
context->bufferLen = partInLen);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/* Assume partOut buffer is at least 7 bytes.
|
|
*/
|
|
int R_OpenFinal (context, partOut, partOutLen)
|
|
R_ENVELOPE_CTX *context; /* context */
|
|
unsigned char *partOut; /* last recovered data part */
|
|
unsigned int *partOutLen; /* length of last recovered data part */
|
|
{
|
|
int status;
|
|
unsigned char lastPart[8];
|
|
unsigned int padLen;
|
|
|
|
status = 0;
|
|
do {
|
|
if (context->bufferLen == 0)
|
|
/* There was no input data to decrypt */
|
|
*partOutLen = 0;
|
|
else {
|
|
if (context->bufferLen != 8) {
|
|
status = RE_KEY;
|
|
break;
|
|
}
|
|
|
|
/* Decrypt and strip padding from final block which is in buffer.
|
|
*/
|
|
CipherUpdate (context, lastPart, context->buffer, 8);
|
|
|
|
padLen = lastPart[7];
|
|
if (padLen == 0 || padLen > 8) {
|
|
status = RE_KEY;
|
|
break;
|
|
}
|
|
if (R_memcmp
|
|
((POINTER)&lastPart[8 - padLen], PADDING[padLen], padLen) != 0) {
|
|
status = RE_KEY;
|
|
break;
|
|
}
|
|
|
|
R_memcpy ((POINTER)partOut, (POINTER)lastPart, *partOutLen = 8 - padLen);
|
|
}
|
|
|
|
/* Restart the context.
|
|
*/
|
|
CipherRestart (context);
|
|
context->bufferLen = 0;
|
|
} while (0);
|
|
|
|
/* Zeroize sensitive information.
|
|
*/
|
|
R_memset ((POINTER)lastPart, 0, sizeof (lastPart));
|
|
|
|
return (status);
|
|
}
|
|
|
|
int R_SignPEMBlock
|
|
(encodedContent, encodedContentLen, encodedSignature, encodedSignatureLen,
|
|
content, contentLen, recode, digestAlgorithm, privateKey)
|
|
unsigned char *encodedContent; /* encoded content */
|
|
unsigned int *encodedContentLen; /* length of encoded content */
|
|
unsigned char *encodedSignature; /* encoded signature */
|
|
unsigned int *encodedSignatureLen; /* length of encoded signature */
|
|
unsigned char *content; /* content */
|
|
unsigned int contentLen; /* length of content */
|
|
int recode; /* recoding flag */
|
|
int digestAlgorithm; /* message-digest algorithm */
|
|
R_RSA_PRIVATE_KEY *privateKey; /* signer's RSA private key */
|
|
{
|
|
int status;
|
|
unsigned char signature[MAX_SIGNATURE_LEN];
|
|
unsigned int signatureLen;
|
|
|
|
if ((status = R_SignBlock
|
|
(signature, &signatureLen, content, contentLen, digestAlgorithm,
|
|
privateKey)) != 0)
|
|
return (status);
|
|
|
|
R_EncodePEMBlock
|
|
(encodedSignature, encodedSignatureLen, signature, signatureLen);
|
|
|
|
if (recode)
|
|
R_EncodePEMBlock
|
|
(encodedContent, encodedContentLen, content, contentLen);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int R_SignBlock
|
|
(signature, signatureLen, block, blockLen, digestAlgorithm, privateKey)
|
|
unsigned char *signature; /* signature */
|
|
unsigned int *signatureLen; /* length of signature */
|
|
unsigned char *block; /* block */
|
|
unsigned int blockLen; /* length of block */
|
|
int digestAlgorithm; /* message-digest algorithm */
|
|
R_RSA_PRIVATE_KEY *privateKey; /* signer's RSA private key */
|
|
{
|
|
R_SIGNATURE_CTX context;
|
|
int status;
|
|
|
|
do {
|
|
if ((status = R_SignInit (&context, digestAlgorithm)) != 0)
|
|
break;
|
|
if ((status = R_SignUpdate (&context, block, blockLen)) != 0)
|
|
break;
|
|
if ((status = R_SignFinal (&context, signature, signatureLen, privateKey))
|
|
!= 0)
|
|
break;
|
|
} while (0);
|
|
|
|
/* Zeroize sensitive information. */
|
|
R_memset ((POINTER)&context, 0, sizeof (context));
|
|
|
|
return (status);
|
|
}
|
|
|
|
int R_VerifyPEMSignature
|
|
(content, contentLen, encodedContent, encodedContentLen, encodedSignature,
|
|
encodedSignatureLen, recode, digestAlgorithm, publicKey)
|
|
unsigned char *content; /* content */
|
|
unsigned int *contentLen; /* length of content */
|
|
unsigned char *encodedContent; /* (possibly) encoded content */
|
|
unsigned int encodedContentLen; /* length of encoded content */
|
|
unsigned char *encodedSignature; /* encoded signature */
|
|
unsigned int encodedSignatureLen; /* length of encoded signature */
|
|
int recode; /* recoding flag */
|
|
int digestAlgorithm; /* message-digest algorithm */
|
|
R_RSA_PUBLIC_KEY *publicKey; /* signer's RSA public key */
|
|
{
|
|
unsigned char signature[MAX_SIGNATURE_LEN];
|
|
unsigned int signatureLen;
|
|
|
|
if (encodedSignatureLen > MAX_PEM_SIGNATURE_LEN)
|
|
return (RE_SIGNATURE_ENCODING);
|
|
|
|
if (recode) {
|
|
if (R_DecodePEMBlock
|
|
(content, contentLen, encodedContent, encodedContentLen))
|
|
return (RE_CONTENT_ENCODING);
|
|
}
|
|
else {
|
|
content = encodedContent;
|
|
*contentLen = encodedContentLen;
|
|
}
|
|
|
|
if (R_DecodePEMBlock
|
|
(signature, &signatureLen, encodedSignature, encodedSignatureLen))
|
|
return (RE_SIGNATURE_ENCODING);
|
|
|
|
return (R_VerifyBlockSignature
|
|
(content, *contentLen, signature, signatureLen, digestAlgorithm,
|
|
publicKey));
|
|
}
|
|
|
|
int R_VerifyBlockSignature
|
|
(block, blockLen, signature, signatureLen, digestAlgorithm, publicKey)
|
|
unsigned char *block; /* block */
|
|
unsigned int blockLen; /* length of block */
|
|
unsigned char *signature; /* signature */
|
|
unsigned int signatureLen; /* length of signature */
|
|
int digestAlgorithm; /* message-digest algorithm */
|
|
R_RSA_PUBLIC_KEY *publicKey; /* signer's RSA public key */
|
|
{
|
|
R_SIGNATURE_CTX context;
|
|
int status;
|
|
|
|
do {
|
|
if ((status = R_VerifyInit (&context, digestAlgorithm)) != 0)
|
|
break;
|
|
if ((status = R_VerifyUpdate (&context, block, blockLen)) != 0)
|
|
break;
|
|
if ((status = R_VerifyFinal (&context, signature, signatureLen, publicKey))
|
|
!= 0)
|
|
break;
|
|
} while (0);
|
|
|
|
/* Zeroize sensitive information. */
|
|
R_memset ((POINTER)&context, 0, sizeof (context));
|
|
|
|
return (status);
|
|
}
|
|
|
|
int R_SealPEMBlock
|
|
(encryptedContent, encryptedContentLen, encryptedKey, encryptedKeyLen,
|
|
encryptedSignature, encryptedSignatureLen, iv, content, contentLen,
|
|
digestAlgorithm, publicKey, privateKey, randomStruct)
|
|
unsigned char *encryptedContent; /* encoded, encrypted content */
|
|
unsigned int *encryptedContentLen; /* length */
|
|
unsigned char *encryptedKey; /* encoded, encrypted key */
|
|
unsigned int *encryptedKeyLen; /* length */
|
|
unsigned char *encryptedSignature; /* encoded, encrypted signature */
|
|
unsigned int *encryptedSignatureLen; /* length */
|
|
unsigned char iv[8]; /* DES initialization vector */
|
|
unsigned char *content; /* content */
|
|
unsigned int contentLen; /* length of content */
|
|
int digestAlgorithm; /* message-digest algorithms */
|
|
R_RSA_PUBLIC_KEY *publicKey; /* recipient's RSA public key */
|
|
R_RSA_PRIVATE_KEY *privateKey; /* signer's RSA private key */
|
|
R_RANDOM_STRUCT *randomStruct; /* random structure */
|
|
{
|
|
R_ENVELOPE_CTX context;
|
|
R_RSA_PUBLIC_KEY *publicKeys[1];
|
|
int status;
|
|
unsigned char encryptedKeyBlock[MAX_ENCRYPTED_KEY_LEN],
|
|
signature[MAX_SIGNATURE_LEN], *encryptedKeys[1];
|
|
unsigned int signatureLen, encryptedKeyBlockLen;
|
|
|
|
do {
|
|
if ((status = R_SignBlock
|
|
(signature, &signatureLen, content, contentLen, digestAlgorithm,
|
|
privateKey)) != 0)
|
|
break;
|
|
|
|
publicKeys[0] = publicKey;
|
|
encryptedKeys[0] = encryptedKeyBlock;
|
|
if ((status = R_SealInit
|
|
(&context, encryptedKeys, &encryptedKeyBlockLen, iv, 1, publicKeys,
|
|
EA_DES_CBC, randomStruct)) != 0)
|
|
break;
|
|
|
|
R_EncodePEMBlock
|
|
(encryptedKey, encryptedKeyLen, encryptedKeyBlock,
|
|
encryptedKeyBlockLen);
|
|
|
|
EncryptPEMUpdateFinal
|
|
(&context, encryptedContent, encryptedContentLen, content,
|
|
contentLen);
|
|
|
|
EncryptPEMUpdateFinal
|
|
(&context, encryptedSignature, encryptedSignatureLen, signature,
|
|
signatureLen);
|
|
} while (0);
|
|
|
|
/* Zeroize sensitive information.
|
|
*/
|
|
R_memset ((POINTER)&context, 0, sizeof (context));
|
|
R_memset ((POINTER)signature, 0, sizeof (signature));
|
|
|
|
return (status);
|
|
}
|
|
|
|
int R_OpenPEMBlock
|
|
(content, contentLen, encryptedContent, encryptedContentLen, encryptedKey,
|
|
encryptedKeyLen, encryptedSignature, encryptedSignatureLen,
|
|
iv, digestAlgorithm, privateKey, publicKey)
|
|
unsigned char *content; /* content */
|
|
unsigned int *contentLen; /* length of content */
|
|
unsigned char *encryptedContent; /* encoded, encrypted content */
|
|
unsigned int encryptedContentLen; /* length */
|
|
unsigned char *encryptedKey; /* encoded, encrypted key */
|
|
unsigned int encryptedKeyLen; /* length */
|
|
unsigned char *encryptedSignature; /* encoded, encrypted signature */
|
|
unsigned int encryptedSignatureLen; /* length */
|
|
unsigned char iv[8]; /* DES initialization vector */
|
|
int digestAlgorithm; /* message-digest algorithms */
|
|
R_RSA_PRIVATE_KEY *privateKey; /* recipient's RSA private key */
|
|
R_RSA_PUBLIC_KEY *publicKey; /* signer's RSA public key */
|
|
{
|
|
R_ENVELOPE_CTX context;
|
|
int status;
|
|
unsigned char encryptedKeyBlock[MAX_ENCRYPTED_KEY_LEN],
|
|
signature[MAX_SIGNATURE_LEN];
|
|
unsigned int encryptedKeyBlockLen, signatureLen;
|
|
|
|
if (encryptedKeyLen > MAX_PEM_ENCRYPTED_KEY_LEN)
|
|
return (RE_KEY_ENCODING);
|
|
|
|
if (encryptedSignatureLen > MAX_PEM_ENCRYPTED_SIGNATURE_LEN)
|
|
return (RE_SIGNATURE_ENCODING);
|
|
|
|
do {
|
|
if (R_DecodePEMBlock
|
|
(encryptedKeyBlock, &encryptedKeyBlockLen, encryptedKey,
|
|
encryptedKeyLen) != 0) {
|
|
status = RE_KEY_ENCODING;
|
|
break;
|
|
}
|
|
|
|
if ((status = R_OpenInit
|
|
(&context, EA_DES_CBC, encryptedKeyBlock, encryptedKeyBlockLen,
|
|
iv, privateKey)) != 0)
|
|
break;
|
|
|
|
if ((status = DecryptPEMUpdateFinal
|
|
(&context, content, contentLen, encryptedContent,
|
|
encryptedContentLen)) != 0) {
|
|
if ((status == RE_LEN || status == RE_ENCODING))
|
|
status = RE_CONTENT_ENCODING;
|
|
else
|
|
status = RE_KEY;
|
|
break;
|
|
}
|
|
|
|
if ((status = DecryptPEMUpdateFinal
|
|
(&context, signature, &signatureLen, encryptedSignature,
|
|
encryptedSignatureLen))) {
|
|
if ((status == RE_LEN || status == RE_ENCODING))
|
|
status = RE_SIGNATURE_ENCODING;
|
|
else
|
|
status = RE_KEY;
|
|
break;
|
|
}
|
|
|
|
if ((status = R_VerifyBlockSignature
|
|
(content, *contentLen, signature, signatureLen, digestAlgorithm,
|
|
publicKey)) != 0)
|
|
break;
|
|
} while (0);
|
|
|
|
/* Zeroize sensitive information.
|
|
*/
|
|
R_memset ((POINTER)&context, 0, sizeof (context));
|
|
R_memset ((POINTER)signature, 0, sizeof (signature));
|
|
|
|
return (status);
|
|
}
|
|
|
|
int R_DigestBlock (digest, digestLen, block, blockLen, digestAlgorithm)
|
|
unsigned char *digest; /* message digest */
|
|
unsigned int *digestLen; /* length of message digest */
|
|
unsigned char *block; /* block */
|
|
unsigned int blockLen; /* length of block */
|
|
int digestAlgorithm; /* message-digest algorithm */
|
|
{
|
|
R_DIGEST_CTX context;
|
|
int status;
|
|
|
|
do {
|
|
if ((status = R_DigestInit (&context, digestAlgorithm)) != 0)
|
|
break;
|
|
if ((status = R_DigestUpdate (&context, block, blockLen)) != 0)
|
|
break;
|
|
if ((status = R_DigestFinal (&context, digest, digestLen)) != 0)
|
|
break;
|
|
} while (0);
|
|
|
|
/* Zeroize sensitive information. */
|
|
R_memset ((POINTER)&context, 0, sizeof (context));
|
|
|
|
return (status);
|
|
}
|
|
|
|
/* Assumes digestAlgorithm is DA_MD2 or DA_MD5 and digest length is 16.
|
|
*/
|
|
static void R_EncodeDigestInfo (digestInfo, digestAlgorithm, digest)
|
|
unsigned char *digestInfo; /* DigestInfo encoding */
|
|
int digestAlgorithm; /* message-digest algorithm */
|
|
unsigned char *digest; /* message digest */
|
|
{
|
|
R_memcpy
|
|
((POINTER)digestInfo, (POINTER)DIGEST_INFO_A, DIGEST_INFO_A_LEN);
|
|
|
|
digestInfo[DIGEST_INFO_A_LEN] =
|
|
(digestAlgorithm == DA_MD2) ? (unsigned char)2 : (unsigned char)5;
|
|
|
|
R_memcpy
|
|
((POINTER)&digestInfo[DIGEST_INFO_A_LEN + 1], (POINTER)DIGEST_INFO_B,
|
|
DIGEST_INFO_B_LEN);
|
|
|
|
R_memcpy
|
|
((POINTER)&digestInfo[DIGEST_INFO_A_LEN + 1 + DIGEST_INFO_B_LEN],
|
|
(POINTER)digest, 16);
|
|
}
|
|
|
|
/* Call SealUpdate and SealFinal on the input and ASCII recode.
|
|
*/
|
|
static void EncryptPEMUpdateFinal
|
|
(context, output, outputLen, input, inputLen)
|
|
R_ENVELOPE_CTX *context;
|
|
unsigned char *output; /* encrypted, encoded block */
|
|
unsigned int *outputLen; /* length of output */
|
|
unsigned char *input; /* block to encrypt */
|
|
unsigned int inputLen; /* length */
|
|
{
|
|
unsigned char encryptedPart[24];
|
|
unsigned int i, lastPartLen, tempLen, len;
|
|
|
|
/* Choose a buffer size of 24 bytes to hold the temporary encrypted output
|
|
which will be encoded.
|
|
Encrypt and encode as many 24-byte blocks as possible.
|
|
*/
|
|
for (i = 0; i < inputLen / 24; ++i) {
|
|
/* Assume part out length will equal part in length since it is
|
|
a multiple of 8. Also assume no error output. */
|
|
R_SealUpdate (context, encryptedPart, &tempLen, &input[24*i], 24);
|
|
|
|
/* len is always 32 */
|
|
R_EncodePEMBlock (&output[32*i], &tempLen, encryptedPart, 24);
|
|
}
|
|
|
|
/* Encrypt the last part into encryptedPart.
|
|
*/
|
|
R_SealUpdate
|
|
(context, encryptedPart, &lastPartLen, &input[24*i], inputLen - 24*i);
|
|
R_SealFinal (context, encryptedPart + lastPartLen, &len);
|
|
lastPartLen += len;
|
|
|
|
R_EncodePEMBlock (&output[32*i], &len, encryptedPart, lastPartLen);
|
|
*outputLen = 32*i + len;
|
|
|
|
/* Zeroize sensitive information.
|
|
*/
|
|
R_memset ((POINTER)encryptedPart, 0, sizeof (encryptedPart));
|
|
}
|
|
|
|
static int DecryptPEMUpdateFinal (context, output, outputLen, input, inputLen)
|
|
R_ENVELOPE_CTX *context;
|
|
unsigned char *output; /* decoded, decrypted block */
|
|
unsigned int *outputLen; /* length of output */
|
|
unsigned char *input; /* encrypted, encoded block */
|
|
unsigned int inputLen; /* length */
|
|
{
|
|
int status = 0;
|
|
unsigned char encryptedPart[24];
|
|
unsigned int i, len;
|
|
|
|
do {
|
|
/* Choose a buffer size of 24 bytes to hold the temporary decoded output
|
|
which will be decrypted.
|
|
Decode and decrypt as many 32-byte input blocks as possible.
|
|
*/
|
|
*outputLen = 0;
|
|
for (i = 0; i < inputLen/32; i++) {
|
|
/* len is always 24 */
|
|
if ((status = R_DecodePEMBlock
|
|
(encryptedPart, &len, &input[32*i], 32)) != 0)
|
|
break;
|
|
|
|
/* Excpect no error return */
|
|
R_OpenUpdate (context, output, &len, encryptedPart, 24);
|
|
output += len;
|
|
*outputLen += len;
|
|
}
|
|
if (status)
|
|
break;
|
|
|
|
/* Decode the last part */
|
|
if ((status = R_DecodePEMBlock
|
|
(encryptedPart, &len, &input[32*i], inputLen - 32*i)) != 0)
|
|
break;
|
|
|
|
/* Decrypt the last part.
|
|
*/
|
|
R_OpenUpdate (context, output, &len, encryptedPart, len);
|
|
output += len;
|
|
*outputLen += len;
|
|
if ((status = R_OpenFinal (context, output, &len)) != 0)
|
|
break;
|
|
*outputLen += len;
|
|
} while (0);
|
|
|
|
/* Zeroize sensitive information.
|
|
*/
|
|
R_memset ((POINTER)&context, 0, sizeof (context));
|
|
R_memset ((POINTER)encryptedPart, 0, sizeof (encryptedPart));
|
|
|
|
return (status);
|
|
}
|
|
|
|
static int CipherInit (context, encryptionAlgorithm, key, iv, encrypt)
|
|
R_ENVELOPE_CTX *context;
|
|
int encryptionAlgorithm;
|
|
unsigned char *key; /* DES key */
|
|
unsigned char *iv; /* DES initialization vector */
|
|
int encrypt; /* encrypt flag (1 = encrypt, 0 = decrypt) */
|
|
{
|
|
switch (encryptionAlgorithm) {
|
|
case EA_DES_CBC:
|
|
DES_CBCInit (&context->cipherContext.des, key, iv, encrypt);
|
|
return (0);
|
|
case EA_DESX_CBC:
|
|
DESX_CBCInit (&context->cipherContext.desx, key, iv, encrypt);
|
|
return (0);
|
|
case EA_DES_EDE2_CBC:
|
|
case EA_DES_EDE3_CBC:
|
|
DES3_CBCInit (&context->cipherContext.des3, key, iv, encrypt);
|
|
return (0);
|
|
|
|
default:
|
|
return (RE_ENCRYPTION_ALGORITHM);
|
|
}
|
|
}
|
|
|
|
/* Assume len is a multiple of 8.
|
|
*/
|
|
static void CipherUpdate (context, output, input, len)
|
|
R_ENVELOPE_CTX *context;
|
|
unsigned char *output; /* output block */
|
|
unsigned char *input; /* input block */
|
|
unsigned int len; /* length of input and output blocks */
|
|
{
|
|
if (context->encryptionAlgorithm == EA_DES_CBC)
|
|
DES_CBCUpdate (&context->cipherContext.des, output, input, len);
|
|
else if (context->encryptionAlgorithm == EA_DESX_CBC)
|
|
DESX_CBCUpdate (&context->cipherContext.desx, output, input, len);
|
|
else
|
|
DES3_CBCUpdate (&context->cipherContext.des3, output, input, len);
|
|
}
|
|
|
|
static void CipherRestart (context)
|
|
R_ENVELOPE_CTX *context;
|
|
{
|
|
if (context->encryptionAlgorithm == EA_DES_CBC)
|
|
DES_CBCRestart (&context->cipherContext.des);
|
|
else if (context->encryptionAlgorithm == EA_DESX_CBC)
|
|
DESX_CBCRestart (&context->cipherContext.desx);
|
|
else
|
|
DES3_CBCRestart (&context->cipherContext.des3);
|
|
}
|