mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-15 04:00:12 +01:00
383 lines
9.9 KiB
C
383 lines
9.9 KiB
C
/*
|
|
* aeskeywrap.c - implement AES Key Wrap algorithm from RFC 3394
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#ifdef FREEBL_NO_DEPEND
|
|
#include "stubs.h"
|
|
#endif
|
|
|
|
#include "prcpucfg.h"
|
|
#if defined(IS_LITTLE_ENDIAN) || defined(SHA_NO_LONG_LONG)
|
|
#define BIG_ENDIAN_WITH_64_BIT_REGISTERS 0
|
|
#else
|
|
#define BIG_ENDIAN_WITH_64_BIT_REGISTERS 1
|
|
#endif
|
|
#include "prtypes.h" /* for PRUintXX */
|
|
#include "secport.h" /* for PORT_XXX */
|
|
#include "secerr.h"
|
|
#include "blapi.h" /* for AES_ functions */
|
|
#include "rijndael.h"
|
|
|
|
struct AESKeyWrapContextStr {
|
|
unsigned char iv[AES_KEY_WRAP_IV_BYTES];
|
|
AESContext aescx;
|
|
};
|
|
|
|
/******************************************/
|
|
/*
|
|
** AES key wrap algorithm, RFC 3394
|
|
*/
|
|
|
|
AESKeyWrapContext *
|
|
AESKeyWrap_AllocateContext(void)
|
|
{
|
|
AESKeyWrapContext * cx = PORT_New(AESKeyWrapContext);
|
|
return cx;
|
|
}
|
|
|
|
SECStatus
|
|
AESKeyWrap_InitContext(AESKeyWrapContext *cx,
|
|
const unsigned char *key,
|
|
unsigned int keylen,
|
|
const unsigned char *iv,
|
|
int x1,
|
|
unsigned int encrypt,
|
|
unsigned int x2)
|
|
{
|
|
SECStatus rv = SECFailure;
|
|
if (!cx) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return SECFailure;
|
|
}
|
|
if (iv) {
|
|
memcpy(cx->iv, iv, sizeof cx->iv);
|
|
} else {
|
|
memset(cx->iv, 0xA6, sizeof cx->iv);
|
|
}
|
|
rv = AES_InitContext(&cx->aescx, key, keylen, NULL, NSS_AES, encrypt,
|
|
AES_BLOCK_SIZE);
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
** Create a new AES context suitable for AES encryption/decryption.
|
|
** "key" raw key data
|
|
** "keylen" the number of bytes of key data (16, 24, or 32)
|
|
*/
|
|
extern AESKeyWrapContext *
|
|
AESKeyWrap_CreateContext(const unsigned char *key, const unsigned char *iv,
|
|
int encrypt, unsigned int keylen)
|
|
{
|
|
SECStatus rv;
|
|
AESKeyWrapContext * cx = AESKeyWrap_AllocateContext();
|
|
if (!cx)
|
|
return NULL; /* error is already set */
|
|
rv = AESKeyWrap_InitContext(cx, key, keylen, iv, 0, encrypt, 0);
|
|
if (rv != SECSuccess) {
|
|
PORT_Free(cx);
|
|
cx = NULL; /* error should already be set */
|
|
}
|
|
return cx;
|
|
}
|
|
|
|
/*
|
|
** Destroy a AES KeyWrap context.
|
|
** "cx" the context
|
|
** "freeit" if PR_TRUE then free the object as well as its sub-objects
|
|
*/
|
|
extern void
|
|
AESKeyWrap_DestroyContext(AESKeyWrapContext *cx, PRBool freeit)
|
|
{
|
|
if (cx) {
|
|
AES_DestroyContext(&cx->aescx, PR_FALSE);
|
|
/* memset(cx, 0, sizeof *cx); */
|
|
if (freeit)
|
|
PORT_Free(cx);
|
|
}
|
|
}
|
|
|
|
#if !BIG_ENDIAN_WITH_64_BIT_REGISTERS
|
|
|
|
/* The AES Key Wrap algorithm has 64-bit values that are ALWAYS big-endian
|
|
** (Most significant byte first) in memory. The only ALU operations done
|
|
** on them are increment, decrement, and XOR. So, on little-endian CPUs,
|
|
** and on CPUs that lack 64-bit registers, these big-endian 64-bit operations
|
|
** are simulated in the following code. This is thought to be faster and
|
|
** simpler than trying to convert the data to little-endian and back.
|
|
*/
|
|
|
|
/* A and T point to two 64-bit values stored most signficant byte first
|
|
** (big endian). This function increments the 64-bit value T, and then
|
|
** XORs it with A, changing A.
|
|
*/
|
|
static void
|
|
increment_and_xor(unsigned char *A, unsigned char *T)
|
|
{
|
|
if (!++T[7])
|
|
if (!++T[6])
|
|
if (!++T[5])
|
|
if (!++T[4])
|
|
if (!++T[3])
|
|
if (!++T[2])
|
|
if (!++T[1])
|
|
++T[0];
|
|
|
|
A[0] ^= T[0];
|
|
A[1] ^= T[1];
|
|
A[2] ^= T[2];
|
|
A[3] ^= T[3];
|
|
A[4] ^= T[4];
|
|
A[5] ^= T[5];
|
|
A[6] ^= T[6];
|
|
A[7] ^= T[7];
|
|
}
|
|
|
|
/* A and T point to two 64-bit values stored most signficant byte first
|
|
** (big endian). This function XORs T with A, giving a new A, then
|
|
** decrements the 64-bit value T.
|
|
*/
|
|
static void
|
|
xor_and_decrement(unsigned char *A, unsigned char *T)
|
|
{
|
|
A[0] ^= T[0];
|
|
A[1] ^= T[1];
|
|
A[2] ^= T[2];
|
|
A[3] ^= T[3];
|
|
A[4] ^= T[4];
|
|
A[5] ^= T[5];
|
|
A[6] ^= T[6];
|
|
A[7] ^= T[7];
|
|
|
|
if (!T[7]--)
|
|
if (!T[6]--)
|
|
if (!T[5]--)
|
|
if (!T[4]--)
|
|
if (!T[3]--)
|
|
if (!T[2]--)
|
|
if (!T[1]--)
|
|
T[0]--;
|
|
|
|
}
|
|
|
|
/* Given an unsigned long t (in host byte order), store this value as a
|
|
** 64-bit big-endian value (MSB first) in *pt.
|
|
*/
|
|
static void
|
|
set_t(unsigned char *pt, unsigned long t)
|
|
{
|
|
pt[7] = (unsigned char)t; t >>= 8;
|
|
pt[6] = (unsigned char)t; t >>= 8;
|
|
pt[5] = (unsigned char)t; t >>= 8;
|
|
pt[4] = (unsigned char)t; t >>= 8;
|
|
pt[3] = (unsigned char)t; t >>= 8;
|
|
pt[2] = (unsigned char)t; t >>= 8;
|
|
pt[1] = (unsigned char)t; t >>= 8;
|
|
pt[0] = (unsigned char)t;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
** Perform AES key wrap.
|
|
** "cx" the context
|
|
** "output" the output buffer to store the encrypted data.
|
|
** "outputLen" how much data is stored in "output". Set by the routine
|
|
** after some data is stored in output.
|
|
** "maxOutputLen" the maximum amount of data that can ever be
|
|
** stored in "output"
|
|
** "input" the input data
|
|
** "inputLen" the amount of input data
|
|
*/
|
|
extern SECStatus
|
|
AESKeyWrap_Encrypt(AESKeyWrapContext *cx, unsigned char *output,
|
|
unsigned int *pOutputLen, unsigned int maxOutputLen,
|
|
const unsigned char *input, unsigned int inputLen)
|
|
{
|
|
PRUint64 * R = NULL;
|
|
unsigned int nBlocks;
|
|
unsigned int i, j;
|
|
unsigned int aesLen = AES_BLOCK_SIZE;
|
|
unsigned int outLen = inputLen + AES_KEY_WRAP_BLOCK_SIZE;
|
|
SECStatus s = SECFailure;
|
|
/* These PRUint64s are ALWAYS big endian, regardless of CPU orientation. */
|
|
PRUint64 t;
|
|
PRUint64 B[2];
|
|
|
|
#define A B[0]
|
|
|
|
/* Check args */
|
|
if (!inputLen || 0 != inputLen % AES_KEY_WRAP_BLOCK_SIZE) {
|
|
PORT_SetError(SEC_ERROR_INPUT_LEN);
|
|
return s;
|
|
}
|
|
#ifdef maybe
|
|
if (!output && pOutputLen) { /* caller is asking for output size */
|
|
*pOutputLen = outLen;
|
|
return SECSuccess;
|
|
}
|
|
#endif
|
|
if (maxOutputLen < outLen) {
|
|
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
|
|
return s;
|
|
}
|
|
if (cx == NULL || output == NULL || input == NULL) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return s;
|
|
}
|
|
nBlocks = inputLen / AES_KEY_WRAP_BLOCK_SIZE;
|
|
R = PORT_NewArray(PRUint64, nBlocks + 1);
|
|
if (!R)
|
|
return s; /* error is already set. */
|
|
/*
|
|
** 1) Initialize variables.
|
|
*/
|
|
memcpy(&A, cx->iv, AES_KEY_WRAP_IV_BYTES);
|
|
memcpy(&R[1], input, inputLen);
|
|
#if BIG_ENDIAN_WITH_64_BIT_REGISTERS
|
|
t = 0;
|
|
#else
|
|
memset(&t, 0, sizeof t);
|
|
#endif
|
|
/*
|
|
** 2) Calculate intermediate values.
|
|
*/
|
|
for (j = 0; j < 6; ++j) {
|
|
for (i = 1; i <= nBlocks; ++i) {
|
|
B[1] = R[i];
|
|
s = AES_Encrypt(&cx->aescx, (unsigned char *)B, &aesLen,
|
|
sizeof B, (unsigned char *)B, sizeof B);
|
|
if (s != SECSuccess)
|
|
break;
|
|
R[i] = B[1];
|
|
/* here, increment t and XOR A with t (in big endian order); */
|
|
#if BIG_ENDIAN_WITH_64_BIT_REGISTERS
|
|
A ^= ++t;
|
|
#else
|
|
increment_and_xor((unsigned char *)&A, (unsigned char *)&t);
|
|
#endif
|
|
}
|
|
}
|
|
/*
|
|
** 3) Output the results.
|
|
*/
|
|
if (s == SECSuccess) {
|
|
R[0] = A;
|
|
memcpy(output, &R[0], outLen);
|
|
if (pOutputLen)
|
|
*pOutputLen = outLen;
|
|
} else if (pOutputLen) {
|
|
*pOutputLen = 0;
|
|
}
|
|
PORT_ZFree(R, outLen);
|
|
return s;
|
|
}
|
|
#undef A
|
|
|
|
/*
|
|
** Perform AES key unwrap.
|
|
** "cx" the context
|
|
** "output" the output buffer to store the decrypted data.
|
|
** "outputLen" how much data is stored in "output". Set by the routine
|
|
** after some data is stored in output.
|
|
** "maxOutputLen" the maximum amount of data that can ever be
|
|
** stored in "output"
|
|
** "input" the input data
|
|
** "inputLen" the amount of input data
|
|
*/
|
|
extern SECStatus
|
|
AESKeyWrap_Decrypt(AESKeyWrapContext *cx, unsigned char *output,
|
|
unsigned int *pOutputLen, unsigned int maxOutputLen,
|
|
const unsigned char *input, unsigned int inputLen)
|
|
{
|
|
PRUint64 * R = NULL;
|
|
unsigned int nBlocks;
|
|
unsigned int i, j;
|
|
unsigned int aesLen = AES_BLOCK_SIZE;
|
|
unsigned int outLen;
|
|
SECStatus s = SECFailure;
|
|
/* These PRUint64s are ALWAYS big endian, regardless of CPU orientation. */
|
|
PRUint64 t;
|
|
PRUint64 B[2];
|
|
|
|
#define A B[0]
|
|
|
|
/* Check args */
|
|
if (inputLen < 3 * AES_KEY_WRAP_BLOCK_SIZE ||
|
|
0 != inputLen % AES_KEY_WRAP_BLOCK_SIZE) {
|
|
PORT_SetError(SEC_ERROR_INPUT_LEN);
|
|
return s;
|
|
}
|
|
outLen = inputLen - AES_KEY_WRAP_BLOCK_SIZE;
|
|
#ifdef maybe
|
|
if (!output && pOutputLen) { /* caller is asking for output size */
|
|
*pOutputLen = outLen;
|
|
return SECSuccess;
|
|
}
|
|
#endif
|
|
if (maxOutputLen < outLen) {
|
|
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
|
|
return s;
|
|
}
|
|
if (cx == NULL || output == NULL || input == NULL) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
return s;
|
|
}
|
|
nBlocks = inputLen / AES_KEY_WRAP_BLOCK_SIZE;
|
|
R = PORT_NewArray(PRUint64, nBlocks);
|
|
if (!R)
|
|
return s; /* error is already set. */
|
|
nBlocks--;
|
|
/*
|
|
** 1) Initialize variables.
|
|
*/
|
|
memcpy(&R[0], input, inputLen);
|
|
A = R[0];
|
|
#if BIG_ENDIAN_WITH_64_BIT_REGISTERS
|
|
t = 6UL * nBlocks;
|
|
#else
|
|
set_t((unsigned char *)&t, 6UL * nBlocks);
|
|
#endif
|
|
/*
|
|
** 2) Calculate intermediate values.
|
|
*/
|
|
for (j = 0; j < 6; ++j) {
|
|
for (i = nBlocks; i; --i) {
|
|
/* here, XOR A with t (in big endian order) and decrement t; */
|
|
#if BIG_ENDIAN_WITH_64_BIT_REGISTERS
|
|
A ^= t--;
|
|
#else
|
|
xor_and_decrement((unsigned char *)&A, (unsigned char *)&t);
|
|
#endif
|
|
B[1] = R[i];
|
|
s = AES_Decrypt(&cx->aescx, (unsigned char *)B, &aesLen,
|
|
sizeof B, (unsigned char *)B, sizeof B);
|
|
if (s != SECSuccess)
|
|
break;
|
|
R[i] = B[1];
|
|
}
|
|
}
|
|
/*
|
|
** 3) Output the results.
|
|
*/
|
|
if (s == SECSuccess) {
|
|
int bad = memcmp(&A, cx->iv, AES_KEY_WRAP_IV_BYTES);
|
|
if (!bad) {
|
|
memcpy(output, &R[1], outLen);
|
|
if (pOutputLen)
|
|
*pOutputLen = outLen;
|
|
} else {
|
|
PORT_SetError(SEC_ERROR_BAD_DATA);
|
|
if (pOutputLen)
|
|
*pOutputLen = 0;
|
|
}
|
|
} else if (pOutputLen) {
|
|
*pOutputLen = 0;
|
|
}
|
|
PORT_ZFree(R, inputLen);
|
|
return s;
|
|
}
|
|
#undef A
|