| /* 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/. */ |
| |
| #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 |
| |
| 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 */ |
| sprintf(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_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++) { |
| sprintf(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++) { |
| sprintf(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++) { |
| sprintf(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++) { |
| sprintf(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 |
| /* #define DEFAULT_CURVE_OID_TAG SEC_OID_SECG_EC_SECP160R1 */ |
| |
| static CurveNameTagPair nameTagPair[] = |
| { |
| { "sect163k1", SEC_OID_SECG_EC_SECT163K1 }, |
| { "nistk163", SEC_OID_SECG_EC_SECT163K1 }, |
| { "sect163r1", SEC_OID_SECG_EC_SECT163R1 }, |
| { "sect163r2", SEC_OID_SECG_EC_SECT163R2 }, |
| { "nistb163", SEC_OID_SECG_EC_SECT163R2 }, |
| { "sect193r1", SEC_OID_SECG_EC_SECT193R1 }, |
| { "sect193r2", SEC_OID_SECG_EC_SECT193R2 }, |
| { "sect233k1", SEC_OID_SECG_EC_SECT233K1 }, |
| { "nistk233", SEC_OID_SECG_EC_SECT233K1 }, |
| { "sect233r1", SEC_OID_SECG_EC_SECT233R1 }, |
| { "nistb233", SEC_OID_SECG_EC_SECT233R1 }, |
| { "sect239k1", SEC_OID_SECG_EC_SECT239K1 }, |
| { "sect283k1", SEC_OID_SECG_EC_SECT283K1 }, |
| { "nistk283", SEC_OID_SECG_EC_SECT283K1 }, |
| { "sect283r1", SEC_OID_SECG_EC_SECT283R1 }, |
| { "nistb283", SEC_OID_SECG_EC_SECT283R1 }, |
| { "sect409k1", SEC_OID_SECG_EC_SECT409K1 }, |
| { "nistk409", SEC_OID_SECG_EC_SECT409K1 }, |
| { "sect409r1", SEC_OID_SECG_EC_SECT409R1 }, |
| { "nistb409", SEC_OID_SECG_EC_SECT409R1 }, |
| { "sect571k1", SEC_OID_SECG_EC_SECT571K1 }, |
| { "nistk571", SEC_OID_SECG_EC_SECT571K1 }, |
| { "sect571r1", SEC_OID_SECG_EC_SECT571R1 }, |
| { "nistb571", SEC_OID_SECG_EC_SECT571R1 }, |
| { "secp160k1", SEC_OID_SECG_EC_SECP160K1 }, |
| { "secp160r1", SEC_OID_SECG_EC_SECP160R1 }, |
| { "secp160r2", SEC_OID_SECG_EC_SECP160R2 }, |
| { "secp192k1", SEC_OID_SECG_EC_SECP192K1 }, |
| { "secp192r1", SEC_OID_SECG_EC_SECP192R1 }, |
| { "nistp192", SEC_OID_SECG_EC_SECP192R1 }, |
| { "secp224k1", SEC_OID_SECG_EC_SECP224K1 }, |
| { "secp224r1", SEC_OID_SECG_EC_SECP224R1 }, |
| { "nistp224", SEC_OID_SECG_EC_SECP224R1 }, |
| { "secp256k1", SEC_OID_SECG_EC_SECP256K1 }, |
| { "secp256r1", SEC_OID_SECG_EC_SECP256R1 }, |
| { "nistp256", SEC_OID_SECG_EC_SECP256R1 }, |
| { "secp384r1", SEC_OID_SECG_EC_SECP384R1 }, |
| { "nistp384", SEC_OID_SECG_EC_SECP384R1 }, |
| { "secp521r1", SEC_OID_SECG_EC_SECP521R1 }, |
| { "nistp521", SEC_OID_SECG_EC_SECP521R1 }, |
| |
| { "prime192v1", SEC_OID_ANSIX962_EC_PRIME192V1 }, |
| { "prime192v2", SEC_OID_ANSIX962_EC_PRIME192V2 }, |
| { "prime192v3", SEC_OID_ANSIX962_EC_PRIME192V3 }, |
| { "prime239v1", SEC_OID_ANSIX962_EC_PRIME239V1 }, |
| { "prime239v2", SEC_OID_ANSIX962_EC_PRIME239V2 }, |
| { "prime239v3", SEC_OID_ANSIX962_EC_PRIME239V3 }, |
| |
| { "c2pnb163v1", SEC_OID_ANSIX962_EC_C2PNB163V1 }, |
| { "c2pnb163v2", SEC_OID_ANSIX962_EC_C2PNB163V2 }, |
| { "c2pnb163v3", SEC_OID_ANSIX962_EC_C2PNB163V3 }, |
| { "c2pnb176v1", SEC_OID_ANSIX962_EC_C2PNB176V1 }, |
| { "c2tnb191v1", SEC_OID_ANSIX962_EC_C2TNB191V1 }, |
| { "c2tnb191v2", SEC_OID_ANSIX962_EC_C2TNB191V2 }, |
| { "c2tnb191v3", SEC_OID_ANSIX962_EC_C2TNB191V3 }, |
| { "c2onb191v4", SEC_OID_ANSIX962_EC_C2ONB191V4 }, |
| { "c2onb191v5", SEC_OID_ANSIX962_EC_C2ONB191V5 }, |
| { "c2pnb208w1", SEC_OID_ANSIX962_EC_C2PNB208W1 }, |
| { "c2tnb239v1", SEC_OID_ANSIX962_EC_C2TNB239V1 }, |
| { "c2tnb239v2", SEC_OID_ANSIX962_EC_C2TNB239V2 }, |
| { "c2tnb239v3", SEC_OID_ANSIX962_EC_C2TNB239V3 }, |
| { "c2onb239v4", SEC_OID_ANSIX962_EC_C2ONB239V4 }, |
| { "c2onb239v5", SEC_OID_ANSIX962_EC_C2ONB239V5 }, |
| { "c2pnb272w1", SEC_OID_ANSIX962_EC_C2PNB272W1 }, |
| { "c2pnb304w1", SEC_OID_ANSIX962_EC_C2PNB304W1 }, |
| { "c2tnb359v1", SEC_OID_ANSIX962_EC_C2TNB359V1 }, |
| { "c2pnb368w1", SEC_OID_ANSIX962_EC_C2PNB368W1 }, |
| { "c2tnb431r1", SEC_OID_ANSIX962_EC_C2TNB431R1 }, |
| |
| { "secp112r1", SEC_OID_SECG_EC_SECP112R1 }, |
| { "secp112r2", SEC_OID_SECG_EC_SECP112R2 }, |
| { "secp128r1", SEC_OID_SECG_EC_SECP128R1 }, |
| { "secp128r2", SEC_OID_SECG_EC_SECP128R2 }, |
| |
| { "sect113r1", SEC_OID_SECG_EC_SECT113R1 }, |
| { "sect113r2", SEC_OID_SECG_EC_SECT113R2 }, |
| { "sect131r1", SEC_OID_SECG_EC_SECT131R1 }, |
| { "sect131r2", SEC_OID_SECG_EC_SECT131R2 }, |
| }; |
| |
| static SECItem * |
| getECParams(const char *curve) |
| { |
| SECItem *ecparams; |
| SECOidData *oidData = NULL; |
| SECOidTag curveOidTag = SEC_OID_UNKNOWN; /* default */ |
| int i, numCurves; |
| |
| if (curve != NULL) { |
| numCurves = sizeof(nameTagPair) / sizeof(CurveNameTagPair); |
| for (i = 0; ((i < numCurves) && (curveOidTag == SEC_OID_UNKNOWN)); |
| i++) { |
| if (PL_strcmp(curve, nameTagPair[i].curveName) == 0) |
| curveOidTag = nameTagPair[i].curveOidTag; |
| } |
| } |
| |
| /* Return NULL if curve name is not recognized */ |
| if ((curveOidTag == SEC_OID_UNKNOWN) || |
| (oidData = SECOID_FindOIDByTag(curveOidTag)) == NULL) { |
| fprintf(stderr, "Unrecognized elliptic curve %s\n", curve); |
| return NULL; |
| } |
| |
| ecparams = SECITEM_AllocItem(NULL, NULL, (2 + oidData->oid.len)); |
| |
| /* |
| * ecparams->data needs to contain the ASN encoding of an object ID (OID) |
| * representing the named curve. The actual OID is in |
| * oidData->oid.data so we simply prepend 0x06 and OID length |
| */ |
| ecparams->data[0] = SEC_ASN1_OBJECT_ID; |
| ecparams->data[1] = oidData->oid.len; |
| memcpy(ecparams->data + 2, oidData->oid.data, oidData->oid.len); |
| |
| return ecparams; |
| } |
| |
| /* |
| * HASH_ functions are available to full NSS apps and internally inside |
| * freebl, but not exported to users of freebl. Create short stubs to |
| * replace the functionality for fipstest. |
| */ |
| SECStatus |
| fips_hashBuf(HASH_HashType type, unsigned char *hashBuf, |
| unsigned char *msg, int len) |
| { |
| SECStatus rv = SECFailure; |
| |
| switch (type) { |
| case HASH_AlgSHA1: |
| rv = SHA1_HashBuf(hashBuf, msg, len); |
| break; |
| case HASH_AlgSHA224: |
| rv = SHA224_HashBuf(hashBuf, msg, len); |
| break; |
| case HASH_AlgSHA256: |
| rv = SHA256_HashBuf(hashBuf, msg, len); |
| break; |
| case HASH_AlgSHA384: |
| rv = SHA384_HashBuf(hashBuf, msg, len); |
| break; |
| case HASH_AlgSHA512: |
| rv = SHA512_HashBuf(hashBuf, msg, len); |
| break; |
| default: |
| break; |
| } |
| return rv; |
| } |
| |
| int |
| fips_hashLen(HASH_HashType type) |
| { |
| int len = 0; |
| |
| switch (type) { |
| case HASH_AlgSHA1: |
| len = SHA1_LENGTH; |
| break; |
| case HASH_AlgSHA224: |
| len = SHA224_LENGTH; |
| break; |
| case HASH_AlgSHA256: |
| len = SHA256_LENGTH; |
| break; |
| case HASH_AlgSHA384: |
| len = SHA384_LENGTH; |
| break; |
| case HASH_AlgSHA512: |
| len = SHA512_LENGTH; |
| break; |
| default: |
| break; |
| } |
| return len; |
| } |
| |
| SECOidTag |
| fips_hashOid(HASH_HashType type) |
| { |
| SECOidTag oid = SEC_OID_UNKNOWN; |
| |
| switch (type) { |
| case HASH_AlgSHA1: |
| oid = SEC_OID_SHA1; |
| break; |
| case HASH_AlgSHA224: |
| oid = SEC_OID_SHA224; |
| break; |
| case HASH_AlgSHA256: |
| oid = SEC_OID_SHA256; |
| break; |
| case HASH_AlgSHA384: |
| oid = SEC_OID_SHA384; |
| break; |
| case HASH_AlgSHA512: |
| oid = SEC_OID_SHA512; |
| break; |
| default: |
| break; |
| } |
| return oid; |
| } |
| |
| HASH_HashType |
| sha_get_hashType(int hashbits) |
| { |
| HASH_HashType hashType = HASH_AlgNULL; |
| |
| switch (hashbits) { |
| case 1: |
| case (SHA1_LENGTH * PR_BITS_PER_BYTE): |
| hashType = HASH_AlgSHA1; |
| break; |
| case (SHA224_LENGTH * PR_BITS_PER_BYTE): |
| hashType = HASH_AlgSHA224; |
| break; |
| case (SHA256_LENGTH * PR_BITS_PER_BYTE): |
| hashType = HASH_AlgSHA256; |
| break; |
| case (SHA384_LENGTH * PR_BITS_PER_BYTE): |
| hashType = HASH_AlgSHA384; |
| break; |
| case (SHA512_LENGTH * PR_BITS_PER_BYTE): |
| hashType = HASH_AlgSHA512; |
| break; |
| default: |
| break; |
| } |
| return hashType; |
| } |
| |
| HASH_HashType |
| hash_string_to_hashType(const char *src) |
| { |
| HASH_HashType shaAlg = HASH_AlgNULL; |
| if (strncmp(src, "SHA-1", 5) == 0) { |
| shaAlg = HASH_AlgSHA1; |
| } else if (strncmp(src, "SHA-224", 7) == 0) { |
| shaAlg = HASH_AlgSHA224; |
| } else if (strncmp(src, "SHA-256", 7) == 0) { |
| shaAlg = HASH_AlgSHA256; |
| } else if (strncmp(src, "SHA-384", 7) == 0) { |
| shaAlg = HASH_AlgSHA384; |
| } else if (strncmp(src, "SHA-512", 7) == 0) { |
| shaAlg = HASH_AlgSHA512; |
| } else if (strncmp(src, "SHA1", 4) == 0) { |
| shaAlg = HASH_AlgSHA1; |
| } else if (strncmp(src, "SHA224", 6) == 0) { |
| shaAlg = HASH_AlgSHA224; |
| } else if (strncmp(src, "SHA256", 6) == 0) { |
| shaAlg = HASH_AlgSHA256; |
| } else if (strncmp(src, "SHA384", 6) == 0) { |
| shaAlg = HASH_AlgSHA384; |
| } else if (strncmp(src, "SHA512", 6) == 0) { |
| shaAlg = HASH_AlgSHA512; |
| } |
| return shaAlg; |
| } |
| |
| /* |
| * Perform the ECDSA Key Pair Generation Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| ecdsa_keypair_test(char *reqfn) |
| { |
| char buf[256]; /* holds one line from the input REQUEST file |
| * or to the output RESPONSE file. |
| * needs to be large enough to hold the longest |
| * line "Qx = <144 hex digits>\n". |
| */ |
| FILE *ecdsareq; /* input stream from the REQUEST file */ |
| FILE *ecdsaresp; /* output stream to the RESPONSE file */ |
| char curve[16]; /* "nistxddd" */ |
| ECParams *ecparams = NULL; |
| int N; |
| int i; |
| unsigned int len; |
| |
| ecdsareq = fopen(reqfn, "r"); |
| ecdsaresp = stdout; |
| strcpy(curve, "nist"); |
| while (fgets(buf, sizeof buf, ecdsareq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, ecdsaresp); |
| continue; |
| } |
| /* [X-ddd] */ |
| if (buf[0] == '[') { |
| const char *src; |
| char *dst; |
| SECItem *encodedparams; |
| |
| if (buf[1] == 'B') { |
| fputs(buf, ecdsaresp); |
| continue; |
| } |
| if (ecparams) { |
| PORT_FreeArena(ecparams->arena, PR_FALSE); |
| ecparams = NULL; |
| } |
| |
| src = &buf[1]; |
| dst = &curve[4]; |
| *dst++ = tolower(*src); |
| src += 2; /* skip the hyphen */ |
| *dst++ = *src++; |
| *dst++ = *src++; |
| *dst++ = *src++; |
| *dst = '\0'; |
| encodedparams = getECParams(curve); |
| if (encodedparams == NULL) { |
| fprintf(stderr, "Unknown curve %s.", curve); |
| goto loser; |
| } |
| if (EC_DecodeParams(encodedparams, &ecparams) != SECSuccess) { |
| fprintf(stderr, "Curve %s not supported.\n", curve); |
| goto loser; |
| } |
| SECITEM_FreeItem(encodedparams, PR_TRUE); |
| fputs(buf, ecdsaresp); |
| continue; |
| } |
| /* N = x */ |
| if (buf[0] == 'N') { |
| if (sscanf(buf, "N = %d", &N) != 1) { |
| goto loser; |
| } |
| for (i = 0; i < N; i++) { |
| ECPrivateKey *ecpriv; |
| |
| if (EC_NewKey(ecparams, &ecpriv) != SECSuccess) { |
| goto loser; |
| } |
| fputs("d = ", ecdsaresp); |
| to_hex_str(buf, ecpriv->privateValue.data, |
| ecpriv->privateValue.len); |
| fputs(buf, ecdsaresp); |
| fputc('\n', ecdsaresp); |
| if (EC_ValidatePublicKey(ecparams, &ecpriv->publicValue) != |
| SECSuccess) { |
| goto loser; |
| } |
| len = ecpriv->publicValue.len; |
| if (len % 2 == 0) { |
| goto loser; |
| } |
| len = (len - 1) / 2; |
| if (ecpriv->publicValue.data[0] != |
| EC_POINT_FORM_UNCOMPRESSED) { |
| goto loser; |
| } |
| fputs("Qx = ", ecdsaresp); |
| to_hex_str(buf, &ecpriv->publicValue.data[1], len); |
| fputs(buf, ecdsaresp); |
| fputc('\n', ecdsaresp); |
| fputs("Qy = ", ecdsaresp); |
| to_hex_str(buf, &ecpriv->publicValue.data[1 + len], len); |
| fputs(buf, ecdsaresp); |
| fputc('\n', ecdsaresp); |
| fputc('\n', ecdsaresp); |
| PORT_FreeArena(ecpriv->ecParams.arena, PR_TRUE); |
| } |
| continue; |
| } |
| } |
| loser: |
| if (ecparams) { |
| PORT_FreeArena(ecparams->arena, PR_FALSE); |
| ecparams = NULL; |
| } |
| fclose(ecdsareq); |
| } |
| |
| /* |
| * Perform the ECDSA Public Key Validation Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| ecdsa_pkv_test(char *reqfn) |
| { |
| char buf[256]; /* holds one line from the input REQUEST file. |
| * needs to be large enough to hold the longest |
| * line "Qx = <144 hex digits>\n". |
| */ |
| FILE *ecdsareq; /* input stream from the REQUEST file */ |
| FILE *ecdsaresp; /* output stream to the RESPONSE file */ |
| char curve[16]; /* "nistxddd" */ |
| ECParams *ecparams = NULL; |
| SECItem pubkey; |
| unsigned int i; |
| unsigned int len = 0; |
| PRBool keyvalid = PR_TRUE; |
| |
| ecdsareq = fopen(reqfn, "r"); |
| ecdsaresp = stdout; |
| strcpy(curve, "nist"); |
| pubkey.data = NULL; |
| while (fgets(buf, sizeof buf, ecdsareq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, ecdsaresp); |
| continue; |
| } |
| /* [X-ddd] */ |
| if (buf[0] == '[') { |
| const char *src; |
| char *dst; |
| SECItem *encodedparams; |
| |
| src = &buf[1]; |
| dst = &curve[4]; |
| *dst++ = tolower(*src); |
| src += 2; /* skip the hyphen */ |
| *dst++ = *src++; |
| *dst++ = *src++; |
| *dst++ = *src++; |
| *dst = '\0'; |
| if (ecparams != NULL) { |
| PORT_FreeArena(ecparams->arena, PR_FALSE); |
| ecparams = NULL; |
| } |
| encodedparams = getECParams(curve); |
| if (encodedparams == NULL) { |
| fprintf(stderr, "Unknown curve %s.", curve); |
| goto loser; |
| } |
| if (EC_DecodeParams(encodedparams, &ecparams) != SECSuccess) { |
| fprintf(stderr, "Curve %s not supported.\n", curve); |
| goto loser; |
| } |
| SECITEM_FreeItem(encodedparams, PR_TRUE); |
| len = (ecparams->fieldID.size + 7) >> 3; |
| if (pubkey.data != NULL) { |
| PORT_Free(pubkey.data); |
| pubkey.data = NULL; |
| } |
| SECITEM_AllocItem(NULL, &pubkey, EC_GetPointSize(ecparams)); |
| if (pubkey.data == NULL) { |
| goto loser; |
| } |
| pubkey.data[0] = EC_POINT_FORM_UNCOMPRESSED; |
| fputs(buf, ecdsaresp); |
| continue; |
| } |
| /* Qx = ... */ |
| if (strncmp(buf, "Qx", 2) == 0) { |
| fputs(buf, ecdsaresp); |
| i = 2; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| keyvalid = from_hex_str(&pubkey.data[1], len, &buf[i]); |
| continue; |
| } |
| /* Qy = ... */ |
| if (strncmp(buf, "Qy", 2) == 0) { |
| fputs(buf, ecdsaresp); |
| if (!keyvalid) { |
| fputs("Result = F\n", ecdsaresp); |
| continue; |
| } |
| i = 2; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| keyvalid = from_hex_str(&pubkey.data[1 + len], len, &buf[i]); |
| if (!keyvalid) { |
| fputs("Result = F\n", ecdsaresp); |
| continue; |
| } |
| if (EC_ValidatePublicKey(ecparams, &pubkey) == SECSuccess) { |
| fputs("Result = P\n", ecdsaresp); |
| } else if (PORT_GetError() == SEC_ERROR_BAD_KEY) { |
| fputs("Result = F\n", ecdsaresp); |
| } else { |
| goto loser; |
| } |
| continue; |
| } |
| } |
| loser: |
| if (ecparams != NULL) { |
| PORT_FreeArena(ecparams->arena, PR_FALSE); |
| } |
| if (pubkey.data != NULL) { |
| PORT_Free(pubkey.data); |
| } |
| fclose(ecdsareq); |
| } |
| |
| /* |
| * Perform the ECDSA Signature Generation Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| ecdsa_siggen_test(char *reqfn) |
| { |
| char buf[1024]; /* holds one line from the input REQUEST file |
| * or to the output RESPONSE file. |
| * needs to be large enough to hold the longest |
| * line "Msg = <256 hex digits>\n". |
| */ |
| FILE *ecdsareq; /* input stream from the REQUEST file */ |
| FILE *ecdsaresp; /* output stream to the RESPONSE file */ |
| char curve[16]; /* "nistxddd" */ |
| ECParams *ecparams = NULL; |
| int i, j; |
| unsigned int len; |
| unsigned char msg[512]; /* message to be signed (<= 128 bytes) */ |
| unsigned int msglen; |
| unsigned char sha[HASH_LENGTH_MAX]; /* SHA digest */ |
| unsigned int shaLength = 0; /* length of SHA */ |
| HASH_HashType shaAlg = HASH_AlgNULL; /* type of SHA Alg */ |
| unsigned char sig[2 * MAX_ECKEY_LEN]; |
| SECItem signature, digest; |
| |
| ecdsareq = fopen(reqfn, "r"); |
| ecdsaresp = stdout; |
| strcpy(curve, "nist"); |
| while (fgets(buf, sizeof buf, ecdsareq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, ecdsaresp); |
| continue; |
| } |
| /* [X-ddd] */ |
| if (buf[0] == '[') { |
| const char *src; |
| char *dst; |
| SECItem *encodedparams; |
| |
| src = &buf[1]; |
| dst = &curve[4]; |
| *dst++ = tolower(*src); |
| src += 2; /* skip the hyphen */ |
| *dst++ = *src++; |
| *dst++ = *src++; |
| *dst++ = *src++; |
| *dst = '\0'; |
| src++; /* skip the comma */ |
| /* set the SHA Algorithm */ |
| shaAlg = hash_string_to_hashType(src); |
| if (shaAlg == HASH_AlgNULL) { |
| fprintf(ecdsaresp, "ERROR: Unable to find SHAAlg type"); |
| goto loser; |
| } |
| if (ecparams != NULL) { |
| PORT_FreeArena(ecparams->arena, PR_FALSE); |
| ecparams = NULL; |
| } |
| encodedparams = getECParams(curve); |
| if (encodedparams == NULL) { |
| fprintf(stderr, "Unknown curve %s.", curve); |
| goto loser; |
| } |
| if (EC_DecodeParams(encodedparams, &ecparams) != SECSuccess) { |
| fprintf(stderr, "Curve %s not supported.\n", curve); |
| goto loser; |
| } |
| SECITEM_FreeItem(encodedparams, PR_TRUE); |
| fputs(buf, ecdsaresp); |
| continue; |
| } |
| /* Msg = ... */ |
| if (strncmp(buf, "Msg", 3) == 0) { |
| ECPrivateKey *ecpriv; |
| |
| i = 3; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; isxdigit(buf[i]); i += 2, j++) { |
| hex_to_byteval(&buf[i], &msg[j]); |
| } |
| msglen = j; |
| shaLength = fips_hashLen(shaAlg); |
| if (fips_hashBuf(shaAlg, sha, msg, msglen) != SECSuccess) { |
| if (shaLength == 0) { |
| fprintf(ecdsaresp, "ERROR: SHAAlg not defined."); |
| } |
| fprintf(ecdsaresp, "ERROR: Unable to generate SHA%x", |
| shaLength == 160 ? 1 : shaLength); |
| goto loser; |
| } |
| fputs(buf, ecdsaresp); |
| |
| if (EC_NewKey(ecparams, &ecpriv) != SECSuccess) { |
| goto loser; |
| } |
| if (EC_ValidatePublicKey(ecparams, &ecpriv->publicValue) != |
| SECSuccess) { |
| goto loser; |
| } |
| len = ecpriv->publicValue.len; |
| if (len % 2 == 0) { |
| goto loser; |
| } |
| len = (len - 1) / 2; |
| if (ecpriv->publicValue.data[0] != EC_POINT_FORM_UNCOMPRESSED) { |
| goto loser; |
| } |
| fputs("Qx = ", ecdsaresp); |
| to_hex_str(buf, &ecpriv->publicValue.data[1], len); |
| fputs(buf, ecdsaresp); |
| fputc('\n', ecdsaresp); |
| fputs("Qy = ", ecdsaresp); |
| to_hex_str(buf, &ecpriv->publicValue.data[1 + len], len); |
| fputs(buf, ecdsaresp); |
| fputc('\n', ecdsaresp); |
| |
| digest.type = siBuffer; |
| digest.data = sha; |
| digest.len = shaLength; |
| signature.type = siBuffer; |
| signature.data = sig; |
| signature.len = sizeof sig; |
| if (ECDSA_SignDigest(ecpriv, &signature, &digest) != SECSuccess) { |
| goto loser; |
| } |
| len = signature.len; |
| if (len % 2 != 0) { |
| goto loser; |
| } |
| len = len / 2; |
| fputs("R = ", ecdsaresp); |
| to_hex_str(buf, &signature.data[0], len); |
| fputs(buf, ecdsaresp); |
| fputc('\n', ecdsaresp); |
| fputs("S = ", ecdsaresp); |
| to_hex_str(buf, &signature.data[len], len); |
| fputs(buf, ecdsaresp); |
| fputc('\n', ecdsaresp); |
| |
| PORT_FreeArena(ecpriv->ecParams.arena, PR_TRUE); |
| continue; |
| } |
| } |
| loser: |
| if (ecparams != NULL) { |
| PORT_FreeArena(ecparams->arena, PR_FALSE); |
| } |
| fclose(ecdsareq); |
| } |
| |
| /* |
| * Perform the ECDSA Signature Verification Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| ecdsa_sigver_test(char *reqfn) |
| { |
| char buf[1024]; /* holds one line from the input REQUEST file. |
| * needs to be large enough to hold the longest |
| * line "Msg = <256 hex digits>\n". |
| */ |
| FILE *ecdsareq; /* input stream from the REQUEST file */ |
| FILE *ecdsaresp; /* output stream to the RESPONSE file */ |
| char curve[16]; /* "nistxddd" */ |
| ECPublicKey ecpub; |
| unsigned int i, j; |
| unsigned int flen = 0; /* length in bytes of the field size */ |
| unsigned int olen = 0; /* length in bytes of the base point order */ |
| unsigned char msg[512]; /* message that was signed (<= 128 bytes) */ |
| unsigned int msglen = 0; |
| unsigned char sha[HASH_LENGTH_MAX]; /* SHA digest */ |
| unsigned int shaLength = 0; /* length of SHA */ |
| HASH_HashType shaAlg = HASH_AlgNULL; /* type of SHA Alg */ |
| unsigned char sig[2 * MAX_ECKEY_LEN]; |
| SECItem signature, digest; |
| PRBool keyvalid = PR_TRUE; |
| PRBool sigvalid = PR_TRUE; |
| |
| ecdsareq = fopen(reqfn, "r"); |
| ecdsaresp = stdout; |
| ecpub.ecParams.arena = NULL; |
| strcpy(curve, "nist"); |
| while (fgets(buf, sizeof buf, ecdsareq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, ecdsaresp); |
| continue; |
| } |
| /* [X-ddd] */ |
| if (buf[0] == '[') { |
| const char *src; |
| char *dst; |
| SECItem *encodedparams; |
| ECParams *ecparams; |
| |
| src = &buf[1]; |
| dst = &curve[4]; |
| *dst++ = tolower(*src); |
| src += 2; /* skip the hyphen */ |
| *dst++ = *src++; |
| *dst++ = *src++; |
| *dst++ = *src++; |
| *dst = '\0'; |
| src++; /* skip the comma */ |
| /* set the SHA Algorithm */ |
| shaAlg = hash_string_to_hashType(src); |
| if (shaAlg == HASH_AlgNULL) { |
| fprintf(ecdsaresp, "ERROR: Unable to find SHAAlg type"); |
| goto loser; |
| } |
| encodedparams = getECParams(curve); |
| if (encodedparams == NULL) { |
| fprintf(stderr, "Unknown curve %s.", curve); |
| goto loser; |
| } |
| if (EC_DecodeParams(encodedparams, &ecparams) != SECSuccess) { |
| fprintf(stderr, "Curve %s not supported.\n", curve); |
| goto loser; |
| } |
| SECITEM_FreeItem(encodedparams, PR_TRUE); |
| if (ecpub.ecParams.arena != NULL) { |
| PORT_FreeArena(ecpub.ecParams.arena, PR_FALSE); |
| } |
| ecpub.ecParams.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); |
| if (ecpub.ecParams.arena == NULL) { |
| goto loser; |
| } |
| if (EC_CopyParams(ecpub.ecParams.arena, &ecpub.ecParams, ecparams) != |
| SECSuccess) { |
| goto loser; |
| } |
| PORT_FreeArena(ecparams->arena, PR_FALSE); |
| flen = (ecpub.ecParams.fieldID.size + 7) >> 3; |
| olen = ecpub.ecParams.order.len; |
| if (2 * olen > sizeof sig) { |
| goto loser; |
| } |
| ecpub.publicValue.type = siBuffer; |
| ecpub.publicValue.data = NULL; |
| ecpub.publicValue.len = 0; |
| SECITEM_AllocItem(ecpub.ecParams.arena, |
| &ecpub.publicValue, 2 * flen + 1); |
| if (ecpub.publicValue.data == NULL) { |
| goto loser; |
| } |
| ecpub.publicValue.data[0] = EC_POINT_FORM_UNCOMPRESSED; |
| fputs(buf, ecdsaresp); |
| continue; |
| } |
| /* Msg = ... */ |
| if (strncmp(buf, "Msg", 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], &msg[j]); |
| } |
| msglen = j; |
| shaLength = fips_hashLen(shaAlg); |
| if (fips_hashBuf(shaAlg, sha, msg, msglen) != SECSuccess) { |
| if (shaLength == 0) { |
| fprintf(ecdsaresp, "ERROR: SHAAlg not defined."); |
| } |
| fprintf(ecdsaresp, "ERROR: Unable to generate SHA%x", |
| shaLength == 160 ? 1 : shaLength); |
| goto loser; |
| } |
| fputs(buf, ecdsaresp); |
| |
| digest.type = siBuffer; |
| digest.data = sha; |
| digest.len = shaLength; |
| |
| continue; |
| } |
| /* Qx = ... */ |
| if (strncmp(buf, "Qx", 2) == 0) { |
| fputs(buf, ecdsaresp); |
| i = 2; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| keyvalid = from_hex_str(&ecpub.publicValue.data[1], flen, |
| &buf[i]); |
| continue; |
| } |
| /* Qy = ... */ |
| if (strncmp(buf, "Qy", 2) == 0) { |
| fputs(buf, ecdsaresp); |
| if (!keyvalid) { |
| continue; |
| } |
| i = 2; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| keyvalid = from_hex_str(&ecpub.publicValue.data[1 + flen], flen, |
| &buf[i]); |
| if (!keyvalid) { |
| continue; |
| } |
| if (EC_ValidatePublicKey(&ecpub.ecParams, &ecpub.publicValue) != |
| SECSuccess) { |
| if (PORT_GetError() == SEC_ERROR_BAD_KEY) { |
| keyvalid = PR_FALSE; |
| } else { |
| goto loser; |
| } |
| } |
| continue; |
| } |
| /* R = ... */ |
| if (buf[0] == 'R') { |
| fputs(buf, ecdsaresp); |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| sigvalid = from_hex_str(sig, olen, &buf[i]); |
| continue; |
| } |
| /* S = ... */ |
| if (buf[0] == 'S') { |
| fputs(buf, ecdsaresp); |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| if (sigvalid) { |
| sigvalid = from_hex_str(&sig[olen], olen, &buf[i]); |
| } |
| signature.type = siBuffer; |
| signature.data = sig; |
| signature.len = 2 * olen; |
| |
| if (!keyvalid || !sigvalid) { |
| fputs("Result = F\n", ecdsaresp); |
| } else if (ECDSA_VerifyDigest(&ecpub, &signature, &digest) == |
| SECSuccess) { |
| fputs("Result = P\n", ecdsaresp); |
| } else { |
| fputs("Result = F\n", ecdsaresp); |
| } |
| continue; |
| } |
| } |
| loser: |
| if (ecpub.ecParams.arena != NULL) { |
| PORT_FreeArena(ecpub.ecParams.arena, PR_FALSE); |
| } |
| fclose(ecdsareq); |
| } |
| |
| /* |
| * Perform the ECDH Functional Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| #define MAX_ECC_PARAMS 256 |
| void |
| ecdh_functional(char *reqfn, PRBool response) |
| { |
| char buf[256]; /* holds one line from the input REQUEST file. |
| * needs to be large enough to hold the longest |
| * line "Qx = <144 hex digits>\n". |
| */ |
| FILE *ecdhreq; /* input stream from the REQUEST file */ |
| FILE *ecdhresp; /* output stream to the RESPONSE file */ |
| char curve[16]; /* "nistxddd" */ |
| unsigned char hashBuf[HASH_LENGTH_MAX]; |
| ECParams *ecparams[MAX_ECC_PARAMS] = { NULL }; |
| ECPrivateKey *ecpriv = NULL; |
| ECParams *current_ecparams = NULL; |
| SECItem pubkey; |
| SECItem ZZ; |
| unsigned int i; |
| unsigned int len = 0; |
| unsigned int uit_len = 0; |
| int current_curve = -1; |
| HASH_HashType hash = HASH_AlgNULL; /* type of SHA Alg */ |
| |
| ecdhreq = fopen(reqfn, "r"); |
| ecdhresp = stdout; |
| strcpy(curve, "nist"); |
| pubkey.data = NULL; |
| while (fgets(buf, sizeof buf, ecdhreq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r') { |
| fputs(buf, ecdhresp); |
| continue; |
| } |
| if (buf[0] == '[') { |
| /* [Ex] */ |
| if (buf[1] == 'E' && buf[3] == ']') { |
| current_curve = buf[2] - 'A'; |
| fputs(buf, ecdhresp); |
| continue; |
| } |
| /* [Curve selected: x-nnn */ |
| if (strncmp(buf, "[Curve ", 7) == 0) { |
| const char *src; |
| char *dst; |
| SECItem *encodedparams; |
| |
| if ((current_curve < 0) || (current_curve > MAX_ECC_PARAMS)) { |
| fprintf(stderr, "No curve type defined\n"); |
| goto loser; |
| } |
| |
| src = &buf[1]; |
| /* skip passed the colon */ |
| while (*src && *src != ':') |
| src++; |
| if (*src != ':') { |
| fprintf(stderr, |
| "No colon in curve selected statement\n%s", buf); |
| goto loser; |
| } |
| src++; |
| /* skip to the first non-space */ |
| while (*src && *src == ' ') |
| src++; |
| dst = &curve[4]; |
| *dst++ = tolower(*src); |
| src += 2; /* skip the hyphen */ |
| *dst++ = *src++; |
| *dst++ = *src++; |
| *dst++ = *src++; |
| *dst = '\0'; |
| if (ecparams[current_curve] != NULL) { |
| PORT_FreeArena(ecparams[current_curve]->arena, PR_FALSE); |
| ecparams[current_curve] = NULL; |
| } |
| encodedparams = getECParams(curve); |
| if (encodedparams == NULL) { |
| fprintf(stderr, "Unknown curve %s.", curve); |
| goto loser; |
| } |
| if (EC_DecodeParams(encodedparams, &ecparams[current_curve]) != SECSuccess) { |
| fprintf(stderr, "Curve %s not supported.\n", curve); |
| goto loser; |
| } |
| SECITEM_FreeItem(encodedparams, PR_TRUE); |
| fputs(buf, ecdhresp); |
| continue; |
| } |
| /* [Ex - SHAxxx] */ |
| if (buf[1] == 'E' && buf[3] == ' ') { |
| const char *src; |
| current_curve = buf[2] - 'A'; |
| if ((current_curve < 0) || (current_curve > 256)) { |
| fprintf(stderr, "bad curve type defined (%c)\n", buf[2]); |
| goto loser; |
| } |
| current_ecparams = ecparams[current_curve]; |
| if (current_ecparams == NULL) { |
| fprintf(stderr, "no curve defined for type %c defined\n", |
| buf[2]); |
| goto loser; |
| } |
| /* skip passed the colon */ |
| src = &buf[1]; |
| while (*src && *src != '-') |
| src++; |
| if (*src != '-') { |
| fprintf(stderr, |
| "No data in curve selected statement\n%s", buf); |
| goto loser; |
| } |
| src++; |
| /* skip to the first non-space */ |
| while (*src && *src == ' ') |
| src++; |
| hash = hash_string_to_hashType(src); |
| if (hash == HASH_AlgNULL) { |
| fprintf(ecdhresp, "ERROR: Unable to find SHAAlg type"); |
| goto loser; |
| } |
| fputs(buf, ecdhresp); |
| continue; |
| } |
| fputs(buf, ecdhresp); |
| continue; |
| } |
| /* COUNT = ... */ |
| if (strncmp(buf, "COUNT", 5) == 0) { |
| fputs(buf, ecdhresp); |
| if (current_ecparams == NULL) { |
| fprintf(stderr, "no curve defined for type %c defined\n", |
| buf[2]); |
| goto loser; |
| } |
| len = (current_ecparams->fieldID.size + 7) >> 3; |
| if (pubkey.data != NULL) { |
| PORT_Free(pubkey.data); |
| pubkey.data = NULL; |
| } |
| SECITEM_AllocItem(NULL, &pubkey, EC_GetPointSize(current_ecparams)); |
| if (pubkey.data == NULL) { |
| goto loser; |
| } |
| pubkey.data[0] = EC_POINT_FORM_UNCOMPRESSED; |
| continue; |
| } |
| /* QeCAVSx = ... */ |
| if (strncmp(buf, "QeCAVSx", 7) == 0) { |
| fputs(buf, ecdhresp); |
| i = 7; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| from_hex_str(&pubkey.data[1], len, &buf[i]); |
| continue; |
| } |
| /* QeCAVSy = ... */ |
| if (strncmp(buf, "QeCAVSy", 7) == 0) { |
| fputs(buf, ecdhresp); |
| i = 7; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| from_hex_str(&pubkey.data[1 + len], len, &buf[i]); |
| if (current_ecparams == NULL) { |
| fprintf(stderr, "no curve defined\n"); |
| goto loser; |
| } |
| /* validate CAVS public key */ |
| if (EC_ValidatePublicKey(current_ecparams, &pubkey) != SECSuccess) { |
| fprintf(stderr, "BAD key detected\n"); |
| goto loser; |
| } |
| |
| /* generate ECC key pair */ |
| if (EC_NewKey(current_ecparams, &ecpriv) != SECSuccess) { |
| fprintf(stderr, "Failed to generate new key\n"); |
| goto loser; |
| } |
| /* validate UIT generated public key */ |
| if (EC_ValidatePublicKey(current_ecparams, &ecpriv->publicValue) != |
| SECSuccess) { |
| fprintf(stderr, "generate key did not validate\n"); |
| goto loser; |
| } |
| /* output UIT public key */ |
| uit_len = ecpriv->publicValue.len; |
| if (uit_len % 2 == 0) { |
| fprintf(stderr, "generate key had invalid public value len\n"); |
| goto loser; |
| } |
| uit_len = (uit_len - 1) / 2; |
| if (ecpriv->publicValue.data[0] != EC_POINT_FORM_UNCOMPRESSED) { |
| fprintf(stderr, "generate key was compressed\n"); |
| goto loser; |
| } |
| fputs("QeIUTx = ", ecdhresp); |
| to_hex_str(buf, &ecpriv->publicValue.data[1], uit_len); |
| fputs(buf, ecdhresp); |
| fputc('\n', ecdhresp); |
| fputs("QeIUTy = ", ecdhresp); |
| to_hex_str(buf, &ecpriv->publicValue.data[1 + uit_len], uit_len); |
| fputs(buf, ecdhresp); |
| fputc('\n', ecdhresp); |
| /* ECDH */ |
| if (ECDH_Derive(&pubkey, current_ecparams, &ecpriv->privateValue, |
| PR_FALSE, &ZZ) != SECSuccess) { |
| fprintf(stderr, "Derive failed\n"); |
| goto loser; |
| } |
| /* output hash of ZZ */ |
| if (fips_hashBuf(hash, hashBuf, ZZ.data, ZZ.len) != SECSuccess) { |
| fprintf(stderr, "hash of derived key failed\n"); |
| goto loser; |
| } |
| SECITEM_FreeItem(&ZZ, PR_FALSE); |
| fputs("HashZZ = ", ecdhresp); |
| to_hex_str(buf, hashBuf, fips_hashLen(hash)); |
| fputs(buf, ecdhresp); |
| fputc('\n', ecdhresp); |
| fputc('\n', ecdhresp); |
| PORT_FreeArena(ecpriv->ecParams.arena, PR_TRUE); |
| ecpriv = NULL; |
| continue; |
| } |
| } |
| loser: |
| if (ecpriv != NULL) { |
| PORT_FreeArena(ecpriv->ecParams.arena, PR_TRUE); |
| } |
| for (i = 0; i < MAX_ECC_PARAMS; i++) { |
| if (ecparams[i] != NULL) { |
| PORT_FreeArena(ecparams[i]->arena, PR_FALSE); |
| ecparams[i] = NULL; |
| } |
| } |
| if (pubkey.data != NULL) { |
| PORT_Free(pubkey.data); |
| } |
| fclose(ecdhreq); |
| } |
| |
| #define MATCH_OPENSSL 1 |
| /* |
| * Perform the ECDH Validity Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| ecdh_verify(char *reqfn, PRBool response) |
| { |
| char buf[256]; /* holds one line from the input REQUEST file. |
| * needs to be large enough to hold the longest |
| * line "Qx = <144 hex digits>\n". |
| */ |
| FILE *ecdhreq; /* input stream from the REQUEST file */ |
| FILE *ecdhresp; /* output stream to the RESPONSE file */ |
| char curve[16]; /* "nistxddd" */ |
| unsigned char hashBuf[HASH_LENGTH_MAX]; |
| unsigned char cavsHashBuf[HASH_LENGTH_MAX]; |
| unsigned char private_data[MAX_ECKEY_LEN]; |
| ECParams *ecparams[MAX_ECC_PARAMS] = { NULL }; |
| ECParams *current_ecparams = NULL; |
| SECItem pubkey; |
| SECItem ZZ; |
| SECItem private_value; |
| unsigned int i; |
| unsigned int len = 0; |
| int current_curve = -1; |
| HASH_HashType hash = HASH_AlgNULL; /* type of SHA Alg */ |
| |
| ecdhreq = fopen(reqfn, "r"); |
| ecdhresp = stdout; |
| strcpy(curve, "nist"); |
| pubkey.data = NULL; |
| while (fgets(buf, sizeof buf, ecdhreq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r') { |
| fputs(buf, ecdhresp); |
| continue; |
| } |
| if (buf[0] == '[') { |
| /* [Ex] */ |
| if (buf[1] == 'E' && buf[3] == ']') { |
| current_curve = buf[2] - 'A'; |
| fputs(buf, ecdhresp); |
| continue; |
| } |
| /* [Curve selected: x-nnn */ |
| if (strncmp(buf, "[Curve ", 7) == 0) { |
| const char *src; |
| char *dst; |
| SECItem *encodedparams; |
| |
| if ((current_curve < 0) || (current_curve > MAX_ECC_PARAMS)) { |
| fprintf(stderr, "No curve type defined\n"); |
| goto loser; |
| } |
| |
| src = &buf[1]; |
| /* skip passed the colon */ |
| while (*src && *src != ':') |
| src++; |
| if (*src != ':') { |
| fprintf(stderr, |
| "No colon in curve selected statement\n%s", buf); |
| goto loser; |
| } |
| src++; |
| /* skip to the first non-space */ |
| while (*src && *src == ' ') |
| src++; |
| dst = &curve[4]; |
| *dst++ = tolower(*src); |
| src += 2; /* skip the hyphen */ |
| *dst++ = *src++; |
| *dst++ = *src++; |
| *dst++ = *src++; |
| *dst = '\0'; |
| if (ecparams[current_curve] != NULL) { |
| PORT_FreeArena(ecparams[current_curve]->arena, PR_FALSE); |
| ecparams[current_curve] = NULL; |
| } |
| encodedparams = getECParams(curve); |
| if (encodedparams == NULL) { |
| fprintf(stderr, "Unknown curve %s.\n", curve); |
| goto loser; |
| } |
| if (EC_DecodeParams(encodedparams, &ecparams[current_curve]) != SECSuccess) { |
| fprintf(stderr, "Curve %s not supported.\n", curve); |
| goto loser; |
| } |
| SECITEM_FreeItem(encodedparams, PR_TRUE); |
| fputs(buf, ecdhresp); |
| continue; |
| } |
| /* [Ex - SHAxxx] */ |
| if (buf[1] == 'E' && buf[3] == ' ') { |
| const char *src; |
| current_curve = buf[2] - 'A'; |
| if ((current_curve < 0) || (current_curve > 256)) { |
| fprintf(stderr, "bad curve type defined (%c)\n", buf[2]); |
| goto loser; |
| } |
| current_ecparams = ecparams[current_curve]; |
| if (current_ecparams == NULL) { |
| fprintf(stderr, "no curve defined for type %c defined\n", |
| buf[2]); |
| goto loser; |
| } |
| /* skip passed the colon */ |
| src = &buf[1]; |
| while (*src && *src != '-') |
| src++; |
| if (*src != '-') { |
| fprintf(stderr, |
| "No data in curve selected statement\n%s", buf); |
| goto loser; |
| } |
| src++; |
| /* skip to the first non-space */ |
| while (*src && *src == ' ') |
| src++; |
| hash = hash_string_to_hashType(src); |
| if (hash == HASH_AlgNULL) { |
| fprintf(ecdhresp, "ERROR: Unable to find SHAAlg type"); |
| goto loser; |
| } |
| fputs(buf, ecdhresp); |
| continue; |
| } |
| fputs(buf, ecdhresp); |
| continue; |
| } |
| /* COUNT = ... */ |
| if (strncmp(buf, "COUNT", 5) == 0) { |
| fputs(buf, ecdhresp); |
| if (current_ecparams == NULL) { |
| fprintf(stderr, "no curve defined for type %c defined\n", |
| buf[2]); |
| goto loser; |
| } |
| len = (current_ecparams->fieldID.size + 7) >> 3; |
| if (pubkey.data != NULL) { |
| PORT_Free(pubkey.data); |
| pubkey.data = NULL; |
| } |
| SECITEM_AllocItem(NULL, &pubkey, EC_GetPointSize(current_ecparams)); |
| if (pubkey.data == NULL) { |
| goto loser; |
| } |
| pubkey.data[0] = EC_POINT_FORM_UNCOMPRESSED; |
| continue; |
| } |
| /* QeCAVSx = ... */ |
| if (strncmp(buf, "QeCAVSx", 7) == 0) { |
| fputs(buf, ecdhresp); |
| i = 7; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| from_hex_str(&pubkey.data[1], len, &buf[i]); |
| continue; |
| } |
| /* QeCAVSy = ... */ |
| if (strncmp(buf, "QeCAVSy", 7) == 0) { |
| fputs(buf, ecdhresp); |
| i = 7; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| from_hex_str(&pubkey.data[1 + len], len, &buf[i]); |
| continue; |
| } |
| if (strncmp(buf, "deIUT", 5) == 0) { |
| fputs(buf, ecdhresp); |
| i = 5; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| from_hex_str(private_data, len, &buf[i]); |
| private_value.data = private_data; |
| private_value.len = len; |
| continue; |
| } |
| if (strncmp(buf, "QeIUTx", 6) == 0) { |
| fputs(buf, ecdhresp); |
| continue; |
| } |
| if (strncmp(buf, "QeIUTy", 6) == 0) { |
| fputs(buf, ecdhresp); |
| continue; |
| } |
| if (strncmp(buf, "CAVSHashZZ", 10) == 0) { |
| fputs(buf, ecdhresp); |
| i = 10; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| from_hex_str(cavsHashBuf, fips_hashLen(hash), &buf[i]); |
| if (current_ecparams == NULL) { |
| fprintf(stderr, "no curve defined for type defined\n"); |
| goto loser; |
| } |
| /* validate CAVS public key */ |
| if (EC_ValidatePublicKey(current_ecparams, &pubkey) != SECSuccess) { |
| #ifdef MATCH_OPENSSL |
| fprintf(ecdhresp, "Result = F\n"); |
| #else |
| fprintf(ecdhresp, "Result = F # key didn't validate\n"); |
| #endif |
| continue; |
| } |
| |
| /* ECDH */ |
| if (ECDH_Derive(&pubkey, current_ecparams, &private_value, |
| PR_FALSE, &ZZ) != SECSuccess) { |
| fprintf(stderr, "Derive failed\n"); |
| goto loser; |
| } |
| /* output ZZ */ |
| #ifndef MATCH_OPENSSL |
| fputs("Z = ", ecdhresp); |
| to_hex_str(buf, ZZ.data, ZZ.len); |
| fputs(buf, ecdhresp); |
| fputc('\n', ecdhresp); |
| #endif |
| |
| if (fips_hashBuf(hash, hashBuf, ZZ.data, ZZ.len) != SECSuccess) { |
| fprintf(stderr, "hash of derived key failed\n"); |
| goto loser; |
| } |
| SECITEM_FreeItem(&ZZ, PR_FALSE); |
| #ifndef MATCH_NIST |
| fputs("IUTHashZZ = ", ecdhresp); |
| to_hex_str(buf, hashBuf, fips_hashLen(hash)); |
| fputs(buf, ecdhresp); |
| fputc('\n', ecdhresp); |
| #endif |
| if (memcmp(hashBuf, cavsHashBuf, fips_hashLen(hash)) != 0) { |
| #ifdef MATCH_OPENSSL |
| fprintf(ecdhresp, "Result = F\n"); |
| #else |
| fprintf(ecdhresp, "Result = F # hash doesn't match\n"); |
| #endif |
| } else { |
| fprintf(ecdhresp, "Result = P\n"); |
| } |
| #ifndef MATCH_OPENSSL |
| fputc('\n', ecdhresp); |
| #endif |
| continue; |
| } |
| } |
| loser: |
| for (i = 0; i < MAX_ECC_PARAMS; i++) { |
| if (ecparams[i] != NULL) { |
| PORT_FreeArena(ecparams[i]->arena, PR_FALSE); |
| ecparams[i] = NULL; |
| } |
| } |
| if (pubkey.data != NULL) { |
| PORT_Free(pubkey.data); |
| } |
| fclose(ecdhreq); |
| } |
| |
| /* |
| * Perform the DH Functional Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| #define MAX_ECC_PARAMS 256 |
| void |
| dh_functional(char *reqfn, PRBool response) |
| { |
| char buf[1024]; /* holds one line from the input REQUEST file. |
| * needs to be large enough to hold the longest |
| * line "YephCAVS = <512 hex digits>\n". |
| */ |
| FILE *dhreq; /* input stream from the REQUEST file */ |
| FILE *dhresp; /* output stream to the RESPONSE file */ |
| unsigned char hashBuf[HASH_LENGTH_MAX]; |
| DSAPrivateKey *dsapriv = NULL; |
| PQGParams pqg = { 0 }; |
| unsigned char pubkeydata[DSA_MAX_P_BITS / 8]; |
| SECItem pubkey; |
| SECItem ZZ; |
| unsigned int i, j; |
| unsigned int pgySize; |
| HASH_HashType hash = HASH_AlgNULL; /* type of SHA Alg */ |
| |
| dhreq = fopen(reqfn, "r"); |
| dhresp = stdout; |
| while (fgets(buf, sizeof buf, dhreq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r') { |
| fputs(buf, dhresp); |
| continue; |
| } |
| if (buf[0] == '[') { |
| /* [Fx - SHAxxx] */ |
| if (buf[1] == 'F' && buf[3] == ' ') { |
| const char *src; |
| /* skip passed the colon */ |
| src = &buf[1]; |
| while (*src && *src != '-') |
| src++; |
| if (*src != '-') { |
| fprintf(stderr, "No hash specified\n%s", buf); |
| goto loser; |
| } |
| src++; |
| /* skip to the first non-space */ |
| while (*src && *src == ' ') |
| src++; |
| hash = hash_string_to_hashType(src); |
| if (hash == HASH_AlgNULL) { |
| fprintf(dhresp, "ERROR: Unable to find SHAAlg type"); |
| goto loser; |
| } |
| /* clear the PQG parameters */ |
| if (pqg.prime.data) { /* P */ |
| SECITEM_ZfreeItem(&pqg.prime, PR_FALSE); |
| } |
| if (pqg.subPrime.data) { /* Q */ |
| SECITEM_ZfreeItem(&pqg.subPrime, PR_FALSE); |
| } |
| if (pqg.base.data) { /* G */ |
| SECITEM_ZfreeItem(&pqg.base, PR_FALSE); |
| } |
| pgySize = DSA_MAX_P_BITS / 8; /* change if more key sizes are supported in CAVS */ |
| SECITEM_AllocItem(NULL, &pqg.prime, pgySize); |
| SECITEM_AllocItem(NULL, &pqg.base, pgySize); |
| pqg.prime.len = pqg.base.len = pgySize; |
| |
| /* set q to the max allows */ |
| SECITEM_AllocItem(NULL, &pqg.subPrime, DSA_MAX_Q_BITS / 8); |
| pqg.subPrime.len = DSA_MAX_Q_BITS / 8; |
| fputs(buf, dhresp); |
| continue; |
| } |
| fputs(buf, dhresp); |
| continue; |
| } |
| if (buf[0] == 'P') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < pqg.prime.len; i += 2, j++) { |
| if (!isxdigit(buf[i])) { |
| pqg.prime.len = j; |
| break; |
| } |
| hex_to_byteval(&buf[i], &pqg.prime.data[j]); |
| } |
| |
| fputs(buf, dhresp); |
| continue; |
| } |
| |
| /* Q = ... */ |
| if (buf[0] == 'Q') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < pqg.subPrime.len; i += 2, j++) { |
| if (!isxdigit(buf[i])) { |
| pqg.subPrime.len = j; |
| break; |
| } |
| hex_to_byteval(&buf[i], &pqg.subPrime.data[j]); |
| } |
| |
| fputs(buf, dhresp); |
| continue; |
| } |
| |
| /* G = ... */ |
| if (buf[0] == 'G') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < pqg.base.len; i += 2, j++) { |
| if (!isxdigit(buf[i])) { |
| pqg.base.len = j; |
| break; |
| } |
| hex_to_byteval(&buf[i], &pqg.base.data[j]); |
| } |
| |
| fputs(buf, dhresp); |
| continue; |
| } |
| |
| /* COUNT = ... */ |
| if (strncmp(buf, "COUNT", 5) == 0) { |
| fputs(buf, dhresp); |
| continue; |
| } |
| |
| /* YephemCAVS = ... */ |
| if (strncmp(buf, "YephemCAVS", 10) == 0) { |
| fputs(buf, dhresp); |
| i = 10; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| from_hex_str(pubkeydata, pqg.prime.len, &buf[i]); |
| pubkey.data = pubkeydata; |
| pubkey.len = pqg.prime.len; |
| |
| /* generate FCC key pair, nist uses pqg rather then pg, |
| * so use DSA to generate the key */ |
| if (DSA_NewKey(&pqg, &dsapriv) != SECSuccess) { |
| fprintf(stderr, "Failed to generate new key\n"); |
| goto loser; |
| } |
| fputs("XephemIUT = ", dhresp); |
| to_hex_str(buf, dsapriv->privateValue.data, dsapriv->privateValue.len); |
| fputs(buf, dhresp); |
| fputc('\n', dhresp); |
| fputs("YephemIUT = ", dhresp); |
| to_hex_str(buf, dsapriv->publicValue.data, dsapriv->publicValue.len); |
| fputs(buf, dhresp); |
| fputc('\n', dhresp); |
| /* DH */ |
| if (DH_Derive(&pubkey, &pqg.prime, &dsapriv->privateValue, |
| &ZZ, pqg.prime.len) != SECSuccess) { |
| fprintf(stderr, "Derive failed\n"); |
| goto loser; |
| } |
| /* output hash of ZZ */ |
| if (fips_hashBuf(hash, hashBuf, ZZ.data, ZZ.len) != SECSuccess) { |
| fprintf(stderr, "hash of derived key failed\n"); |
| goto loser; |
| } |
| SECITEM_FreeItem(&ZZ, PR_FALSE); |
| fputs("HashZZ = ", dhresp); |
| to_hex_str(buf, hashBuf, fips_hashLen(hash)); |
| fputs(buf, dhresp); |
| fputc('\n', dhresp); |
| fputc('\n', dhresp); |
| PORT_FreeArena(dsapriv->params.arena, PR_TRUE); |
| dsapriv = NULL; |
| continue; |
| } |
| } |
| loser: |
| if (dsapriv != NULL) { |
| PORT_FreeArena(dsapriv->params.arena, PR_TRUE); |
| } |
| fclose(dhreq); |
| } |
| |
| #define MATCH_OPENSSL 1 |
| /* |
| * Perform the DH Validity Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| dh_verify(char *reqfn, PRBool response) |
| { |
| char buf[1024]; /* holds one line from the input REQUEST file. |
| * needs to be large enough to hold the longest |
| * line "YephCAVS = <512 hex digits>\n". |
| */ |
| FILE *dhreq; /* input stream from the REQUEST file */ |
| FILE *dhresp; /* output stream to the RESPONSE file */ |
| unsigned char hashBuf[HASH_LENGTH_MAX]; |
| unsigned char cavsHashBuf[HASH_LENGTH_MAX]; |
| PQGParams pqg = { 0 }; |
| unsigned char pubkeydata[DSA_MAX_P_BITS / 8]; |
| unsigned char privkeydata[DSA_MAX_P_BITS / 8]; |
| SECItem pubkey; |
| SECItem privkey; |
| SECItem ZZ; |
| unsigned int i, j; |
| unsigned int pgySize; |
| HASH_HashType hash = HASH_AlgNULL; /* type of SHA Alg */ |
| |
| dhreq = fopen(reqfn, "r"); |
| dhresp = stdout; |
| while (fgets(buf, sizeof buf, dhreq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r') { |
| fputs(buf, dhresp); |
| continue; |
| } |
| if (buf[0] == '[') { |
| /* [Fx - SHAxxx] */ |
| if (buf[1] == 'F' && buf[3] == ' ') { |
| const char *src; |
| /* skip passed the colon */ |
| src = &buf[1]; |
| while (*src && *src != '-') |
| src++; |
| if (*src != '-') { |
| fprintf(stderr, "No hash specified\n%s", buf); |
| goto loser; |
| } |
| src++; |
| /* skip to the first non-space */ |
| while (*src && *src == ' ') |
| src++; |
| hash = hash_string_to_hashType(src); |
| if (hash == HASH_AlgNULL) { |
| fprintf(dhresp, "ERROR: Unable to find SHAAlg type"); |
| goto loser; |
| } |
| /* clear the PQG parameters */ |
| if (pqg.prime.data) { /* P */ |
| SECITEM_ZfreeItem(&pqg.prime, PR_FALSE); |
| } |
| if (pqg.subPrime.data) { /* Q */ |
| SECITEM_ZfreeItem(&pqg.subPrime, PR_FALSE); |
| } |
| if (pqg.base.data) { /* G */ |
| SECITEM_ZfreeItem(&pqg.base, PR_FALSE); |
| } |
| pgySize = DSA_MAX_P_BITS / 8; /* change if more key sizes are supported in CAVS */ |
| SECITEM_AllocItem(NULL, &pqg.prime, pgySize); |
| SECITEM_AllocItem(NULL, &pqg.base, pgySize); |
| pqg.prime.len = pqg.base.len = pgySize; |
| |
| /* set q to the max allows */ |
| SECITEM_AllocItem(NULL, &pqg.subPrime, DSA_MAX_Q_BITS / 8); |
| pqg.subPrime.len = DSA_MAX_Q_BITS / 8; |
| fputs(buf, dhresp); |
| continue; |
| } |
| fputs(buf, dhresp); |
| continue; |
| } |
| if (buf[0] == 'P') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < pqg.prime.len; i += 2, j++) { |
| if (!isxdigit(buf[i])) { |
| pqg.prime.len = j; |
| break; |
| } |
| hex_to_byteval(&buf[i], &pqg.prime.data[j]); |
| } |
| |
| fputs(buf, dhresp); |
| continue; |
| } |
| |
| /* Q = ... */ |
| if (buf[0] == 'Q') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < pqg.subPrime.len; i += 2, j++) { |
| if (!isxdigit(buf[i])) { |
| pqg.subPrime.len = j; |
| break; |
| } |
| hex_to_byteval(&buf[i], &pqg.subPrime.data[j]); |
| } |
| |
| fputs(buf, dhresp); |
| continue; |
| } |
| |
| /* G = ... */ |
| if (buf[0] == 'G') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < pqg.base.len; i += 2, j++) { |
| if (!isxdigit(buf[i])) { |
| pqg.base.len = j; |
| break; |
| } |
| hex_to_byteval(&buf[i], &pqg.base.data[j]); |
| } |
| |
| fputs(buf, dhresp); |
| continue; |
| } |
| |
| /* COUNT = ... */ |
| if (strncmp(buf, "COUNT", 5) == 0) { |
| fputs(buf, dhresp); |
| continue; |
| } |
| |
| /* YephemCAVS = ... */ |
| if (strncmp(buf, "YephemCAVS", 10) == 0) { |
| fputs(buf, dhresp); |
| i = 10; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| from_hex_str(pubkeydata, pqg.prime.len, &buf[i]); |
| pubkey.data = pubkeydata; |
| pubkey.len = pqg.prime.len; |
| continue; |
| } |
| /* XephemUIT = ... */ |
| if (strncmp(buf, "XephemIUT", 9) == 0) { |
| fputs(buf, dhresp); |
| i = 9; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| from_hex_str(privkeydata, pqg.subPrime.len, &buf[i]); |
| privkey.data = privkeydata; |
| privkey.len = pqg.subPrime.len; |
| continue; |
| } |
| /* YephemUIT = ... */ |
| if (strncmp(buf, "YephemIUT", 9) == 0) { |
| fputs(buf, dhresp); |
| continue; |
| } |
| /* CAVSHashZZ = ... */ |
| if (strncmp(buf, "CAVSHashZZ", 10) == 0) { |
| fputs(buf, dhresp); |
| i = 10; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| from_hex_str(cavsHashBuf, fips_hashLen(hash), &buf[i]); |
| /* do the DH operation*/ |
| if (DH_Derive(&pubkey, &pqg.prime, &privkey, |
| &ZZ, pqg.prime.len) != SECSuccess) { |
| fprintf(stderr, "Derive failed\n"); |
| goto loser; |
| } |
| /* output ZZ */ |
| #ifndef MATCH_OPENSSL |
| fputs("Z = ", dhresp); |
| to_hex_str(buf, ZZ.data, ZZ.len); |
| fputs(buf, dhresp); |
| fputc('\n', dhresp); |
| #endif |
| if (fips_hashBuf(hash, hashBuf, ZZ.data, ZZ.len) != SECSuccess) { |
| fprintf(stderr, "hash of derived key failed\n"); |
| goto loser; |
| } |
| SECITEM_FreeItem(&ZZ, PR_FALSE); |
| #ifndef MATCH_NIST_ |
| fputs("IUTHashZZ = ", dhresp); |
| to_hex_str(buf, hashBuf, fips_hashLen(hash)); |
| fputs(buf, dhresp); |
| fputc('\n', dhresp); |
| #endif |
| if (memcmp(hashBuf, cavsHashBuf, fips_hashLen(hash)) != 0) { |
| fprintf(dhresp, "Result = F\n"); |
| } else { |
| fprintf(dhresp, "Result = P\n"); |
| } |
| #ifndef MATCH_OPENSSL |
| fputc('\n', dhresp); |
| #endif |
| continue; |
| } |
| } |
| loser: |
| fclose(dhreq); |
| } |
| |
| PRBool |
| isblankline(char *b) |
| { |
| while (isspace(*b)) |
| b++; |
| if ((*b == '\n') || (*b == 0)) { |
| return PR_TRUE; |
| } |
| return PR_FALSE; |
| } |
| |
| static int debug = 0; |
| |
| /* |
| * Perform the Hash_DRBG (CAVS) for the RNG algorithm |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| drbg(char *reqfn) |
| { |
| char buf[2000]; /* test case has some very long lines, returned bits |
| * as high as 800 bytes (6400 bits). That 1600 byte |
| * plus a tag */ |
| char buf2[2000]; |
| FILE *rngreq; /* input stream from the REQUEST file */ |
| FILE *rngresp; /* output stream to the RESPONSE file */ |
| |
| unsigned int i, j; |
| #ifdef HANDLE_PREDICTION_RESISTANCE |
| PRBool predictionResistance = PR_FALSE; |
| #endif |
| unsigned char *nonce = NULL; |
| int nonceLen = 0; |
| unsigned char *personalizationString = NULL; |
| int personalizationStringLen = 0; |
| unsigned char *additionalInput = NULL; |
| int additionalInputLen = 0; |
| unsigned char *entropyInput = NULL; |
| int entropyInputLen = 0; |
| unsigned char *predictedreturn_bytes = NULL; |
| unsigned char *return_bytes = NULL; |
| int return_bytes_len = 0; |
| enum { NONE, |
| INSTANTIATE, |
| GENERATE, |
| RESEED, |
| RESULT } command = |
| NONE; |
| PRBool genResult = PR_FALSE; |
| SECStatus rv; |
| |
| rngreq = fopen(reqfn, "r"); |
| rngresp = stdout; |
| while (fgets(buf, sizeof buf, rngreq) != NULL) { |
| switch (command) { |
| case INSTANTIATE: |
| if (debug) { |
| fputs("# PRNGTEST_Instantiate(", rngresp); |
| to_hex_str(buf2, entropyInput, entropyInputLen); |
| fputs(buf2, rngresp); |
| fprintf(rngresp, ",%d,", entropyInputLen); |
| to_hex_str(buf2, nonce, nonceLen); |
| fputs(buf2, rngresp); |
| fprintf(rngresp, ",%d,", nonceLen); |
| to_hex_str(buf2, personalizationString, |
| personalizationStringLen); |
| fputs(buf2, rngresp); |
| fprintf(rngresp, ",%d)\n", personalizationStringLen); |
| } |
| rv = PRNGTEST_Instantiate(entropyInput, entropyInputLen, |
| nonce, nonceLen, |
| personalizationString, |
| personalizationStringLen); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| break; |
| |
| case GENERATE: |
| case RESULT: |
| memset(return_bytes, 0, return_bytes_len); |
| if (debug) { |
| fputs("# PRNGTEST_Generate(returnbytes", rngresp); |
| fprintf(rngresp, ",%d,", return_bytes_len); |
| to_hex_str(buf2, additionalInput, additionalInputLen); |
| fputs(buf2, rngresp); |
| fprintf(rngresp, ",%d)\n", additionalInputLen); |
| } |
| rv = PRNGTEST_Generate((PRUint8 *)return_bytes, |
| return_bytes_len, |
| (PRUint8 *)additionalInput, |
| additionalInputLen); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| |
| if (command == RESULT) { |
| fputs("ReturnedBits = ", rngresp); |
| to_hex_str(buf2, return_bytes, return_bytes_len); |
| fputs(buf2, rngresp); |
| fputc('\n', rngresp); |
| if (debug) { |
| fputs("# PRNGTEST_Uninstantiate()\n", rngresp); |
| } |
| rv = PRNGTEST_Uninstantiate(); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| } else if (debug) { |
| fputs("#ReturnedBits = ", rngresp); |
| to_hex_str(buf2, return_bytes, return_bytes_len); |
| fputs(buf2, rngresp); |
| fputc('\n', rngresp); |
| } |
| |
| memset(additionalInput, 0, additionalInputLen); |
| break; |
| |
| case RESEED: |
| if (entropyInput || additionalInput) { |
| if (debug) { |
| fputs("# PRNGTEST_Reseed(", rngresp); |
| fprintf(rngresp, ",%d,", return_bytes_len); |
| to_hex_str(buf2, entropyInput, entropyInputLen); |
| fputs(buf2, rngresp); |
| fprintf(rngresp, ",%d,", entropyInputLen); |
| to_hex_str(buf2, additionalInput, additionalInputLen); |
| fputs(buf2, rngresp); |
| fprintf(rngresp, ",%d)\n", additionalInputLen); |
| } |
| rv = PRNGTEST_Reseed(entropyInput, entropyInputLen, |
| additionalInput, additionalInputLen); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| } |
| memset(entropyInput, 0, entropyInputLen); |
| memset(additionalInput, 0, additionalInputLen); |
| break; |
| case NONE: |
| break; |
| } |
| command = NONE; |
| |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n' || buf[0] == '\r') { |
| fputs(buf, rngresp); |
| continue; |
| } |
| |
| /* [Hash - SHA256] */ |
| if (strncmp(buf, "[SHA-256]", 9) == 0) { |
| fputs(buf, rngresp); |
| continue; |
| } |
| |
| if (strncmp(buf, "[PredictionResistance", 21) == 0) { |
| #ifdef HANDLE_PREDICTION_RESISTANCE |
| i = 21; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| if (strncmp(buf, "False", 5) == 0) { |
| predictionResistance = PR_FALSE; |
| } else { |
| predictionResistance = PR_TRUE; |
| } |
| #endif |
| |
| fputs(buf, rngresp); |
| continue; |
| } |
| |
| if (strncmp(buf, "[ReturnedBitsLen", 16) == 0) { |
| if (return_bytes) { |
| PORT_ZFree(return_bytes, return_bytes_len); |
| return_bytes = NULL; |
| } |
| if (predictedreturn_bytes) { |
| PORT_ZFree(predictedreturn_bytes, return_bytes_len); |
| predictedreturn_bytes = NULL; |
| } |
| return_bytes_len = 0; |
| if (sscanf(buf, "[ReturnedBitsLen = %d]", &return_bytes_len) != 1) { |
| goto loser; |
| } |
| return_bytes_len = return_bytes_len / 8; |
| if (return_bytes_len > 0) { |
| return_bytes = PORT_Alloc(return_bytes_len); |
| predictedreturn_bytes = PORT_Alloc(return_bytes_len); |
| } |
| fputs(buf, rngresp); |
| continue; |
| } |
| |
| if (strncmp(buf, "[EntropyInputLen", 16) == 0) { |
| if (entropyInput) { |
| PORT_ZFree(entropyInput, entropyInputLen); |
| entropyInput = NULL; |
| entropyInputLen = 0; |
| } |
| if (sscanf(buf, "[EntropyInputLen = %d]", &entropyInputLen) != 1) { |
| goto loser; |
| } |
| entropyInputLen = entropyInputLen / 8; |
| if (entropyInputLen > 0) { |
| entropyInput = PORT_Alloc(entropyInputLen); |
| } |
| fputs(buf, rngresp); |
| continue; |
| } |
| |
| if (strncmp(buf, "[NonceLen", 9) == 0) { |
| if (nonce) { |
| PORT_ZFree(nonce, nonceLen); |
| nonce = NULL; |
| nonceLen = 0; |
| } |
| |
| if (sscanf(buf, "[NonceLen = %d]", &nonceLen) != 1) { |
| goto loser; |
| } |
| nonceLen = nonceLen / 8; |
| if (nonceLen > 0) { |
| nonce = PORT_Alloc(nonceLen); |
| } |
| fputs(buf, rngresp); |
| continue; |
| } |
| |
| if (strncmp(buf, "[PersonalizationStringLen", 16) == 0) { |
| if (personalizationString) { |
| PORT_ZFree(personalizationString, personalizationStringLen); |
| personalizationString = NULL; |
| personalizationStringLen = 0; |
| } |
| |
| if (sscanf(buf, "[PersonalizationStringLen = %d]", &personalizationStringLen) != 1) { |
| goto loser; |
| } |
| personalizationStringLen = personalizationStringLen / 8; |
| if (personalizationStringLen > 0) { |
| personalizationString = PORT_Alloc(personalizationStringLen); |
| } |
| fputs(buf, rngresp); |
| |
| continue; |
| } |
| |
| if (strncmp(buf, "[AdditionalInputLen", 16) == 0) { |
| if (additionalInput) { |
| PORT_ZFree(additionalInput, additionalInputLen); |
| additionalInput = NULL; |
| additionalInputLen = 0; |
| } |
| |
| if (sscanf(buf, "[AdditionalInputLen = %d]", &additionalInputLen) != 1) { |
| goto loser; |
| } |
| additionalInputLen = additionalInputLen / 8; |
| if (additionalInputLen > 0) { |
| additionalInput = PORT_Alloc(additionalInputLen); |
| } |
| fputs(buf, rngresp); |
| continue; |
| } |
| |
| if (strncmp(buf, "COUNT", 5) == 0) { |
| /* zeroize the variables for the test with this data set */ |
| if (entropyInput) { |
| memset(entropyInput, 0, entropyInputLen); |
| } |
| if (nonce) { |
| memset(nonce, 0, nonceLen); |
| } |
| if (personalizationString) { |
| memset(personalizationString, 0, personalizationStringLen); |
| } |
| if (additionalInput) { |
| memset(additionalInput, 0, additionalInputLen); |
| } |
| genResult = PR_FALSE; |
| |
| fputs(buf, rngresp); |
| continue; |
| } |
| |
| /* EntropyInputReseed = ... */ |
| if (strncmp(buf, "EntropyInputReseed", 18) == 0) { |
| if (entropyInput) { |
| memset(entropyInput, 0, entropyInputLen); |
| i = 18; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| |
| for (j = 0; isxdigit(buf[i]); i += 2, j++) { /*j<entropyInputLen*/ |
| hex_to_byteval(&buf[i], &entropyInput[j]); |
| } |
| } |
| fputs(buf, rngresp); |
| continue; |
| } |
| |
| /* AttionalInputReseed = ... */ |
| if (strncmp(buf, "AdditionalInputReseed", 21) == 0) { |
| if (additionalInput) { |
| memset(additionalInput, 0, additionalInputLen); |
| i = 21; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; isxdigit(buf[i]); i += 2, j++) { /*j<additionalInputLen*/ |
| hex_to_byteval(&buf[i], &additionalInput[j]); |
| } |
| } |
| command = RESEED; |
| fputs(buf, rngresp); |
| continue; |
| } |
| |
| /* Entropy input = ... */ |
| if (strncmp(buf, "EntropyInput", 12) == 0) { |
| i = 12; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; isxdigit(buf[i]); i += 2, j++) { /*j<entropyInputLen*/ |
| hex_to_byteval(&buf[i], &entropyInput[j]); |
| } |
| fputs(buf, rngresp); |
| continue; |
| } |
| |
| /* nouce = ... */ |
| if (strncmp(buf, "Nonce", 5) == 0) { |
| i = 5; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; isxdigit(buf[i]); i += 2, j++) { /*j<nonceLen*/ |
| hex_to_byteval(&buf[i], &nonce[j]); |
| } |
| fputs(buf, rngresp); |
| continue; |
| } |
| |
| /* Personalization string = ... */ |
| if (strncmp(buf, "PersonalizationString", 21) == 0) { |
| if (personalizationString) { |
| i = 21; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; isxdigit(buf[i]); i += 2, j++) { /*j<personalizationStringLen*/ |
| hex_to_byteval(&buf[i], &personalizationString[j]); |
| } |
| } |
| fputs(buf, rngresp); |
| command = INSTANTIATE; |
| continue; |
| } |
| |
| /* Additional input = ... */ |
| if (strncmp(buf, "AdditionalInput", 15) == 0) { |
| if (additionalInput) { |
| i = 15; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; isxdigit(buf[i]); i += 2, j++) { /*j<additionalInputLen*/ |
| hex_to_byteval(&buf[i], &additionalInput[j]); |
| } |
| } |
| if (genResult) { |
| command = RESULT; |
| } else { |
| command = GENERATE; |
| genResult = PR_TRUE; /* next time generate result */ |
| } |
| fputs(buf, rngresp); |
| continue; |
| } |
| |
| /* Returned bits = ... */ |
| if (strncmp(buf, "ReturnedBits", 12) == 0) { |
| i = 12; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; isxdigit(buf[i]); i += 2, j++) { /*j<additionalInputLen*/ |
| hex_to_byteval(&buf[i], &predictedreturn_bytes[j]); |
| } |
| |
| if (memcmp(return_bytes, |
| predictedreturn_bytes, return_bytes_len) != 0) { |
| if (debug) { |
| fprintf(rngresp, "# Generate failed:\n"); |
| fputs("# predicted=", rngresp); |
| to_hex_str(buf, predictedreturn_bytes, |
| return_bytes_len); |
| fputs(buf, rngresp); |
| fputs("\n# actual = ", rngresp); |
| fputs(buf2, rngresp); |
| fputc('\n', rngresp); |
| |
| } else { |
| fprintf(stderr, "Generate failed:\n"); |
| fputs(" predicted=", stderr); |
| to_hex_str(buf, predictedreturn_bytes, |
| return_bytes_len); |
| fputs(buf, stderr); |
| fputs("\n actual = ", stderr); |
| fputs(buf2, stderr); |
| fputc('\n', stderr); |
| } |
| } |
| memset(predictedreturn_bytes, 0, return_bytes_len); |
| |
| continue; |
| } |
| } |
| loser: |
| if (predictedreturn_bytes) { |
| PORT_Free(predictedreturn_bytes); |
| } |
| if (return_bytes) { |
| PORT_Free(return_bytes); |
| } |
| if (additionalInput) { |
| PORT_Free(additionalInput); |
| } |
| if (personalizationString) { |
| PORT_Free(personalizationString); |
| } |
| if (nonce) { |
| PORT_Free(nonce); |
| } |
| if (entropyInput) { |
| PORT_Free(entropyInput); |
| } |
| fclose(rngreq); |
| } |
| |
| /* |
| * Perform the RNG Variable Seed Test (VST) for the RNG algorithm |
| * "DSA - Generation of X", used both as specified and as a generic |
| * purpose RNG. The presence of "Q = ..." in the REQUEST file |
| * indicates we are using the algorithm as specified. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| rng_vst(char *reqfn) |
| { |
| char buf[256]; /* holds one line from the input REQUEST file. |
| * needs to be large enough to hold the longest |
| * line "XSeed = <128 hex digits>\n". |
| */ |
| FILE *rngreq; /* input stream from the REQUEST file */ |
| FILE *rngresp; /* output stream to the RESPONSE file */ |
| unsigned int i, j; |
| unsigned char Q[DSA1_SUBPRIME_LEN]; |
| PRBool hasQ = PR_FALSE; |
| unsigned int b = 0; /* 160 <= b <= 512, b is a multiple of 8 */ |
| unsigned char XKey[512 / 8]; |
| unsigned char XSeed[512 / 8]; |
| unsigned char GENX[DSA1_SIGNATURE_LEN]; |
| unsigned char DSAX[DSA1_SUBPRIME_LEN]; |
| SECStatus rv; |
| |
| rngreq = fopen(reqfn, "r"); |
| rngresp = stdout; |
| while (fgets(buf, sizeof buf, rngreq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, rngresp); |
| continue; |
| } |
| /* [Xchange - SHA1] */ |
| if (buf[0] == '[') { |
| fputs(buf, rngresp); |
| continue; |
| } |
| /* Q = ... */ |
| if (buf[0] == 'Q') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < sizeof Q; i += 2, j++) { |
| hex_to_byteval(&buf[i], &Q[j]); |
| } |
| fputs(buf, rngresp); |
| hasQ = PR_TRUE; |
| continue; |
| } |
| /* "COUNT = x" begins a new data set */ |
| if (strncmp(buf, "COUNT", 5) == 0) { |
| /* zeroize the variables for the test with this data set */ |
| b = 0; |
| memset(XKey, 0, sizeof XKey); |
| memset(XSeed, 0, sizeof XSeed); |
| fputs(buf, rngresp); |
| continue; |
| } |
| /* b = ... */ |
| if (buf[0] == 'b') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| b = atoi(&buf[i]); |
| if (b < 160 || b > 512 || b % 8 != 0) { |
| goto loser; |
| } |
| fputs(buf, rngresp); |
| continue; |
| } |
| /* XKey = ... */ |
| if (strncmp(buf, "XKey", 4) == 0) { |
| i = 4; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < b / 8; i += 2, j++) { |
| hex_to_byteval(&buf[i], &XKey[j]); |
| } |
| fputs(buf, rngresp); |
| continue; |
| } |
| /* XSeed = ... */ |
| if (strncmp(buf, "XSeed", 5) == 0) { |
| i = 5; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < b / 8; i += 2, j++) { |
| hex_to_byteval(&buf[i], &XSeed[j]); |
| } |
| fputs(buf, rngresp); |
| |
| rv = FIPS186Change_GenerateX(XKey, XSeed, GENX); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| fputs("X = ", rngresp); |
| if (hasQ) { |
| rv = FIPS186Change_ReduceModQForDSA(GENX, Q, DSAX); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| to_hex_str(buf, DSAX, sizeof DSAX); |
| } else { |
| to_hex_str(buf, GENX, sizeof GENX); |
| } |
| fputs(buf, rngresp); |
| fputc('\n', rngresp); |
| continue; |
| } |
| } |
| loser: |
| fclose(rngreq); |
| } |
| |
| /* |
| * Perform the RNG Monte Carlo Test (MCT) for the RNG algorithm |
| * "DSA - Generation of X", used both as specified and as a generic |
| * purpose RNG. The presence of "Q = ..." in the REQUEST file |
| * indicates we are using the algorithm as specified. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| rng_mct(char *reqfn) |
| { |
| char buf[256]; /* holds one line from the input REQUEST file. |
| * needs to be large enough to hold the longest |
| * line "XSeed = <128 hex digits>\n". |
| */ |
| FILE *rngreq; /* input stream from the REQUEST file */ |
| FILE *rngresp; /* output stream to the RESPONSE file */ |
| unsigned int i, j; |
| unsigned char Q[DSA1_SUBPRIME_LEN]; |
| PRBool hasQ = PR_FALSE; |
| unsigned int b = 0; /* 160 <= b <= 512, b is a multiple of 8 */ |
| unsigned char XKey[512 / 8]; |
| unsigned char XSeed[512 / 8]; |
| unsigned char GENX[2 * SHA1_LENGTH]; |
| unsigned char DSAX[DSA1_SUBPRIME_LEN]; |
| SECStatus rv; |
| |
| rngreq = fopen(reqfn, "r"); |
| rngresp = stdout; |
| while (fgets(buf, sizeof buf, rngreq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, rngresp); |
| continue; |
| } |
| /* [Xchange - SHA1] */ |
| if (buf[0] == '[') { |
| fputs(buf, rngresp); |
| continue; |
| } |
| /* Q = ... */ |
| if (buf[0] == 'Q') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < sizeof Q; i += 2, j++) { |
| hex_to_byteval(&buf[i], &Q[j]); |
| } |
| fputs(buf, rngresp); |
| hasQ = PR_TRUE; |
| continue; |
| } |
| /* "COUNT = x" begins a new data set */ |
| if (strncmp(buf, "COUNT", 5) == 0) { |
| /* zeroize the variables for the test with this data set */ |
| b = 0; |
| memset(XKey, 0, sizeof XKey); |
| memset(XSeed, 0, sizeof XSeed); |
| fputs(buf, rngresp); |
| continue; |
| } |
| /* b = ... */ |
| if (buf[0] == 'b') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| b = atoi(&buf[i]); |
| if (b < 160 || b > 512 || b % 8 != 0) { |
| goto loser; |
| } |
| fputs(buf, rngresp); |
| continue; |
| } |
| /* XKey = ... */ |
| if (strncmp(buf, "XKey", 4) == 0) { |
| i = 4; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < b / 8; i += 2, j++) { |
| hex_to_byteval(&buf[i], &XKey[j]); |
| } |
| fputs(buf, rngresp); |
| continue; |
| } |
| /* XSeed = ... */ |
| if (strncmp(buf, "XSeed", 5) == 0) { |
| unsigned int k; |
| i = 5; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < b / 8; i += 2, j++) { |
| hex_to_byteval(&buf[i], &XSeed[j]); |
| } |
| fputs(buf, rngresp); |
| |
| for (k = 0; k < 10000; k++) { |
| rv = FIPS186Change_GenerateX(XKey, XSeed, GENX); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| } |
| fputs("X = ", rngresp); |
| if (hasQ) { |
| rv = FIPS186Change_ReduceModQForDSA(GENX, Q, DSAX); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| to_hex_str(buf, DSAX, sizeof DSAX); |
| } else { |
| to_hex_str(buf, GENX, sizeof GENX); |
| } |
| fputs(buf, rngresp); |
| fputc('\n', rngresp); |
| continue; |
| } |
| } |
| loser: |
| fclose(rngreq); |
| } |
| |
| /* |
| * Calculate the SHA Message Digest |
| * |
| * MD = Message digest |
| * MDLen = length of Message Digest and SHA_Type |
| * msg = message to digest |
| * msgLen = length of message to digest |
| */ |
| SECStatus |
| sha_calcMD(unsigned char *MD, unsigned int MDLen, unsigned char *msg, unsigned int msgLen) |
| { |
| HASH_HashType hashType = sha_get_hashType(MDLen * PR_BITS_PER_BYTE); |
| |
| return fips_hashBuf(hashType, MD, msg, msgLen); |
| } |
| |
| /* |
| * Perform the SHA Monte Carlo Test |
| * |
| * MDLen = length of Message Digest and SHA_Type |
| * seed = input seed value |
| * resp = is the output response file. |
| */ |
| SECStatus |
| sha_mct_test(unsigned int MDLen, unsigned char *seed, FILE *resp) |
| { |
| int i, j; |
| unsigned int msgLen = MDLen * 3; |
| unsigned char MD_i3[HASH_LENGTH_MAX]; /* MD[i-3] */ |
| unsigned char MD_i2[HASH_LENGTH_MAX]; /* MD[i-2] */ |
| unsigned char MD_i1[HASH_LENGTH_MAX]; /* MD[i-1] */ |
| unsigned char MD_i[HASH_LENGTH_MAX]; /* MD[i] */ |
| unsigned char msg[HASH_LENGTH_MAX * 3]; |
| char buf[HASH_LENGTH_MAX * 2 + 1]; /* MAX buf MD_i as a hex string */ |
| |
| for (j = 0; j < 100; j++) { |
| /* MD_0 = MD_1 = MD_2 = seed */ |
| memcpy(MD_i3, seed, MDLen); |
| memcpy(MD_i2, seed, MDLen); |
| memcpy(MD_i1, seed, MDLen); |
| |
| for (i = 3; i < 1003; i++) { |
| /* Mi = MD[i-3] || MD [i-2] || MD [i-1] */ |
| memcpy(msg, MD_i3, MDLen); |
| memcpy(&msg[MDLen], MD_i2, MDLen); |
| memcpy(&msg[MDLen * 2], MD_i1, MDLen); |
| |
| /* MDi = SHA(Msg) */ |
| if (sha_calcMD(MD_i, MDLen, |
| msg, msgLen) != SECSuccess) { |
| return SECFailure; |
| } |
| |
| /* save MD[i-3] MD[i-2] MD[i-1] */ |
| memcpy(MD_i3, MD_i2, MDLen); |
| memcpy(MD_i2, MD_i1, MDLen); |
| memcpy(MD_i1, MD_i, MDLen); |
| } |
| |
| /* seed = MD_i */ |
| memcpy(seed, MD_i, MDLen); |
| |
| sprintf(buf, "COUNT = %d\n", j); |
| fputs(buf, resp); |
| |
| /* output MD_i */ |
| fputs("MD = ", resp); |
| to_hex_str(buf, MD_i, MDLen); |
| fputs(buf, resp); |
| fputc('\n', resp); |
| } |
| |
| return SECSuccess; |
| } |
| |
| /* |
| * Perform the SHA Tests. |
| * |
| * reqfn is the pathname of the input REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| sha_test(char *reqfn) |
| { |
| unsigned int i, j; |
| unsigned int MDlen = 0; /* the length of the Message Digest in Bytes */ |
| unsigned int msgLen = 0; /* the length of the input Message in Bytes */ |
| unsigned char *msg = NULL; /* holds the message to digest.*/ |
| size_t bufSize = 256 * 128; /*MAX buffer size */ |
| char *buf = NULL; /* holds one line from the input REQUEST file.*/ |
| unsigned char seed[HASH_LENGTH_MAX]; /* max size of seed 64 bytes */ |
| unsigned char MD[HASH_LENGTH_MAX]; /* message digest */ |
| |
| FILE *req = NULL; /* input stream from the REQUEST file */ |
| FILE *resp; /* output stream to the RESPONSE file */ |
| |
| buf = PORT_ZAlloc(bufSize); |
| if (buf == NULL) { |
| goto loser; |
| } |
| |
| /* zeroize the variables for the test with this data set */ |
| memset(seed, 0, sizeof seed); |
| |
| req = fopen(reqfn, "r"); |
| resp = stdout; |
| while (fgets(buf, bufSize, req) != NULL) { |
| |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, resp); |
| continue; |
| } |
| /* [L = Length of the Message Digest and sha_type */ |
| if (buf[0] == '[') { |
| if (strncmp(&buf[1], "L ", 1) == 0) { |
| i = 2; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| MDlen = atoi(&buf[i]); |
| fputs(buf, resp); |
| continue; |
| } |
| } |
| /* Len = Length of the Input Message Length ... */ |
| if (strncmp(buf, "Len", 3) == 0) { |
| i = 3; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| if (msg) { |
| PORT_ZFree(msg, msgLen); |
| msg = NULL; |
| } |
| msgLen = atoi(&buf[i]); /* in bits */ |
| if (msgLen % 8 != 0) { |
| fprintf(stderr, "SHA tests are incorrectly configured for " |
| "BIT oriented implementations\n"); |
| goto loser; |
| } |
| msgLen = msgLen / 8; /* convert to bytes */ |
| fputs(buf, resp); |
| msg = PORT_ZAlloc(msgLen); |
| if (msg == NULL && msgLen != 0) { |
| goto loser; |
| } |
| continue; |
| } |
| /* MSG = ... */ |
| if (strncmp(buf, "Msg", 3) == 0) { |
| i = 3; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < msgLen; i += 2, j++) { |
| hex_to_byteval(&buf[i], &msg[j]); |
| } |
| fputs(buf, resp); |
| /* calculate the Message Digest */ |
| memset(MD, 0, sizeof MD); |
| if (sha_calcMD(MD, MDlen, |
| msg, msgLen) != SECSuccess) { |
| goto loser; |
| } |
| |
| fputs("MD = ", resp); |
| to_hex_str(buf, MD, MDlen); |
| fputs(buf, resp); |
| fputc('\n', resp); |
| |
| continue; |
| } |
| /* Seed = ... */ |
| if (strncmp(buf, "Seed", 4) == 0) { |
| i = 4; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < sizeof seed; i += 2, j++) { |
| hex_to_byteval(&buf[i], &seed[j]); |
| } |
| |
| fputs(buf, resp); |
| fputc('\n', resp); |
| |
| /* do the Monte Carlo test */ |
| if (sha_mct_test(MDlen, seed, resp) != SECSuccess) { |
| goto loser; |
| } |
| |
| continue; |
| } |
| } |
| loser: |
| if (req) { |
| fclose(req); |
| } |
| if (buf) { |
| PORT_ZFree(buf, bufSize); |
| } |
| if (msg) { |
| PORT_ZFree(msg, msgLen); |
| } |
| } |
| |
| /****************************************************/ |
| /* HMAC SHA-X calc */ |
| /* hmac_computed - the computed HMAC */ |
| /* hmac_length - the length of the computed HMAC */ |
| /* secret_key - secret key to HMAC */ |
| /* secret_key_length - length of secret key, */ |
| /* message - message to HMAC */ |
| /* message_length - length ofthe message */ |
| /****************************************************/ |
| static SECStatus |
| hmac_calc(unsigned char *hmac_computed, |
| const unsigned int hmac_length, |
| const unsigned char *secret_key, |
| const unsigned int secret_key_length, |
| const unsigned char *message, |
| const unsigned int message_length, |
| const HASH_HashType hashAlg) |
| { |
| SECStatus hmac_status = SECFailure; |
| HMACContext *cx = NULL; |
| SECHashObject *hashObj = NULL; |
| unsigned int bytes_hashed = 0; |
| |
| hashObj = (SECHashObject *)HASH_GetRawHashObject(hashAlg); |
| |
| if (!hashObj) |
| return (SECFailure); |
| |
| cx = HMAC_Create(hashObj, secret_key, |
| secret_key_length, |
| PR_TRUE); /* PR_TRUE for in FIPS mode */ |
| |
| if (cx == NULL) |
| return (SECFailure); |
| |
| HMAC_Begin(cx); |
| HMAC_Update(cx, message, message_length); |
| hmac_status = HMAC_Finish(cx, hmac_computed, &bytes_hashed, |
| hmac_length); |
| |
| HMAC_Destroy(cx, PR_TRUE); |
| |
| return (hmac_status); |
| } |
| |
| /* |
| * Perform the HMAC Tests. |
| * |
| * reqfn is the pathname of the input REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| hmac_test(char *reqfn) |
| { |
| unsigned int i, j; |
| size_t bufSize = 400; /* MAX buffer size */ |
| char *buf = NULL; /* holds one line from the input REQUEST file.*/ |
| unsigned int keyLen = 0; /* Key Length */ |
| unsigned char key[200]; /* key MAX size = 184 */ |
| unsigned int msgLen = 128; /* the length of the input */ |
| /* Message is always 128 Bytes */ |
| unsigned char *msg = NULL; /* holds the message to digest.*/ |
| unsigned int HMACLen = 0; /* the length of the HMAC Bytes */ |
| unsigned int TLen = 0; /* the length of the requested */ |
| /* truncated HMAC Bytes */ |
| unsigned char HMAC[HASH_LENGTH_MAX]; /* computed HMAC */ |
| unsigned char expectedHMAC[HASH_LENGTH_MAX]; /* for .fax files that have */ |
| /* supplied known answer */ |
| HASH_HashType hash_alg = HASH_AlgNULL; /* HMAC type */ |
| |
| FILE *req = NULL; /* input stream from the REQUEST file */ |
| FILE *resp; /* output stream to the RESPONSE file */ |
| |
| buf = PORT_ZAlloc(bufSize); |
| if (buf == NULL) { |
| goto loser; |
| } |
| msg = PORT_ZAlloc(msgLen); |
| if (msg == NULL) { |
| goto loser; |
| } |
| |
| req = fopen(reqfn, "r"); |
| resp = stdout; |
| while (fgets(buf, bufSize, req) != NULL) { |
| if (strncmp(buf, "Mac", 3) == 0) { |
| i = 3; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| memset(expectedHMAC, 0, HASH_LENGTH_MAX); |
| for (j = 0; isxdigit(buf[i]); i += 2, j++) { |
| hex_to_byteval(&buf[i], &expectedHMAC[j]); |
| } |
| if (memcmp(HMAC, expectedHMAC, TLen) != 0) { |
| fprintf(stderr, "Generate failed:\n"); |
| fputs(" expected=", stderr); |
| to_hex_str(buf, expectedHMAC, |
| TLen); |
| fputs(buf, stderr); |
| fputs("\n generated=", stderr); |
| to_hex_str(buf, HMAC, |
| TLen); |
| fputs(buf, stderr); |
| fputc('\n', stderr); |
| } |
| } |
| |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, resp); |
| continue; |
| } |
| /* [L = Length of the MAC and HASH_type */ |
| if (buf[0] == '[') { |
| if (strncmp(&buf[1], "L ", 1) == 0) { |
| i = 2; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| /* HMACLen will get reused for Tlen */ |
| HMACLen = atoi(&buf[i]); |
| hash_alg = sha_get_hashType(HMACLen * PR_BITS_PER_BYTE); |
| if (hash_alg == HASH_AlgNULL) { |
| goto loser; |
| } |
| fputs(buf, resp); |
| continue; |
| } |
| } |
| /* Count = test iteration number*/ |
| if (strncmp(buf, "Count ", 5) == 0) { |
| /* count can just be put into resp file */ |
| fputs(buf, resp); |
| /* zeroize the variables for the test with this data set */ |
| keyLen = 0; |
| TLen = 0; |
| memset(key, 0, sizeof key); |
| memset(msg, 0, msgLen); |
| memset(HMAC, 0, sizeof HMAC); |
| continue; |
| } |
| /* KLen = Length of the Input Secret Key ... */ |
| if (strncmp(buf, "Klen", 4) == 0) { |
| i = 4; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| keyLen = atoi(&buf[i]); /* in bytes */ |
| fputs(buf, resp); |
| continue; |
| } |
| /* key = the secret key for the key to MAC */ |
| if (strncmp(buf, "Key", 3) == 0) { |
| i = 3; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < keyLen; i += 2, j++) { |
| hex_to_byteval(&buf[i], &key[j]); |
| } |
| fputs(buf, resp); |
| } |
| /* TLen = Length of the calculated HMAC */ |
| if (strncmp(buf, "Tlen", 4) == 0) { |
| i = 4; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| TLen = atoi(&buf[i]); /* in bytes */ |
| fputs(buf, resp); |
| continue; |
| } |
| /* MSG = to HMAC always 128 bytes for these tests */ |
| if (strncmp(buf, "Msg", 3) == 0) { |
| i = 3; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < msgLen; i += 2, j++) { |
| hex_to_byteval(&buf[i], &msg[j]); |
| } |
| fputs(buf, resp); |
| /* calculate the HMAC and output */ |
| if (hmac_calc(HMAC, HMACLen, key, keyLen, |
| msg, msgLen, hash_alg) != SECSuccess) { |
| goto loser; |
| } |
| fputs("Mac = ", resp); |
| to_hex_str(buf, HMAC, TLen); |
| fputs(buf, resp); |
| fputc('\n', resp); |
| continue; |
| } |
| } |
| loser: |
| if (req) { |
| fclose(req); |
| } |
| if (buf) { |
| PORT_ZFree(buf, bufSize); |
| } |
| if (msg) { |
| PORT_ZFree(msg, msgLen); |
| } |
| } |
| |
| /* |
| * Perform the DSA Key Pair Generation Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| dsa_keypair_test(char *reqfn) |
| { |
| char buf[800]; /* holds one line from the input REQUEST file |
| * or to the output RESPONSE file. |
| * 800 to hold (384 public key (x2 for HEX) + 1'\n' |
| */ |
| FILE *dsareq; /* input stream from the REQUEST file */ |
| FILE *dsaresp; /* output stream to the RESPONSE file */ |
| int count; |
| int N; |
| int L; |
| int i; |
| PQGParams *pqg = NULL; |
| PQGVerify *vfy = NULL; |
| PRBool use_dsa1 = PR_FALSE; |
| int keySizeIndex; /* index for valid key sizes */ |
| |
| dsareq = fopen(reqfn, "r"); |
| dsaresp = stdout; |
| while (fgets(buf, sizeof buf, dsareq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* [Mod = x] */ |
| if (buf[0] == '[') { |
| if (pqg != NULL) { |
| PQG_DestroyParams(pqg); |
| pqg = NULL; |
| } |
| if (vfy != NULL) { |
| PQG_DestroyVerify(vfy); |
| vfy = NULL; |
| } |
| |
| if (sscanf(buf, "[mod = L=%d, N=%d]", &L, &N) != 2) { |
| use_dsa1 = PR_TRUE; |
| if (sscanf(buf, "[mod = %d]", &L) != 1) { |
| goto loser; |
| } |
| } |
| fputs(buf, dsaresp); |
| fputc('\n', dsaresp); |
| |
| if (use_dsa1) { |
| /************************************************************* |
| * PQG_ParamGenSeedLen doesn't take a key size, it takes an |
| * index that points to a valid key size. |
| */ |
| keySizeIndex = PQG_PBITS_TO_INDEX(L); |
| if (keySizeIndex == -1 || L < 512 || L > 1024) { |
| fprintf(dsaresp, |
| "DSA key size must be a multiple of 64 between 512 " |
| "and 1024, inclusive"); |
| goto loser; |
| } |
| |
| /* Generate the parameters P, Q, and G */ |
| if (PQG_ParamGenSeedLen(keySizeIndex, PQG_TEST_SEED_BYTES, |
| &pqg, &vfy) != |
| SECSuccess) { |
| fprintf(dsaresp, |
| "ERROR: Unable to generate PQG parameters"); |
| goto loser; |
| } |
| } else { |
| if (PQG_ParamGenV2(L, N, N, &pqg, &vfy) != SECSuccess) { |
| fprintf(dsaresp, |
| "ERROR: Unable to generate PQG parameters"); |
| goto loser; |
| } |
| } |
| |
| /* output P, Q, and G */ |
| to_hex_str(buf, pqg->prime.data, pqg->prime.len); |
| fprintf(dsaresp, "P = %s\n", buf); |
| to_hex_str(buf, pqg->subPrime.data, pqg->subPrime.len); |
| fprintf(dsaresp, "Q = %s\n", buf); |
| to_hex_str(buf, pqg->base.data, pqg->base.len); |
| fprintf(dsaresp, "G = %s\n\n", buf); |
| continue; |
| } |
| /* N = ...*/ |
| if (buf[0] == 'N') { |
| |
| if (sscanf(buf, "N = %d", &count) != 1) { |
| goto loser; |
| } |
| /* Generate a DSA key, and output the key pair for N times */ |
| for (i = 0; i < count; i++) { |
| DSAPrivateKey *dsakey = NULL; |
| if (DSA_NewKey(pqg, &dsakey) != SECSuccess) { |
| fprintf(dsaresp, "ERROR: Unable to generate DSA key"); |
| goto loser; |
| } |
| to_hex_str(buf, dsakey->privateValue.data, |
| dsakey->privateValue.len); |
| fprintf(dsaresp, "X = %s\n", buf); |
| to_hex_str(buf, dsakey->publicValue.data, |
| dsakey->publicValue.len); |
| fprintf(dsaresp, "Y = %s\n\n", buf); |
| PORT_FreeArena(dsakey->params.arena, PR_TRUE); |
| dsakey = NULL; |
| } |
| continue; |
| } |
| } |
| loser: |
| fclose(dsareq); |
| } |
| |
| /* |
| * pqg generation type |
| */ |
| typedef enum { |
| FIPS186_1, /* Generate/Verify P,Q & G according to FIPS 186-1 */ |
| A_1_2_1, /* Generate Provable P & Q */ |
| A_1_1_3, /* Verify Probable P & Q */ |
| A_1_2_2, /* Verify Provable P & Q */ |
| A_2_1, /* Generate Unverifiable G */ |
| A_2_2, /* Assure Unverifiable G */ |
| A_2_3, /* Generate Verifiable G */ |
| A_2_4 /* Verify Verifiable G */ |
| } dsa_pqg_type; |
| |
| /* |
| * Perform the DSA Domain Parameter Validation Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| dsa_pqgver_test(char *reqfn) |
| { |
| char buf[800]; /* holds one line from the input REQUEST file |
| * or to the output RESPONSE file. |
| * 800 to hold (384 public key (x2 for HEX) + P = ... |
| */ |
| FILE *dsareq; /* input stream from the REQUEST file */ |
| FILE *dsaresp; /* output stream to the RESPONSE file */ |
| int N; |
| int L; |
| unsigned int i, j; |
| PQGParams pqg; |
| PQGVerify vfy; |
| unsigned int pghSize = 0; /* size for p, g, and h */ |
| dsa_pqg_type type = FIPS186_1; |
| |
| dsareq = fopen(reqfn, "r"); |
| dsaresp = stdout; |
| memset(&pqg, 0, sizeof(pqg)); |
| memset(&vfy, 0, sizeof(vfy)); |
| |
| while (fgets(buf, sizeof buf, dsareq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* [A.xxxxx ] */ |
| if (buf[0] == '[' && buf[1] == 'A') { |
| |
| if (strncmp(&buf[1], "A.1.1.3", 7) == 0) { |
| type = A_1_1_3; |
| } else if (strncmp(&buf[1], "A.2.2", 5) == 0) { |
| type = A_2_2; |
| } else if (strncmp(&buf[1], "A.2.4", 5) == 0) { |
| type = A_2_4; |
| } else if (strncmp(&buf[1], "A.1.2.2", 7) == 0) { |
| type = A_1_2_2; |
| /* validate our output from PQGGEN */ |
| } else if (strncmp(&buf[1], "A.1.1.2", 7) == 0) { |
| type = A_2_4; /* validate PQ and G together */ |
| } else { |
| fprintf(stderr, "Unknown dsa ver test %s\n", &buf[1]); |
| exit(1); |
| } |
| |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* [Mod = x] */ |
| if (buf[0] == '[') { |
| |
| if (type == FIPS186_1) { |
| N = 160; |
| if (sscanf(buf, "[mod = %d]", &L) != 1) { |
| goto loser; |
| } |
| } else if (sscanf(buf, "[mod = L=%d, N=%d", &L, &N) != 2) { |
| goto loser; |
| } |
| |
| if (pqg.prime.data) { /* P */ |
| SECITEM_ZfreeItem(&pqg.prime, PR_FALSE); |
| } |
| if (pqg.subPrime.data) { /* Q */ |
| SECITEM_ZfreeItem(&pqg.subPrime, PR_FALSE); |
| } |
| if (pqg.base.data) { /* G */ |
| SECITEM_ZfreeItem(&pqg.base, PR_FALSE); |
| } |
| if (vfy.seed.data) { /* seed */ |
| SECITEM_ZfreeItem(&vfy.seed, PR_FALSE); |
| } |
| if (vfy.h.data) { /* H */ |
| SECITEM_ZfreeItem(&vfy.h, PR_FALSE); |
| } |
| |
| fputs(buf, dsaresp); |
| |
| /*calculate the size of p, g, and h then allocate items */ |
| pghSize = L / 8; |
| |
| pqg.base.data = vfy.h.data = NULL; |
| vfy.seed.len = pqg.base.len = vfy.h.len = 0; |
| SECITEM_AllocItem(NULL, &pqg.prime, pghSize); |
| SECITEM_AllocItem(NULL, &vfy.seed, pghSize * 3); |
| if (type == A_2_2) { |
| SECITEM_AllocItem(NULL, &vfy.h, pghSize); |
| vfy.h.len = pghSize; |
| } else if (type == A_2_4) { |
| SECITEM_AllocItem(NULL, &vfy.h, 1); |
| vfy.h.len = 1; |
| } |
| pqg.prime.len = pghSize; |
| /* q is always N bits */ |
| SECITEM_AllocItem(NULL, &pqg.subPrime, N / 8); |
| pqg.subPrime.len = N / 8; |
| vfy.counter = -1; |
| |
| continue; |
| } |
| /* P = ... */ |
| if (buf[0] == 'P') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < pqg.prime.len; i += 2, j++) { |
| hex_to_byteval(&buf[i], &pqg.prime.data[j]); |
| } |
| |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* Q = ... */ |
| if (buf[0] == 'Q') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < pqg.subPrime.len; i += 2, j++) { |
| hex_to_byteval(&buf[i], &pqg.subPrime.data[j]); |
| } |
| |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* G = ... */ |
| if (buf[0] == 'G') { |
| i = 1; |
| if (pqg.base.data) { |
| SECITEM_ZfreeItem(&pqg.base, PR_FALSE); |
| } |
| SECITEM_AllocItem(NULL, &pqg.base, pghSize); |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < pqg.base.len; i += 2, j++) { |
| hex_to_byteval(&buf[i], &pqg.base.data[j]); |
| } |
| |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* Seed = ... or domain_parameter_seed = ... */ |
| if (strncmp(buf, "Seed", 4) == 0) { |
| i = 4; |
| } else if (strncmp(buf, "domain_parameter_seed", 21) == 0) { |
| i = 21; |
| } else if (strncmp(buf, "firstseed", 9) == 0) { |
| i = 9; |
| } else { |
| i = 0; |
| } |
| if (i) { |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; isxdigit(buf[i]); i += 2, j++) { |
| hex_to_byteval(&buf[i], &vfy.seed.data[j]); |
| } |
| vfy.seed.len = j; |
| |
| fputs(buf, dsaresp); |
| if (type == A_2_4) { |
| SECStatus result; |
| |
| /* Verify the Parameters */ |
| SECStatus rv = PQG_VerifyParams(&pqg, &vfy, &result); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| if (result == SECSuccess) { |
| fprintf(dsaresp, "Result = P\n"); |
| } else { |
| fprintf(dsaresp, "Result = F\n"); |
| } |
| } |
| continue; |
| } |
| if ((strncmp(buf, "pseed", 5) == 0) || |
| (strncmp(buf, "qseed", 5) == 0)) { |
| i = 5; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = vfy.seed.len; isxdigit(buf[i]); i += 2, j++) { |
| hex_to_byteval(&buf[i], &vfy.seed.data[j]); |
| } |
| vfy.seed.len = j; |
| fputs(buf, dsaresp); |
| |
| continue; |
| } |
| if (strncmp(buf, "index", 4) == 0) { |
| i = 5; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| hex_to_byteval(&buf[i], &vfy.h.data[0]); |
| vfy.h.len = 1; |
| fputs(buf, dsaresp); |
| } |
| |
| /* c = ... or counter=*/ |
| if (buf[0] == 'c') { |
| if (strncmp(buf, "counter", 7) == 0) { |
| if (sscanf(buf, "counter = %u", &vfy.counter) != 1) { |
| goto loser; |
| } |
| } else { |
| if (sscanf(buf, "c = %u", &vfy.counter) != 1) { |
| goto loser; |
| } |
| } |
| |
| fputs(buf, dsaresp); |
| if (type == A_1_1_3) { |
| SECStatus result; |
| /* only verify P and Q, we have everything now. do it */ |
| SECStatus rv = PQG_VerifyParams(&pqg, &vfy, &result); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| if (result == SECSuccess) { |
| fprintf(dsaresp, "Result = P\n"); |
| } else { |
| fprintf(dsaresp, "Result = F\n"); |
| } |
| fprintf(dsaresp, "\n"); |
| } |
| continue; |
| } |
| if (strncmp(buf, "pgen_counter", 12) == 0) { |
| if (sscanf(buf, "pgen_counter = %u", &vfy.counter) != 1) { |
| goto loser; |
| } |
| fputs(buf, dsaresp); |
| continue; |
| } |
| if (strncmp(buf, "qgen_counter", 12) == 0) { |
| fputs(buf, dsaresp); |
| if (type == A_1_2_2) { |
| SECStatus result; |
| /* only verify P and Q, we have everything now. do it */ |
| SECStatus rv = PQG_VerifyParams(&pqg, &vfy, &result); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| if (result == SECSuccess) { |
| fprintf(dsaresp, "Result = P\n"); |
| } else { |
| fprintf(dsaresp, "Result = F\n"); |
| } |
| fprintf(dsaresp, "\n"); |
| } |
| continue; |
| } |
| /* H = ... */ |
| if (buf[0] == 'H') { |
| SECStatus rv, result = SECFailure; |
| |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; isxdigit(buf[i]); i += 2, j++) { |
| hex_to_byteval(&buf[i], &vfy.h.data[j]); |
| } |
| vfy.h.len = j; |
| fputs(buf, dsaresp); |
| |
| /* this should be a byte value. Remove the leading zeros. If |
| * it doesn't reduce to a byte, PQG_VerifyParams will catch it |
| if (type == A_2_2) { |
| data_save = vfy.h.data; |
| while(vfy.h.data[0] && (vfy.h.len > 1)) { |
| vfy.h.data++; |
| vfy.h.len--; |
| } |
| } */ |
| |
| /* Verify the Parameters */ |
| rv = PQG_VerifyParams(&pqg, &vfy, &result); |
| if (rv != SECSuccess) { |
| goto loser; |
| } |
| if (result == SECSuccess) { |
| fprintf(dsaresp, "Result = P\n"); |
| } else { |
| fprintf(dsaresp, "Result = F\n"); |
| } |
| fprintf(dsaresp, "\n"); |
| continue; |
| } |
| } |
| loser: |
| fclose(dsareq); |
| if (pqg.prime.data) { /* P */ |
| SECITEM_ZfreeItem(&pqg.prime, PR_FALSE); |
| } |
| if (pqg.subPrime.data) { /* Q */ |
| SECITEM_ZfreeItem(&pqg.subPrime, PR_FALSE); |
| } |
| if (pqg.base.data) { /* G */ |
| SECITEM_ZfreeItem(&pqg.base, PR_FALSE); |
| } |
| if (vfy.seed.data) { /* seed */ |
| SECITEM_ZfreeItem(&vfy.seed, PR_FALSE); |
| } |
| if (vfy.h.data) { /* H */ |
| SECITEM_ZfreeItem(&vfy.h, PR_FALSE); |
| } |
| } |
| |
| /* |
| * Perform the DSA Public Key Validation Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| dsa_pqggen_test(char *reqfn) |
| { |
| char buf[800]; /* holds one line from the input REQUEST file |
| * or to the output RESPONSE file. |
| * 800 to hold seed = (384 public key (x2 for HEX) |
| */ |
| FILE *dsareq; /* input stream from the REQUEST file */ |
| FILE *dsaresp; /* output stream to the RESPONSE file */ |
| int count; /* number of times to generate parameters */ |
| int N; |
| int L; |
| int i; |
| unsigned int j; |
| int output_g = 1; |
| PQGParams *pqg = NULL; |
| PQGVerify *vfy = NULL; |
| unsigned int keySizeIndex = 0; |
| dsa_pqg_type type = FIPS186_1; |
| |
| dsareq = fopen(reqfn, "r"); |
| dsaresp = stdout; |
| while (fgets(buf, sizeof buf, dsareq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* [A.xxxxx ] */ |
| if (buf[0] == '[' && buf[1] == 'A') { |
| if (strncmp(&buf[1], "A.1.1.2", 7) == 0) { |
| fprintf(stderr, "NSS does Generate Probablistic Primes\n"); |
| exit(1); |
| } else if (strncmp(&buf[1], "A.2.1", 5) == 0) { |
| type = A_1_2_1; |
| output_g = 1; |
| exit(1); |
| } else if (strncmp(&buf[1], "A.2.3", 5) == 0) { |
| fprintf(stderr, "NSS only Generates G with P&Q\n"); |
| exit(1); |
| } else if (strncmp(&buf[1], "A.1.2.1", 7) == 0) { |
| type = A_1_2_1; |
| output_g = 0; |
| } else { |
| fprintf(stderr, "Unknown dsa pqggen test %s\n", &buf[1]); |
| exit(1); |
| } |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* [Mod = ... ] */ |
| if (buf[0] == '[') { |
| |
| if (type == FIPS186_1) { |
| N = 160; |
| if (sscanf(buf, "[mod = %d]", &L) != 1) { |
| goto loser; |
| } |
| } else if (sscanf(buf, "[mod = L=%d, N=%d", &L, &N) != 2) { |
| goto loser; |
| } |
| |
| fputs(buf, dsaresp); |
| fputc('\n', dsaresp); |
| |
| if (type == FIPS186_1) { |
| /************************************************************ |
| * PQG_ParamGenSeedLen doesn't take a key size, it takes an |
| * index that points to a valid key size. |
| */ |
| keySizeIndex = PQG_PBITS_TO_INDEX(L); |
| if (keySizeIndex == -1 || L < 512 || L > 1024) { |
| fprintf(dsaresp, |
| "DSA key size must be a multiple of 64 between 512 " |
| "and 1024, inclusive"); |
| goto loser; |
| } |
| } |
| continue; |
| } |
| /* N = ... */ |
| if (buf[0] == 'N') { |
| if (strncmp(buf, "Num", 3) == 0) { |
| if (sscanf(buf, "Num = %d", &count) != 1) { |
| goto loser; |
| } |
| } else if (sscanf(buf, "N = %d", &count) != 1) { |
| goto loser; |
| } |
| for (i = 0; i < count; i++) { |
| SECStatus rv; |
| |
| if (type == FIPS186_1) { |
| rv = PQG_ParamGenSeedLen(keySizeIndex, PQG_TEST_SEED_BYTES, |
| &pqg, &vfy); |
| } else { |
| rv = PQG_ParamGenV2(L, N, N, &pqg, &vfy); |
| } |
| if (rv != SECSuccess) { |
| fprintf(dsaresp, |
| "ERROR: Unable to generate PQG parameters"); |
| goto loser; |
| } |
| to_hex_str(buf, pqg->prime.data, pqg->prime.len); |
| fprintf(dsaresp, "P = %s\n", buf); |
| to_hex_str(buf, pqg->subPrime.data, pqg->subPrime.len); |
| fprintf(dsaresp, "Q = %s\n", buf); |
| if (output_g) { |
| to_hex_str(buf, pqg->base.data, pqg->base.len); |
| fprintf(dsaresp, "G = %s\n", buf); |
| } |
| if (type == FIPS186_1) { |
| to_hex_str(buf, vfy->seed.data, vfy->seed.len); |
| fprintf(dsaresp, "Seed = %s\n", buf); |
| fprintf(dsaresp, "c = %d\n", vfy->counter); |
| to_hex_str(buf, vfy->h.data, vfy->h.len); |
| fputs("H = ", dsaresp); |
| for (j = vfy->h.len; j < pqg->prime.len; j++) { |
| fprintf(dsaresp, "00"); |
| } |
| fprintf(dsaresp, "%s\n", buf); |
| } else { |
| unsigned int seedlen = vfy->seed.len / 2; |
| unsigned int pgen_counter = vfy->counter >> 16; |
| unsigned int qgen_counter = vfy->counter & 0xffff; |
| /*fprintf(dsaresp, "index = %02x\n", vfy->h.data[0]); */ |
| to_hex_str(buf, vfy->seed.data, seedlen); |
| fprintf(dsaresp, "pseed = %s\n", buf); |
| to_hex_str(buf, vfy->seed.data + seedlen, seedlen); |
| fprintf(dsaresp, "qseed = %s\n", buf); |
| fprintf(dsaresp, "pgen_counter = %d\n", pgen_counter); |
| fprintf(dsaresp, "qgen_counter = %d\n", qgen_counter); |
| if (output_g) { |
| to_hex_str(buf, vfy->seed.data, vfy->seed.len); |
| fprintf(dsaresp, "domain_parameter_seed = %s\n", buf); |
| fprintf(dsaresp, "index = %02x\n", vfy->h.data[0]); |
| } |
| } |
| fputc('\n', dsaresp); |
| if (pqg != NULL) { |
| PQG_DestroyParams(pqg); |
| pqg = NULL; |
| } |
| if (vfy != NULL) { |
| PQG_DestroyVerify(vfy); |
| vfy = NULL; |
| } |
| } |
| |
| continue; |
| } |
| } |
| loser: |
| fclose(dsareq); |
| if (pqg != NULL) { |
| PQG_DestroyParams(pqg); |
| } |
| if (vfy != NULL) { |
| PQG_DestroyVerify(vfy); |
| } |
| } |
| |
| /* |
| * Perform the DSA Signature Generation Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| dsa_siggen_test(char *reqfn) |
| { |
| char buf[800]; /* holds one line from the input REQUEST file |
| * or to the output RESPONSE file. |
| * max for Msg = .... |
| */ |
| FILE *dsareq; /* input stream from the REQUEST file */ |
| FILE *dsaresp; /* output stream to the RESPONSE file */ |
| int modulus; |
| int L; |
| int N; |
| int i, j; |
| PRBool use_dsa1 = PR_FALSE; |
| PQGParams *pqg = NULL; |
| PQGVerify *vfy = NULL; |
| DSAPrivateKey *dsakey = NULL; |
| int keySizeIndex; /* index for valid key sizes */ |
| unsigned char hashBuf[HASH_LENGTH_MAX]; /* SHA-x hash (160-512 bits) */ |
| unsigned char sig[DSA_MAX_SIGNATURE_LEN]; |
| SECItem digest, signature; |
| HASH_HashType hashType = HASH_AlgNULL; |
| int hashNum = 0; |
| |
| dsareq = fopen(reqfn, "r"); |
| dsaresp = stdout; |
| |
| while (fgets(buf, sizeof buf, dsareq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* [Mod = x] */ |
| if (buf[0] == '[') { |
| if (pqg != NULL) { |
| PQG_DestroyParams(pqg); |
| pqg = NULL; |
| } |
| if (vfy != NULL) { |
| PQG_DestroyVerify(vfy); |
| vfy = NULL; |
| } |
| if (dsakey != NULL) { |
| PORT_FreeArena(dsakey->params.arena, PR_TRUE); |
| dsakey = NULL; |
| } |
| |
| if (sscanf(buf, "[mod = L=%d, N=%d, SHA-%d]", &L, &N, |
| &hashNum) != 3) { |
| use_dsa1 = PR_TRUE; |
| hashNum = 1; |
| if (sscanf(buf, "[mod = %d]", &modulus) != 1) { |
| goto loser; |
| } |
| } |
| fputs(buf, dsaresp); |
| fputc('\n', dsaresp); |
| |
| /**************************************************************** |
| * PQG_ParamGenSeedLen doesn't take a key size, it takes an index |
| * that points to a valid key size. |
| */ |
| if (use_dsa1) { |
| keySizeIndex = PQG_PBITS_TO_INDEX(modulus); |
| if (keySizeIndex == -1 || modulus < 512 || modulus > 1024) { |
| fprintf(dsaresp, |
| "DSA key size must be a multiple of 64 between 512 " |
| "and 1024, inclusive"); |
| goto loser; |
| } |
| /* Generate PQG and output PQG */ |
| if (PQG_ParamGenSeedLen(keySizeIndex, PQG_TEST_SEED_BYTES, |
| &pqg, &vfy) != |
| SECSuccess) { |
| fprintf(dsaresp, |
| "ERROR: Unable to generate PQG parameters"); |
| goto loser; |
| } |
| } else { |
| if (PQG_ParamGenV2(L, N, N, &pqg, &vfy) != SECSuccess) { |
| fprintf(dsaresp, |
| "ERROR: Unable to generate PQG parameters"); |
| goto loser; |
| } |
| } |
| to_hex_str(buf, pqg->prime.data, pqg->prime.len); |
| fprintf(dsaresp, "P = %s\n", buf); |
| to_hex_str(buf, pqg->subPrime.data, pqg->subPrime.len); |
| fprintf(dsaresp, "Q = %s\n", buf); |
| to_hex_str(buf, pqg->base.data, pqg->base.len); |
| fprintf(dsaresp, "G = %s\n", buf); |
| |
| /* create DSA Key */ |
| if (DSA_NewKey(pqg, &dsakey) != SECSuccess) { |
| fprintf(dsaresp, "ERROR: Unable to generate DSA key"); |
| goto loser; |
| } |
| |
| hashType = sha_get_hashType(hashNum); |
| if (hashType == HASH_AlgNULL) { |
| fprintf(dsaresp, "ERROR: invalid hash (SHA-%d)", hashNum); |
| goto loser; |
| } |
| continue; |
| } |
| |
| /* Msg = ... */ |
| if (strncmp(buf, "Msg", 3) == 0) { |
| unsigned char msg[128]; /* MAX msg 128 */ |
| unsigned int len = 0; |
| |
| if (hashType == HASH_AlgNULL) { |
| fprintf(dsaresp, "ERROR: Hash Alg not set"); |
| goto loser; |
| } |
| |
| memset(hashBuf, 0, sizeof hashBuf); |
| memset(sig, 0, sizeof sig); |
| |
| i = 3; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; isxdigit(buf[i]); i += 2, j++) { |
| hex_to_byteval(&buf[i], &msg[j]); |
| } |
| if (fips_hashBuf(hashType, hashBuf, msg, j) != SECSuccess) { |
| fprintf(dsaresp, "ERROR: Unable to generate SHA% digest", |
| hashNum); |
| goto loser; |
| } |
| |
| digest.type = siBuffer; |
| digest.data = hashBuf; |
| digest.len = fips_hashLen(hashType); |
| signature.type = siBuffer; |
| signature.data = sig; |
| signature.len = sizeof sig; |
| |
| if (DSA_SignDigest(dsakey, &signature, &digest) != SECSuccess) { |
| fprintf(dsaresp, "ERROR: Unable to generate DSA signature"); |
| goto loser; |
| } |
| len = signature.len; |
| if (len % 2 != 0) { |
| goto loser; |
| } |
| len = len / 2; |
| |
| /* output the orginal Msg, and generated Y, R, and S */ |
| fputs(buf, dsaresp); |
| to_hex_str(buf, dsakey->publicValue.data, |
| dsakey->publicValue.len); |
| fprintf(dsaresp, "Y = %s\n", buf); |
| to_hex_str(buf, &signature.data[0], len); |
| fprintf(dsaresp, "R = %s\n", buf); |
| to_hex_str(buf, &signature.data[len], len); |
| fprintf(dsaresp, "S = %s\n", buf); |
| fputc('\n', dsaresp); |
| continue; |
| } |
| } |
| loser: |
| fclose(dsareq); |
| if (pqg != NULL) { |
| PQG_DestroyParams(pqg); |
| pqg = NULL; |
| } |
| if (vfy != NULL) { |
| PQG_DestroyVerify(vfy); |
| vfy = NULL; |
| } |
| if (dsakey) { |
| PORT_FreeArena(dsakey->params.arena, PR_TRUE); |
| dsakey = NULL; |
| } |
| } |
| |
| /* |
| * Perform the DSA Signature Verification Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| dsa_sigver_test(char *reqfn) |
| { |
| char buf[800]; /* holds one line from the input REQUEST file |
| * or to the output RESPONSE file. |
| * max for Msg = .... |
| */ |
| FILE *dsareq; /* input stream from the REQUEST file */ |
| FILE *dsaresp; /* output stream to the RESPONSE file */ |
| int L; |
| int N; |
| unsigned int i, j; |
| SECItem digest, signature; |
| DSAPublicKey pubkey; |
| unsigned int pgySize; /* size for p, g, and y */ |
| unsigned char hashBuf[HASH_LENGTH_MAX]; /* SHA-x hash (160-512 bits) */ |
| unsigned char sig[DSA_MAX_SIGNATURE_LEN]; |
| HASH_HashType hashType = HASH_AlgNULL; |
| int hashNum = 0; |
| |
| dsareq = fopen(reqfn, "r"); |
| dsaresp = stdout; |
| memset(&pubkey, 0, sizeof(pubkey)); |
| |
| while (fgets(buf, sizeof buf, dsareq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* [Mod = x] */ |
| if (buf[0] == '[') { |
| |
| if (sscanf(buf, "[mod = L=%d, N=%d, SHA-%d]", &L, &N, |
| &hashNum) != 3) { |
| N = 160; |
| hashNum = 1; |
| if (sscanf(buf, "[mod = %d]", &L) != 1) { |
| goto loser; |
| } |
| } |
| |
| if (pubkey.params.prime.data) { /* P */ |
| SECITEM_ZfreeItem(&pubkey.params.prime, PR_FALSE); |
| } |
| if (pubkey.params.subPrime.data) { /* Q */ |
| SECITEM_ZfreeItem(&pubkey.params.subPrime, PR_FALSE); |
| } |
| if (pubkey.params.base.data) { /* G */ |
| SECITEM_ZfreeItem(&pubkey.params.base, PR_FALSE); |
| } |
| if (pubkey.publicValue.data) { /* Y */ |
| SECITEM_ZfreeItem(&pubkey.publicValue, PR_FALSE); |
| } |
| fputs(buf, dsaresp); |
| |
| /* calculate the size of p, g, and y then allocate items */ |
| pgySize = L / 8; |
| SECITEM_AllocItem(NULL, &pubkey.params.prime, pgySize); |
| SECITEM_AllocItem(NULL, &pubkey.params.base, pgySize); |
| SECITEM_AllocItem(NULL, &pubkey.publicValue, pgySize); |
| pubkey.params.prime.len = pubkey.params.base.len = pgySize; |
| pubkey.publicValue.len = pgySize; |
| |
| /* q always N/8 bytes */ |
| SECITEM_AllocItem(NULL, &pubkey.params.subPrime, N / 8); |
| pubkey.params.subPrime.len = N / 8; |
| |
| hashType = sha_get_hashType(hashNum); |
| if (hashType == HASH_AlgNULL) { |
| fprintf(dsaresp, "ERROR: invalid hash (SHA-%d)", hashNum); |
| goto loser; |
| } |
| |
| continue; |
| } |
| /* P = ... */ |
| if (buf[0] == 'P') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| memset(pubkey.params.prime.data, 0, pubkey.params.prime.len); |
| for (j = 0; j < pubkey.params.prime.len; i += 2, j++) { |
| hex_to_byteval(&buf[i], &pubkey.params.prime.data[j]); |
| } |
| |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* Q = ... */ |
| if (buf[0] == 'Q') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| memset(pubkey.params.subPrime.data, 0, pubkey.params.subPrime.len); |
| for (j = 0; j < pubkey.params.subPrime.len; i += 2, j++) { |
| hex_to_byteval(&buf[i], &pubkey.params.subPrime.data[j]); |
| } |
| |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* G = ... */ |
| if (buf[0] == 'G') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| memset(pubkey.params.base.data, 0, pubkey.params.base.len); |
| for (j = 0; j < pubkey.params.base.len; i += 2, j++) { |
| hex_to_byteval(&buf[i], &pubkey.params.base.data[j]); |
| } |
| |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* Msg = ... */ |
| if (strncmp(buf, "Msg", 3) == 0) { |
| unsigned char msg[128]; /* MAX msg 128 */ |
| memset(hashBuf, 0, sizeof hashBuf); |
| |
| if (hashType == HASH_AlgNULL) { |
| fprintf(dsaresp, "ERROR: Hash Alg not set"); |
| 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], &msg[j]); |
| } |
| if (fips_hashBuf(hashType, hashBuf, msg, j) != SECSuccess) { |
| fprintf(dsaresp, "ERROR: Unable to generate SHA-%d digest", |
| hashNum); |
| goto loser; |
| } |
| |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* Y = ... */ |
| if (buf[0] == 'Y') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| memset(pubkey.publicValue.data, 0, pubkey.params.subPrime.len); |
| for (j = 0; j < pubkey.publicValue.len; i += 2, j++) { |
| hex_to_byteval(&buf[i], &pubkey.publicValue.data[j]); |
| } |
| |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* R = ... */ |
| if (buf[0] == 'R') { |
| memset(sig, 0, sizeof sig); |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < pubkey.params.subPrime.len; i += 2, j++) { |
| hex_to_byteval(&buf[i], &sig[j]); |
| } |
| |
| fputs(buf, dsaresp); |
| continue; |
| } |
| |
| /* S = ... */ |
| if (buf[0] == 'S') { |
| if (hashType == HASH_AlgNULL) { |
| fprintf(dsaresp, "ERROR: Hash Alg not set"); |
| goto loser; |
| } |
| |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = pubkey.params.subPrime.len; |
| j < pubkey.params.subPrime.len * 2; i += 2, j++) { |
| hex_to_byteval(&buf[i], &sig[j]); |
| } |
| fputs(buf, dsaresp); |
| |
| digest.type = siBuffer; |
| digest.data = hashBuf; |
| digest.len = fips_hashLen(hashType); |
| signature.type = siBuffer; |
| signature.data = sig; |
| signature.len = pubkey.params.subPrime.len * 2; |
| |
| if (DSA_VerifyDigest(&pubkey, &signature, &digest) == SECSuccess) { |
| fprintf(dsaresp, "Result = P\n"); |
| } else { |
| fprintf(dsaresp, "Result = F\n"); |
| } |
| fprintf(dsaresp, "\n"); |
| continue; |
| } |
| } |
| loser: |
| fclose(dsareq); |
| if (pubkey.params.prime.data) { /* P */ |
| SECITEM_ZfreeItem(&pubkey.params.prime, PR_FALSE); |
| } |
| if (pubkey.params.subPrime.data) { /* Q */ |
| SECITEM_ZfreeItem(&pubkey.params.subPrime, PR_FALSE); |
| } |
| if (pubkey.params.base.data) { /* G */ |
| SECITEM_ZfreeItem(&pubkey.params.base, PR_FALSE); |
| } |
| if (pubkey.publicValue.data) { /* Y */ |
| SECITEM_ZfreeItem(&pubkey.publicValue, PR_FALSE); |
| } |
| } |
| |
| static void |
| pad(unsigned char *buf, int pad_len, unsigned char *src, int src_len) |
| { |
| int offset = 0; |
| /* this shouldn't happen, fail right away rather than produce bad output */ |
| if (pad_len < src_len) { |
| fprintf(stderr, "data bigger than expected! %d > %d\n", src_len, pad_len); |
| exit(1); |
| } |
| |
| offset = pad_len - src_len; |
| memset(buf, 0, offset); |
| memcpy(buf + offset, src, src_len); |
| return; |
| } |
| |
| /* |
| * Perform the DSA Key Pair Generation Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| rsa_keypair_test(char *reqfn) |
| { |
| char buf[800]; /* holds one line from the input REQUEST file |
| * or to the output RESPONSE file. |
| * 800 to hold (384 public key (x2 for HEX) + 1'\n' |
| */ |
| unsigned char buf2[400]; /* can't need more then 1/2 buf length */ |
| FILE *rsareq; /* input stream from the REQUEST file */ |
| FILE *rsaresp; /* output stream to the RESPONSE file */ |
| int count; |
| int i; |
| int keySize = 1; /* key size in bits*/ |
| int len = 0; /* key size in bytes */ |
| int len2 = 0; /* key size in bytes/2 (prime size) */ |
| SECItem e; |
| unsigned char default_e[] = { 0x1, 0x0, 0x1 }; |
| |
| e.data = default_e; |
| e.len = sizeof(default_e); |
| |
| rsareq = fopen(reqfn, "r"); |
| rsaresp = stdout; |
| while (fgets(buf, sizeof buf, rsareq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, rsaresp); |
| continue; |
| } |
| |
| /* [Mod = x] */ |
| if (buf[0] == '[') { |
| if (buf[1] == 'm') { |
| if (sscanf(buf, "[mod = %d]", &keySize) != 1) { |
| goto loser; |
| } |
| len = keySize / 8; |
| len2 = keySize / 16; |
| } |
| fputs(buf, rsaresp); |
| continue; |
| } |
| /* N = ...*/ |
| if (buf[0] == 'N') { |
| |
| if (sscanf(buf, "N = %d", &count) != 1) { |
| goto loser; |
| } |
| |
| /* Generate a DSA key, and output the key pair for N times */ |
| for (i = 0; i < count; i++) { |
| RSAPrivateKey *rsakey = NULL; |
| if ((rsakey = RSA_NewKey(keySize, &e)) == NULL) { |
| fprintf(rsaresp, "ERROR: Unable to generate RSA key"); |
| goto loser; |
| } |
| pad(buf2, len, rsakey->publicExponent.data, |
| rsakey->publicExponent.len); |
| to_hex_str(buf, buf2, len); |
| fprintf(rsaresp, "e = %s\n", buf); |
| pad(buf2, len2, rsakey->prime1.data, |
| rsakey->prime1.len); |
| to_hex_str(buf, buf2, len2); |
| fprintf(rsaresp, "p = %s\n", buf); |
| pad(buf2, len2, rsakey->prime2.data, |
| rsakey->prime2.len); |
| to_hex_str(buf, buf2, len2); |
| fprintf(rsaresp, "q = %s\n", buf); |
| pad(buf2, len, rsakey->modulus.data, |
| rsakey->modulus.len); |
| to_hex_str(buf, buf2, len); |
| fprintf(rsaresp, "n = %s\n", buf); |
| pad(buf2, len, rsakey->privateExponent.data, |
| rsakey->privateExponent.len); |
| to_hex_str(buf, buf2, len); |
| fprintf(rsaresp, "d = %s\n", buf); |
| fprintf(rsaresp, "\n"); |
| PORT_FreeArena(rsakey->arena, PR_TRUE); |
| rsakey = NULL; |
| } |
| continue; |
| } |
| } |
| loser: |
| fclose(rsareq); |
| } |
| |
| /* |
| * Perform the RSA Signature Generation Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| rsa_siggen_test(char *reqfn) |
| { |
| char buf[2 * RSA_MAX_TEST_MODULUS_BYTES + 1]; |
| /* buf holds one line from the input REQUEST file |
| * or to the output RESPONSE file. |
| * 2x for HEX output + 1 for \n |
| */ |
| FILE *rsareq; /* input stream from the REQUEST file */ |
| FILE *rsaresp; /* output stream to the RESPONSE file */ |
| int i, j; |
| unsigned char sha[HASH_LENGTH_MAX]; /* SHA digest */ |
| unsigned int shaLength = 0; /* length of SHA */ |
| HASH_HashType shaAlg = HASH_AlgNULL; /* type of SHA Alg */ |
| SECOidTag shaOid = SEC_OID_UNKNOWN; |
| int modulus; /* the Modulus size */ |
| int publicExponent = DEFAULT_RSA_PUBLIC_EXPONENT; |
| SECItem pe = { 0, 0, 0 }; |
| unsigned char pubEx[4]; |
| int peCount = 0; |
| |
| RSAPrivateKey *rsaBlapiPrivKey = NULL; /* holds RSA private and |
| * public keys */ |
| RSAPublicKey *rsaBlapiPublicKey = NULL; /* hold RSA public key */ |
| |
| rsareq = fopen(reqfn, "r"); |
| rsaresp = stdout; |
| |
| /* calculate the exponent */ |
| for (i = 0; i < 4; i++) { |
| if (peCount || (publicExponent & |
| ((unsigned long)0xff000000L >> (i * |
| 8)))) { |
| pubEx[peCount] = |
| (unsigned char)((publicExponent >> (3 - i) * 8) & 0xff); |
| peCount++; |
| } |
| } |
| pe.len = peCount; |
| pe.data = &pubEx[0]; |
| pe.type = siBuffer; |
| |
| while (fgets(buf, sizeof buf, rsareq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, rsaresp); |
| continue; |
| } |
| |
| /* [mod = ...] */ |
| if (buf[0] == '[') { |
| |
| if (sscanf(buf, "[mod = %d]", &modulus) != 1) { |
| goto loser; |
| } |
| if (modulus > RSA_MAX_TEST_MODULUS_BITS) { |
| fprintf(rsaresp, "ERROR: modulus greater than test maximum\n"); |
| goto loser; |
| } |
| |
| fputs(buf, rsaresp); |
| |
| if (rsaBlapiPrivKey != NULL) { |
| PORT_FreeArena(rsaBlapiPrivKey->arena, PR_TRUE); |
| rsaBlapiPrivKey = NULL; |
| rsaBlapiPublicKey = NULL; |
| } |
| |
| rsaBlapiPrivKey = RSA_NewKey(modulus, &pe); |
| if (rsaBlapiPrivKey == NULL) { |
| fprintf(rsaresp, "Error unable to create RSA key\n"); |
| goto loser; |
| } |
| |
| to_hex_str(buf, rsaBlapiPrivKey->modulus.data, |
| rsaBlapiPrivKey->modulus.len); |
| fprintf(rsaresp, "\nn = %s\n\n", buf); |
| to_hex_str(buf, rsaBlapiPrivKey->publicExponent.data, |
| rsaBlapiPrivKey->publicExponent.len); |
| fprintf(rsaresp, "e = %s\n", buf); |
| /* convert private key to public key. Memory |
| * is freed with private key's arena */ |
| rsaBlapiPublicKey = (RSAPublicKey *)PORT_ArenaAlloc( |
| rsaBlapiPrivKey->arena, |
| sizeof(RSAPublicKey)); |
| |
| rsaBlapiPublicKey->modulus.len = rsaBlapiPrivKey->modulus.len; |
| rsaBlapiPublicKey->modulus.data = rsaBlapiPrivKey->modulus.data; |
| rsaBlapiPublicKey->publicExponent.len = |
| rsaBlapiPrivKey->publicExponent.len; |
| rsaBlapiPublicKey->publicExponent.data = |
| rsaBlapiPrivKey->publicExponent.data; |
| continue; |
| } |
| |
| /* SHAAlg = ... */ |
| if (strncmp(buf, "SHAAlg", 6) == 0) { |
| i = 6; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| /* set the SHA Algorithm */ |
| shaAlg = hash_string_to_hashType(&buf[i]); |
| if (shaAlg == HASH_AlgNULL) { |
| fprintf(rsaresp, "ERROR: Unable to find SHAAlg type"); |
| goto loser; |
| } |
| fputs(buf, rsaresp); |
| continue; |
| } |
| /* Msg = ... */ |
| if (strncmp(buf, "Msg", 3) == 0) { |
| |
| unsigned char msg[128]; /* MAX msg 128 */ |
| unsigned int rsa_bytes_signed; |
| unsigned char rsa_computed_signature[RSA_MAX_TEST_MODULUS_BYTES]; |
| SECStatus rv = SECFailure; |
| NSSLOWKEYPublicKey *rsa_public_key; |
| NSSLOWKEYPrivateKey *rsa_private_key; |
| NSSLOWKEYPrivateKey low_RSA_private_key = { NULL, |
| NSSLOWKEYRSAKey }; |
| NSSLOWKEYPublicKey low_RSA_public_key = { NULL, |
| NSSLOWKEYRSAKey }; |
| |
| low_RSA_private_key.u.rsa = *rsaBlapiPrivKey; |
| low_RSA_public_key.u.rsa = *rsaBlapiPublicKey; |
| |
| rsa_private_key = &low_RSA_private_key; |
| rsa_public_key = &low_RSA_public_key; |
| |
| memset(sha, 0, sizeof sha); |
| memset(msg, 0, sizeof msg); |
| rsa_bytes_signed = 0; |
| memset(rsa_computed_signature, 0, sizeof rsa_computed_signature); |
| |
| i = 3; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; isxdigit(buf[i]) && j < sizeof(msg); i += 2, j++) { |
| hex_to_byteval(&buf[i], &msg[j]); |
| } |
| shaLength = fips_hashLen(shaAlg); |
| if (fips_hashBuf(shaAlg, sha, msg, j) != SECSuccess) { |
| if (shaLength == 0) { |
| fprintf(rsaresp, "ERROR: SHAAlg not defined."); |
| } |
| fprintf(rsaresp, "ERROR: Unable to generate SHA%x", |
| shaLength == 160 ? 1 : shaLength); |
| goto loser; |
| } |
| shaOid = fips_hashOid(shaAlg); |
| |
| /* Perform RSA signature with the RSA private key. */ |
| rv = RSA_HashSign(shaOid, |
| rsa_private_key, |
| rsa_computed_signature, |
| &rsa_bytes_signed, |
| nsslowkey_PrivateModulusLen(rsa_private_key), |
| sha, |
| shaLength); |
| |
| if (rv != SECSuccess) { |
| fprintf(rsaresp, "ERROR: RSA_HashSign failed"); |
| goto loser; |
| } |
| |
| /* Output the signature */ |
| fputs(buf, rsaresp); |
| to_hex_str(buf, rsa_computed_signature, rsa_bytes_signed); |
| fprintf(rsaresp, "S = %s\n", buf); |
| |
| /* Perform RSA verification with the RSA public key. */ |
| rv = RSA_HashCheckSign(shaOid, |
| rsa_public_key, |
| rsa_computed_signature, |
| rsa_bytes_signed, |
| sha, |
| shaLength); |
| if (rv != SECSuccess) { |
| fprintf(rsaresp, "ERROR: RSA_HashCheckSign failed"); |
| goto loser; |
| } |
| continue; |
| } |
| } |
| loser: |
| fclose(rsareq); |
| |
| if (rsaBlapiPrivKey != NULL) { |
| /* frees private and public key */ |
| PORT_FreeArena(rsaBlapiPrivKey->arena, PR_TRUE); |
| rsaBlapiPrivKey = NULL; |
| rsaBlapiPublicKey = NULL; |
| } |
| } |
| /* |
| * Perform the RSA Signature Verification Test. |
| * |
| * reqfn is the pathname of the REQUEST file. |
| * |
| * The output RESPONSE file is written to stdout. |
| */ |
| void |
| rsa_sigver_test(char *reqfn) |
| { |
| char buf[2 * RSA_MAX_TEST_MODULUS_BYTES + 7]; |
| /* buf holds one line from the input REQUEST file |
| * or to the output RESPONSE file. |
| * s = 2x for HEX output + 1 for \n |
| */ |
| FILE *rsareq; /* input stream from the REQUEST file */ |
| FILE *rsaresp; /* output stream to the RESPONSE file */ |
| int i, j; |
| unsigned char sha[HASH_LENGTH_MAX]; /* SHA digest */ |
| unsigned int shaLength = 0; /* actual length of the digest */ |
| HASH_HashType shaAlg = HASH_AlgNULL; |
| SECOidTag shaOid = SEC_OID_UNKNOWN; |
| int modulus = 0; /* the Modulus size */ |
| unsigned char signature[513]; /* largest signature size + '\n' */ |
| unsigned int signatureLength = 0; /* actual length of the signature */ |
| PRBool keyvalid = PR_TRUE; |
| |
| RSAPublicKey rsaBlapiPublicKey; /* hold RSA public key */ |
| |
| rsareq = fopen(reqfn, "r"); |
| rsaresp = stdout; |
| memset(&rsaBlapiPublicKey, 0, sizeof(RSAPublicKey)); |
| |
| while (fgets(buf, sizeof buf, rsareq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, rsaresp); |
| continue; |
| } |
| |
| /* [Mod = ...] */ |
| if (buf[0] == '[') { |
| unsigned int flen; /* length in bytes of the field size */ |
| |
| if (rsaBlapiPublicKey.modulus.data) { /* n */ |
| SECITEM_ZfreeItem(&rsaBlapiPublicKey.modulus, PR_FALSE); |
| } |
| if (sscanf(buf, "[mod = %d]", &modulus) != 1) { |
| goto loser; |
| } |
| |
| if (modulus > RSA_MAX_TEST_MODULUS_BITS) { |
| fprintf(rsaresp, "ERROR: modulus greater than test maximum\n"); |
| goto loser; |
| } |
| |
| fputs(buf, rsaresp); |
| |
| signatureLength = flen = modulus / 8; |
| |
| SECITEM_AllocItem(NULL, &rsaBlapiPublicKey.modulus, flen); |
| if (rsaBlapiPublicKey.modulus.data == NULL) { |
| goto loser; |
| } |
| continue; |
| } |
| |
| /* n = ... modulus */ |
| if (buf[0] == 'n') { |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| keyvalid = from_hex_str(&rsaBlapiPublicKey.modulus.data[0], |
| rsaBlapiPublicKey.modulus.len, |
| &buf[i]); |
| |
| if (!keyvalid) { |
| fprintf(rsaresp, "ERROR: rsa_sigver n not valid.\n"); |
| goto loser; |
| } |
| fputs(buf, rsaresp); |
| continue; |
| } |
| |
| /* SHAAlg = ... */ |
| if (strncmp(buf, "SHAAlg", 6) == 0) { |
| i = 6; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| /* set the SHA Algorithm */ |
| shaAlg = hash_string_to_hashType(&buf[i]); |
| if (shaAlg == HASH_AlgNULL) { |
| fprintf(rsaresp, "ERROR: Unable to find SHAAlg type"); |
| goto loser; |
| } |
| fputs(buf, rsaresp); |
| continue; |
| } |
| |
| /* e = ... public Key */ |
| if (buf[0] == 'e') { |
| unsigned char data[RSA_MAX_TEST_EXPONENT_BYTES]; |
| unsigned char t; |
| |
| memset(data, 0, sizeof data); |
| |
| if (rsaBlapiPublicKey.publicExponent.data) { /* e */ |
| SECITEM_ZfreeItem(&rsaBlapiPublicKey.publicExponent, PR_FALSE); |
| } |
| |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| /* skip leading zero's */ |
| while (isxdigit(buf[i])) { |
| hex_to_byteval(&buf[i], &t); |
| if (t == 0) { |
| i += 2; |
| } else |
| break; |
| } |
| |
| /* get the exponent */ |
| for (j = 0; isxdigit(buf[i]) && j < sizeof data; i += 2, j++) { |
| hex_to_byteval(&buf[i], &data[j]); |
| } |
| |
| if (j == 0) { |
| j = 1; |
| } /* to handle 1 byte length exponents */ |
| |
| SECITEM_AllocItem(NULL, &rsaBlapiPublicKey.publicExponent, j); |
| if (rsaBlapiPublicKey.publicExponent.data == NULL) { |
| goto loser; |
| } |
| |
| for (i = 0; i < j; i++) { |
| rsaBlapiPublicKey.publicExponent.data[i] = data[i]; |
| } |
| |
| fputs(buf, rsaresp); |
| continue; |
| } |
| |
| /* Msg = ... */ |
| if (strncmp(buf, "Msg", 3) == 0) { |
| unsigned char msg[128]; /* MAX msg 128 */ |
| |
| memset(sha, 0, sizeof sha); |
| memset(msg, 0, sizeof msg); |
| |
| i = 3; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| |
| for (j = 0; isxdigit(buf[i]) && j < sizeof msg; i += 2, j++) { |
| hex_to_byteval(&buf[i], &msg[j]); |
| } |
| |
| shaLength = fips_hashLen(shaAlg); |
| if (fips_hashBuf(shaAlg, sha, msg, j) != SECSuccess) { |
| if (shaLength == 0) { |
| fprintf(rsaresp, "ERROR: SHAAlg not defined."); |
| } |
| fprintf(rsaresp, "ERROR: Unable to generate SHA%x", |
| shaLength == 160 ? 1 : shaLength); |
| goto loser; |
| } |
| |
| fputs(buf, rsaresp); |
| continue; |
| } |
| |
| /* S = ... */ |
| if (buf[0] == 'S') { |
| SECStatus rv = SECFailure; |
| NSSLOWKEYPublicKey *rsa_public_key; |
| NSSLOWKEYPublicKey low_RSA_public_key = { NULL, |
| NSSLOWKEYRSAKey }; |
| |
| /* convert to a low RSA public key */ |
| low_RSA_public_key.u.rsa = rsaBlapiPublicKey; |
| rsa_public_key = &low_RSA_public_key; |
| |
| memset(signature, 0, sizeof(signature)); |
| i = 1; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| |
| for (j = 0; isxdigit(buf[i]) && j < sizeof signature; i += 2, j++) { |
| hex_to_byteval(&buf[i], &signature[j]); |
| } |
| |
| signatureLength = j; |
| fputs(buf, rsaresp); |
| |
| shaOid = fips_hashOid(shaAlg); |
| |
| /* Perform RSA verification with the RSA public key. */ |
| rv = RSA_HashCheckSign(shaOid, |
| rsa_public_key, |
| signature, |
| signatureLength, |
| sha, |
| shaLength); |
| if (rv == SECSuccess) { |
| fputs("Result = P\n", rsaresp); |
| } else { |
| fputs("Result = F\n", rsaresp); |
| } |
| continue; |
| } |
| } |
| loser: |
| fclose(rsareq); |
| if (rsaBlapiPublicKey.modulus.data) { /* n */ |
| SECITEM_ZfreeItem(&rsaBlapiPublicKey.modulus, PR_FALSE); |
| } |
| if (rsaBlapiPublicKey.publicExponent.data) { /* e */ |
| SECITEM_ZfreeItem(&rsaBlapiPublicKey.publicExponent, PR_FALSE); |
| } |
| } |
| |
| void |
| tls(char *reqfn) |
| { |
| char buf[256]; /* holds one line from the input REQUEST file. |
| * needs to be large enough to hold the longest |
| * line "XSeed = <128 hex digits>\n". |
| */ |
| unsigned char *pms = NULL; |
| int pms_len; |
| unsigned char *master_secret = NULL; |
| unsigned char *key_block = NULL; |
| int key_block_len; |
| unsigned char serverHello_random[SSL3_RANDOM_LENGTH]; |
| unsigned char clientHello_random[SSL3_RANDOM_LENGTH]; |
| unsigned char server_random[SSL3_RANDOM_LENGTH]; |
| unsigned char client_random[SSL3_RANDOM_LENGTH]; |
| FILE *tlsreq = NULL; /* input stream from the REQUEST file */ |
| FILE *tlsresp; /* output stream to the RESPONSE file */ |
| unsigned int i, j; |
| CK_SLOT_ID slotList[10]; |
| CK_SLOT_ID slotID; |
| CK_ULONG slotListCount = sizeof(slotList) / sizeof(slotList[0]); |
| CK_ULONG count; |
| static const CK_C_INITIALIZE_ARGS pk11args = { |
| NULL, NULL, NULL, NULL, CKF_LIBRARY_CANT_CREATE_OS_THREADS, |
| (void *)"flags=readOnly,noCertDB,noModDB", NULL |
| }; |
| static CK_OBJECT_CLASS ck_secret = CKO_SECRET_KEY; |
| static CK_KEY_TYPE ck_generic = CKK_GENERIC_SECRET; |
| static CK_BBOOL ck_true = CK_TRUE; |
| static CK_ULONG one = 1; |
| CK_ATTRIBUTE create_template[] = { |
| { CKA_VALUE, NULL, 0 }, |
| { CKA_CLASS, &ck_secret, sizeof(ck_secret) }, |
| { CKA_KEY_TYPE, &ck_generic, sizeof(ck_generic) }, |
| { CKA_DERIVE, &ck_true, sizeof(ck_true) }, |
| }; |
| CK_ULONG create_template_count = |
| sizeof(create_template) / sizeof(create_template[0]); |
| CK_ATTRIBUTE derive_template[] = { |
| { CKA_CLASS, &ck_secret, sizeof(ck_secret) }, |
| { CKA_KEY_TYPE, &ck_generic, sizeof(ck_generic) }, |
| { CKA_DERIVE, &ck_true, sizeof(ck_true) }, |
| { CKA_VALUE_LEN, &one, sizeof(one) }, |
| }; |
| CK_ULONG derive_template_count = |
| sizeof(derive_template) / sizeof(derive_template[0]); |
| CK_ATTRIBUTE master_template = |
| { CKA_VALUE, NULL, 0 }; |
| CK_ATTRIBUTE kb1_template = |
| { CKA_VALUE, NULL, 0 }; |
| CK_ATTRIBUTE kb2_template = |
| { CKA_VALUE, NULL, 0 }; |
| |
| CK_MECHANISM master_mech = { CKM_TLS_MASTER_KEY_DERIVE, NULL, 0 }; |
| CK_MECHANISM key_block_mech = { CKM_TLS_KEY_AND_MAC_DERIVE, NULL, 0 }; |
| CK_SSL3_MASTER_KEY_DERIVE_PARAMS master_params; |
| CK_SSL3_KEY_MAT_PARAMS key_block_params; |
| CK_SSL3_KEY_MAT_OUT key_material; |
| CK_RV crv; |
| |
| /* set up PKCS #11 parameters */ |
| master_params.pVersion = NULL; |
| master_params.RandomInfo.pClientRandom = clientHello_random; |
| master_params.RandomInfo.ulClientRandomLen = sizeof(clientHello_random); |
| master_params.RandomInfo.pServerRandom = serverHello_random; |
| master_params.RandomInfo.ulServerRandomLen = sizeof(serverHello_random); |
| master_mech.pParameter = (void *)&master_params; |
| master_mech.ulParameterLen = sizeof(master_params); |
| key_block_params.ulMacSizeInBits = 0; |
| key_block_params.ulKeySizeInBits = 0; |
| key_block_params.ulIVSizeInBits = 0; |
| key_block_params.bIsExport = PR_FALSE; /* ignored anyway for TLS mech */ |
| key_block_params.RandomInfo.pClientRandom = client_random; |
| key_block_params.RandomInfo.ulClientRandomLen = sizeof(client_random); |
| key_block_params.RandomInfo.pServerRandom = server_random; |
| key_block_params.RandomInfo.ulServerRandomLen = sizeof(server_random); |
| key_block_params.pReturnedKeyMaterial = &key_material; |
| key_block_mech.pParameter = (void *)&key_block_params; |
| key_block_mech.ulParameterLen = sizeof(key_block_params); |
| |
| crv = NSC_Initialize((CK_VOID_PTR)&pk11args); |
| if (crv != CKR_OK) { |
| fprintf(stderr, "NSC_Initialize failed crv=0x%x\n", (unsigned int)crv); |
| goto loser; |
| } |
| count = slotListCount; |
| crv = NSC_GetSlotList(PR_TRUE, slotList, &count); |
| if (crv != CKR_OK) { |
| fprintf(stderr, "NSC_GetSlotList failed crv=0x%x\n", (unsigned int)crv); |
| goto loser; |
| } |
| if ((count > slotListCount) || count < 1) { |
| fprintf(stderr, |
| "NSC_GetSlotList returned too many or too few slots: %d slots max=%d min=1\n", |
| (int)count, (int)slotListCount); |
| goto loser; |
| } |
| slotID = slotList[0]; |
| tlsreq = fopen(reqfn, "r"); |
| tlsresp = stdout; |
| while (fgets(buf, sizeof buf, tlsreq) != NULL) { |
| /* a comment or blank line */ |
| if (buf[0] == '#' || buf[0] == '\n') { |
| fputs(buf, tlsresp); |
| continue; |
| } |
| /* [Xchange - SHA1] */ |
| if (buf[0] == '[') { |
| if (strncmp(buf, "[TLS", 4) == 0) { |
| if (buf[7] == '0') { |
| master_mech.mechanism = CKM_TLS_MASTER_KEY_DERIVE; |
| key_block_mech.mechanism = CKM_TLS_KEY_AND_MAC_DERIVE; |
| } else if (buf[7] == '2') { |
| master_mech.mechanism = |
| CKM_NSS_TLS_MASTER_KEY_DERIVE_SHA256; |
| key_block_mech.mechanism = |
| CKM_NSS_TLS_KEY_AND_MAC_DERIVE_SHA256; |
| } else { |
| fprintf(stderr, "Unknown TLS type %x\n", |
| (unsigned int)buf[0]); |
| goto loser; |
| } |
| } |
| if (strncmp(buf, "[pre-master", 11) == 0) { |
| if (sscanf(buf, "[pre-master secret length = %d]", |
| &pms_len) != 1) { |
| goto loser; |
| } |
| pms_len = pms_len / 8; |
| pms = malloc(pms_len); |
| master_secret = malloc(pms_len); |
| create_template[0].pValue = pms; |
| create_template[0].ulValueLen = pms_len; |
| master_template.pValue = master_secret; |
| master_template.ulValueLen = pms_len; |
| } |
| if (strncmp(buf, "[key", 4) == 0) { |
| if (sscanf(buf, "[key block length = %d]", &key_block_len) != 1) { |
| goto loser; |
| } |
| key_block_params.ulKeySizeInBits = 8; |
| key_block_params.ulIVSizeInBits = key_block_len / 2 - 8; |
| key_block_len = key_block_len / 8; |
| key_block = malloc(key_block_len); |
| kb1_template.pValue = &key_block[0]; |
| kb1_template.ulValueLen = 1; |
| kb2_template.pValue = &key_block[1]; |
| kb2_template.ulValueLen = 1; |
| key_material.pIVClient = &key_block[2]; |
| key_material.pIVServer = &key_block[2 + key_block_len / 2 - 1]; |
| } |
| fputs(buf, tlsresp); |
| 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(pms, 0, pms_len); |
| memset(master_secret, 0, pms_len); |
| memset(key_block, 0, key_block_len); |
| fputs(buf, tlsresp); |
| continue; |
| } |
| /* pre_master_secret = ... */ |
| if (strncmp(buf, "pre_master_secret", 17) == 0) { |
| i = 17; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < pms_len; i += 2, j++) { |
| hex_to_byteval(&buf[i], &pms[j]); |
| } |
| fputs(buf, tlsresp); |
| continue; |
| } |
| /* serverHello_random = ... */ |
| if (strncmp(buf, "serverHello_random", 18) == 0) { |
| i = 18; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < SSL3_RANDOM_LENGTH; i += 2, j++) { |
| hex_to_byteval(&buf[i], &serverHello_random[j]); |
| } |
| fputs(buf, tlsresp); |
| continue; |
| } |
| /* clientHello_random = ... */ |
| if (strncmp(buf, "clientHello_random", 18) == 0) { |
| i = 18; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < SSL3_RANDOM_LENGTH; i += 2, j++) { |
| hex_to_byteval(&buf[i], &clientHello_random[j]); |
| } |
| fputs(buf, tlsresp); |
| continue; |
| } |
| /* server_random = ... */ |
| if (strncmp(buf, "server_random", 13) == 0) { |
| i = 13; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < SSL3_RANDOM_LENGTH; i += 2, j++) { |
| hex_to_byteval(&buf[i], &server_random[j]); |
| } |
| fputs(buf, tlsresp); |
| continue; |
| } |
| /* client_random = ... */ |
| if (strncmp(buf, "client_random", 13) == 0) { |
| CK_SESSION_HANDLE session; |
| CK_OBJECT_HANDLE pms_handle; |
| CK_OBJECT_HANDLE master_handle; |
| CK_OBJECT_HANDLE fake_handle; |
| i = 13; |
| while (isspace(buf[i]) || buf[i] == '=') { |
| i++; |
| } |
| for (j = 0; j < SSL3_RANDOM_LENGTH; i += 2, j++) { |
| hex_to_byteval(&buf[i], &client_random[j]); |
| } |
| fputs(buf, tlsresp); |
| crv = NSC_OpenSession(slotID, 0, NULL, NULL, &session); |
| if (crv != CKR_OK) { |
| fprintf(stderr, "NSC_OpenSession failed crv=0x%x\n", |
| (unsigned int)crv); |
| goto loser; |
| } |
| crv = NSC_CreateObject(session, create_template, |
| create_template_count, &pms_handle); |
| if (crv != CKR_OK) { |
| fprintf(stderr, "NSC_CreateObject failed crv=0x%x\n", |
| (unsigned int)crv); |
| goto loser; |
| } |
| crv = NSC_DeriveKey(session, &master_mech, pms_handle, |
| derive_template, derive_template_count - 1, |
| &master_handle); |
| if (crv != CKR_OK) { |
| fprintf(stderr, "NSC_DeriveKey(master) failed crv=0x%x\n", |
| (unsigned int)crv); |
| goto loser; |
| } |
| crv = NSC_GetAttributeValue(session, master_handle, |
| &master_template, 1); |
| if (crv != CKR_OK) { |
| fprintf(stderr, "NSC_GetAttribute failed crv=0x%x\n", |
| (unsigned int)crv); |
| goto loser; |
| } |
| fputs("master_secret = ", tlsresp); |
| to_hex_str(buf, master_secret, pms_len); |
| fputs(buf, tlsresp); |
| fputc('\n', tlsresp); |
| crv = NSC_DeriveKey(session, &key_block_mech, master_handle, |
| derive_template, derive_template_count, &fake_handle); |
| if (crv != CKR_OK) { |
| fprintf(stderr, |
| "NSC_DeriveKey(keyblock) failed crv=0x%x\n", |
| (unsigned int)crv); |
| goto loser; |
| } |
| crv = NSC_GetAttributeValue(session, key_material.hClientKey, |
| &kb1_template, 1); |
| if (crv != CKR_OK) { |
| fprintf(stderr, "NSC_GetAttribute failed crv=0x%x\n", |
| (unsigned int)crv); |
| goto loser; |
| } |
| crv = NSC_GetAttributeValue(session, key_material.hServerKey, |
| &kb2_template, 1); |
| if (crv != CKR_OK) { |
| fprintf(stderr, "NSC_GetAttribute failed crv=0x%x\n", |
| (unsigned int)crv); |
| goto loser; |
| } |
| fputs("key_block = ", tlsresp); |
| to_hex_str(buf, key_block, key_block_len); |
| fputs(buf, tlsresp); |
| fputc('\n', tlsresp); |
| crv = NSC_CloseSession(session); |
| continue; |
| } |
| } |
| loser: |
| NSC_Finalize(NULL); |
| if (pms) |
| free(pms); |
| if (master_secret) |
| free(master_secret); |
| if (key_block) |
| free(key_block); |
| if (tlsreq) |
| fclose(tlsreq); |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| if (argc < 2) |
| exit(-1); |
| |
| RNG_RNGInit(); |
| SECOID_Init(); |
| |
| /*************/ |
| /* TDEA */ |
| /*************/ |
| if (strcmp(argv[1], "tdea") == 0) { |
| /* argv[2]=kat|mmt|mct argv[3]=ecb|cbc argv[4]=<test name>.req */ |
| if (strcmp(argv[2], "kat") == 0) { |
| /* Known Answer Test (KAT) */ |
| tdea_kat_mmt(argv[4]); |
| } else if (strcmp(argv[2], "mmt") == 0) { |
| /* Multi-block Message Test (MMT) */ |
| tdea_kat_mmt(argv[4]); |
| } else if (strcmp(argv[2], "mct") == 0) { |
| /* Monte Carlo Test (MCT) */ |
| if (strcmp(argv[3], "ecb") == 0) { |
| /* ECB mode */ |
| tdea_mct(NSS_DES_EDE3, argv[4]); |
| } else if (strcmp(argv[3], "cbc") == 0) { |
| /* CBC mode */ |
| tdea_mct(NSS_DES_EDE3_CBC, argv[4]); |
| } |
| } |
| /*************/ |
| /* AES */ |
| /*************/ |
| } else if (strcmp(argv[1], "aes") == 0) { |
| /* argv[2]=kat|mmt|mct argv[3]=ecb|cbc argv[4]=<test name>.req */ |
| if (strcmp(argv[2], "kat") == 0) { |
| /* Known Answer Test (KAT) */ |
| aes_kat_mmt(argv[4]); |
| } else if (strcmp(argv[2], "mmt") == 0) { |
| /* Multi-block Message Test (MMT) */ |
| aes_kat_mmt(argv[4]); |
| } else if (strcmp(argv[2], "gcm") == 0) { |
| if (strcmp(argv[3], "decrypt") == 0) { |
| aes_gcm(argv[4], 0); |
| } else if (strcmp(argv[3], "encrypt_extiv") == 0) { |
| aes_gcm(argv[4], 1); |
| } else if (strcmp(argv[3], "encrypt_intiv") == 0) { |
| aes_gcm(argv[4], 2); |
| } |
| } else if (strcmp(argv[2], "mct") == 0) { |
| /* Monte Carlo Test (MCT) */ |
| if (strcmp(argv[3], "ecb") == 0) { |
| /* ECB mode */ |
| aes_ecb_mct(argv[4]); |
| } else if (strcmp(argv[3], "cbc") == 0) { |
| /* CBC mode */ |
| aes_cbc_mct(argv[4]); |
| } |
| } |
| /*************/ |
| /* SHA */ |
| /*************/ |
| } else if (strcmp(argv[1], "sha") == 0) { |
| sha_test(argv[2]); |
| /*************/ |
| /* RSA */ |
| /*************/ |
| } else if (strcmp(argv[1], "rsa") == 0) { |
| /* argv[2]=siggen|sigver */ |
| /* argv[3]=<test name>.req */ |
| if (strcmp(argv[2], "siggen") == 0) { |
| /* Signature Generation Test */ |
| rsa_siggen_test(argv[3]); |
| } else if (strcmp(argv[2], "sigver") == 0) { |
| /* Signature Verification Test */ |
| rsa_sigver_test(argv[3]); |
| } else if (strcmp(argv[2], "keypair") == 0) { |
| /* Key Pair Generation Test */ |
| rsa_keypair_test(argv[3]); |
| } |
| /*************/ |
| /* HMAC */ |
| /*************/ |
| } else if (strcmp(argv[1], "hmac") == 0) { |
| hmac_test(argv[2]); |
| /*************/ |
| /* DSA */ |
| /*************/ |
| } else if (strcmp(argv[1], "dsa") == 0) { |
| /* argv[2]=keypair|pqggen|pqgver|siggen|sigver */ |
| /* argv[3]=<test name>.req */ |
| if (strcmp(argv[2], "keypair") == 0) { |
| /* Key Pair Generation Test */ |
| dsa_keypair_test(argv[3]); |
| } else if (strcmp(argv[2], "pqggen") == 0) { |
| /* Domain Parameter Generation Test */ |
| dsa_pqggen_test(argv[3]); |
| } else if (strcmp(argv[2], "pqgver") == 0) { |
| /* Domain Parameter Validation Test */ |
| dsa_pqgver_test(argv[3]); |
| } else if (strcmp(argv[2], "siggen") == 0) { |
| /* Signature Generation Test */ |
| dsa_siggen_test(argv[3]); |
| } else if (strcmp(argv[2], "sigver") == 0) { |
| /* Signature Verification Test */ |
| dsa_sigver_test(argv[3]); |
| } |
| /*************/ |
| /* ECDSA */ |
| /*************/ |
| } else if (strcmp(argv[1], "ecdsa") == 0) { |
| /* argv[2]=keypair|pkv|siggen|sigver argv[3]=<test name>.req */ |
| if (strcmp(argv[2], "keypair") == 0) { |
| /* Key Pair Generation Test */ |
| ecdsa_keypair_test(argv[3]); |
| } else if (strcmp(argv[2], "pkv") == 0) { |
| /* Public Key Validation Test */ |
| ecdsa_pkv_test(argv[3]); |
| } else if (strcmp(argv[2], "siggen") == 0) { |
| /* Signature Generation Test */ |
| ecdsa_siggen_test(argv[3]); |
| } else if (strcmp(argv[2], "sigver") == 0) { |
| /* Signature Verification Test */ |
| ecdsa_sigver_test(argv[3]); |
| } |
| /*************/ |
| /* ECDH */ |
| /*************/ |
| } else if (strcmp(argv[1], "ecdh") == 0) { |
| /* argv[2]={init|resp}-{func|verify} argv[3]=<test name>.req */ |
| if (strcmp(argv[2], "init-func") == 0) { |
| ecdh_functional(argv[3], 0); |
| } else if (strcmp(argv[2], "resp-func") == 0) { |
| ecdh_functional(argv[3], 1); |
| } else if (strcmp(argv[2], "init-verify") == 0) { |
| ecdh_verify(argv[3], 0); |
| } else if (strcmp(argv[2], "resp-verify") == 0) { |
| ecdh_verify(argv[3], 1); |
| } |
| /*************/ |
| /* DH */ |
| /*************/ |
| } else if (strcmp(argv[1], "dh") == 0) { |
| /* argv[2]={init|resp}-{func|verify} argv[3]=<test name>.req */ |
| if (strcmp(argv[2], "init-func") == 0) { |
| dh_functional(argv[3], 0); |
| } else if (strcmp(argv[2], "resp-func") == 0) { |
| dh_functional(argv[3], 1); |
| } else if (strcmp(argv[2], "init-verify") == 0) { |
| dh_verify(argv[3], 0); |
| } else if (strcmp(argv[2], "resp-verify") == 0) { |
| dh_verify(argv[3], 1); |
| } |
| /*************/ |
| /* RNG */ |
| /*************/ |
| } else if (strcmp(argv[1], "rng") == 0) { |
| /* argv[2]=vst|mct argv[3]=<test name>.req */ |
| if (strcmp(argv[2], "vst") == 0) { |
| /* Variable Seed Test */ |
| rng_vst(argv[3]); |
| } else if (strcmp(argv[2], "mct") == 0) { |
| /* Monte Carlo Test */ |
| rng_mct(argv[3]); |
| } |
| } else if (strcmp(argv[1], "drbg") == 0) { |
| /* Variable Seed Test */ |
| drbg(argv[2]); |
| } else if (strcmp(argv[1], "ddrbg") == 0) { |
| debug = 1; |
| drbg(argv[2]); |
| } |
| return 0; |
| } |