diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:44:51 +0000 |
commit | 9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /security/nss/cmd/rsapoptst/rsapoptst.c | |
parent | Initial commit. (diff) | |
download | thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/cmd/rsapoptst/rsapoptst.c')
-rw-r--r-- | security/nss/cmd/rsapoptst/rsapoptst.c | 639 |
1 files changed, 639 insertions, 0 deletions
diff --git a/security/nss/cmd/rsapoptst/rsapoptst.c b/security/nss/cmd/rsapoptst/rsapoptst.c new file mode 100644 index 0000000000..4b9618e3b0 --- /dev/null +++ b/security/nss/cmd/rsapoptst/rsapoptst.c @@ -0,0 +1,639 @@ +/* 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 "plgetopt.h" +#include "nss.h" +#include "secutil.h" +#include "pk11table.h" +#include "secmodt.h" +#include "pk11pub.h" + +struct test_args { + char *arg; + int mask_value; + char *description; +}; + +static const struct test_args test_array[] = { + { "all", 0x1f, "run all the tests" }, + { "e_n_p", 0x01, "public exponent, modulus, prime1" }, + { "d_n_q", 0x02, "private exponent, modulus, prime2" }, + { "d_p_q", 0x04, "private exponent, prime1, prime2" }, + { "e_d_q", 0x08, "public exponent, private exponent, prime2" }, + { "e_d_n", 0x10, "public exponent, private exponent, modulus" } +}; +static const int test_array_size = + (sizeof(test_array) / sizeof(struct test_args)); + +static void +Usage(char *progName) +{ + int i; +#define PRINTUSAGE(subject, option, predicate) \ + fprintf(stderr, "%10s %s\t%s\n", subject, option, predicate); + fprintf(stderr, "%s [-k keysize] [-e exp] [-r rounds] [-t tests]\n " + "Test creating RSA private keys from Partial components\n", + progName); + PRINTUSAGE("", "-k", "key size (in bit)"); + PRINTUSAGE("", "-e", "rsa public exponent"); + PRINTUSAGE("", "-r", "number times to repeat the test"); + PRINTUSAGE("", "-t", "run the specified tests"); + for (i = 0; i < test_array_size; i++) { + PRINTUSAGE("", test_array[i].arg, test_array[i].description); + } + fprintf(stderr, "\n"); +} + +/* + * Test the RSA populate command to see that it can really build + * keys from it's components. + */ + +const static CK_ATTRIBUTE rsaTemplate[] = { + { CKA_CLASS, NULL, 0 }, + { CKA_KEY_TYPE, NULL, 0 }, + { CKA_TOKEN, NULL, 0 }, + { CKA_SENSITIVE, NULL, 0 }, + { CKA_PRIVATE, NULL, 0 }, + { CKA_ID, NULL, 0 }, + { CKA_MODULUS, NULL, 0 }, + { CKA_PUBLIC_EXPONENT, NULL, 0 }, + { CKA_PRIVATE_EXPONENT, NULL, 0 }, + { CKA_PRIME_1, NULL, 0 }, + { CKA_PRIME_2, NULL, 0 }, + { CKA_EXPONENT_1, NULL, 0 }, + { CKA_EXPONENT_2, NULL, 0 }, + { CKA_COEFFICIENT, NULL, 0 }, +}; + +#define RSA_SIZE (sizeof(rsaTemplate)) +#define RSA_ATTRIBUTES (sizeof(rsaTemplate) / sizeof(CK_ATTRIBUTE)) + +static void +resetTemplate(CK_ATTRIBUTE *attribute, int start, int end) +{ + int i; + for (i = start; i < end; i++) { + if (attribute[i].pValue) { + PORT_Free(attribute[i].pValue); + } + attribute[i].pValue = NULL; + attribute[i].ulValueLen = 0; + } +} + +static SECStatus +copyAttribute(PK11ObjectType objType, void *object, CK_ATTRIBUTE *template, + int offset, CK_ATTRIBUTE_TYPE attrType) +{ + SECItem attributeItem = { 0, 0, 0 }; + SECStatus rv; + + rv = PK11_ReadRawAttribute(objType, object, attrType, &attributeItem); + if (rv != SECSuccess) { + return rv; + } + template[offset].type = attrType; + template[offset].pValue = attributeItem.data; + template[offset].ulValueLen = attributeItem.len; + return SECSuccess; +} + +static SECStatus +readKey(PK11ObjectType objType, void *object, CK_ATTRIBUTE *template, + int start, int end) +{ + int i; + SECStatus rv; + + for (i = start; i < end; i++) { + rv = copyAttribute(objType, object, template, i, template[i].type); + if (rv != SECSuccess) { + goto fail; + } + } + return SECSuccess; + +fail: + resetTemplate(template, start, i); + return rv; +} + +#define ATTR_STRING(x) getNameFromAttribute(x) + +static void +dumphex(FILE *file, const unsigned char *cpval, int start, int end) +{ + int i; + for (i = start; i < end; i++) { + if ((i % 16) == 0) + fprintf(file, "\n "); + fprintf(file, " %02x", cpval[i]); + } + return; +} + +void +dumpTemplate(FILE *file, const CK_ATTRIBUTE *template, int start, int end) +{ + int i; + for (i = start; i < end; i++) { + unsigned char cval; + CK_ULONG ulval; + const unsigned char *cpval; + + fprintf(file, "%s:", ATTR_STRING(template[i].type)); + switch (template[i].ulValueLen) { + case 1: + cval = *(unsigned char *)template[i].pValue; + switch (cval) { + case 0: + fprintf(file, " false"); + break; + case 1: + fprintf(file, " true"); + break; + default: + fprintf(file, " %d (=0x%02x,'%c')", cval, cval, cval); + break; + } + break; + case sizeof(CK_ULONG): + ulval = *(CK_ULONG *)template[i].pValue; + fprintf(file, " %ld (=0x%04lx)", ulval, ulval); + break; + default: + cpval = (const unsigned char *)template[i].pValue; + dumphex(file, cpval, 0, template[i].ulValueLen); + break; + } + fprintf(file, "\n"); + } +} + +void +dumpItem(FILE *file, const SECItem *item) +{ + const unsigned char *cpval; + + if (item == NULL) { + fprintf(file, " pNULL "); + return; + } + if (item->data == NULL) { + fprintf(file, " NULL "); + return; + } + if (item->len == 0) { + fprintf(file, " Empty "); + return; + } + cpval = item->data; + dumphex(file, cpval, 0, item->len); + fprintf(file, " "); + return; +} + +PRBool +rsaKeysAreEqual(PK11ObjectType srcType, void *src, + PK11ObjectType destType, void *dest) +{ + + CK_ATTRIBUTE srcTemplate[RSA_ATTRIBUTES]; + CK_ATTRIBUTE destTemplate[RSA_ATTRIBUTES]; + PRBool areEqual = PR_TRUE; + SECStatus rv; + int i; + + memcpy(srcTemplate, rsaTemplate, RSA_SIZE); + memcpy(destTemplate, rsaTemplate, RSA_SIZE); + + rv = readKey(srcType, src, srcTemplate, 0, RSA_ATTRIBUTES); + if (rv != SECSuccess) { + printf("Could read source key\n"); + return PR_FALSE; + } + rv = readKey(destType, dest, destTemplate, 0, RSA_ATTRIBUTES); + if (rv != SECSuccess) { + printf("Could read dest key\n"); + return PR_FALSE; + } + + for (i = 0; i < RSA_ATTRIBUTES; i++) { + if (srcTemplate[i].type == CKA_ID) { + continue; /* we purposefully make the CKA_ID different */ + } + if (srcTemplate[i].ulValueLen != destTemplate[i].ulValueLen) { + printf("key->%s not equal src_len = %ld, dest_len=%ld\n", + ATTR_STRING(srcTemplate[i].type), + srcTemplate[i].ulValueLen, destTemplate[i].ulValueLen); + areEqual = 0; + } else if (memcmp(srcTemplate[i].pValue, destTemplate[i].pValue, + destTemplate[i].ulValueLen) != 0) { + printf("key->%s not equal.\n", ATTR_STRING(srcTemplate[i].type)); + areEqual = 0; + } + } + if (!areEqual) { + fprintf(stderr, "original key:\n"); + dumpTemplate(stderr, srcTemplate, 0, RSA_ATTRIBUTES); + fprintf(stderr, "created key:\n"); + dumpTemplate(stderr, destTemplate, 0, RSA_ATTRIBUTES); + } + resetTemplate(srcTemplate, 0, RSA_ATTRIBUTES); + resetTemplate(destTemplate, 0, RSA_ATTRIBUTES); + return areEqual; +} + +static int exp_exp_prime_fail_count = 0; + +#define LEAK_ID 0xf + +static int +doRSAPopulateTest(unsigned int keySize, unsigned long exponent, + int mask, int round, void *pwarg) +{ + SECKEYPrivateKey *rsaPrivKey; + SECKEYPublicKey *rsaPubKey; + PK11GenericObject *tstPrivKey; + CK_ATTRIBUTE tstTemplate[RSA_ATTRIBUTES]; + int tstHeaderCount; + PK11SlotInfo *slot = NULL; + PK11RSAGenParams rsaParams; + CK_OBJECT_CLASS obj_class = CKO_PRIVATE_KEY; + CK_KEY_TYPE key_type = CKK_RSA; + CK_BBOOL ck_false = CK_FALSE; + CK_BYTE cka_id[2] = { 0, 0 }; + int failed = 0; + int leak_found; /* did we find the expected leak */ + int expect_leak = 0; /* are we expecting a leak? */ + + rsaParams.pe = exponent; + rsaParams.keySizeInBits = keySize; + + slot = PK11_GetInternalSlot(); + if (slot == NULL) { + fprintf(stderr, "Couldn't get the internal slot for the test \n"); + return -1; + } + + rsaPrivKey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, + &rsaParams, &rsaPubKey, PR_FALSE, + PR_FALSE, pwarg); + if (rsaPrivKey == NULL) { + fprintf(stderr, "RSA Key Gen failed"); + PK11_FreeSlot(slot); + return -1; + } + + memcpy(tstTemplate, rsaTemplate, RSA_SIZE); + + tstTemplate[0].pValue = &obj_class; + tstTemplate[0].ulValueLen = sizeof(obj_class); + tstTemplate[1].pValue = &key_type; + tstTemplate[1].ulValueLen = sizeof(key_type); + tstTemplate[2].pValue = &ck_false; + tstTemplate[2].ulValueLen = sizeof(ck_false); + tstTemplate[3].pValue = &ck_false; + tstTemplate[3].ulValueLen = sizeof(ck_false); + tstTemplate[4].pValue = &ck_false; + tstTemplate[4].ulValueLen = sizeof(ck_false); + tstTemplate[5].pValue = &cka_id[0]; + tstTemplate[5].ulValueLen = sizeof(cka_id); + tstHeaderCount = 6; + cka_id[0] = round; + + if (mask & 1) { + printf("%s\n", test_array[1].description); + resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES); + cka_id[1] = 0; + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount, CKA_PUBLIC_EXPONENT); + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount + 1, CKA_MODULUS); + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount + 2, CKA_PRIME_1); + + tstPrivKey = PK11_CreateManagedGenericObject(slot, tstTemplate, + tstHeaderCount + + 3, + PR_FALSE); + if (tstPrivKey == NULL) { + fprintf(stderr, "RSA Populate failed: pubExp mod p\n"); + failed = 1; + } else if (!rsaKeysAreEqual(PK11_TypePrivKey, rsaPrivKey, + PK11_TypeGeneric, tstPrivKey)) { + fprintf(stderr, "RSA Populate key mismatch: pubExp mod p\n"); + failed = 1; + } + if (tstPrivKey) + PK11_DestroyGenericObject(tstPrivKey); + } + if (mask & 2) { + printf("%s\n", test_array[2].description); + /* test the basic2 case, public exponent, modulus, prime2 */ + resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES); + cka_id[1] = 1; + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount, CKA_PUBLIC_EXPONENT); + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount + 1, CKA_MODULUS); + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount + 2, CKA_PRIME_2); + /* test with q in the prime1 position */ + tstTemplate[tstHeaderCount + 2].type = CKA_PRIME_1; + + tstPrivKey = PK11_CreateManagedGenericObject(slot, tstTemplate, + tstHeaderCount + + 3, + PR_FALSE); + if (tstPrivKey == NULL) { + fprintf(stderr, "RSA Populate failed: pubExp mod q\n"); + failed = 1; + } else if (!rsaKeysAreEqual(PK11_TypePrivKey, rsaPrivKey, + PK11_TypeGeneric, tstPrivKey)) { + fprintf(stderr, "RSA Populate key mismatch: pubExp mod q\n"); + failed = 1; + } + if (tstPrivKey) + PK11_DestroyGenericObject(tstPrivKey); + } + if (mask & 4) { + printf("%s\n", test_array[3].description); + /* test the medium case, private exponent, prime1, prime2 */ + resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES); + cka_id[1] = 2; + + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount, CKA_PRIVATE_EXPONENT); + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount + 1, CKA_PRIME_1); + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount + 2, CKA_PRIME_2); + /* test with p & q swapped. Underlying code should swap these back */ + tstTemplate[tstHeaderCount + 2].type = CKA_PRIME_1; + tstTemplate[tstHeaderCount + 1].type = CKA_PRIME_2; + + tstPrivKey = PK11_CreateManagedGenericObject(slot, tstTemplate, + tstHeaderCount + + 3, + PR_FALSE); + if (tstPrivKey == NULL) { + fprintf(stderr, "RSA Populate failed: privExp p q\n"); + failed = 1; + } else if (!rsaKeysAreEqual(PK11_TypePrivKey, rsaPrivKey, + PK11_TypeGeneric, tstPrivKey)) { + fprintf(stderr, "RSA Populate key mismatch: privExp p q\n"); + failed = 1; + } + if (tstPrivKey) + PK11_DestroyGenericObject(tstPrivKey); + } + if (mask & 8) { + printf("%s\n", test_array[4].description); + /* test the advanced case, public exponent, private exponent, prime2 */ + resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES); + cka_id[1] = 3; + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount, CKA_PRIVATE_EXPONENT); + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount + 1, CKA_PUBLIC_EXPONENT); + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount + 2, CKA_PRIME_2); + + tstPrivKey = PK11_CreateManagedGenericObject(slot, tstTemplate, + tstHeaderCount + + 3, + PR_FALSE); + if (tstPrivKey == NULL) { + fprintf(stderr, "RSA Populate failed: pubExp privExp q\n"); + fprintf(stderr, " this is expected periodically. It means we\n"); + fprintf(stderr, " had more than one key that meets the " + "specification\n"); + exp_exp_prime_fail_count++; + } else if (!rsaKeysAreEqual(PK11_TypePrivKey, rsaPrivKey, + PK11_TypeGeneric, tstPrivKey)) { + fprintf(stderr, "RSA Populate key mismatch: pubExp privExp q\n"); + failed = 1; + } + if (tstPrivKey) + PK11_DestroyGenericObject(tstPrivKey); + } + if (mask & 0x10) { + printf("%s\n", test_array[5].description); + /* test the advanced case2, public exponent, private exponent, modulus + */ + resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES); + cka_id[1] = LEAK_ID; + + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount, CKA_PRIVATE_EXPONENT); + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount + 1, CKA_PUBLIC_EXPONENT); + copyAttribute(PK11_TypePrivKey, rsaPrivKey, tstTemplate, + tstHeaderCount + 2, CKA_MODULUS); + + /* purposefully use the old version. This will create a leak */ + tstPrivKey = PK11_CreateGenericObject(slot, tstTemplate, + tstHeaderCount + + 3, + PR_FALSE); + if (tstPrivKey == NULL) { + fprintf(stderr, "RSA Populate failed: pubExp privExp mod\n"); + failed = 1; + } else if (!rsaKeysAreEqual(PK11_TypePrivKey, rsaPrivKey, + PK11_TypeGeneric, tstPrivKey)) { + fprintf(stderr, "RSA Populate key mismatch: pubExp privExp mod\n"); + failed = 1; + } + expect_leak = 1; + if (tstPrivKey) + PK11_DestroyGenericObject(tstPrivKey); + } + resetTemplate(tstTemplate, tstHeaderCount, RSA_ATTRIBUTES); + SECKEY_DestroyPrivateKey(rsaPrivKey); + SECKEY_DestroyPublicKey(rsaPubKey); + + /* make sure we didn't leak */ + leak_found = 0; + tstPrivKey = PK11_FindGenericObjects(slot, CKO_PRIVATE_KEY); + if (tstPrivKey) { + SECStatus rv; + PK11GenericObject *thisKey; + int i; + + fprintf(stderr, "Leaking keys...\n"); + for (i = 0, thisKey = tstPrivKey; thisKey; i++, + thisKey = PK11_GetNextGenericObject(thisKey)) { + SECItem id = { 0, NULL, 0 }; + + rv = PK11_ReadRawAttribute(PK11_TypeGeneric, thisKey, + CKA_ID, &id); + if (rv != SECSuccess) { + fprintf(stderr, "Key %d: couldn't read CKA_ID: %s\n", + i, PORT_ErrorToString(PORT_GetError())); + continue; + } + fprintf(stderr, "id = { "); + dumpItem(stderr, &id); + fprintf(stderr, "};"); + if (id.data[1] == LEAK_ID) { + fprintf(stderr, " ---> leak expected\n"); + if (id.data[0] == round) + leak_found = 1; + } else { + if (id.len != sizeof(cka_id)) { + fprintf(stderr, + " ---> ERROR unexpected leak in generated key\n"); + } else { + fprintf(stderr, + " ---> ERROR unexpected leak in constructed key\n"); + } + failed = 1; + } + SECITEM_FreeItem(&id, PR_FALSE); + } + PK11_DestroyGenericObjects(tstPrivKey); + } + if (expect_leak && !leak_found) { + fprintf(stderr, "ERROR expected leak not found\n"); + failed = 1; + } + + PK11_FreeSlot(slot); + return failed ? -1 : 0; +} + +/* populate options */ +enum { + opt_Exponent = 0, + opt_KeySize, + opt_Repeat, + opt_Tests +}; + +static secuCommandFlag populate_options[] = { + { /* opt_Exponent */ 'e', PR_TRUE, 0, PR_FALSE }, + { /* opt_KeySize */ 'k', PR_TRUE, 0, PR_FALSE }, + { /* opt_Repeat */ 'r', PR_TRUE, 0, PR_FALSE }, + { /* opt_Tests */ 't', PR_TRUE, 0, PR_FALSE }, +}; + +int +is_delimiter(char c) +{ + if ((c == '+') || (c == ',') || (c == '|')) { + return 1; + } + return 0; +} + +int +parse_tests(char *test_string) +{ + int mask = 0; + int i; + + while (*test_string) { + if (is_delimiter(*test_string)) { + test_string++; + } + for (i = 0; i < test_array_size; i++) { + char *arg = test_array[i].arg; + int len = strlen(arg); + if (strncmp(test_string, arg, len) == 0) { + test_string += len; + mask |= test_array[i].mask_value; + break; + } + } + if (i == test_array_size) { + break; + } + } + return mask; +} + +int +main(int argc, char **argv) +{ + unsigned int keySize = 1024; + unsigned long exponent = 65537; + int i, repeat = 1, ret = 0; + SECStatus rv = SECFailure; + secuCommand populateArgs; + char *progName; + int mask = 0xff; + + populateArgs.numCommands = 0; + populateArgs.numOptions = sizeof(populate_options) / + sizeof(secuCommandFlag); + populateArgs.commands = NULL; + populateArgs.options = populate_options; + + progName = strrchr(argv[0], '/'); + if (!progName) + progName = strrchr(argv[0], '\\'); + progName = progName ? progName + 1 : argv[0]; + + rv = NSS_NoDB_Init(NULL); + if (rv != SECSuccess) { + SECU_PrintPRandOSError(progName); + return -1; + } + + rv = SECU_ParseCommandLine(argc, argv, progName, &populateArgs); + if (rv == SECFailure) { + fprintf(stderr, "%s: command line parsing error!\n", progName); + Usage(progName); + return -1; + } + rv = SECFailure; + + if (populateArgs.options[opt_KeySize].activated) { + keySize = PORT_Atoi(populateArgs.options[opt_KeySize].arg); + } + if (populateArgs.options[opt_Repeat].activated) { + repeat = PORT_Atoi(populateArgs.options[opt_Repeat].arg); + } + if (populateArgs.options[opt_Exponent].activated) { + exponent = PORT_Atoi(populateArgs.options[opt_Exponent].arg); + } + if (populateArgs.options[opt_Tests].activated) { + char *test_string = populateArgs.options[opt_Tests].arg; + mask = PORT_Atoi(test_string); + if (mask == 0) { + mask = parse_tests(test_string); + } + if (mask == 0) { + Usage(progName); + return -1; + } + } + + exp_exp_prime_fail_count = 0; + for (i = 0; i < repeat; i++) { + printf("Running RSA Populate test run %d\n", i); + ret = doRSAPopulateTest(keySize, exponent, mask, i, NULL); + if (ret != 0) { + i++; + break; + } + } + if (ret != 0) { + fprintf(stderr, "RSA Populate test round %d: FAILED\n", i); + } + if (repeat > 1) { + printf(" pub priv prime test: %d failures out of %d runs (%f %%)\n", + exp_exp_prime_fail_count, i, + (((double)exp_exp_prime_fail_count) * 100.0) / (double)i); + } + if (NSS_Shutdown() != SECSuccess) { + fprintf(stderr, "Shutdown failed\n"); + ret = -1; + } + return ret; +} |