Source code
Revision control
Copy as Markdown
Other Tools
/* 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
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "secitem.h"
#include "blapi.h"
#include "nssutil.h"
#include "secerr.h"
#include "secder.h"
#include "secdig.h"
#include "secoid.h"
#include "ec.h"
#include "hasht.h"
#include "lowkeyi.h"
#include "softoken.h"
#include "pkcs11t.h"
#define __PASTE(x, y) x##y
#undef CK_PKCS11_FUNCTION_INFO
#undef CK_NEED_ARG_LIST
#define CK_EXTERN extern
#define CK_PKCS11_FUNCTION_INFO(func) \
CK_RV __PASTE(NS, func)
#define CK_NEED_ARG_LIST 1
#include "pkcs11f.h"
#undef CK_PKCS11_FUNCTION_INFO
#undef CK_NEED_ARG_LIST
#undef __PASTE
#define SSL3_RANDOM_LENGTH 32
#if 0
#include "../../lib/freebl/mpi/mpi.h"
#endif
#define MATCH_OPENSSL 1
/*#define MATCH_NIST 1 */
#ifdef MATCH_NIST
#define VERBOSE_REASON 1
#endif
extern SECStatus
EC_DecodeParams(const SECItem *encodedParams, ECParams **ecparams);
extern SECStatus
EC_CopyParams(PLArenaPool *arena, ECParams *dstParams,
const ECParams *srcParams);
#define ENCRYPT 1
#define DECRYPT 0
#define BYTE unsigned char
#define DEFAULT_RSA_PUBLIC_EXPONENT 0x10001
#define RSA_MAX_TEST_MODULUS_BITS 4096
#define RSA_MAX_TEST_MODULUS_BYTES RSA_MAX_TEST_MODULUS_BITS / 8
#define RSA_MAX_TEST_EXPONENT_BYTES 8
#define PQG_TEST_SEED_BYTES 20
SECStatus
hex_to_byteval(const char *c2, unsigned char *byteval)
{
int i;
unsigned char offset;
*byteval = 0;
for (i = 0; i < 2; i++) {
if (c2[i] >= '0' && c2[i] <= '9') {
offset = c2[i] - '0';
*byteval |= offset << 4 * (1 - i);
} else if (c2[i] >= 'a' && c2[i] <= 'f') {
offset = c2[i] - 'a';
*byteval |= (offset + 10) << 4 * (1 - i);
} else if (c2[i] >= 'A' && c2[i] <= 'F') {
offset = c2[i] - 'A';
*byteval |= (offset + 10) << 4 * (1 - i);
} else {
return SECFailure;
}
}
return SECSuccess;
}
SECStatus
byteval_to_hex(unsigned char byteval, char *c2, char a)
{
int i;
unsigned char offset;
for (i = 0; i < 2; i++) {
offset = (byteval >> 4 * (1 - i)) & 0x0f;
if (offset < 10) {
c2[i] = '0' + offset;
} else {
c2[i] = a + offset - 10;
}
}
return SECSuccess;
}
void
to_hex_str(char *str, const unsigned char *buf, unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i++) {
byteval_to_hex(buf[i], &str[2 * i], 'a');
}
str[2 * len] = '\0';
}
void
to_hex_str_cap(char *str, const unsigned char *buf, unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i++) {
byteval_to_hex(buf[i], &str[2 * i], 'A');
}
str[2 * len] = '\0';
}
/*
* Convert a string of hex digits (str) to an array (buf) of len bytes.
* Return PR_TRUE if the hex string can fit in the byte array. Return
* PR_FALSE if the hex string is empty or is too long.
*/
PRBool
from_hex_str(unsigned char *buf, unsigned int len, const char *str)
{
unsigned int nxdigit; /* number of hex digits in str */
unsigned int i; /* index into buf */
unsigned int j; /* index into str */
/* count the hex digits */
nxdigit = 0;
for (nxdigit = 0; isxdigit(str[nxdigit]); nxdigit++) {
/* empty body */
}
if (nxdigit == 0) {
return PR_FALSE;
}
if (nxdigit > 2 * len) {
/*
* The input hex string is too long, but we allow it if the
* extra digits are leading 0's.
*/
for (j = 0; j < nxdigit - 2 * len; j++) {
if (str[j] != '0') {
return PR_FALSE;
}
}
/* skip leading 0's */
str += nxdigit - 2 * len;
nxdigit = 2 * len;
}
for (i = 0, j = 0; i < len; i++) {
if (2 * i < 2 * len - nxdigit) {
/* Handle a short input as if we padded it with leading 0's. */
if (2 * i + 1 < 2 * len - nxdigit) {
buf[i] = 0;
} else {
char tmp[2];
tmp[0] = '0';
tmp[1] = str[j];
hex_to_byteval(tmp, &buf[i]);
j++;
}
} else {
hex_to_byteval(&str[j], &buf[i]);
j += 2;
}
}
return PR_TRUE;
}
SECStatus
tdea_encrypt_buf(
int mode,
const unsigned char *key,
const unsigned char *iv,
unsigned char *output, unsigned int *outputlen, unsigned int maxoutputlen,
const unsigned char *input, unsigned int inputlen)
{
SECStatus rv = SECFailure;
DESContext *cx;
unsigned char doublecheck[8 * 20]; /* 1 to 20 blocks */
unsigned int doublechecklen = 0;
cx = DES_CreateContext(key, iv, mode, PR_TRUE);
if (cx == NULL) {
goto loser;
}
rv = DES_Encrypt(cx, output, outputlen, maxoutputlen, input, inputlen);
if (rv != SECSuccess) {
goto loser;
}
if (*outputlen != inputlen) {
goto loser;
}
DES_DestroyContext(cx, PR_TRUE);
cx = NULL;
/*
* Doublecheck our result by decrypting the ciphertext and
* compare the output with the input plaintext.
*/
cx = DES_CreateContext(key, iv, mode, PR_FALSE);
if (cx == NULL) {
goto loser;
}
rv = DES_Decrypt(cx, doublecheck, &doublechecklen, sizeof doublecheck,
output, *outputlen);
if (rv != SECSuccess) {
goto loser;
}
if (doublechecklen != *outputlen) {
goto loser;
}
DES_DestroyContext(cx, PR_TRUE);
cx = NULL;
if (memcmp(doublecheck, input, inputlen) != 0) {
goto loser;
}
rv = SECSuccess;
loser:
if (cx != NULL) {
DES_DestroyContext(cx, PR_TRUE);
}
return rv;
}
SECStatus
tdea_decrypt_buf(
int mode,
const unsigned char *key,
const unsigned char *iv,
unsigned char *output, unsigned int *outputlen, unsigned int maxoutputlen,
const unsigned char *input, unsigned int inputlen)
{
SECStatus rv = SECFailure;
DESContext *cx;
unsigned char doublecheck[8 * 20]; /* 1 to 20 blocks */
unsigned int doublechecklen = 0;
cx = DES_CreateContext(key, iv, mode, PR_FALSE);
if (cx == NULL) {
goto loser;
}
rv = DES_Decrypt(cx, output, outputlen, maxoutputlen,
input, inputlen);
if (rv != SECSuccess) {
goto loser;
}
if (*outputlen != inputlen) {
goto loser;
}
DES_DestroyContext(cx, PR_TRUE);
cx = NULL;
/*
* Doublecheck our result by encrypting the plaintext and
* compare the output with the input ciphertext.
*/
cx = DES_CreateContext(key, iv, mode, PR_TRUE);
if (cx == NULL) {
goto loser;
}
rv = DES_Encrypt(cx, doublecheck, &doublechecklen, sizeof doublecheck,
output, *outputlen);
if (rv != SECSuccess) {
goto loser;
}
if (doublechecklen != *outputlen) {
goto loser;
}
DES_DestroyContext(cx, PR_TRUE);
cx = NULL;
if (memcmp(doublecheck, input, inputlen) != 0) {
goto loser;
}
rv = SECSuccess;
loser:
if (cx != NULL) {
DES_DestroyContext(cx, PR_TRUE);
}
return rv;
}
/*
* Perform the TDEA Known Answer Test (KAT) or Multi-block Message
* Test (MMT) in ECB or CBC mode. The KAT (there are five types)
* and MMT have the same structure: given the key and IV (CBC mode
* only), encrypt the given plaintext or decrypt the given ciphertext.
* So we can handle them the same way.
*
* reqfn is the pathname of the REQUEST file.
*
* The output RESPONSE file is written to stdout.
*/
void
tdea_kat_mmt(char *reqfn)
{
char buf[180]; /* holds one line from the input REQUEST file.
* needs to be large enough to hold the longest
* line "CIPHERTEXT = <180 hex digits>\n".
*/
FILE *req; /* input stream from the REQUEST file */
FILE *resp; /* output stream to the RESPONSE file */
int i, j;
int mode = NSS_DES_EDE3; /* NSS_DES_EDE3 (ECB) or NSS_DES_EDE3_CBC */
int crypt = DECRYPT; /* 1 means encrypt, 0 means decrypt */
unsigned char key[24]; /* TDEA 3 key bundle */
unsigned int numKeys = 0;
unsigned char iv[8]; /* for all modes except ECB */
unsigned char plaintext[8 * 20]; /* 1 to 20 blocks */
unsigned int plaintextlen;
unsigned char ciphertext[8 * 20]; /* 1 to 20 blocks */
unsigned int ciphertextlen;
SECStatus rv;
req = fopen(reqfn, "r");
resp = stdout;
while (fgets(buf, sizeof buf, req) != NULL) {
/* a comment or blank line */
if (buf[0] == '#' || buf[0] == '\n') {
fputs(buf, resp);
continue;
}
/* [ENCRYPT] or [DECRYPT] */
if (buf[0] == '[') {
if (strncmp(&buf[1], "ENCRYPT", 7) == 0) {
crypt = ENCRYPT;
} else {
crypt = DECRYPT;
}
fputs(buf, resp);
continue;
}
/* NumKeys */
if (strncmp(&buf[0], "NumKeys", 7) == 0) {
i = 7;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
numKeys = buf[i];
fputs(buf, resp);
continue;
}
/* "COUNT = x" begins a new data set */
if (strncmp(buf, "COUNT", 5) == 0) {
/* mode defaults to ECB, if dataset has IV mode will be set CBC */
mode = NSS_DES_EDE3;
/* zeroize the variables for the test with this data set */
memset(key, 0, sizeof key);
memset(iv, 0, sizeof iv);
memset(plaintext, 0, sizeof plaintext);
plaintextlen = 0;
memset(ciphertext, 0, sizeof ciphertext);
ciphertextlen = 0;
fputs(buf, resp);
continue;
}
if (numKeys == 0) {
if (strncmp(buf, "KEYs", 4) == 0) {
i = 4;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &key[j]);
key[j + 8] = key[j];
key[j + 16] = key[j];
}
fputs(buf, resp);
continue;
}
} else {
/* KEY1 = ... */
if (strncmp(buf, "KEY1", 4) == 0) {
i = 4;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &key[j]);
}
fputs(buf, resp);
continue;
}
/* KEY2 = ... */
if (strncmp(buf, "KEY2", 4) == 0) {
i = 4;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 8; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &key[j]);
}
fputs(buf, resp);
continue;
}
/* KEY3 = ... */
if (strncmp(buf, "KEY3", 4) == 0) {
i = 4;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 16; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &key[j]);
}
fputs(buf, resp);
continue;
}
}
/* IV = ... */
if (strncmp(buf, "IV", 2) == 0) {
mode = NSS_DES_EDE3_CBC;
i = 2;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; j < sizeof iv; i += 2, j++) {
hex_to_byteval(&buf[i], &iv[j]);
}
fputs(buf, resp);
continue;
}
/* PLAINTEXT = ... */
if (strncmp(buf, "PLAINTEXT", 9) == 0) {
/* sanity check */
if (crypt != ENCRYPT) {
goto loser;
}
i = 9;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &plaintext[j]);
}
plaintextlen = j;
rv = tdea_encrypt_buf(mode, key,
(mode == NSS_DES_EDE3) ? NULL : iv,
ciphertext, &ciphertextlen, sizeof ciphertext,
plaintext, plaintextlen);
if (rv != SECSuccess) {
goto loser;
}
fputs(buf, resp);
fputs("CIPHERTEXT = ", resp);
to_hex_str(buf, ciphertext, ciphertextlen);
fputs(buf, resp);
fputc('\n', resp);
continue;
}
/* CIPHERTEXT = ... */
if (strncmp(buf, "CIPHERTEXT", 10) == 0) {
/* sanity check */
if (crypt != DECRYPT) {
goto loser;
}
i = 10;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &ciphertext[j]);
}
ciphertextlen = j;
rv = tdea_decrypt_buf(mode, key,
(mode == NSS_DES_EDE3) ? NULL : iv,
plaintext, &plaintextlen, sizeof plaintext,
ciphertext, ciphertextlen);
if (rv != SECSuccess) {
goto loser;
}
fputs(buf, resp);
fputs("PLAINTEXT = ", resp);
to_hex_str(buf, plaintext, plaintextlen);
fputs(buf, resp);
fputc('\n', resp);
continue;
}
}
loser:
fclose(req);
}
/*
* Set the parity bit for the given byte
*/
BYTE
odd_parity(BYTE in)
{
BYTE out = in;
in ^= in >> 4;
in ^= in >> 2;
in ^= in >> 1;
return (BYTE)(out ^ !(in & 1));
}
/*
* Generate Keys [i+1] from Key[i], PT/CT[j-2], PT/CT[j-1], and PT/CT[j]
* for TDEA Monte Carlo Test (MCT) in ECB and CBC modes.
*/
void
tdea_mct_next_keys(unsigned char *key,
const unsigned char *text_2, const unsigned char *text_1,
const unsigned char *text, unsigned int numKeys)
{
int k;
/* key1[i+1] = key1[i] xor PT/CT[j] */
for (k = 0; k < 8; k++) {
key[k] ^= text[k];
}
/* key2 */
if (numKeys == 2 || numKeys == 3) {
/* key2 independent */
for (k = 8; k < 16; k++) {
/* key2[i+1] = KEY2[i] xor PT/CT[j-1] */
key[k] ^= text_1[k - 8];
}
} else {
/* key2 == key 1 */
for (k = 8; k < 16; k++) {
/* key2[i+1] = KEY2[i] xor PT/CT[j] */
key[k] = key[k - 8];
}
}
/* key3 */
if (numKeys == 1 || numKeys == 2) {
/* key3 == key 1 */
for (k = 16; k < 24; k++) {
/* key3[i+1] = KEY3[i] xor PT/CT[j] */
key[k] = key[k - 16];
}
} else {
/* key3 independent */
for (k = 16; k < 24; k++) {
/* key3[i+1] = KEY3[i] xor PT/CT[j-2] */
key[k] ^= text_2[k - 16];
}
}
/* set the parity bits */
for (k = 0; k < 24; k++) {
key[k] = odd_parity(key[k]);
}
}
/*
* Perform the Monte Carlo Test
*
* mode = NSS_DES_EDE3 or NSS_DES_EDE3_CBC
* crypt = ENCRYPT || DECRYPT
* inputtext = plaintext or Cyphertext depending on the value of crypt
* inputlength is expected to be size 8 bytes
* iv = needs to be set for NSS_DES_EDE3_CBC mode
* resp = is the output response file.
*/
void
tdea_mct_test(int mode, unsigned char *key, unsigned int numKeys,
unsigned int crypt, unsigned char *inputtext,
unsigned int inputlength, unsigned char *iv, FILE *resp)
{
int i, j;
unsigned char outputtext_1[8]; /* PT/CT[j-1] */
unsigned char outputtext_2[8]; /* PT/CT[j-2] */
char buf[80]; /* holds one line from the input REQUEST file. */
unsigned int outputlen;
unsigned char outputtext[8];
SECStatus rv;
if (mode == NSS_DES_EDE3 && iv != NULL) {
printf("IV must be NULL for NSS_DES_EDE3 mode");
goto loser;
} else if (mode == NSS_DES_EDE3_CBC && iv == NULL) {
printf("IV must not be NULL for NSS_DES_EDE3_CBC mode");
goto loser;
}
/* loop 400 times */
for (i = 0; i < 400; i++) {
/* if i == 0 CV[0] = IV not necessary */
/* record the count and key values and plainText */
snprintf(buf, sizeof(buf), "COUNT = %d\n", i);
fputs(buf, resp);
/* Output KEY1[i] */
fputs("KEY1 = ", resp);
to_hex_str(buf, key, 8);
fputs(buf, resp);
fputc('\n', resp);
/* Output KEY2[i] */
fputs("KEY2 = ", resp);
to_hex_str(buf, &key[8], 8);
fputs(buf, resp);
fputc('\n', resp);
/* Output KEY3[i] */
fputs("KEY3 = ", resp);
to_hex_str(buf, &key[16], 8);
fputs(buf, resp);
fputc('\n', resp);
if (mode == NSS_DES_EDE3_CBC) {
/* Output CV[i] */
fputs("IV = ", resp);
to_hex_str(buf, iv, 8);
fputs(buf, resp);
fputc('\n', resp);
}
if (crypt == ENCRYPT) {
/* Output PT[0] */
fputs("PLAINTEXT = ", resp);
} else {
/* Output CT[0] */
fputs("CIPHERTEXT = ", resp);
}
to_hex_str(buf, inputtext, inputlength);
fputs(buf, resp);
fputc('\n', resp);
/* loop 10,000 times */
for (j = 0; j < 10000; j++) {
outputlen = 0;
if (crypt == ENCRYPT) {
/* inputtext == ciphertext outputtext == plaintext*/
rv = tdea_encrypt_buf(mode, key,
(mode ==
NSS_DES_EDE3)
? NULL
: iv,
outputtext, &outputlen, 8,
inputtext, 8);
} else {
/* inputtext == plaintext outputtext == ciphertext */
rv = tdea_decrypt_buf(mode, key,
(mode ==
NSS_DES_EDE3)
? NULL
: iv,
outputtext, &outputlen, 8,
inputtext, 8);
}
if (rv != SECSuccess) {
goto loser;
}
if (outputlen != inputlength) {
goto loser;
}
if (mode == NSS_DES_EDE3_CBC) {
if (crypt == ENCRYPT) {
if (j == 0) {
/*P[j+1] = CV[0] */
memcpy(inputtext, iv, 8);
} else {
/* p[j+1] = C[j-1] */
memcpy(inputtext, outputtext_1, 8);
}
/* CV[j+1] = C[j] */
memcpy(iv, outputtext, 8);
if (j != 9999) {
/* save C[j-1] */
memcpy(outputtext_1, outputtext, 8);
}
} else { /* DECRYPT */
/* CV[j+1] = C[j] */
memcpy(iv, inputtext, 8);
/* C[j+1] = P[j] */
memcpy(inputtext, outputtext, 8);
}
} else {
/* ECB mode PT/CT[j+1] = CT/PT[j] */
memcpy(inputtext, outputtext, 8);
}
/* Save PT/CT[j-2] and PT/CT[j-1] */
if (j == 9997)
memcpy(outputtext_2, outputtext, 8);
if (j == 9998)
memcpy(outputtext_1, outputtext, 8);
/* done at the end of the for(j) loop */
}
if (crypt == ENCRYPT) {
/* Output CT[j] */
fputs("CIPHERTEXT = ", resp);
} else {
/* Output PT[j] */
fputs("PLAINTEXT = ", resp);
}
to_hex_str(buf, outputtext, 8);
fputs(buf, resp);
fputc('\n', resp);
/* Key[i+1] = Key[i] xor ... outputtext_2 == PT/CT[j-2]
* outputtext_1 == PT/CT[j-1] outputtext == PT/CT[j]
*/
tdea_mct_next_keys(key, outputtext_2,
outputtext_1, outputtext, numKeys);
if (mode == NSS_DES_EDE3_CBC) {
/* taken care of in the j=9999 iteration */
if (crypt == ENCRYPT) {
/* P[i] = C[j-1] */
/* CV[i] = C[j] */
} else {
/* taken care of in the j=9999 iteration */
/* CV[i] = C[j] */
/* C[i] = P[j] */
}
} else {
/* ECB PT/CT[i] = PT/CT[j] */
memcpy(inputtext, outputtext, 8);
}
/* done at the end of the for(i) loop */
fputc('\n', resp);
}
loser:
return;
}
/*
* Perform the TDEA Monte Carlo Test (MCT) in ECB/CBC modes.
* by gathering the input from the request file, and then
* calling tdea_mct_test.
*
* reqfn is the pathname of the input REQUEST file.
*
* The output RESPONSE file is written to stdout.
*/
void
tdea_mct(int mode, char *reqfn)
{
int i, j;
char buf[80]; /* holds one line from the input REQUEST file. */
FILE *req; /* input stream from the REQUEST file */
FILE *resp; /* output stream to the RESPONSE file */
unsigned int crypt = 0; /* 1 means encrypt, 0 means decrypt */
unsigned char key[24]; /* TDEA 3 key bundle */
unsigned int numKeys = 0;
unsigned char plaintext[8]; /* PT[j] */
unsigned char ciphertext[8]; /* CT[j] */
unsigned char iv[8];
/* zeroize the variables for the test with this data set */
memset(key, 0, sizeof key);
memset(plaintext, 0, sizeof plaintext);
memset(ciphertext, 0, sizeof ciphertext);
memset(iv, 0, sizeof iv);
req = fopen(reqfn, "r");
resp = stdout;
while (fgets(buf, sizeof buf, req) != NULL) {
/* a comment or blank line */
if (buf[0] == '#' || buf[0] == '\n') {
fputs(buf, resp);
continue;
}
/* [ENCRYPT] or [DECRYPT] */
if (buf[0] == '[') {
if (strncmp(&buf[1], "ENCRYPT", 7) == 0) {
crypt = ENCRYPT;
} else {
crypt = DECRYPT;
}
fputs(buf, resp);
continue;
}
/* NumKeys */
if (strncmp(&buf[0], "NumKeys", 7) == 0) {
i = 7;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
numKeys = atoi(&buf[i]);
continue;
}
/* KEY1 = ... */
if (strncmp(buf, "KEY1", 4) == 0) {
i = 4;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &key[j]);
}
continue;
}
/* KEY2 = ... */
if (strncmp(buf, "KEY2", 4) == 0) {
i = 4;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 8; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &key[j]);
}
continue;
}
/* KEY3 = ... */
if (strncmp(buf, "KEY3", 4) == 0) {
i = 4;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 16; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &key[j]);
}
continue;
}
/* IV = ... */
if (strncmp(buf, "IV", 2) == 0) {
i = 2;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; j < sizeof iv; i += 2, j++) {
hex_to_byteval(&buf[i], &iv[j]);
}
continue;
}
/* PLAINTEXT = ... */
if (strncmp(buf, "PLAINTEXT", 9) == 0) {
/* sanity check */
if (crypt != ENCRYPT) {
goto loser;
}
/* PT[0] = PT */
i = 9;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; j < sizeof plaintext; i += 2, j++) {
hex_to_byteval(&buf[i], &plaintext[j]);
}
/* do the Monte Carlo test */
if (mode == NSS_DES_EDE3) {
tdea_mct_test(NSS_DES_EDE3, key, numKeys, crypt, plaintext, sizeof plaintext, NULL, resp);
} else {
tdea_mct_test(NSS_DES_EDE3_CBC, key, numKeys, crypt, plaintext, sizeof plaintext, iv, resp);
}
continue;
}
/* CIPHERTEXT = ... */
if (strncmp(buf, "CIPHERTEXT", 10) == 0) {
/* sanity check */
if (crypt != DECRYPT) {
goto loser;
}
/* CT[0] = CT */
i = 10;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &ciphertext[j]);
}
/* do the Monte Carlo test */
if (mode == NSS_DES_EDE3) {
tdea_mct_test(NSS_DES_EDE3, key, numKeys, crypt, ciphertext, sizeof ciphertext, NULL, resp);
} else {
tdea_mct_test(NSS_DES_EDE3_CBC, key, numKeys, crypt, ciphertext, sizeof ciphertext, iv, resp);
}
continue;
}
}
loser:
fclose(req);
}
SECStatus
aes_encrypt_buf(
int mode,
const unsigned char *key, unsigned int keysize,
const unsigned char *iv,
unsigned char *output, unsigned int *outputlen, unsigned int maxoutputlen,
const unsigned char *input, unsigned int inputlen)
{
SECStatus rv = SECFailure;
AESContext *cx;
unsigned char doublecheck[10 * 16]; /* 1 to 10 blocks */
unsigned int doublechecklen = 0;
cx = AES_CreateContext(key, iv, mode, PR_TRUE, keysize, 16);
if (cx == NULL) {
goto loser;
}
rv = AES_Encrypt(cx, output, outputlen, maxoutputlen, input, inputlen);
if (rv != SECSuccess) {
goto loser;
}
if (*outputlen != inputlen) {
goto loser;
}
AES_DestroyContext(cx, PR_TRUE);
cx = NULL;
/*
* Doublecheck our result by decrypting the ciphertext and
* compare the output with the input plaintext.
*/
cx = AES_CreateContext(key, iv, mode, PR_FALSE, keysize, 16);
if (cx == NULL) {
goto loser;
}
rv = AES_Decrypt(cx, doublecheck, &doublechecklen, sizeof doublecheck,
output, *outputlen);
if (rv != SECSuccess) {
goto loser;
}
if (doublechecklen != *outputlen) {
goto loser;
}
AES_DestroyContext(cx, PR_TRUE);
cx = NULL;
if (memcmp(doublecheck, input, inputlen) != 0) {
goto loser;
}
rv = SECSuccess;
loser:
if (cx != NULL) {
AES_DestroyContext(cx, PR_TRUE);
}
return rv;
}
SECStatus
aes_decrypt_buf(
int mode,
const unsigned char *key, unsigned int keysize,
const unsigned char *iv,
unsigned char *output, unsigned int *outputlen, unsigned int maxoutputlen,
const unsigned char *input, unsigned int inputlen)
{
SECStatus rv = SECFailure;
AESContext *cx;
unsigned char doublecheck[10 * 16]; /* 1 to 10 blocks */
unsigned int doublechecklen = 0;
cx = AES_CreateContext(key, iv, mode, PR_FALSE, keysize, 16);
if (cx == NULL) {
goto loser;
}
rv = AES_Decrypt(cx, output, outputlen, maxoutputlen,
input, inputlen);
if (rv != SECSuccess) {
goto loser;
}
if (*outputlen != inputlen) {
goto loser;
}
AES_DestroyContext(cx, PR_TRUE);
cx = NULL;
/*
* Doublecheck our result by encrypting the plaintext and
* compare the output with the input ciphertext.
*/
cx = AES_CreateContext(key, iv, mode, PR_TRUE, keysize, 16);
if (cx == NULL) {
goto loser;
}
rv = AES_Encrypt(cx, doublecheck, &doublechecklen, sizeof doublecheck,
output, *outputlen);
if (rv != SECSuccess) {
goto loser;
}
if (doublechecklen != *outputlen) {
goto loser;
}
AES_DestroyContext(cx, PR_TRUE);
cx = NULL;
if (memcmp(doublecheck, input, inputlen) != 0) {
goto loser;
}
rv = SECSuccess;
loser:
if (cx != NULL) {
AES_DestroyContext(cx, PR_TRUE);
}
return rv;
}
/*
* Perform the AES GCM tests.
*
* reqfn is the pathname of the REQUEST file.
*
* The output RESPONSE file is written to stdout.
*/
void
aes_gcm(char *reqfn, int encrypt)
{
char buf[512]; /* holds one line from the input REQUEST file.
* needs to be large enough to hold the longest
* line "CIPHERTEXT = <320 hex digits>\n".
*/
FILE *aesreq; /* input stream from the REQUEST file */
FILE *aesresp; /* output stream to the RESPONSE file */
int i, j;
unsigned char key[32]; /* 128, 192, or 256 bits */
unsigned int keysize = 0;
unsigned char iv[128]; /* handle large gcm IV's */
unsigned char plaintext[10 * 16]; /* 1 to 10 blocks */
unsigned int plaintextlen;
unsigned char ciphertext[11 * 16]; /* 1 to 10 blocks + tag */
unsigned int ciphertextlen;
unsigned char aad[11 * 16]; /* 1 to 10 blocks + tag */
unsigned int aadlen = 0;
unsigned int tagbits;
unsigned int taglen = 0;
unsigned int ivlen;
CK_NSS_GCM_PARAMS params;
SECStatus rv;
aesreq = fopen(reqfn, "r");
aesresp = stdout;
while (fgets(buf, sizeof buf, aesreq) != NULL) {
/* a comment or blank line */
if (buf[0] == '#' || buf[0] == '\n') {
fputs(buf, aesresp);
continue;
}
/* [ENCRYPT] or [DECRYPT] */
if (buf[0] == '[') {
if (strncmp(buf, "[Taglen", 7) == 0) {
if (sscanf(buf, "[Taglen = %d]", &tagbits) != 1) {
goto loser;
}
taglen = tagbits / 8;
}
if (strncmp(buf, "[IVlen", 6) == 0) {
if (sscanf(buf, "[IVlen = %d]", &ivlen) != 1) {
goto loser;
}
ivlen = ivlen / 8;
}
fputs(buf, aesresp);
continue;
}
/* "COUNT = x" begins a new data set */
if (strncmp(buf, "Count", 5) == 0) {
/* zeroize the variables for the test with this data set */
memset(key, 0, sizeof key);
keysize = 0;
memset(iv, 0, sizeof iv);
memset(plaintext, 0, sizeof plaintext);
plaintextlen = 0;
memset(ciphertext, 0, sizeof ciphertext);
ciphertextlen = 0;
fputs(buf, aesresp);
continue;
}
/* KEY = ... */
if (strncmp(buf, "Key", 3) == 0) {
i = 3;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &key[j]);
}
keysize = j;
fputs(buf, aesresp);
continue;
}
/* IV = ... */
if (strncmp(buf, "IV", 2) == 0) {
i = 2;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; j < sizeof iv; i += 2, j++) {
hex_to_byteval(&buf[i], &iv[j]);
}
fputs(buf, aesresp);
continue;
}
/* PLAINTEXT = ... */
if (strncmp(buf, "PT", 2) == 0) {
/* sanity check */
if (!encrypt) {
goto loser;
}
i = 2;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &plaintext[j]);
}
plaintextlen = j;
fputs(buf, aesresp);
continue;
}
/* CIPHERTEXT = ... */
if (strncmp(buf, "CT", 2) == 0) {
/* sanity check */
if (encrypt) {
goto loser;
}
i = 2;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &ciphertext[j]);
}
ciphertextlen = j;
fputs(buf, aesresp);
continue;
}
if (strncmp(buf, "AAD", 3) == 0) {
i = 3;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &aad[j]);
}
aadlen = j;
fputs(buf, aesresp);
if (encrypt) {
if (encrypt == 2) {
rv = RNG_GenerateGlobalRandomBytes(iv, ivlen);
if (rv != SECSuccess) {
goto loser;
}
}
params.pIv = iv;
params.ulIvLen = ivlen;
params.pAAD = aad;
params.ulAADLen = aadlen;
params.ulTagBits = tagbits;
rv = aes_encrypt_buf(NSS_AES_GCM, key, keysize,
(unsigned char *)¶ms,
ciphertext, &ciphertextlen, sizeof ciphertext,
plaintext, plaintextlen);
if (rv != SECSuccess) {
goto loser;
}
if (encrypt == 2) {
fputs("IV = ", aesresp);
to_hex_str(buf, iv, ivlen);
fputs(buf, aesresp);
fputc('\n', aesresp);
}
fputs("CT = ", aesresp);
j = ciphertextlen - taglen;
to_hex_str(buf, ciphertext, j);
fputs(buf, aesresp);
fputs("\nTag = ", aesresp);
to_hex_str(buf, ciphertext + j, taglen);
fputs(buf, aesresp);
fputc('\n', aesresp);
}
continue;
}
if (strncmp(buf, "Tag", 3) == 0) {
/* sanity check */
if (encrypt) {
goto loser;
}
i = 3;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &ciphertext[j + ciphertextlen]);
}
ciphertextlen += j;
params.pIv = iv;
params.ulIvLen = ivlen;
params.pAAD = aad;
params.ulAADLen = aadlen;
params.ulTagBits = tagbits;
rv = aes_decrypt_buf(NSS_AES_GCM, key, keysize,
(unsigned char *)¶ms,
plaintext, &plaintextlen, sizeof plaintext,
ciphertext, ciphertextlen);
fputs(buf, aesresp);
if (rv != SECSuccess) {
fprintf(aesresp, "FAIL\n");
} else {
fputs("PT = ", aesresp);
to_hex_str(buf, plaintext, plaintextlen);
fputs(buf, aesresp);
fputc('\n', aesresp);
}
continue;
}
}
loser:
fclose(aesreq);
}
/*
* Perform the AES Known Answer Test (KAT) or Multi-block Message
* Test (MMT) in ECB or CBC mode. The KAT (there are four types)
* and MMT have the same structure: given the key and IV (CBC mode
* only), encrypt the given plaintext or decrypt the given ciphertext.
* So we can handle them the same way.
*
* reqfn is the pathname of the REQUEST file.
*
* The output RESPONSE file is written to stdout.
*/
void
aes_kat_mmt(char *reqfn)
{
char buf[512]; /* holds one line from the input REQUEST file.
* needs to be large enough to hold the longest
* line "CIPHERTEXT = <320 hex digits>\n".
*/
FILE *aesreq; /* input stream from the REQUEST file */
FILE *aesresp; /* output stream to the RESPONSE file */
int i, j;
int mode = NSS_AES; /* NSS_AES (ECB) or NSS_AES_CBC */
int encrypt = 0; /* 1 means encrypt, 0 means decrypt */
unsigned char key[32]; /* 128, 192, or 256 bits */
unsigned int keysize = 0;
unsigned char iv[16]; /* for all modes except ECB */
unsigned char plaintext[10 * 16]; /* 1 to 10 blocks */
unsigned int plaintextlen;
unsigned char ciphertext[10 * 16]; /* 1 to 10 blocks */
unsigned int ciphertextlen;
SECStatus rv;
aesreq = fopen(reqfn, "r");
aesresp = stdout;
while (fgets(buf, sizeof buf, aesreq) != NULL) {
/* a comment or blank line */
if (buf[0] == '#' || buf[0] == '\n') {
fputs(buf, aesresp);
continue;
}
/* [ENCRYPT] or [DECRYPT] */
if (buf[0] == '[') {
if (strncmp(&buf[1], "ENCRYPT", 7) == 0) {
encrypt = 1;
} else {
encrypt = 0;
}
fputs(buf, aesresp);
continue;
}
/* "COUNT = x" begins a new data set */
if (strncmp(buf, "COUNT", 5) == 0) {
mode = NSS_AES;
/* zeroize the variables for the test with this data set */
memset(key, 0, sizeof key);
keysize = 0;
memset(iv, 0, sizeof iv);
memset(plaintext, 0, sizeof plaintext);
plaintextlen = 0;
memset(ciphertext, 0, sizeof ciphertext);
ciphertextlen = 0;
fputs(buf, aesresp);
continue;
}
/* KEY = ... */
if (strncmp(buf, "KEY", 3) == 0) {
i = 3;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &key[j]);
}
keysize = j;
fputs(buf, aesresp);
continue;
}
/* IV = ... */
if (strncmp(buf, "IV", 2) == 0) {
mode = NSS_AES_CBC;
i = 2;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; j < sizeof iv; i += 2, j++) {
hex_to_byteval(&buf[i], &iv[j]);
}
fputs(buf, aesresp);
continue;
}
/* PLAINTEXT = ... */
if (strncmp(buf, "PLAINTEXT", 9) == 0) {
/* sanity check */
if (!encrypt) {
goto loser;
}
i = 9;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &plaintext[j]);
}
plaintextlen = j;
rv = aes_encrypt_buf(mode, key, keysize,
(mode ==
NSS_AES)
? NULL
: iv,
ciphertext, &ciphertextlen, sizeof ciphertext,
plaintext, plaintextlen);
if (rv != SECSuccess) {
goto loser;
}
fputs(buf, aesresp);
fputs("CIPHERTEXT = ", aesresp);
to_hex_str(buf, ciphertext, ciphertextlen);
fputs(buf, aesresp);
fputc('\n', aesresp);
continue;
}
/* CIPHERTEXT = ... */
if (strncmp(buf, "CIPHERTEXT", 10) == 0) {
/* sanity check */
if (encrypt) {
goto loser;
}
i = 10;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &ciphertext[j]);
}
ciphertextlen = j;
rv = aes_decrypt_buf(mode, key, keysize,
(mode ==
NSS_AES)
? NULL
: iv,
plaintext, &plaintextlen, sizeof plaintext,
ciphertext, ciphertextlen);
if (rv != SECSuccess) {
goto loser;
}
fputs(buf, aesresp);
fputs("PLAINTEXT = ", aesresp);
to_hex_str(buf, plaintext, plaintextlen);
fputs(buf, aesresp);
fputc('\n', aesresp);
continue;
}
}
loser:
fclose(aesreq);
}
/*
* Generate Key[i+1] from Key[i], CT[j-1], and CT[j] for AES Monte Carlo
* Test (MCT) in ECB and CBC modes.
*/
void
aes_mct_next_key(unsigned char *key, unsigned int keysize,
const unsigned char *ciphertext_1, const unsigned char *ciphertext)
{
int k;
switch (keysize) {
case 16: /* 128-bit key */
/* Key[i+1] = Key[i] xor CT[j] */
for (k = 0; k < 16; k++) {
key[k] ^= ciphertext[k];
}
break;
case 24: /* 192-bit key */
/*
* Key[i+1] = Key[i] xor (last 64-bits of
* CT[j-1] || CT[j])
*/
for (k = 0; k < 8; k++) {
key[k] ^= ciphertext_1[k + 8];
}
for (k = 8; k < 24; k++) {
key[k] ^= ciphertext[k - 8];
}
break;
case 32: /* 256-bit key */
/* Key[i+1] = Key[i] xor (CT[j-1] || CT[j]) */
for (k = 0; k < 16; k++) {
key[k] ^= ciphertext_1[k];
}
for (k = 16; k < 32; k++) {
key[k] ^= ciphertext[k - 16];
}
break;
}
}
/*
* Perform the AES Monte Carlo Test (MCT) in ECB mode. MCT exercises
* our AES code in streaming mode because the plaintext or ciphertext
* is generated block by block as we go, so we can't collect all the
* plaintext or ciphertext in one buffer and encrypt or decrypt it in
* one shot.
*
* reqfn is the pathname of the input REQUEST file.
*
* The output RESPONSE file is written to stdout.
*/
void
aes_ecb_mct(char *reqfn)
{
char buf[80]; /* holds one line from the input REQUEST file.
* needs to be large enough to hold the longest
* line "KEY = <64 hex digits>\n".
*/
FILE *aesreq; /* input stream from the REQUEST file */
FILE *aesresp; /* output stream to the RESPONSE file */
int i, j;
int encrypt = 0; /* 1 means encrypt, 0 means decrypt */
unsigned char key[32]; /* 128, 192, or 256 bits */
unsigned int keysize = 0;
unsigned char plaintext[16]; /* PT[j] */
unsigned char plaintext_1[16]; /* PT[j-1] */
unsigned char ciphertext[16]; /* CT[j] */
unsigned char ciphertext_1[16]; /* CT[j-1] */
unsigned char doublecheck[16];
unsigned int outputlen;
AESContext *cx = NULL; /* the operation being tested */
AESContext *cx2 = NULL; /* the inverse operation done in parallel
* to doublecheck our result.
*/
SECStatus rv;
aesreq = fopen(reqfn, "r");
aesresp = stdout;
while (fgets(buf, sizeof buf, aesreq) != NULL) {
/* a comment or blank line */
if (buf[0] == '#' || buf[0] == '\n') {
fputs(buf, aesresp);
continue;
}
/* [ENCRYPT] or [DECRYPT] */
if (buf[0] == '[') {
if (strncmp(&buf[1], "ENCRYPT", 7) == 0) {
encrypt = 1;
} else {
encrypt = 0;
}
fputs(buf, aesresp);
continue;
}
/* "COUNT = x" begins a new data set */
if (strncmp(buf, "COUNT", 5) == 0) {
/* zeroize the variables for the test with this data set */
memset(key, 0, sizeof key);
keysize = 0;
memset(plaintext, 0, sizeof plaintext);
memset(ciphertext, 0, sizeof ciphertext);
continue;
}
/* KEY = ... */
if (strncmp(buf, "KEY", 3) == 0) {
/* Key[0] = Key */
i = 3;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &key[j]);
}
keysize = j;
continue;
}
/* PLAINTEXT = ... */
if (strncmp(buf, "PLAINTEXT", 9) == 0) {
/* sanity check */
if (!encrypt) {
goto loser;
}
/* PT[0] = PT */
i = 9;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; j < sizeof plaintext; i += 2, j++) {
hex_to_byteval(&buf[i], &plaintext[j]);
}
for (i = 0; i < 100; i++) {
snprintf(buf, sizeof(buf), "COUNT = %d\n", i);
fputs(buf, aesresp);
/* Output Key[i] */
fputs("KEY = ", aesresp);
to_hex_str(buf, key, keysize);
fputs(buf, aesresp);
fputc('\n', aesresp);
/* Output PT[0] */
fputs("PLAINTEXT = ", aesresp);
to_hex_str(buf, plaintext, sizeof plaintext);
fputs(buf, aesresp);
fputc('\n', aesresp);
cx = AES_CreateContext(key, NULL, NSS_AES,
PR_TRUE, keysize, 16);
if (cx == NULL) {
goto loser;
}
/*
* doublecheck our result by decrypting the result
* and comparing the output with the plaintext.
*/
cx2 = AES_CreateContext(key, NULL, NSS_AES,
PR_FALSE, keysize, 16);
if (cx2 == NULL) {
goto loser;
}
for (j = 0; j < 1000; j++) {
/* Save CT[j-1] */
memcpy(ciphertext_1, ciphertext, sizeof ciphertext);
/* CT[j] = AES(Key[i], PT[j]) */
outputlen = 0;
rv = AES_Encrypt(cx,
ciphertext, &outputlen, sizeof ciphertext,
plaintext, sizeof plaintext);
if (rv != SECSuccess) {
goto loser;
}
if (outputlen != sizeof plaintext) {
goto loser;
}
/* doublecheck our result */
outputlen = 0;
rv = AES_Decrypt(cx2,
doublecheck, &outputlen, sizeof doublecheck,
ciphertext, sizeof ciphertext);
if (rv != SECSuccess) {
goto loser;
}
if (outputlen != sizeof ciphertext) {
goto loser;
}
if (memcmp(doublecheck, plaintext, sizeof plaintext)) {
goto loser;
}
/* PT[j+1] = CT[j] */
memcpy(plaintext, ciphertext, sizeof plaintext);
}
AES_DestroyContext(cx, PR_TRUE);
cx = NULL;
AES_DestroyContext(cx2, PR_TRUE);
cx2 = NULL;
/* Output CT[j] */
fputs("CIPHERTEXT = ", aesresp);
to_hex_str(buf, ciphertext, sizeof ciphertext);
fputs(buf, aesresp);
fputc('\n', aesresp);
/* Key[i+1] = Key[i] xor ... */
aes_mct_next_key(key, keysize, ciphertext_1, ciphertext);
/* PT[0] = CT[j] */
/* done at the end of the for(j) loop */
fputc('\n', aesresp);
}
continue;
}
/* CIPHERTEXT = ... */
if (strncmp(buf, "CIPHERTEXT", 10) == 0) {
/* sanity check */
if (encrypt) {
goto loser;
}
/* CT[0] = CT */
i = 10;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &ciphertext[j]);
}
for (i = 0; i < 100; i++) {
snprintf(buf, sizeof(buf), "COUNT = %d\n", i);
fputs(buf, aesresp);
/* Output Key[i] */
fputs("KEY = ", aesresp);
to_hex_str(buf, key, keysize);
fputs(buf, aesresp);
fputc('\n', aesresp);
/* Output CT[0] */
fputs("CIPHERTEXT = ", aesresp);
to_hex_str(buf, ciphertext, sizeof ciphertext);
fputs(buf, aesresp);
fputc('\n', aesresp);
cx = AES_CreateContext(key, NULL, NSS_AES,
PR_FALSE, keysize, 16);
if (cx == NULL) {
goto loser;
}
/*
* doublecheck our result by encrypting the result
* and comparing the output with the ciphertext.
*/
cx2 = AES_CreateContext(key, NULL, NSS_AES,
PR_TRUE, keysize, 16);
if (cx2 == NULL) {
goto loser;
}
for (j = 0; j < 1000; j++) {
/* Save PT[j-1] */
memcpy(plaintext_1, plaintext, sizeof plaintext);
/* PT[j] = AES(Key[i], CT[j]) */
outputlen = 0;
rv = AES_Decrypt(cx,
plaintext, &outputlen, sizeof plaintext,
ciphertext, sizeof ciphertext);
if (rv != SECSuccess) {
goto loser;
}
if (outputlen != sizeof ciphertext) {
goto loser;
}
/* doublecheck our result */
outputlen = 0;
rv = AES_Encrypt(cx2,
doublecheck, &outputlen, sizeof doublecheck,
plaintext, sizeof plaintext);
if (rv != SECSuccess) {
goto loser;
}
if (outputlen != sizeof plaintext) {
goto loser;
}
if (memcmp(doublecheck, ciphertext, sizeof ciphertext)) {
goto loser;
}
/* CT[j+1] = PT[j] */
memcpy(ciphertext, plaintext, sizeof ciphertext);
}
AES_DestroyContext(cx, PR_TRUE);
cx = NULL;
AES_DestroyContext(cx2, PR_TRUE);
cx2 = NULL;
/* Output PT[j] */
fputs("PLAINTEXT = ", aesresp);
to_hex_str(buf, plaintext, sizeof plaintext);
fputs(buf, aesresp);
fputc('\n', aesresp);
/* Key[i+1] = Key[i] xor ... */
aes_mct_next_key(key, keysize, plaintext_1, plaintext);
/* CT[0] = PT[j] */
/* done at the end of the for(j) loop */
fputc('\n', aesresp);
}
continue;
}
}
loser:
if (cx != NULL) {
AES_DestroyContext(cx, PR_TRUE);
}
if (cx2 != NULL) {
AES_DestroyContext(cx2, PR_TRUE);
}
fclose(aesreq);
}
/*
* Perform the AES Monte Carlo Test (MCT) in CBC mode. MCT exercises
* our AES code in streaming mode because the plaintext or ciphertext
* is generated block by block as we go, so we can't collect all the
* plaintext or ciphertext in one buffer and encrypt or decrypt it in
* one shot.
*
* reqfn is the pathname of the input REQUEST file.
*
* The output RESPONSE file is written to stdout.
*/
void
aes_cbc_mct(char *reqfn)
{
char buf[80]; /* holds one line from the input REQUEST file.
* needs to be large enough to hold the longest
* line "KEY = <64 hex digits>\n".
*/
FILE *aesreq; /* input stream from the REQUEST file */
FILE *aesresp; /* output stream to the RESPONSE file */
int i, j;
int encrypt = 0; /* 1 means encrypt, 0 means decrypt */
unsigned char key[32]; /* 128, 192, or 256 bits */
unsigned int keysize = 0;
unsigned char iv[16];
unsigned char plaintext[16]; /* PT[j] */
unsigned char plaintext_1[16]; /* PT[j-1] */
unsigned char ciphertext[16]; /* CT[j] */
unsigned char ciphertext_1[16]; /* CT[j-1] */
unsigned char doublecheck[16];
unsigned int outputlen;
AESContext *cx = NULL; /* the operation being tested */
AESContext *cx2 = NULL; /* the inverse operation done in parallel
* to doublecheck our result.
*/
SECStatus rv;
aesreq = fopen(reqfn, "r");
aesresp = stdout;
while (fgets(buf, sizeof buf, aesreq) != NULL) {
/* a comment or blank line */
if (buf[0] == '#' || buf[0] == '\n') {
fputs(buf, aesresp);
continue;
}
/* [ENCRYPT] or [DECRYPT] */
if (buf[0] == '[') {
if (strncmp(&buf[1], "ENCRYPT", 7) == 0) {
encrypt = 1;
} else {
encrypt = 0;
}
fputs(buf, aesresp);
continue;
}
/* "COUNT = x" begins a new data set */
if (strncmp(buf, "COUNT", 5) == 0) {
/* zeroize the variables for the test with this data set */
memset(key, 0, sizeof key);
keysize = 0;
memset(iv, 0, sizeof iv);
memset(plaintext, 0, sizeof plaintext);
memset(ciphertext, 0, sizeof ciphertext);
continue;
}
/* KEY = ... */
if (strncmp(buf, "KEY", 3) == 0) {
/* Key[0] = Key */
i = 3;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &key[j]);
}
keysize = j;
continue;
}
/* IV = ... */
if (strncmp(buf, "IV", 2) == 0) {
/* IV[0] = IV */
i = 2;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; j < sizeof iv; i += 2, j++) {
hex_to_byteval(&buf[i], &iv[j]);
}
continue;
}
/* PLAINTEXT = ... */
if (strncmp(buf, "PLAINTEXT", 9) == 0) {
/* sanity check */
if (!encrypt) {
goto loser;
}
/* PT[0] = PT */
i = 9;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; j < sizeof plaintext; i += 2, j++) {
hex_to_byteval(&buf[i], &plaintext[j]);
}
for (i = 0; i < 100; i++) {
snprintf(buf, sizeof(buf), "COUNT = %d\n", i);
fputs(buf, aesresp);
/* Output Key[i] */
fputs("KEY = ", aesresp);
to_hex_str(buf, key, keysize);
fputs(buf, aesresp);
fputc('\n', aesresp);
/* Output IV[i] */
fputs("IV = ", aesresp);
to_hex_str(buf, iv, sizeof iv);
fputs(buf, aesresp);
fputc('\n', aesresp);
/* Output PT[0] */
fputs("PLAINTEXT = ", aesresp);
to_hex_str(buf, plaintext, sizeof plaintext);
fputs(buf, aesresp);
fputc('\n', aesresp);
cx = AES_CreateContext(key, iv, NSS_AES_CBC,
PR_TRUE, keysize, 16);
if (cx == NULL) {
goto loser;
}
/*
* doublecheck our result by decrypting the result
* and comparing the output with the plaintext.
*/
cx2 = AES_CreateContext(key, iv, NSS_AES_CBC,
PR_FALSE, keysize, 16);
if (cx2 == NULL) {
goto loser;
}
/* CT[-1] = IV[i] */
memcpy(ciphertext, iv, sizeof ciphertext);
for (j = 0; j < 1000; j++) {
/* Save CT[j-1] */
memcpy(ciphertext_1, ciphertext, sizeof ciphertext);
/*
* If ( j=0 )
* CT[j] = AES(Key[i], IV[i], PT[j])
* PT[j+1] = IV[i] (= CT[j-1])
* Else
* CT[j] = AES(Key[i], PT[j])
* PT[j+1] = CT[j-1]
*/
outputlen = 0;
rv = AES_Encrypt(cx,
ciphertext, &outputlen, sizeof ciphertext,
plaintext, sizeof plaintext);
if (rv != SECSuccess) {
goto loser;
}
if (outputlen != sizeof plaintext) {
goto loser;
}
/* doublecheck our result */
outputlen = 0;
rv = AES_Decrypt(cx2,
doublecheck, &outputlen, sizeof doublecheck,
ciphertext, sizeof ciphertext);
if (rv != SECSuccess) {
goto loser;
}
if (outputlen != sizeof ciphertext) {
goto loser;
}
if (memcmp(doublecheck, plaintext, sizeof plaintext)) {
goto loser;
}
memcpy(plaintext, ciphertext_1, sizeof plaintext);
}
AES_DestroyContext(cx, PR_TRUE);
cx = NULL;
AES_DestroyContext(cx2, PR_TRUE);
cx2 = NULL;
/* Output CT[j] */
fputs("CIPHERTEXT = ", aesresp);
to_hex_str(buf, ciphertext, sizeof ciphertext);
fputs(buf, aesresp);
fputc('\n', aesresp);
/* Key[i+1] = Key[i] xor ... */
aes_mct_next_key(key, keysize, ciphertext_1, ciphertext);
/* IV[i+1] = CT[j] */
memcpy(iv, ciphertext, sizeof iv);
/* PT[0] = CT[j-1] */
/* done at the end of the for(j) loop */
fputc('\n', aesresp);
}
continue;
}
/* CIPHERTEXT = ... */
if (strncmp(buf, "CIPHERTEXT", 10) == 0) {
/* sanity check */
if (encrypt) {
goto loser;
}
/* CT[0] = CT */
i = 10;
while (isspace(buf[i]) || buf[i] == '=') {
i++;
}
for (j = 0; isxdigit(buf[i]); i += 2, j++) {
hex_to_byteval(&buf[i], &ciphertext[j]);
}
for (i = 0; i < 100; i++) {
snprintf(buf, sizeof(buf), "COUNT = %d\n", i);
fputs(buf, aesresp);
/* Output Key[i] */
fputs("KEY = ", aesresp);
to_hex_str(buf, key, keysize);
fputs(buf, aesresp);
fputc('\n', aesresp);
/* Output IV[i] */
fputs("IV = ", aesresp);
to_hex_str(buf, iv, sizeof iv);
fputs(buf, aesresp);
fputc('\n', aesresp);
/* Output CT[0] */
fputs("CIPHERTEXT = ", aesresp);
to_hex_str(buf, ciphertext, sizeof ciphertext);
fputs(buf, aesresp);
fputc('\n', aesresp);
cx = AES_CreateContext(key, iv, NSS_AES_CBC,
PR_FALSE, keysize, 16);
if (cx == NULL) {
goto loser;
}
/*
* doublecheck our result by encrypting the result
* and comparing the output with the ciphertext.
*/
cx2 = AES_CreateContext(key, iv, NSS_AES_CBC,
PR_TRUE, keysize, 16);
if (cx2 == NULL) {
goto loser;
}
/* PT[-1] = IV[i] */
memcpy(plaintext, iv, sizeof plaintext);
for (j = 0; j < 1000; j++) {
/* Save PT[j-1] */
memcpy(plaintext_1, plaintext, sizeof plaintext);
/*
* If ( j=0 )
* PT[j] = AES(Key[i], IV[i], CT[j])
* CT[j+1] = IV[i] (= PT[j-1])
* Else
* PT[j] = AES(Key[i], CT[j])
* CT[j+1] = PT[j-1]
*/
outputlen = 0;
rv = AES_Decrypt(cx,
plaintext, &outputlen, sizeof plaintext,
ciphertext, sizeof ciphertext);
if (rv != SECSuccess) {
goto loser;
}
if (outputlen != sizeof ciphertext) {
goto loser;
}
/* doublecheck our result */
outputlen = 0;
rv = AES_Encrypt(cx2,
doublecheck, &outputlen, sizeof doublecheck,
plaintext, sizeof plaintext);
if (rv != SECSuccess) {
goto loser;
}
if (outputlen != sizeof plaintext) {
goto loser;
}
if (memcmp(doublecheck, ciphertext, sizeof ciphertext)) {
goto loser;
}
memcpy(ciphertext, plaintext_1, sizeof ciphertext);
}
AES_DestroyContext(cx, PR_TRUE);
cx = NULL;
AES_DestroyContext(cx2, PR_TRUE);
cx2 = NULL;
/* Output PT[j] */
fputs("PLAINTEXT = ", aesresp);
to_hex_str(buf, plaintext, sizeof plaintext);
fputs(buf, aesresp);
fputc('\n', aesresp);
/* Key[i+1] = Key[i] xor ... */
aes_mct_next_key(key, keysize, plaintext_1, plaintext);
/* IV[i+1] = PT[j] */
memcpy(iv, plaintext, sizeof iv);
/* CT[0] = PT[j-1] */
/* done at the end of the for(j) loop */
fputc('\n', aesresp);
}
continue;
}
}
loser:
if (cx != NULL) {
AES_DestroyContext(cx, PR_TRUE);
}
if (cx2 != NULL) {
AES_DestroyContext(cx2, PR_TRUE);
}
fclose(aesreq);
}
void
write_compact_string(FILE *out, unsigned char *hash, unsigned int len)
{
unsigned int i;
int j, count = 0, last = -1, z = 0;
long start = ftell(out);
for (i = 0; i < len; i++) {
for (j = 7; j >= 0; j--) {
if (last < 0) {
last = (hash[i] & (1 << j)) ? 1 : 0;
fprintf(out, "%d ", last);
count = 1;
} else if (hash[i] & (1 << j)) {
if (last) {
count++;
} else {
last = 0;
fprintf(out, "%d ", count);
count = 1;
z++;
}
} else {
if (!last) {
count++;
} else {
last = 1;
fprintf(out, "%d ", count);
count = 1;
z++;
}
}
}
}
fprintf(out, "^\n");
fseek(out, start, SEEK_SET);
fprintf(out, "%d ", z);
fseek(out, 0, SEEK_END);
}
int
get_next_line(FILE *req, char *key, char *val, FILE *rsp)
{
int ignore = 0;
char *writeto = key;
int w = 0;
int c;
while ((c = fgetc(req)) != EOF) {
if (ignore) {
fprintf(rsp, "%c", c);
if (c == '\n')
return ignore;
} else if (c == '\n') {
break;
} else if (c == '#') {
ignore = 1;
fprintf(rsp, "%c", c);
} else if (c == '=') {
writeto[w] = '\0';
w = 0;
writeto = val;
} else if (c == ' ' || c == '[' || c == ']') {
continue;
} else {
writeto[w++] = c;
}
}
writeto[w] = '\0';
return (c == EOF) ? -1 : ignore;
}
typedef struct curveNameTagPairStr {
char *curveName;
SECOidTag curveOidTag;
} CurveNameTagPair;
#define DEFAULT_CURVE_OID_TAG SEC_OID_SECG_EC_SECP192R1