From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- .../enc_dec_mac_output_plblic_key_as_csr/index.rst | 1697 ++++++++++++++++ .../index.rst | 2090 ++++++++++++++++++++ .../encrypt_decrypt_mac_using_token/index.rst | 1206 +++++++++++ .../nss/doc/rst/legacy/nss_sample_code/index.rst | 31 + .../nss_sample_code_sample1/index.rst | 713 +++++++ .../nss_sample_code_sample2/index.rst | 166 ++ .../nss_sample_code_sample3/index.rst | 169 ++ .../nss_sample_code_sample4/index.rst | 158 ++ .../nss_sample_code_sample5/index.rst | 174 ++ .../nss_sample_code_sample6/index.rst | 153 ++ .../nss_sample_code_sample_1_hashing/index.rst | 253 +++ .../index.rst | 257 +++ .../index.rst | 1221 ++++++++++++ .../nss_sample_code_utililies_1/index.rst | 553 ++++++ .../rst/legacy/nss_sample_code/sample1/index.rst | 230 +++ .../nss_sample_code/sample1_-_hashing/index.rst | 257 +++ .../rst/legacy/nss_sample_code/sample2/index.rst | 12 + .../sample2_-_initialize_nss_database/index.rst | 250 +++ .../index.rst | 30 + .../utiltiies_for_nss_samples/index.rst | 747 +++++++ 20 files changed, 10367 insertions(+) create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/enc_dec_mac_output_plblic_key_as_csr/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/enc_dec_mac_using_key_wrap_certreq_pkcs10_csr/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/encrypt_decrypt_mac_using_token/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample1/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample2/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample3/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample4/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample5/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample6/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample_1_hashing/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample_2_initialization_of_nss/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample_3_basic_encryption_and_maci/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_utililies_1/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/sample1/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/sample1_-_hashing/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/sample2/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/sample2_-_initialize_nss_database/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/sample3_-_encdecmac_using_token_object/index.rst create mode 100644 security/nss/doc/rst/legacy/nss_sample_code/utiltiies_for_nss_samples/index.rst (limited to 'security/nss/doc/rst/legacy/nss_sample_code') diff --git a/security/nss/doc/rst/legacy/nss_sample_code/enc_dec_mac_output_plblic_key_as_csr/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/enc_dec_mac_output_plblic_key_as_csr/index.rst new file mode 100644 index 0000000000..dfa740a911 --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/enc_dec_mac_output_plblic_key_as_csr/index.rst @@ -0,0 +1,1697 @@ +.. _mozilla_projects_nss_nss_sample_code_enc_dec_mac_output_plblic_key_as_csr: + +Enc Dec MAC Output Public Key as CSR +==================================== + +.. _nss_sample_code_5_encryptiondecryption_and_mac_and_output_public_as_a_csr.: + +`NSS Sample Code 5: Encryption/Decryption and MAC and output Public as a CSR. <#nss_sample_code_5_encryptiondecryption_and_mac_and_output_public_as_a_csr.>`__ +-------------------------------------------------------------------------------------------------------------------------------------------------------------- + +.. container:: + + Generates encryption/mac keys and outputs public key as certificate signing request + + .. code:: c + + /* 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 https://mozilla.org/MPL/2.0/. */ + + /* NSPR Headers */ + #include + #include + #include + #include + #include + #include + #include + + /* NSS headers */ + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + /* our samples utilities */ + #include "util.h" + + #define BUFFERSIZE 80 + #define DIGESTSIZE 16 + #define PTEXT_MAC_BUFFER_SIZE 96 + #define CIPHERSIZE 96 + #define BLOCKSIZE 32 + #define DEFAULT_KEY_BITS 1024 + + + #define CIPHER_HEADER "-----BEGIN CIPHER-----" + #define CIPHER_TRAILER "-----END CIPHER-----" + #define ENCKEY_HEADER "-----BEGIN WRAPPED ENCKEY-----" + #define ENCKEY_TRAILER "-----END WRAPPED ENCKEY-----" + #define MACKEY_HEADER "-----BEGIN WRAPPED MACKEY-----" + #define MACKEY_TRAILER "-----END WRAPPED MACKEY-----" + #define IV_HEADER "-----BEGIN IV-----" + #define IV_TRAILER "-----END IV-----" + #define MAC_HEADER "-----BEGIN MAC-----" + #define MAC_TRAILER "-----END MAC-----" + #define PAD_HEADER "-----BEGIN PAD-----" + #define PAD_TRAILER "-----END PAD-----" + #define LAB_HEADER "-----BEGIN KEY LABEL-----" + #define LAB_TRAILER "-----END KEY LABEL-----" + #define PUBKEY_HEADER "-----BEGIN PUB KEY -----" + #define PUBKEY_TRAILER "-----END PUB KEY -----" + #define NS_CERTREQ_HEADER "-----BEGIN NEW CERTIFICATE REQUEST-----" + #define NS_CERTREQ_TRAILER "-----END NEW CERTIFICATE REQUEST-----" + + + typedef enum { + GEN_CSR, + ENCRYPT, + DECRYPT, + UNKNOWN + } CommandType; + + typedef enum { + SYMKEY = 0, + MACKEY = 1, + IV = 2, + MAC = 3, + PAD = 4, + PUBKEY = 5, + LAB = 6 + } HeaderType; + + /* This is conditionalized because PORT_ErrorToString was introduced with nss 3.13. + * Though PR_ErrorToString was available, support for it in nss wasn't. + * FIXME: samples should determine the version of nss that's available and refuse + * to run if not 3.13 or higher. + */ + #ifndef PORT_ErrorToString + #ifndef SEC_ERROR_BASE + #define SEC_ERROR_BASE (-0x2000) + #define PORT_ErrorToString(err) PR_ErrorToString((err), PR_LANGUAGE_I_DEFAULT) + #endif + #endif + + + /* + * Print usage message and exit + */ + static void + Usage(const char *progName) + { + fprintf(stderr, "\nUsage: %s -c -d [-z ] " + "[-p | -f ] -s -r -i -o \n\n", + progName); + fprintf(stderr, "%-20s Specify 'G' for generating RSA keypair for wrapping\n\n", + "G"); + fprintf(stderr, "%-20s Specify 'E' for encrypt operation\n\n", + "E"); + fprintf(stderr, "%-20s Specify 'D' for decrypt operation\n\n", + "D"); + fprintf(stderr, "%-20s Specify db directory path\n\n", + "-d "); + fprintf(stderr, "%-20s Specify db password [optional]\n\n", + "-p "); + fprintf(stderr, "%-20s Specify db password file [optional]\n\n", + "-f "); + fprintf(stderr, "%-20s Specify noise file name [optional]\n\n", + "-z "); + fprintf(stderr, "%-21s Specify subject\n\n", + "-s "); + fprintf(stderr, "%-21s Specify certficate request file name\n\n", + "-r "); + fprintf(stderr, "%-21s Specify an input file name\n\n", + "-i "); + fprintf(stderr, "%-21s Specify an output file name\n\n", + "-o "); + fprintf(stderr, "%-7s For encrypt, it takes as an input file and produces\n", + "Note :"); + fprintf(stderr, "%-7s .enc and .header as intermediate output files.\n\n", + ""); + fprintf(stderr, "%-7s For decrypt, it takes .enc and .header\n", + ""); + fprintf(stderr, "%-7s as input files and produces as a final output file.\n\n", + ""); + exit(-1); + } + + + /* Map option letter enumerated commad type */ + static CommandType option2Command(const char* c) + { + switch (*c) { + case 'G': return GEN_CSR; + case 'E': return ENCRYPT; + case 'D': return DECRYPT; + default: return UNKNOWN; + } + } + + /* + * Wrap the symkey using public key + */ + SECStatus + WrapKey(PK11SymKey* key, SECKEYPublicKey *pubKey, SECItem **wrappedKey) + { + SECStatus rv; + SECItem *data = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + + if (!data) { + PR_fprintf(PR_STDERR, "Error while allocating memory\n"); + rv = SECFailure; + goto cleanup; + } + + data->len = SECKEY_PublicKeyStrength(pubKey); + data->data = (unsigned char*)PORT_ZAlloc((data->len)*sizeof(unsigned int)); + + if (!data->data) { + PR_fprintf(PR_STDERR, "Error while allocating memory\n"); + rv = SECFailure; + goto cleanup; + } + + rv = PK11_PubWrapSymKey(CKM_RSA_PKCS, pubKey, key, data); + if (rv != SECSuccess) { + rv = SECFailure; + } else { + *wrappedKey = data; + return SECSuccess; + } + + cleanup: + if (data) { + SECITEM_FreeItem(data, PR_TRUE); + } + return rv; + } + + /* + * Generate a Symmetric Key + */ + PK11SymKey * + GenerateSYMKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE mechanism, + int keySize, SECItem *keyID, secuPWData *pwdata) + { + SECStatus rv; + PK11SymKey *key; + + /* Generate the symmetric key */ + key = PK11_TokenKeyGen(slot, mechanism, + NULL, keySize, keyID, PR_FALSE, pwdata); + + if (!key) { + PR_fprintf(PR_STDERR, "Symmetric Key Generation Failed \n"); + } + + return key; + } + + /* + * MacInit + */ + SECStatus + MacInit(PK11Context *ctx) + { + SECStatus rv = PK11_DigestBegin(ctx); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Compute MAC Failed : PK11_DigestBegin()\n"); + } + return rv; + } + + /* + * MacUpdate + */ + SECStatus + MacUpdate(PK11Context *ctx, + unsigned char *msg, unsigned int msgLen) + { + SECStatus rv = PK11_DigestOp(ctx, msg, msgLen); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Compute MAC Failed : DigestOp()\n"); + } + return rv; + } + + /* + * Finalize MACing + */ + SECStatus + MacFinal(PK11Context *ctx, + unsigned char *mac, unsigned int *macLen, unsigned int maxLen) + { + SECStatus rv = PK11_DigestFinal(ctx, mac, macLen, maxLen); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Compute MAC Failed : PK11_DigestFinal()\n"); + } + return SECSuccess; + } + + /* + * Compute Mac + */ + SECStatus + ComputeMac(PK11Context *ctxmac, + unsigned char *ptext, unsigned int ptextLen, + unsigned char *mac, unsigned int *macLen, + unsigned int maxLen) + { + SECStatus rv = MacInit(ctxmac); + if (rv != SECSuccess) return rv; + rv = MacUpdate(ctxmac, ptext, ptextLen); + if (rv != SECSuccess) return rv; + rv = MacFinal(ctxmac, mac, macLen, maxLen); + return rv; + } + + /* + * WriteToHeaderFile + */ + SECStatus + WriteToHeaderFile(const char *buf, unsigned int len, HeaderType type, + PRFileDesc *outFile) + { + SECStatus rv; + const char *header; + const char *trailer; + + switch (type) { + case SYMKEY: + header = ENCKEY_HEADER; + trailer = ENCKEY_TRAILER; + break; + case MACKEY: + header = MACKEY_HEADER; + trailer = MACKEY_TRAILER; + break; + case IV: + header = IV_HEADER; + trailer = IV_TRAILER; + break; + case MAC: + header = MAC_HEADER; + trailer = MAC_TRAILER; + break; + case PAD: + header = PAD_HEADER; + trailer = PAD_TRAILER; + break; + case PUBKEY: + header = PUBKEY_HEADER; + trailer = PUBKEY_TRAILER; + break; + case LAB: + header = LAB_HEADER; + trailer = LAB_TRAILER; + PR_fprintf(outFile, "%s\n", header); + PR_fprintf(outFile, "%s\n", buf); + PR_fprintf(outFile, "%s\n\n", trailer); + return SECSuccess; + break; + default: + return SECFailure; + } + + PR_fprintf(outFile, "%s\n", header); + PrintAsAscii(outFile, buf, len); + PR_fprintf(outFile, "%s\n\n", trailer); + return SECSuccess; + } + + /* + * Initialize for encryption or decryption - common code + */ + PK11Context * + CryptInit(PK11SymKey *key, + unsigned char *iv, unsigned int ivLen, + CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation) + { + SECItem ivItem = { siBuffer, iv, ivLen }; + PK11Context *ctx = NULL; + + SECItem *secParam = PK11_ParamFromIV(type, &ivItem); + if (secParam == NULL) { + PR_fprintf(PR_STDERR, "Crypt Failed : secParam NULL\n"); + return NULL; + } + ctx = PK11_CreateContextBySymKey(type, operation, key, secParam); + if (ctx == NULL) { + PR_fprintf(PR_STDERR, "Crypt Failed : can't create a context\n"); + goto cleanup; + + } + cleanup: + if (secParam) { + SECITEM_FreeItem(secParam, PR_TRUE); + } + return ctx; + } + + /* + * Common encryption and decryption code + */ + SECStatus + Crypt(PK11Context *ctx, + unsigned char *out, unsigned int *outLen, unsigned int maxOut, + unsigned char *in, unsigned int inLen) + { + SECStatus rv; + + rv = PK11_CipherOp(ctx, out, outLen, maxOut, in, inLen); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Crypt Failed : PK11_CipherOp returned %d\n", rv); + goto cleanup; + } + + cleanup: + if (rv != SECSuccess) { + return rv; + } + return SECSuccess; + } + + /* + * Decrypt + */ + SECStatus + Decrypt(PK11Context *ctx, + unsigned char *out, unsigned int *outLen, unsigned int maxout, + unsigned char *in, unsigned int inLen) + { + return Crypt(ctx, out, outLen, maxout, in, inLen); + } + + /* + * Encrypt + */ + SECStatus + Encrypt(PK11Context* ctx, + unsigned char *out, unsigned int *outLen, unsigned int maxout, + unsigned char *in, unsigned int inLen) + { + return Crypt(ctx, out, outLen, maxout, in, inLen); + } + + /* + * EncryptInit + */ + PK11Context * + EncryptInit(PK11SymKey *ek, unsigned char *iv, unsigned int ivLen, + CK_MECHANISM_TYPE type) + { + return CryptInit(ek, iv, ivLen, type, CKA_ENCRYPT); + } + + /* + * DecryptInit + */ + PK11Context * + DecryptInit(PK11SymKey *dk, unsigned char *iv, unsigned int ivLen, + CK_MECHANISM_TYPE type) + { + return CryptInit(dk, iv, ivLen, type, CKA_DECRYPT); + } + + /* + * Read cryptographic parameters from the header file + */ + SECStatus + ReadFromHeaderFile(const char *fileName, HeaderType type, + SECItem *item, PRBool isHexData) + { + SECStatus rv; + SECItem filedata; + SECItem outbuf; + unsigned char *nonbody; + unsigned char *body; + char *header; + char *trailer; + PRFileDesc *file = NULL; + + outbuf.type = siBuffer; + file = PR_Open(fileName, PR_RDONLY, 0); + if (!file) { + PR_fprintf(PR_STDERR, "Failed to open %s\n", fileName); + return SECFailure; + } + switch (type) { + case PUBKEY: + header = PUBKEY_HEADER; + trailer = PUBKEY_TRAILER; + break; + case SYMKEY: + header = ENCKEY_HEADER; + trailer = ENCKEY_TRAILER; + break; + case MACKEY: + header = MACKEY_HEADER; + trailer = MACKEY_TRAILER; + break; + case IV: + header = IV_HEADER; + trailer = IV_TRAILER; + break; + case MAC: + header = MAC_HEADER; + trailer = MAC_TRAILER; + break; + case PAD: + header = PAD_HEADER; + trailer = PAD_TRAILER; + break; + case LAB: + header = LAB_HEADER; + trailer = LAB_TRAILER; + break; + default: + PR_Close(file); + return SECFailure; + } + + rv = FileToItem(&filedata, file); + nonbody = (char *)filedata.data; + if (!nonbody) { + PR_fprintf(PR_STDERR, "unable to read data from input file\n"); + rv = SECFailure; + goto cleanup; + } + + /* check for headers and trailers and remove them */ + char *trail = NULL; + if ((body = strstr(nonbody, header)) != NULL) { + char *trail = NULL; + nonbody = body; + body = PORT_Strchr(body, '\n'); + if (!body) + body = PORT_Strchr(nonbody, '\r'); /* maybe this is a MAC file */ + if (body) + trail = strstr(++body, trailer); + if (trail != NULL) { + *trail = '\0'; + } else { + PR_fprintf(PR_STDERR, "input has header but no trailer\n"); + PORT_Free(filedata.data); + return SECFailure; + } + } else { + /* headers didn't exist */ + body = nonbody; + if (body) { + trail = strstr(++body, trailer); + if (trail != NULL) { + PR_fprintf(PR_STDERR, + "input has no header but has trailer\n"); + PORT_Free(filedata.data); + return SECFailure; + } + } + } + + cleanup: + PR_Close(file); + ATOB_ConvertAsciiToItem(item, body); + return SECSuccess; + } + + /* + * Generate the private key + */ + SECKEYPrivateKey * + GeneratePrivateKey(KeyType keytype, PK11SlotInfo *slot, int size, + int publicExponent, const char *noiseFileName, + SECKEYPublicKey **pubkeyp, const char *pqgFile, + secuPWData *pwdata) + { + CK_MECHANISM_TYPE mechanism; + SECOidTag algtag; + PK11RSAGenParams rsaparams; + void *params; + SECKEYPrivateKey *privKey = NULL; + SECStatus rv; + unsigned char randbuf[BLOCKSIZE + 1]; + + rv = GenerateRandom(randbuf, BLOCKSIZE); + if (rv != SECSuccess) { + fprintf(stderr, "Error while generating the random numbers : %s\n", + PORT_ErrorToString(rv)); + goto cleanup; + } + PK11_RandomUpdate(randbuf, BLOCKSIZE); + switch (keytype) { + case rsaKey: + rsaparams.keySizeInBits = size; + rsaparams.pe = publicExponent; + mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; + algtag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; + params = &rsaparams; + break; + default: + goto cleanup; + } + fprintf(stderr, "\n\n"); + fprintf(stderr, "Generating key. This may take a few moments...\n\n"); + privKey = PK11_GenerateKeyPair(slot, mechanism, params, pubkeyp, + PR_TRUE /*isPerm*/, PR_TRUE /*isSensitive*/, + pwdata); + cleanup: + return privKey; + } + + /* + * Extract the public key request from CSR + */ + SECKEYPublicKey * + ExtractPublicKeyFromCertRequest(const char *inFileName, PRBool ascii) + { + CERTSignedData signedData; + SECItem reqDER; + CERTCertificateRequest *certReq = NULL; + SECStatus rv = SECSuccess; + PRArenaPool *arena = NULL; + SECKEYPublicKey *publicKey = NULL; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + rv = SECFailure; + goto cleanup; + } + + rv = ReadDERFromFile(&reqDER, inFileName, ascii); + if (rv) { + rv = SECFailure; + goto cleanup; + } + certReq = (CERTCertificateRequest*) PORT_ArenaZAlloc + (arena, sizeof(CERTCertificateRequest)); + if (!certReq) { + rv = SECFailure; + goto cleanup; + } + certReq->arena = arena; + + /* Since cert request is a signed data, must decode to get the inner + data + */ + PORT_Memset(&signedData, 0, sizeof(signedData)); + rv = SEC_ASN1DecodeItem(arena, &signedData, + SEC_ASN1_GET(CERT_SignedDataTemplate), &reqDER); + if (rv) { + rv = SECFailure; + goto cleanup; + } + rv = SEC_ASN1DecodeItem(arena, certReq, + SEC_ASN1_GET(CERT_CertificateRequestTemplate), &signedData.data); + if (rv) { + rv = SECFailure; + goto cleanup; + } + rv = CERT_VerifySignedDataWithPublicKeyInfo(&signedData, + &certReq->subjectPublicKeyInfo, NULL /* wincx */); + publicKey = SECKEY_ExtractPublicKey(&certReq->subjectPublicKeyInfo); + + cleanup: + if (reqDER.data) { + SECITEM_FreeItem(&reqDER, PR_FALSE); + } + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + return publicKey; + } + + /* + * Get the private key corresponding to public key + */ + SECKEYPrivateKey * + GetRSAPrivateKey(PK11SlotInfo *slot, + secuPWData *pwdata, + SECKEYPublicKey *pubKey) + { + SECKEYPrivateKey *privKey = NULL; + SECItem *cka_id; + + if (slot == NULL) { + fprintf(stderr, "Empty Slot\n"); + goto cleanup; + } + if (PK11_Authenticate(slot, PR_TRUE, pwdata) != SECSuccess) { + fprintf(stderr, "could not authenticate to token %s.", + PK11_GetTokenName(slot)); + goto cleanup; + } + cka_id = &pubKey->u.rsa.modulus; + cka_id = PK11_MakeIDFromPubKey(cka_id); + privKey = PK11_FindKeyByKeyID(slot, cka_id, pwdata); + cleanup: + return privKey; + } + + /* + * Generate the certificate request with subject + */ + static SECStatus + CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, + SECOidTag hashAlgTag, CERTName *subject, PRBool ascii, + const char *certReqFileName) + { + CERTSubjectPublicKeyInfo *spki = NULL; + CERTCertificateRequest *cr = NULL; + SECItem *encoding = NULL; + SECOidTag signAlgTag; + SECItem result; + SECStatus rv = SECSuccess; + PRInt32 numBytes; + void *extHandle; + PRArenaPool *arena = NULL; + PRFileDesc *outFile = NULL; + + /* Open the certificate request file to write */ + outFile = PR_Open(certReqFileName, PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660); + if (!outFile) { + PR_fprintf(PR_STDERR, + "unable to open \"%s\" for writing (%ld, %ld).\n", + certReqFileName, PR_GetError(), PR_GetOSError()); + goto cleanup; + } + /* Create info about public key */ + spki = SECKEY_CreateSubjectPublicKeyInfo(pubk); + if (!spki) { + PR_fprintf(PR_STDERR, "unable to create subject public key\n"); + rv = SECFailure; + goto cleanup; + } + + /* Generate certificate request */ + cr = CERT_CreateCertificateRequest(subject, spki, NULL); + if (!cr) { + PR_fprintf(PR_STDERR, "unable to make certificate request\n"); + rv = SECFailure; + goto cleanup; + } + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + fprintf(stderr, "out of memory"); + rv = SECFailure; + goto cleanup; + } + + extHandle = CERT_StartCertificateRequestAttributes(cr); + if (extHandle == NULL) { + PORT_FreeArena (arena, PR_FALSE); + rv = SECFailure; + goto cleanup; + } + + CERT_FinishExtensions(extHandle); + CERT_FinishCertificateRequestAttributes(cr); + + /* Der encode the request */ + encoding = SEC_ASN1EncodeItem(arena, NULL, cr, + SEC_ASN1_GET(CERT_CertificateRequestTemplate)); + if (encoding == NULL) { + PR_fprintf(PR_STDERR, "der encoding of request failed\n"); + rv = SECFailure; + goto cleanup; + } + + /* Sign the request */ + signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag); + if (signAlgTag == SEC_OID_UNKNOWN) { + PR_fprintf(PR_STDERR, "unknown Key or Hash type\n"); + rv = SECFailure; + goto cleanup; + } + rv = SEC_DerSignData(arena, &result, encoding->data, encoding->len, + privk, signAlgTag); + if (rv) { + PR_fprintf(PR_STDERR, "signing of data failed\n"); + rv = SECFailure; + goto cleanup; + } + + /* Encode request in specified format */ + if (ascii) { + char *obuf; + char *name, *email, *org, *state, *country; + SECItem *it; + int total; + + it = &result; + + obuf = BTOA_ConvertItemToAscii(it); + total = PL_strlen(obuf); + + name = CERT_GetCommonName(subject); + if (!name) { + name = strdup("(not specified)"); + } + + email = CERT_GetCertEmailAddress(subject); + if (!email) + email = strdup("(not specified)"); + + org = CERT_GetOrgName(subject); + if (!org) + org = strdup("(not specified)"); + + state = CERT_GetStateName(subject); + if (!state) + state = strdup("(not specified)"); + + country = CERT_GetCountryName(subject); + if (!country) + country = strdup("(not specified)"); + + PR_fprintf(outFile, + "\nCertificate request generated by Netscape certutil\n"); + PR_fprintf(outFile, "Common Name: %s\n", name); + PR_fprintf(outFile, "Email: %s\n", email); + PR_fprintf(outFile, "Organization: %s\n", org); + PR_fprintf(outFile, "State: %s\n", state); + PR_fprintf(outFile, "Country: %s\n\n", country); + + PR_fprintf(outFile, "%s\n", NS_CERTREQ_HEADER); + numBytes = PR_Write(outFile, obuf, total); + if (numBytes != total) { + PR_fprintf(PR_STDERR, "write error\n"); + return SECFailure; + } + PR_fprintf(outFile, "\n%s\n", NS_CERTREQ_TRAILER); + if (obuf) { + PORT_Free(obuf); + } + } else { + numBytes = PR_Write(outFile, result.data, result.len); + if (numBytes != (int)result.len) { + PR_fprintf(PR_STDERR, "write error\n"); + rv = SECFailure; + goto cleanup; + } + } + cleanup: + if (spki) { + SECKEY_DestroySubjectPublicKeyInfo(spki); + } + if (cr) { + CERT_DestroyCertificateRequest (cr); + } + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + if (outFile) { + PR_Close(outFile); + } + return rv; + } + + /* + * Mac and Encrypt the input file content + */ + SECStatus + EncryptAndMac(PRFileDesc *inFile, + PRFileDesc *headerFile, + PRFileDesc *encFile, + PK11SymKey *ek, + PK11SymKey *mk, + unsigned char *iv, unsigned int ivLen, + PRBool ascii) + { + SECStatus rv; + unsigned char ptext[BLOCKSIZE]; + unsigned int ptextLen; + unsigned char mac[DIGESTSIZE]; + unsigned int macLen; + unsigned int nwritten; + unsigned char encbuf[BLOCKSIZE]; + unsigned int encbufLen; + SECItem noParams = { siBuffer, NULL, 0 }; + PK11Context *ctxmac = NULL; + PK11Context *ctxenc = NULL; + unsigned int pad[1]; + SECItem padItem; + unsigned int paddingLength = 0; + + static unsigned int firstTime = 1; + int j; + + ctxmac = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, mk, &noParams); + if (ctxmac == NULL) { + PR_fprintf(PR_STDERR, "Can't create MAC context\n"); + rv = SECFailure; + goto cleanup; + } + rv = MacInit(ctxmac); + if (rv != SECSuccess) { + goto cleanup; + } + + ctxenc = EncryptInit(ek, iv, ivLen, CKM_AES_CBC); + + /* read a buffer of plaintext from input file */ + while ((ptextLen = PR_Read(inFile, ptext, sizeof(ptext))) > 0) { + /* Encrypt using it using CBC, using previously created IV */ + if (ptextLen != BLOCKSIZE) { + paddingLength = BLOCKSIZE - ptextLen; + for ( j=0; j < paddingLength; j++) { + ptext[ptextLen+j] = (unsigned char)paddingLength; + } + ptextLen = BLOCKSIZE; + } + rv = Encrypt(ctxenc, + encbuf, &encbufLen, sizeof(encbuf), + ptext, ptextLen); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Encrypt Failure\n"); + goto cleanup; + } + + /* save the last block of ciphertext as the next IV */ + iv = encbuf; + ivLen = encbufLen; + + /* write the cipher text to intermediate file */ + nwritten = PR_Write(encFile, encbuf, encbufLen); + /*PR_Assert(nwritten == encbufLen);*/ + + rv = MacUpdate(ctxmac, ptext, ptextLen); + if (rv != SECSuccess) + goto cleanup; + } + + rv = MacFinal(ctxmac, mac, &macLen, DIGESTSIZE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "MacFinal Failure\n"); + goto cleanup; + } + if (macLen == 0) { + PR_fprintf(PR_STDERR, "Bad MAC length\n"); + rv = SECFailure; + goto cleanup; + } + WriteToHeaderFile(mac, macLen, MAC, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Write MAC Failure\n"); + goto cleanup; + } + + pad[0] = paddingLength; + padItem.type = siBuffer; + padItem.data = (unsigned char *)pad; + padItem.len = sizeof(pad[0]); + + WriteToHeaderFile(padItem.data, padItem.len, PAD, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Write PAD Failure\n"); + goto cleanup; + } + + rv = SECSuccess; + + cleanup: + if (ctxmac != NULL) { + PK11_DestroyContext(ctxmac, PR_TRUE); + } + if (ctxenc != NULL) { + PK11_DestroyContext(ctxenc, PR_TRUE); + } + + return rv; + } + + /* + * Decrypt and Verify MAC + */ + SECStatus + DecryptAndVerifyMac(PRFileDesc *outFile, + PRFileDesc *inFile, unsigned int inFileLength, + SECItem *cItem, SECItem *macItem, + PK11SymKey* ek, PK11SymKey* mk, SECItem *ivItem, SECItem *padItem) + { + SECStatus rv; + unsigned char decbuf[64]; + unsigned int decbufLen; + + unsigned char ptext[BLOCKSIZE]; + unsigned int ptextLen = 0; + unsigned char ctext[64]; + unsigned int ctextLen; + unsigned char newmac[DIGESTSIZE]; + unsigned int newmacLen = 0; + unsigned int newptextLen = 0; + unsigned int count = 0; + unsigned int temp = 0; + unsigned int blockNumber = 0; + SECItem noParams = { siBuffer, NULL, 0 }; + PK11Context *ctxmac = NULL; + PK11Context *ctxenc = NULL; + + unsigned char iv[BLOCKSIZE]; + unsigned int ivLen = ivItem->len; + unsigned int paddingLength; + int j; + + memcpy(iv, ivItem->data, ivItem->len); + paddingLength = (unsigned int)padItem->data[0]; + + ctxmac = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, mk, &noParams); + if (ctxmac == NULL) { + PR_fprintf(PR_STDERR, "Can't create MAC context\n"); + rv = SECFailure; + goto cleanup; + } + + rv = MacInit(ctxmac); + if (rv != SECSuccess) goto cleanup; + + ctxenc = DecryptInit(ek, iv, ivLen, CKM_AES_CBC); + + while ((ctextLen = PR_Read(inFile, ctext, sizeof(ctext))) > 0) { + + count += ctextLen; + + /* decrypt cipher text buffer using CBC and IV */ + + rv = Decrypt(ctxenc, decbuf, &decbufLen, sizeof(decbuf), + ctext, ctextLen); + + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Decrypt Failure\n"); + goto cleanup; + } + + if (decbufLen == 0) break; + + rv = MacUpdate(ctxmac, decbuf, decbufLen); + if (rv != SECSuccess) { goto cleanup; } + if (count == inFileLength) { + decbufLen = decbufLen-paddingLength; + } + + /* write the plain text to out file */ + temp = PR_Write(outFile, decbuf, decbufLen); + if (temp != decbufLen) { + PR_fprintf(PR_STDERR, "write error\n"); + rv = SECFailure; + break; + } + + blockNumber++; + } + + if (rv != SECSuccess) { goto cleanup; } + + rv = MacFinal(ctxmac, newmac, &newmacLen, sizeof(newmac)); + if (rv != SECSuccess) { goto cleanup; } + + if (PORT_Memcmp(macItem->data, newmac, newmacLen) == 0) { + rv = SECSuccess; + } else { + PR_fprintf(PR_STDERR, "Check MAC : Failure\n"); + PR_fprintf(PR_STDERR, "Extracted : "); + PrintAsAscii(PR_STDERR, macItem->data, macItem->len); + PR_fprintf(PR_STDERR, "Computed : "); + PrintAsAscii(PR_STDERR, newmac, newmacLen); + rv = SECFailure; + } + cleanup: + if (ctxmac) { + PK11_DestroyContext(ctxmac, PR_TRUE); + } + if (ctxenc) { + PK11_DestroyContext(ctxenc, PR_TRUE); + } + + return rv; + } + + /* + * Open intermediate file, read in IV, wrapped encryption key, + * wrapped MAC key, MAC, PAD and public key from header file + */ + SECStatus + GetDataFromHeader(const char *headerFileName, + SECItem *ivItem, + SECItem *wrappedEncKeyItem, + SECItem *wrappedMacKeyItem, + SECItem *macItem, + SECItem *padItem, + SECKEYPublicKey **pubKey) + { + SECStatus rv = SECSuccess; + CERTSubjectPublicKeyInfo *keyInfo = NULL; + SECItem pubKeyData; + + /* Read in the IV into item from the header file */ + rv = ReadFromHeaderFile(headerFileName, IV, ivItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not retrieve IV from cipher file\n"); + goto cleanup; + } + + rv = ReadFromHeaderFile(headerFileName, SYMKEY, wrappedEncKeyItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not retrieve wrapped AES key from header file\n"); + goto cleanup; + } + /* Read in the MAC key into item from the header file */ + rv = ReadFromHeaderFile(headerFileName, MACKEY, wrappedMacKeyItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not retrieve wrapped MAC key from header file\n"); + goto cleanup; + } + + /* Get the public key from header file */ + rv = ReadFromHeaderFile(headerFileName, PUBKEY, &pubKeyData, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not retrieve public key from header file\n"); + goto cleanup; + } + keyInfo = SECKEY_DecodeDERSubjectPublicKeyInfo(&pubKeyData); + if (!keyInfo) { + PR_fprintf(PR_STDERR, "Could not decode public key\n"); + rv = SECFailure; + goto cleanup; + } + *pubKey = SECKEY_ExtractPublicKey(keyInfo); + if (*pubKey == NULL) { + PR_fprintf(PR_STDERR, "Error while getting RSA public key\n"); + rv = SECFailure; + goto cleanup; + } + /* Read in the Mac into item from the header file */ + rv = ReadFromHeaderFile(headerFileName, MAC, macItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not retrieve MAC from cipher file\n"); + goto cleanup; + } + if (macItem->data == NULL) { + PR_fprintf(PR_STDERR, "MAC has NULL data\n"); + rv = SECFailure; + goto cleanup; + } + if (macItem->len == 0) { + PR_fprintf(PR_STDERR, "MAC has data has 0 length\n"); + rv = SECFailure; + goto cleanup; + } + + /* Read in the PAD into item from the header file */ + rv = ReadFromHeaderFile(headerFileName, PAD, padItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not retrieve PAD detail from header file\n"); + goto cleanup; + } + + cleanup: + return rv; + } + + + /* + * DecryptFile + */ + SECStatus + DecryptFile(PK11SlotInfo *slot, + const char *outFileName, + const char *headerFileName, + char *encryptedFileName, + secuPWData *pwdata, + PRBool ascii) + { + /* + * The DB is open read only and we have authenticated to it + * open input file, read in header, get IV and wrapped keys and + * public key + * Unwrap the wrapped keys + * loop until EOF(input): + * read a buffer of ciphertext from input file, + * Save last block of ciphertext + * decrypt ciphertext buffer using CBC and IV, + * compute and check MAC, then remove MAC from plaintext + * replace IV with saved last block of ciphertext + * write the plain text to output file + * close files + * report success + */ + + SECStatus rv; + SECItem ivItem; + SECItem wrappedEncKeyItem; + SECItem wrappedMacKeyItem; + SECItem cipherItem; + SECItem macItem; + SECItem padItem; + SECKEYPublicKey *pubKey = NULL; + PK11SymKey *encKey = NULL; + PK11SymKey *macKey = NULL; + SECKEYPrivateKey *privKey = NULL; + PRFileDesc *outFile = NULL; + PRFileDesc *inFile = NULL; + unsigned int inFileLength = 0; + + /* open intermediate file, read in header, get IV, public key and + * CKA_IDs of two keys from it + */ + rv = GetDataFromHeader(headerFileName, + &ivItem, + &wrappedEncKeyItem, + &wrappedMacKeyItem, + &macItem, + &padItem, + &pubKey); + if (rv != SECSuccess) { + goto cleanup; + } + + /* find private key from the DB token using public key */ + privKey = GetRSAPrivateKey(slot, pwdata, pubKey); + if (privKey == NULL) { + PR_fprintf(PR_STDERR, "Can't find private key\n"); + rv = SECFailure; + goto cleanup; + } + + encKey = PK11_PubUnwrapSymKey(privKey, &wrappedEncKeyItem, + CKM_AES_CBC, CKA_ENCRYPT, 0); + if (encKey == NULL) { + PR_fprintf(PR_STDERR, "Can't unwrap the encryption key\n"); + rv = SECFailure; + goto cleanup; + } + + /* CKM_MD5_HMAC or CKM_EXTRACT_KEY_FROM_KEY */ + macKey = PK11_PubUnwrapSymKey(privKey, &wrappedMacKeyItem, + CKM_MD5_HMAC, CKA_SIGN, 160/8); + if (macKey == NULL) { + PR_fprintf(PR_STDERR, "Can't unwrap the Mac key\n"); + rv = SECFailure; + goto cleanup; + } + + /* Open the input file. */ + inFile = PR_Open(encryptedFileName, PR_RDONLY , 0); + if (!inFile) { + PR_fprintf(PR_STDERR, + "Unable to open \"%s\" for writing.\n", + encryptedFileName); + return SECFailure; + } + /* Open the output file. */ + outFile = PR_Open(outFileName, + PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR , 00660); + if (!outFile) { + PR_fprintf(PR_STDERR, + "Unable to open \"%s\" for writing.\n", + outFileName); + return SECFailure; + } + inFileLength = FileSize(encryptedFileName); + + if (rv == SECSuccess) { + /* Decrypt and Remove Mac */ + rv = DecryptAndVerifyMac(outFile, inFile, inFileLength, + &cipherItem, &macItem, encKey, macKey, &ivItem, &padItem); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed while decrypting and removing MAC\n"); + } + } + + cleanup: + if (encKey) { + PK11_FreeSymKey(encKey); + } + if (macKey) { + PK11_FreeSymKey(macKey); + } + if (privKey) { + SECKEY_DestroyPrivateKey(privKey); + } + if (pubKey) { + SECKEY_DestroyPublicKey(pubKey); + } + return rv; + } + + /* + * EncryptFile + */ + SECStatus + EncryptFile(PK11SlotInfo *slot, + const char *inFileName, + const char *certReqFileName, + const char *headerFileName, + const char *encryptedFileName, + const char *noiseFileName, + secuPWData *pwdata, + PRBool ascii) + { + /* + * The DB is open for read/write and we have authenticated to it. + * Read public key from certificate request + * generate a symmetric AES key as a session object. + * generate a second key to use for MACing, also a session object. + * generate a random value to use as IV for AES CBC + * open an input file and an output file, + * Wrap the symmetric and MAC keys using public key + * write a header to the output that identifies the two wrapped keys + * and public key + * loop until EOF(input) + * read a buffer of plaintext from input file, + * MAC it, append the MAC to the plaintext + * encrypt it using CBC, using previously created IV, + * store the last block of ciphertext as the new IV, + * write the cipher text to intermediate file + * close files + * report success + */ + SECStatus rv; + SECKEYPublicKey *pubKey = NULL; + SECItem *pubKeyData = NULL; + PRFileDesc *inFile = NULL; + PRFileDesc *headerFile = NULL; + PRFileDesc *encFile = NULL; + + unsigned char *encKeyId = (unsigned char *) "Encrypt Key"; + unsigned char *macKeyId = (unsigned char *) "MAC Key"; + SECItem encKeyID = { siAsciiString, encKeyId, PL_strlen(encKeyId) }; + SECItem macKeyID = { siAsciiString, macKeyId, PL_strlen(macKeyId) }; + + unsigned char iv[BLOCKSIZE]; + SECItem ivItem; + PK11SymKey *encKey = NULL; + PK11SymKey *macKey = NULL; + SECItem *wrappedEncKey = NULL; + SECItem *wrappedMacKey = NULL; + unsigned char c; + + pubKey = ExtractPublicKeyFromCertRequest(certReqFileName, ascii); + if (pubKey == NULL) { + PR_fprintf(PR_STDERR, "Error while getting RSA public key\n"); + rv = SECFailure; + goto cleanup; + } + /* generate a symmetric AES key as a token object. */ + encKey = GenerateSYMKey(slot, CKM_AES_KEY_GEN, 128/8, &encKeyID, pwdata); + if (encKey == NULL) { + PR_fprintf(PR_STDERR, "GenerateSYMKey for AES returned NULL.\n"); + rv = SECFailure; + goto cleanup; + } + + /* generate a second key to use for MACing, also a token object. */ + macKey = GenerateSYMKey(slot, CKM_GENERIC_SECRET_KEY_GEN, 160/8, &macKeyID, pwdata); + if (macKey == NULL) { + PR_fprintf(PR_STDERR, "GenerateSYMKey for MACing returned NULL.\n"); + rv = SECFailure; + goto cleanup; + } + + /* Wrap encrypt key */ + rv = WrapKey(encKey, pubKey, &wrappedEncKey); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Error while wrapping encrypt key\n"); + goto cleanup; + } + + /* Wrap Mac key */ + rv = WrapKey(macKey, pubKey, &wrappedMacKey); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Error while wrapping Mac key\n"); + goto cleanup; + } + + if (noiseFileName) { + rv = SeedFromNoiseFile(noiseFileName); + if (rv != SECSuccess) { + PORT_SetError(PR_END_OF_FILE_ERROR); + return SECFailure; + } + rv = PK11_GenerateRandom(iv, BLOCKSIZE); + if (rv != SECSuccess) { + goto cleanup; + } + + } else { + /* generate a random value to use as IV for AES CBC */ + GenerateRandom(iv, BLOCKSIZE); + } + + headerFile = PR_Open(headerFileName, + PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); + if (!headerFile) { + PR_fprintf(PR_STDERR, + "Unable to open \"%s\" for writing.\n", + headerFileName); + return SECFailure; + } + encFile = PR_Open(encryptedFileName, + PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); + if (!encFile) { + PR_fprintf(PR_STDERR, + "Unable to open \"%s\" for writing.\n", + encryptedFileName); + return SECFailure; + } + /* write to a header file the IV and the CKA_IDs + * identifying the two keys + */ + ivItem.type = siBuffer; + ivItem.data = iv; + ivItem.len = BLOCKSIZE; + + rv = WriteToHeaderFile(iv, BLOCKSIZE, IV, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Error writing IV to cipher file - %s\n", + headerFileName); + goto cleanup; + } + + rv = WriteToHeaderFile(wrappedEncKey->data, wrappedEncKey->len, SYMKEY, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Error writing wrapped AES key to cipher file - %s\n", + encryptedFileName); + goto cleanup; + } + rv = WriteToHeaderFile(wrappedMacKey->data, wrappedMacKey->len, MACKEY, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Error writing wrapped MAC key to cipher file - %s\n", + headerFileName); + goto cleanup; + } + + pubKeyData = SECKEY_EncodeDERSubjectPublicKeyInfo(pubKey); + rv = WriteToHeaderFile(pubKeyData->data, pubKeyData->len, PUBKEY, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Error writing wrapped AES key to cipher file - %s\n", + headerFileName); + goto cleanup; + } + + /* Open the input file. */ + inFile = PR_Open(inFileName, PR_RDONLY, 0); + if (!inFile) { + PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", + inFileName); + return SECFailure; + } + + /* Macing and Encryption */ + if (rv == SECSuccess) { + rv = EncryptAndMac(inFile, headerFile, encFile, + encKey, macKey, ivItem.data, ivItem.len, ascii); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed : Macing and Encryption\n"); + goto cleanup; + } + } + + cleanup: + if (inFile) { + PR_Close(inFile); + } + if (headerFile) { + PR_Close(headerFile); + } + if (encFile) { + PR_Close(encFile); + } + if (encKey) { + PK11_FreeSymKey(encKey); + } + if (macKey) { + PK11_FreeSymKey(macKey); + } + if (wrappedEncKey) { + SECITEM_FreeItem(wrappedEncKey, PR_TRUE); + } + if (wrappedMacKey) { + SECITEM_FreeItem(wrappedMacKey, PR_TRUE); + } + if (pubKey) { + SECKEY_DestroyPublicKey(pubKey); + } + if (pubKeyData) { + SECITEM_FreeItem(pubKeyData, PR_TRUE); + } + return rv; + } + + /* + * Create certificate request with subject + */ + SECStatus CreateCertificateRequest(PK11SlotInfo *slot, + const char *dbdir, + secuPWData *pwdata, + CERTName *subject, + const char *certReqFileName, + PRBool ascii) + { + SECStatus rv; + SECKEYPrivateKey *privkey = NULL; + SECKEYPublicKey *pubkey = NULL; + KeyType keytype = rsaKey; + int keysize = DEFAULT_KEY_BITS; + int publicExponent = 0x010001; + SECOidTag hashAlgTag = SEC_OID_UNKNOWN; + + privkey = GeneratePrivateKey(keytype, slot, keysize, + publicExponent, NULL, + &pubkey, NULL, pwdata); + if (privkey == NULL) { + PR_fprintf(PR_STDERR, "unable to generate key(s)\n"); + rv = SECFailure; + goto cleanup; + } + privkey->wincx = pwdata; + PORT_Assert(pubkey != NULL); + + rv = CertReq(privkey, pubkey, keytype, hashAlgTag, subject, + ascii, certReqFileName); + + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed to create Certificate Request\n"); + } + cleanup: + if (privkey) { + SECKEY_DestroyPrivateKey(privkey); + } + if (pubkey) { + SECKEY_DestroyPublicKey(pubkey); + } + return rv; + } + + /* + * This example illustrates basic encryption/decryption and MACing + * Generates the RSA key pair as token object and outputs public key as cert request. + * Generates the encryption/mac keys as session objects. + * Encrypts/MACs the input file using encryption keys and outputs the encrypted + * contents into intermediate header file. + * Extracts the public key from cert request file and Wraps the encryption keys using + * RSA public key and outputs wrapped keys and public key into intermediate header file. + * Reads the intermediate headerfile for wrapped keys,RSA public key and encrypted + * contents and decrypts into output file. + * + * How this sample is different from sample 4 ? + * + * 1. Generate same keys as sample 4, outputs public key as cert request. + * 2. Like sample 4, except that it reads in public key from cert request file instead + * of looking it up by label name, and writes public key into header instead of a + * label name. Rest is the same. + * 3. Like sample 4, except that it reads in RSA public key, and then finds matching + * private key (by key ID). Rest is the same. + */ + int + main(int argc, char **argv) + { + SECStatus rv; + SECStatus rvShutdown; + PLOptState *optstate; + PLOptStatus status; + char headerFileName[50]; + char encryptedFileName[50]; + PK11SlotInfo *slot = NULL; + PRBool ascii = PR_FALSE; + CommandType cmd = UNKNOWN; + PRFileDesc *inFile = NULL; + PRFileDesc *outFile = NULL; + char *subjectStr = NULL; + CERTName *subject = NULL; + const char *dbdir = NULL; + const char *inFileName = NULL; + const char *outFileName = NULL; + const char *certReqFileName = NULL; + const char *noiseFileName = NULL; + secuPWData pwdata = { PW_NONE, 0 }; + + char * progName = strrchr(argv[0], '/'); + progName = progName ? progName + 1 : argv[0]; + + /* Parse command line arguments */ + optstate = PL_CreateOptState(argc, argv, "c:d:i:o:f:p:z:a:s:r:"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + switch (optstate->option) { + case 'a': + ascii = PR_TRUE; + break; + case 'c': + cmd = option2Command(optstate->value); + break; + case 'd': + dbdir = strdup(optstate->value); + break; + case 'f': + pwdata.source = PW_FROMFILE; + pwdata.data = strdup(optstate->value); + break; + case 'p': + pwdata.source = PW_PLAINTEXT; + pwdata.data = strdup(optstate->value); + break; + case 'i': + inFileName = strdup(optstate->value); + break; + case 'o': + outFileName = strdup(optstate->value); + break; + case 'r': + certReqFileName = strdup(optstate->value); + break; + case 's': + subjectStr = strdup(optstate->value); + subject = CERT_AsciiToName(subjectStr); + break; + case 'z': + noiseFileName = strdup(optstate->value); + break; + default: + Usage(progName); + break; + } + } + PL_DestroyOptState(optstate); + + if (cmd == UNKNOWN || !dbdir) { + Usage(progName); + } + + /* For intermediate header file, choose filename as inputfile name + with extension ".header" */ + strcpy(headerFileName, progName); + strcat(headerFileName, ".header"); + + /* For intermediate encrypted file, choose filename as inputfile name + with extension ".enc" */ + strcpy(encryptedFileName, progName); + strcat(encryptedFileName, ".enc"); + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + /* Open DB for read/write and authenticate to it. */ + rv = NSS_InitReadWrite(dbdir); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "NSS_InitReadWrite Failed\n"); + goto cleanup; + } + + PK11_SetPasswordFunc(GetModulePassword); + slot = PK11_GetInternalKeySlot(); + rv = PK11_Authenticate(slot, PR_TRUE, &pwdata); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", + PK11_GetTokenName(slot)); + goto cleanup; + } + + switch (cmd) { + case GEN_CSR: + + /* Validate command for Generate CSR */ + if (!certReqFileName || !subject) { + Usage(progName); + } + /* + * Generate the cert request and save it + * in a file so public key can be retrieved later to wrap the symmetric key + */ + rv = CreateCertificateRequest(slot, dbdir, &pwdata, subject, certReqFileName, ascii); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Create Certificate Request: Failed\n"); + goto cleanup; + } + break; + case ENCRYPT: + /* Validate command for Encrypt */ + if (!certReqFileName && !inFileName) { + Usage(progName); + } + + /* + * Read cert request from a file and extract public key + * Generates an AES encryption key, session object + * Generates a MAC key, session object + * Wraps each of those keys with RSA public key + * Write wrapped keys and public key into intermediate header file + * Encryption and MACing loop + * Destroy session keys + * Close files + */ + rv = EncryptFile(slot, inFileName, certReqFileName, + headerFileName, encryptedFileName, + noiseFileName, &pwdata, ascii); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "EncryptFile : Failed\n"); + return SECFailure; + } + break; + case DECRYPT: + /* Validate command for Decrypt */ + if (!inFileName && !outFileName) { + Usage(progName); + } + /* + * Reads intermediate header including public key and wrapped keys + * Finds RSA private key corresponding to the public key + * unwraps two keys, creating session key objects + * Decryption and MAC checking loop to write to output file + * Destroy session keys + * CLose files + */ + rv = DecryptFile(slot, + outFileName, headerFileName, + encryptedFileName, &pwdata, ascii); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "DecryptFile : Failed\n"); + return SECFailure; + } + break; + } + + cleanup: + if (slot) { + PK11_FreeSlot(slot); + } + rvShutdown = NSS_Shutdown(); + if (rvShutdown != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed : NSS_Shutdown()\n"); + rv = SECFailure; + } + PR_Cleanup(); + + return rv; + } \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/enc_dec_mac_using_key_wrap_certreq_pkcs10_csr/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/enc_dec_mac_using_key_wrap_certreq_pkcs10_csr/index.rst new file mode 100644 index 0000000000..793555ce5a --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/enc_dec_mac_using_key_wrap_certreq_pkcs10_csr/index.rst @@ -0,0 +1,2090 @@ +.. _mozilla_projects_nss_nss_sample_code_enc_dec_mac_using_key_wrap_certreq_pkcs10_csr: + +Enc Dec MAC Using Key Wrap CertReq PKCS10 CSR +============================================= + +.. _nss_sample_code_6_encryptiondecryption_and_mac_and_output_public_as_a_pkcs_11_csr.: + +`NSS Sample Code 6: Encryption/Decryption and MAC and output Public as a PKCS 11 CSR. <#nss_sample_code_6_encryptiondecryption_and_mac_and_output_public_as_a_pkcs_11_csr.>`__ +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ + +.. container:: + + Generates encryption/mac keys and outputs public key as pkcs11 certificate signing request + + .. code:: c + + /* 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 https://mozilla.org/MPL/2.0/. */ + + /* NSPR Headers */ + #include + #include + #include + #include + #include + #include + #include + + /* NSS headers */ + #include + #include + + /* our samples utilities */ + #include "util.h" + + /* Constants */ + #define BLOCKSIZE 32 + #define MODBLOCKSIZE 128 + #define DEFAULT_KEY_BITS 1024 + + /* Header file Constants */ + #define ENCKEY_HEADER "-----BEGIN WRAPPED ENCKEY-----" + #define ENCKEY_TRAILER "-----END WRAPPED ENCKEY-----" + #define MACKEY_HEADER "-----BEGIN WRAPPED MACKEY-----" + #define MACKEY_TRAILER "-----END WRAPPED MACKEY-----" + #define IV_HEADER "-----BEGIN IV-----" + #define IV_TRAILER "-----END IV-----" + #define MAC_HEADER "-----BEGIN MAC-----" + #define MAC_TRAILER "-----END MAC-----" + #define PAD_HEADER "-----BEGIN PAD-----" + #define PAD_TRAILER "-----END PAD-----" + #define LAB_HEADER "-----BEGIN KEY LABEL-----" + #define LAB_TRAILER "-----END KEY LABEL-----" + #define PUBKEY_HEADER "-----BEGIN PUB KEY -----" + #define PUBKEY_TRAILER "-----END PUB KEY -----" + #define NS_CERTREQ_HEADER "-----BEGIN NEW CERTIFICATE REQUEST-----" + #define NS_CERTREQ_TRAILER "-----END NEW CERTIFICATE REQUEST-----" + #define NS_CERT_ENC_HEADER "-----BEGIN CERTIFICATE FOR ENCRYPTION-----" + #define NS_CERT_ENC_TRAILER "-----END CERTIFICATE FOR ENCRYPTION-----" + #define NS_CERT_VFY_HEADER "-----BEGIN CERTIFICATE FOR SIGNATURE VERIFICATION-----" + #define NS_CERT_VFY_TRAILER "-----END CERTIFICATE FOR SIGNATURE VERIFICATION-----" + #define NS_SIG_HEADER "-----BEGIN SIGNATURE-----" + #define NS_SIG_TRAILER "-----END SIGNATURE-----" + #define NS_CERT_HEADER "-----BEGIN CERTIFICATE-----" + #define NS_CERT_TRAILER "-----END CERTIFICATE-----" + + /* sample 6 commands */ + typedef enum { + GENERATE_CSR, + ADD_CERT_TO_DB, + SAVE_CERT_TO_HEADER, + ENCRYPT, + DECRYPT, + SIGN, + VERIFY, + UNKNOWN + } CommandType; + + typedef enum { + SYMKEY = 0, + MACKEY = 1, + IV = 2, + MAC = 3, + PAD = 4, + PUBKEY = 5, + LAB = 6, + CERTENC= 7, + CERTVFY= 8, + SIG = 9 + } HeaderType; + + + /* + * Print usage message and exit + */ + static void + Usage(const char *progName) + { + fprintf(stderr, "\nUsage: %s %s %s %s %s %s %s %s %s %s\n\n", + progName, + " - -d ", + "[-p | -f ] [-z ] [-a <\"\">]", + "-s -r | ", + "-n -t -c [ -r -u [-x <\"\">] -m ] | ", + "-n -b | ", + "-b -i -e | ", + "-b -i | ", + "-b -i | ", + "-b -e -o \n"); + fprintf(stderr, "commands:\n\n"); + fprintf(stderr, "%s %s\n --for generating cert request (for CA also)\n\n", + progName, "-G -s -r "); + fprintf(stderr, "%s %s\n --to input and store cert (for CA also)\n\n", + progName, "-A -n -t -c [ -r -u [-x <\"\">] -m ]"); + fprintf(stderr, "%s %s\n --to put cert in header\n\n", + progName, "-H -n -b [-v <\"\">]"); + fprintf(stderr, "%s %s\n --to find public key from cert in header and encrypt\n\n", + progName, "-E -b -i -e "); + fprintf(stderr, "%s %s\n --decrypt using corresponding private key \n\n", + progName, "-D -b -e -o "); + fprintf(stderr, "%s %s\n --Sign using private key \n\n", + progName, "-S -b -i "); + fprintf(stderr, "%s %s\n --Verify using public key \n\n", + progName, "-V -b -i "); + fprintf(stderr, "options:\n\n"); + fprintf(stderr, "%-30s - db directory path\n\n", + "-d "); + fprintf(stderr, "%-30s - db password [optional]\n\n", + "-p "); + fprintf(stderr, "%-30s - db password file [optional]\n\n", + "-f "); + fprintf(stderr, "%-30s - noise file name [optional]\n\n", + "-z "); + fprintf(stderr, "%-30s - input file name\n\n", + "-i "); + fprintf(stderr, "%-30s - header file name\n\n", + "-b "); + fprintf(stderr, "%-30s - encrypt file name\n\n", + "-e "); + fprintf(stderr, "%-30s - output file name\n\n", + "-o "); + fprintf(stderr, "%-30s - certificate serial number\n\n", + "-m "); + fprintf(stderr, "%-30s - certificate nickname\n\n", + "-n "); + fprintf(stderr, "%-30s - certificate trust\n\n", + "-t "); + fprintf(stderr, "%-30s - certificate issuer nickname\n\n", + "-u "); + fprintf(stderr, "%-30s - certificate signing request \n\n", + "-r "); + fprintf(stderr, "%-30s - generate a self-signed cert [optional]\n\n", + "-x"); + fprintf(stderr, "%-30s - to enable ascii [optional]\n\n", + "-a"); + fprintf(stderr, "%-30s - to save certificate to header file as sig verification [optional]\n\n", + "-v"); + exit(-1); + } + + /* + * Validate the options used for Generate CSR command + */ + static void + ValidateGenerateCSRCommand(const char *progName, + const char *dbdir, + CERTName *subject, + const char *subjectStr, + const char *certReqFileName) + { + PRBool validationFailed = PR_FALSE; + if (!subject) { + PR_fprintf(PR_STDERR, "%s -G -d %s -s: improperly formatted name: \"%s\"\n", + progName, dbdir, subjectStr); + validationFailed = PR_TRUE; + } + if (!certReqFileName) { + PR_fprintf(PR_STDERR, "%s -G -d %s -s %s -r: certificate request file name not found\n", + progName, dbdir, subjectStr); + validationFailed = PR_TRUE; + } + if (validationFailed) { + fprintf(stderr, "\nUsage: %s %s \n\n", progName, + "-G -d -s -r \n"); + exit(-1); + } + } + + /* + * Validate the options used for Add Cert to DB command + */ + static void + ValidateAddCertToDBCommand(const char *progName, + const char *dbdir, + const char *nickNameStr, + const char *trustStr, + const char *certFileName, + const char *certReqFileName, + const char *issuerNameStr, + const char *serialNumberStr, + PRBool selfsign) + { + PRBool validationFailed = PR_FALSE; + if (!nickNameStr) { + PR_fprintf(PR_STDERR, "%s -A -d %s -n : nick name is missing\n", + progName, dbdir); + validationFailed = PR_TRUE; + } + if (!trustStr) { + PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t: trust flag is missing\n", + progName, dbdir, nickNameStr); + validationFailed = PR_TRUE; + } + if (!certFileName) { + PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c: certificate file name not found\n", + progName, dbdir, nickNameStr, trustStr, serialNumberStr, certReqFileName); + validationFailed = PR_TRUE; + } + if (PR_Access(certFileName, PR_ACCESS_EXISTS) == PR_FAILURE) { + if (!certReqFileName) { + PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c %s -r: certificate file or certificate request file is not found\n", + progName, dbdir, nickNameStr, trustStr, certFileName); + validationFailed = PR_TRUE; + } + if (!selfsign && !issuerNameStr) { + PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c %s -r %s -u : issuer name is missing\n", + progName, dbdir, nickNameStr, trustStr, certFileName, certReqFileName); + validationFailed = PR_TRUE; + } + if (!serialNumberStr) { + PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c %s -r %s -u %s -m : serial number is missing\n", + progName, dbdir, nickNameStr, trustStr, certFileName, certReqFileName, issuerNameStr); + validationFailed = PR_TRUE; + } + } + if (validationFailed) { + fprintf(stderr, "\nUsage: %s %s \n\n", progName, + " -A -d -n -t -c \n"); + fprintf(stderr, " OR\n"); + fprintf(stderr, "\nUsage: %s %s \n\n", progName, + "-A -d -n -t -c -r -u -m [-x <\"\">] \n"); + exit(-1); + } + } + + /* + * Validate the options used for Save Cert To Header command + */ + static void + ValidateSaveCertToHeaderCommand(const char *progName, + const char *dbdir, + const char *nickNameStr, + const char *headerFileName) + { + PRBool validationFailed = PR_FALSE; + if (!nickNameStr) { + PR_fprintf(PR_STDERR, "%s -S -d %s -n : nick name is missing\n", + progName, dbdir); + validationFailed = PR_TRUE; + } + if (!headerFileName) { + PR_fprintf(PR_STDERR, "%s -S -d %s -n %s -b : header file name is not found\n", + progName, dbdir, nickNameStr); + validationFailed = PR_TRUE; + } + if (validationFailed) { + fprintf(stderr, "\nUsage: %s %s \n\n", progName, + "-S -d -n -b [-v <\"\">]\n"); + exit(-1); + } + } + + /* + * Validate the options used for Encrypt command + */ + static void + ValidateEncryptCommand(const char *progName, + const char *dbdir, + const char *nickNameStr, + const char *headerFileName, + const char *inFileName, + const char *encryptedFileName) + { + PRBool validationFailed = PR_FALSE; + if (!nickNameStr) { + PR_fprintf(PR_STDERR, "%s -E -d %s -n : nick name is missing\n", + progName, dbdir); + validationFailed = PR_TRUE; + } + if (!headerFileName) { + PR_fprintf(PR_STDERR, "%s -E -d %s -n %s -b : header file name is not found\n", + progName, dbdir, nickNameStr); + validationFailed = PR_TRUE; + } + if (!inFileName) { + PR_fprintf(PR_STDERR, "%s -E -d %s -n %s -b %s -i : input file name is not found\n", + progName, dbdir, nickNameStr, headerFileName); + validationFailed = PR_TRUE; + } + if (!encryptedFileName) { + PR_fprintf(PR_STDERR, "%s -E -d %s -n %s -b %s -i %s -e : encrypt file name is not found\n", + progName, dbdir, nickNameStr, headerFileName, inFileName); + validationFailed = PR_TRUE; + } + if (validationFailed) { + fprintf(stderr, "\nUsage: %s %s \n\n", progName, + "-E -d -b -i -e -n \n"); + exit(-1); + } + } + + /* + * Validate the options used for Sign command + */ + static void + ValidateSignCommand(const char *progName, + const char *dbdir, + const char *nickNameStr, + const char *headerFileName, + const char *inFileName) + { + PRBool validationFailed = PR_FALSE; + if (!nickNameStr) { + PR_fprintf(PR_STDERR, "%s -I -d %s -n : nick name is missing\n", + progName, dbdir); + validationFailed = PR_TRUE; + } + if (!headerFileName) { + PR_fprintf(PR_STDERR, "%s -I -d %s -n %s -b : header file name is not found\n", + progName, dbdir, nickNameStr); + validationFailed = PR_TRUE; + } + if (!inFileName) { + PR_fprintf(PR_STDERR, "%s -I -d %s -n %s -b %s -i : input file name is not found\n", + progName, dbdir, nickNameStr, headerFileName); + validationFailed = PR_TRUE; + } + if (validationFailed) { + fprintf(stderr, "\nUsage: %s %s \n\n", progName, + "-I -d -b -i -n \n"); + exit(-1); + } + } + + /* + * Validate the options used for verify command + */ + static void + ValidateVerifyCommand(const char *progName, + const char *dbdir, + const char *headerFileName, + const char *inFileName) + { + PRBool validationFailed = PR_FALSE; + if (!headerFileName) { + PR_fprintf(PR_STDERR, "%s -V -d %s -b : header file name is not found\n", + progName, dbdir); + validationFailed = PR_TRUE; + } + if (!inFileName) { + PR_fprintf(PR_STDERR, "%s -I -d %s -b %s -i : input file name is not found\n", + progName, dbdir, headerFileName); + validationFailed = PR_TRUE; + } + if (validationFailed) { + fprintf(stderr, "\nUsage: %s %s \n\n", progName, + "-I -d -b -i \n"); + exit(-1); + } + } + + /* + * Validate the options used for Decrypt command + */ + static void + ValidateDecryptCommand(const char *progName, + const char *dbdir, + const char *headerFileName, + const char *encryptedFileName, + const char *outFileName) + { + PRBool validationFailed = PR_FALSE; + if (!headerFileName) { + PR_fprintf(PR_STDERR, "%s -D -d %s -b : header file name is not found\n", + progName, dbdir); + validationFailed = PR_TRUE; + } + if (!encryptedFileName) { + PR_fprintf(PR_STDERR, "%s -D -d %s -b %s -e : encrypt file name is not found\n", + progName, dbdir, headerFileName); + validationFailed = PR_TRUE; + } + if (!outFileName) { + PR_fprintf(PR_STDERR, "%s -D -d %s -b %s -e %s -o : output file name is not found\n", + progName, dbdir, headerFileName, encryptedFileName); + validationFailed = PR_TRUE; + } + if (validationFailed) { + fprintf(stderr, "\nUsage: %s %s \n\n", progName, + "-D -d -b -e -o \n"); + exit(-1); + } + } + + /* + * Sign the contents of input file using private key and + * return result as SECItem + */ + SECStatus + SignData(const char *inFileName, SECKEYPrivateKey *pk, SECItem *res) + { + SECStatus rv = SECFailure; + unsigned int nb; + unsigned char ibuf[4096]; + PRFileDesc *inFile = NULL; + SGNContext *sgn = NULL; + + /* Open the input file for reading */ + inFile = PR_Open(inFileName, PR_RDONLY, 0); + if (!inFile) { + PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", + inFileName); + rv = SECFailure; + goto cleanup; + } + + /* Sign using private key */ + + sgn = SGN_NewContext(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, pk); + if (!sgn) { + PR_fprintf(PR_STDERR, "unable to create context for signing\n"); + rv = SECFailure; + goto cleanup; + } + + rv = SGN_Begin(sgn); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "problem while SGN_Begin\n"); + goto cleanup; + } + while ((nb = PR_Read(inFile, ibuf, sizeof(ibuf))) > 0) { + rv = SGN_Update(sgn, ibuf, nb); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "problem while SGN_Update\n"); + goto cleanup; + } + } + rv = SGN_End(sgn, res); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "problem while SGN_End\n"); + goto cleanup; + } + cleanup: + if (inFile) { + PR_Close(inFile); + } + if (sgn) { + SGN_DestroyContext(sgn, PR_TRUE); + } + return rv; + } + + /* + * Verify the signature using public key + */ + SECStatus + VerifyData(const char *inFileName, SECKEYPublicKey *pk, + SECItem *sigItem, secuPWData *pwdata) + { + unsigned int nb; + unsigned char ibuf[4096]; + SECStatus rv = SECFailure; + VFYContext *vfy = NULL; + PRFileDesc *inFile = NULL; + + /* Open the input file for reading */ + inFile = PR_Open(inFileName, PR_RDONLY, 0); + if (!inFile) { + PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", + inFileName); + rv = SECFailure; + goto cleanup; + } + + vfy = VFY_CreateContext(pk, + sigItem, + SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, + pwdata); + if (!vfy) { + PR_fprintf(PR_STDERR, "unable to create context for verifying signature\n"); + rv = SECFailure; + goto cleanup; + } + rv = VFY_Begin(vfy); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "problem while VFY_Begin\n"); + goto cleanup; + } + while ((nb = PR_Read(inFile, ibuf, sizeof(ibuf))) > 0) { + rv = VFY_Update(vfy, ibuf, nb); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "problem while VFY_Update\n"); + goto cleanup; + } + } + rv = VFY_End(vfy); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "problem while VFY_End\n"); + goto cleanup; + } + + cleanup: + if (inFile) { + PR_Close(inFile); + } + if (vfy) { + VFY_DestroyContext(vfy, PR_TRUE); + } + return rv; + } + + /* + * Write Cryptographic parameters to header file + */ + SECStatus + WriteToHeaderFile(const char *buf, unsigned int len, HeaderType type, + PRFileDesc *outFile) + { + SECStatus rv; + const char *header; + const char *trailer; + + switch (type) { + case SYMKEY: + header = ENCKEY_HEADER; + trailer = ENCKEY_TRAILER; + break; + case MACKEY: + header = MACKEY_HEADER; + trailer = MACKEY_TRAILER; + break; + case IV: + header = IV_HEADER; + trailer = IV_TRAILER; + break; + case MAC: + header = MAC_HEADER; + trailer = MAC_TRAILER; + break; + case PAD: + header = PAD_HEADER; + trailer = PAD_TRAILER; + break; + case PUBKEY: + header = PUBKEY_HEADER; + trailer = PUBKEY_TRAILER; + break; + case CERTENC: + header = NS_CERT_ENC_HEADER; + trailer = NS_CERT_ENC_TRAILER; + break; + case CERTVFY: + header = NS_CERT_VFY_HEADER; + trailer = NS_CERT_VFY_TRAILER; + break; + case SIG: + header = NS_SIG_HEADER; + trailer = NS_SIG_TRAILER; + break; + case LAB: + header = LAB_HEADER; + trailer = LAB_TRAILER; + PR_fprintf(outFile, "%s\n", header); + PR_fprintf(outFile, "%s\n", buf); + PR_fprintf(outFile, "%s\n\n", trailer); + return SECSuccess; + break; + default: + return SECFailure; + } + + PR_fprintf(outFile, "%s\n", header); + PrintAsHex(outFile, buf, len); + PR_fprintf(outFile, "%s\n\n", trailer); + return SECSuccess; + } + + /* + * Read cryptographic parameters from the header file + */ + SECStatus + ReadFromHeaderFile(const char *fileName, HeaderType type, + SECItem *item, PRBool isHexData) + { + SECStatus rv = SECSuccess; + PRFileDesc* file = NULL; + SECItem filedata; + SECItem outbuf; + unsigned char *nonbody; + unsigned char *body; + char *header; + char *trailer; + + outbuf.type = siBuffer; + file = PR_Open(fileName, PR_RDONLY, 0); + if (!file) { + PR_fprintf(PR_STDERR, "Failed to open %s\n", fileName); + rv = SECFailure; + goto cleanup; + } + switch (type) { + case PUBKEY: + header = PUBKEY_HEADER; + trailer = PUBKEY_TRAILER; + break; + case SYMKEY: + header = ENCKEY_HEADER; + trailer = ENCKEY_TRAILER; + break; + case MACKEY: + header = MACKEY_HEADER; + trailer = MACKEY_TRAILER; + break; + case IV: + header = IV_HEADER; + trailer = IV_TRAILER; + break; + case MAC: + header = MAC_HEADER; + trailer = MAC_TRAILER; + break; + case PAD: + header = PAD_HEADER; + trailer = PAD_TRAILER; + break; + case LAB: + header = LAB_HEADER; + trailer = LAB_TRAILER; + break; + case CERTENC: + header = NS_CERT_ENC_HEADER; + trailer = NS_CERT_ENC_TRAILER; + break; + case CERTVFY: + header = NS_CERT_VFY_HEADER; + trailer = NS_CERT_VFY_TRAILER; + break; + case SIG: + header = NS_SIG_HEADER; + trailer = NS_SIG_TRAILER; + break; + default: + rv = SECFailure; + goto cleanup; + } + + rv = FileToItem(&filedata, file); + nonbody = (char *)filedata.data; + if (!nonbody) { + PR_fprintf(PR_STDERR, "unable to read data from input file\n"); + rv = SECFailure; + goto cleanup; + } + + /* check for headers and trailers and remove them */ + if ((body = strstr(nonbody, header)) != NULL) { + char *trail = NULL; + nonbody = body; + body = PORT_Strchr(body, '\n'); + if (!body) + body = PORT_Strchr(nonbody, '\r'); /* maybe this is a MAC file */ + if (body) + trail = strstr(++body, trailer); + if (trail != NULL) { + *trail = '\0'; + } else { + PR_fprintf(PR_STDERR, "input has header but no trailer\n"); + PORT_Free(filedata.data); + rv = SECFailure; + goto cleanup; + } + } else { + /* headers didn't exist */ + char *trail = NULL; + body = nonbody; + if (body) { + trail = strstr(++body, trailer); + if (trail != NULL) { + PR_fprintf(PR_STDERR, "input has no header but has trailer\n"); + PORT_Free(filedata.data); + rv = SECFailure; + goto cleanup; + } + } + } + HexToBuf(body, item, isHexData); + cleanup: + if (file) { + PR_Close(file); + } + return rv; + } + + /* + * Generate the private key + */ + SECKEYPrivateKey * + GeneratePrivateKey(KeyType keytype, PK11SlotInfo *slot, int size, + int publicExponent, const char *noise, + SECKEYPublicKey **pubkeyp, const char *pqgFile, + secuPWData *pwdata) + { + CK_MECHANISM_TYPE mechanism; + SECOidTag algtag; + PK11RSAGenParams rsaparams; + void *params; + SECKEYPrivateKey *privKey = NULL; + SECStatus rv; + unsigned char randbuf[BLOCKSIZE + 1]; + + rv = GenerateRandom(randbuf, BLOCKSIZE); + if (rv != SECSuccess) { + fprintf(stderr, "Error while generating the random numbers : %s\n", + PORT_ErrorToString(rv)); + goto cleanup; + } + PK11_RandomUpdate(randbuf, BLOCKSIZE); + switch (keytype) { + case rsaKey: + rsaparams.keySizeInBits = size; + rsaparams.pe = publicExponent; + mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; + algtag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; + params = &rsaparams; + break; + default: + goto cleanup; + } + fprintf(stderr, "\n\n"); + fprintf(stderr, "Generating key. This may take a few moments...\n\n"); + privKey = PK11_GenerateKeyPair(slot, mechanism, params, pubkeyp, + PR_TRUE /*isPerm*/, PR_TRUE /*isSensitive*/, + pwdata); + cleanup: + return privKey; + } + + /* + * Get the certificate request from CSR + */ + static CERTCertificateRequest * + GetCertRequest(char *inFileName, PRBool ascii) + { + CERTSignedData signedData; + SECItem reqDER; + CERTCertificateRequest *certReq = NULL; + SECStatus rv = SECSuccess; + PRArenaPool *arena = NULL; + + reqDER.data = NULL; + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + rv = SECFailure; + goto cleanup; + } + + rv = ReadDERFromFile(&reqDER, inFileName, ascii); + if (rv) { + rv = SECFailure; + goto cleanup; + } + certReq = (CERTCertificateRequest*) PORT_ArenaZAlloc + (arena, sizeof(CERTCertificateRequest)); + if (!certReq) { + rv = SECFailure; + goto cleanup; + } + certReq->arena = arena; + + /* Since cert request is a signed data, must decode to get the inner data */ + PORT_Memset(&signedData, 0, sizeof(signedData)); + rv = SEC_ASN1DecodeItem(arena, &signedData, + SEC_ASN1_GET(CERT_SignedDataTemplate), &reqDER); + if (rv) { + rv = SECFailure; + goto cleanup; + } + rv = SEC_ASN1DecodeItem(arena, certReq, + SEC_ASN1_GET(CERT_CertificateRequestTemplate), &signedData.data); + if (rv) { + rv = SECFailure; + goto cleanup; + } + rv = CERT_VerifySignedDataWithPublicKeyInfo(&signedData, + &certReq->subjectPublicKeyInfo, NULL /* wincx */); + if (reqDER.data) { + SECITEM_FreeItem(&reqDER, PR_FALSE); + } + + cleanup: + if (rv) { + PR_fprintf(PR_STDERR, "bad certificate request\n"); + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + certReq = NULL; + } + return certReq; + } + + /* + * Sign Cert + */ + static SECItem * + SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, + PRBool selfsign, SECOidTag hashAlgTag, + SECKEYPrivateKey *privKey, char *issuerNickName, void *pwarg) + { + SECItem der; + SECStatus rv; + SECOidTag algID; + void *dummy; + PRArenaPool *arena = NULL; + SECItem *result = NULL; + SECKEYPrivateKey *caPrivateKey = NULL; + + if (!selfsign) { + CERTCertificate *issuer = PK11_FindCertFromNickname(issuerNickName, pwarg); + if ((CERTCertificate *)NULL == issuer) { + PR_fprintf(PR_STDERR, "unable to find issuer with nickname %s\n", + issuerNickName); + goto cleanup; + } + privKey = caPrivateKey = PK11_FindKeyByAnyCert(issuer, pwarg); + CERT_DestroyCertificate(issuer); + if (caPrivateKey == NULL) { + PR_fprintf(PR_STDERR, "unable to retrieve key %s\n", + issuerNickName); + goto cleanup; + } + } + arena = cert->arena; + algID = SEC_GetSignatureAlgorithmOidTag(privKey->keyType, hashAlgTag); + if (algID == SEC_OID_UNKNOWN) { + PR_fprintf(PR_STDERR, "Unknown key or hash type for issuer.\n"); + goto cleanup; + } + rv = SECOID_SetAlgorithmID(arena, &cert->signature, algID, 0); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not set signature algorithm id.\n%s\n", + PORT_ErrorToString(rv)); + goto cleanup; + } + + /* we only deal with cert v3 here */ + *(cert->version.data) = 2; + cert->version.len = 1; + + der.len = 0; + der.data = NULL; + dummy = SEC_ASN1EncodeItem (arena, &der, cert, + SEC_ASN1_GET(CERT_CertificateTemplate)); + if (!dummy) { + PR_fprintf(PR_STDERR, "Could not encode certificate.\n"); + goto cleanup; + } + + result = (SECItem *) PORT_ArenaZAlloc (arena, sizeof (SECItem)); + if (result == NULL) { + PR_fprintf(PR_STDERR, "Could not allocate item for certificate data.\n"); + goto cleanup; + } + + rv = SEC_DerSignData(arena, result, der.data, der.len, privKey, algID); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not sign encoded certificate data : %s\n", + PORT_ErrorToString(rv)); + /* result allocated out of the arena, it will be freed + * when the arena is freed */ + result = NULL; + goto cleanup; + } + cert->derCert = *result; + cleanup: + if (caPrivateKey) { + SECKEY_DestroyPrivateKey(caPrivateKey); + } + return result; + } + + /* + * MakeV1Cert + */ + static CERTCertificate * + MakeV1Cert(CERTCertDBHandle *handle, + CERTCertificateRequest *req, + char * issuerNickName, + PRBool selfsign, + unsigned int serialNumber, + int warpmonths, + int validityMonths) + { + PRExplodedTime printableTime; + PRTime now; + PRTime after; + CERTValidity *validity = NULL; + CERTCertificate *issuerCert = NULL; + CERTCertificate *cert = NULL; + + if ( !selfsign ) { + issuerCert = CERT_FindCertByNicknameOrEmailAddr(handle, issuerNickName); + if (!issuerCert) { + PR_fprintf(PR_STDERR, "could not find certificate named %s\n", + issuerNickName); + goto cleanup; + } + } + + now = PR_Now(); + PR_ExplodeTime (now, PR_GMTParameters, &printableTime); + if ( warpmonths ) { + printableTime.tm_month += warpmonths; + now = PR_ImplodeTime (&printableTime); + PR_ExplodeTime (now, PR_GMTParameters, &printableTime); + } + printableTime.tm_month += validityMonths; + after = PR_ImplodeTime (&printableTime); + + /* note that the time is now in micro-second unit */ + validity = CERT_CreateValidity (now, after); + if (validity) { + cert = CERT_CreateCertificate(serialNumber, + (selfsign ? &req->subject : &issuerCert->subject), + validity, req); + + CERT_DestroyValidity(validity); + } + cleanup: + if ( issuerCert ) { + CERT_DestroyCertificate (issuerCert); + } + return cert; + } + + /* + * Add a certificate to the nss database + */ + SECStatus + AddCert(PK11SlotInfo *slot, CERTCertDBHandle *handle, + const char *name, char *trusts, char *inFileName, + PRBool ascii, PRBool emailcert, void *pwdata) + { + SECItem certDER; + SECStatus rv; + CERTCertTrust *trust = NULL; + CERTCertificate *cert = NULL; + + certDER.data = NULL; + + /* Read in the entire file specified with the -i argument */ + rv = ReadDERFromFile(&certDER, inFileName, ascii); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "unable to read input file %s : %s\n", + inFileName, PORT_ErrorToString(rv)); + goto cleanup; + } + + /* Read in an ASCII cert and return a CERTCertificate */ + cert = CERT_DecodeCertFromPackage((char *)certDER.data, certDER.len); + if (!cert) { + PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); + rv = SECFailure; + goto cleanup; + } + + /* Create a cert trust */ + trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust)); + if (!trust) { + PR_fprintf(PR_STDERR, "unable to allocate cert trust\n"); + rv = SECFailure; + goto cleanup; + } + + rv = CERT_DecodeTrustString(trust, trusts); + if (rv) { + PR_fprintf(PR_STDERR, "unable to decode trust string\n"); + rv = SECFailure; + goto cleanup; + } + + rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, name, PR_FALSE); + if (rv != SECSuccess) { + /* sigh, PK11_Import Cert and CERT_ChangeCertTrust should have + * been coded to take a password arg. */ + if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) { + rv = PK11_Authenticate(slot, PR_TRUE, pwdata); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "could not authenticate to token %s : %s\n", + PK11_GetTokenName(slot), PORT_ErrorToString(rv)); + rv = SECFailure; + goto cleanup; + } + rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, + name, PR_FALSE); + } + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "could not add certificate to token or database : %s\n", + PORT_ErrorToString(rv)); + rv = SECFailure; + goto cleanup; + } + } + rv = CERT_ChangeCertTrust(handle, cert, trust); + if (rv != SECSuccess) { + if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) { + rv = PK11_Authenticate(slot, PR_TRUE, pwdata); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "could not authenticate to token %s : %s\n", + PK11_GetTokenName(slot), PORT_ErrorToString(rv)); + rv = SECFailure; + goto cleanup; + } + rv = CERT_ChangeCertTrust(handle, cert, trust); + } + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "could not change trust on certificate : %s\n", + PORT_ErrorToString(rv)); + rv = SECFailure; + goto cleanup; + } + } + + if (emailcert) { + CERT_SaveSMimeProfile(cert, NULL, pwdata); + } + + cleanup: + if (cert) { + CERT_DestroyCertificate (cert); + } + if (trust) { + PORT_Free(trust); + } + if (certDER.data) { + PORT_Free(certDER.data); + } + return rv; + } + + /* + * Create a certificate + */ + static SECStatus + CreateCert( + CERTCertDBHandle *handle, + PK11SlotInfo *slot, + char * issuerNickName, + char *inFileName, + char *outFileName, + SECKEYPrivateKey **selfsignprivkey, + void *pwarg, + SECOidTag hashAlgTag, + unsigned int serialNumber, + int warpmonths, + int validityMonths, + const char *dnsNames, + PRBool ascii, + PRBool selfsign) + { + void *extHandle; + SECItem reqDER; + CERTCertExtension **CRexts; + SECStatus rv = SECSuccess; + CERTCertificate *subjectCert = NULL; + CERTCertificateRequest *certReq = NULL; + PRFileDesc *outFile = NULL; + SECItem *certDER = NULL; + + reqDER.data = NULL; + outFile = PR_Open(outFileName, + PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 00660); + + /* Create a cert request object from the input cert request der */ + certReq = GetCertRequest(inFileName, ascii); + if (certReq == NULL) { + rv = SECFailure; + goto cleanup; + } + subjectCert = MakeV1Cert(handle, certReq, issuerNickName, selfsign, + serialNumber, warpmonths, validityMonths); + if (subjectCert == NULL) { + rv = SECFailure; + goto cleanup; + } + + extHandle = CERT_StartCertExtensions (subjectCert); + if (extHandle == NULL) { + rv = SECFailure; + goto cleanup; + } + + if (certReq->attributes != NULL && + certReq->attributes[0] != NULL && + certReq->attributes[0]->attrType.data != NULL && + certReq->attributes[0]->attrType.len > 0 && + SECOID_FindOIDTag(&certReq->attributes[0]->attrType) + == SEC_OID_PKCS9_EXTENSION_REQUEST) { + rv = CERT_GetCertificateRequestExtensions(certReq, &CRexts); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "%s\n", PORT_ErrorToString(rv)); + goto cleanup; + } + rv = CERT_MergeExtensions(extHandle, CRexts); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "%s\n", PORT_ErrorToString(rv)); + goto cleanup; + } + } + + CERT_FinishExtensions(extHandle); + + /* self-signing a cert request, find the private key */ + if (*selfsignprivkey == NULL) { + *selfsignprivkey = PK11_FindKeyByDERCert(slot, subjectCert, pwarg); + if (!*selfsignprivkey) { + PR_fprintf(PR_STDERR, "Failed to locate private key.\n"); + rv = SECFailure; + goto cleanup; + } + } + + certDER = SignCert(handle, subjectCert, selfsign, hashAlgTag, + *selfsignprivkey, issuerNickName,pwarg); + if (certDER) { + if (ascii) { + PR_fprintf(outFile, "%s\n%s\n%s\n", NS_CERT_HEADER, + BTOA_DataToAscii(certDER->data, certDER->len), + NS_CERT_TRAILER); + } else { + PR_Write(outFile, certDER->data, certDER->len); + } + } + if (rv != SECSuccess) { + PRErrorCode perr = PR_GetError(); + PR_fprintf(PR_STDERR, "unable to create cert %s\n", + perr); + } + cleanup: + if (outFile) { + PR_Close(outFile); + } + if (*selfsignprivkey) { + SECKEY_DestroyPrivateKey(*selfsignprivkey); + } + if (certReq) { + CERT_DestroyCertificateRequest(certReq); + } + if (subjectCert) { + CERT_DestroyCertificate(subjectCert); + } + return rv; + } + + /* + * Generate the certificate request with subject + */ + static SECStatus + CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, + SECOidTag hashAlgTag, CERTName *subject, PRBool ascii, + const char *certReqFileName) + { + SECOidTag signAlgTag; + SECItem result; + PRInt32 numBytes; + SECStatus rv = SECSuccess; + PRArenaPool *arena = NULL; + void *extHandle = NULL; + PRFileDesc *outFile = NULL; + CERTSubjectPublicKeyInfo *spki = NULL; + CERTCertificateRequest *cr = NULL; + SECItem *encoding = NULL; + + /* If the certificate request file already exists, delete it */ + if (PR_Access(certReqFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { + PR_Delete(certReqFileName); + } + /* Open the certificate request file to write */ + outFile = PR_Open(certReqFileName, PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660); + if (!outFile) { + PR_fprintf(PR_STDERR, + "unable to open \"%s\" for writing (%ld, %ld).\n", + certReqFileName, PR_GetError(), PR_GetOSError()); + goto cleanup; + } + /* Create info about public key */ + spki = SECKEY_CreateSubjectPublicKeyInfo(pubk); + if (!spki) { + PR_fprintf(PR_STDERR, "unable to create subject public key\n"); + rv = SECFailure; + goto cleanup; + } + + /* Generate certificate request */ + cr = CERT_CreateCertificateRequest(subject, spki, NULL); + if (!cr) { + PR_fprintf(PR_STDERR, "unable to make certificate request\n"); + rv = SECFailure; + goto cleanup; + } + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + fprintf(stderr, "out of memory"); + rv = SECFailure; + goto cleanup; + } + + extHandle = CERT_StartCertificateRequestAttributes(cr); + if (extHandle == NULL) { + PORT_FreeArena (arena, PR_FALSE); + rv = SECFailure; + goto cleanup; + } + + CERT_FinishExtensions(extHandle); + CERT_FinishCertificateRequestAttributes(cr); + + /* Der encode the request */ + encoding = SEC_ASN1EncodeItem(arena, NULL, cr, + SEC_ASN1_GET(CERT_CertificateRequestTemplate)); + if (encoding == NULL) { + PR_fprintf(PR_STDERR, "der encoding of request failed\n"); + rv = SECFailure; + goto cleanup; + } + + /* Sign the request */ + signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag); + if (signAlgTag == SEC_OID_UNKNOWN) { + PR_fprintf(PR_STDERR, "unknown Key or Hash type\n"); + rv = SECFailure; + goto cleanup; + } + rv = SEC_DerSignData(arena, &result, encoding->data, encoding->len, + privk, signAlgTag); + if (rv) { + PR_fprintf(PR_STDERR, "signing of data failed\n"); + rv = SECFailure; + goto cleanup; + } + + /* Encode request in specified format */ + if (ascii) { + char *obuf; + char *name, *email, *org, *state, *country; + SECItem *it; + int total; + + it = &result; + + obuf = BTOA_ConvertItemToAscii(it); + total = PL_strlen(obuf); + + name = CERT_GetCommonName(subject); + if (!name) { + name = strdup("(not specified)"); + } + + email = CERT_GetCertEmailAddress(subject); + if (!email) + email = strdup("(not specified)"); + + org = CERT_GetOrgName(subject); + if (!org) + org = strdup("(not specified)"); + + state = CERT_GetStateName(subject); + if (!state) + state = strdup("(not specified)"); + + country = CERT_GetCountryName(subject); + if (!country) + country = strdup("(not specified)"); + + PR_fprintf(outFile, + "\nCertificate request generated by Netscape certutil\n"); + PR_fprintf(outFile, "Common Name: %s\n", name); + PR_fprintf(outFile, "Email: %s\n", email); + PR_fprintf(outFile, "Organization: %s\n", org); + PR_fprintf(outFile, "State: %s\n", state); + PR_fprintf(outFile, "Country: %s\n\n", country); + + PR_fprintf(outFile, "%s\n", NS_CERTREQ_HEADER); + numBytes = PR_Write(outFile, obuf, total); + if (numBytes != total) { + PR_fprintf(PR_STDERR, "write error\n"); + return SECFailure; + } + PR_fprintf(outFile, "\n%s\n", NS_CERTREQ_TRAILER); + } else { + numBytes = PR_Write(outFile, result.data, result.len); + if (numBytes != (int)result.len) { + PR_fprintf(PR_STDERR, "write error\n"); + rv = SECFailure; + goto cleanup; + } + } + cleanup: + if (outFile) { + PR_Close(outFile); + } + if (privk) { + SECKEY_DestroyPrivateKey(privk); + } + if (pubk) { + SECKEY_DestroyPublicKey(pubk); + } + return rv; + } + + /* + * Create certificate request with subject + */ + SECStatus CreateCertRequest(PK11SlotInfo *slot, + secuPWData *pwdata, + CERTName *subject, + char *certReqFileName, + PRBool ascii) + { + SECStatus rv; + SECKEYPrivateKey *privkey = NULL; + SECKEYPublicKey *pubkey = NULL; + KeyType keytype = rsaKey; + int keysize = DEFAULT_KEY_BITS; + int publicExponent = 0x010001; + SECOidTag hashAlgTag = SEC_OID_UNKNOWN; + + privkey = GeneratePrivateKey(keytype, slot, keysize, + publicExponent, NULL, + &pubkey, NULL, pwdata); + if (privkey == NULL) { + PR_fprintf(PR_STDERR, "unable to generate key(s)\n"); + rv = SECFailure; + goto cleanup; + } + privkey->wincx = pwdata; + PORT_Assert(pubkey != NULL); + rv = CertReq(privkey, pubkey, keytype, hashAlgTag, subject, + ascii, certReqFileName); + + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed to create Certificate Request\n"); + } + cleanup: + return rv; + } + + /* + * Creates the certificate using CSR and adds the certificate to DB + */ + SECStatus AddCertificateToDB(PK11SlotInfo *slot, + secuPWData *pwdata, + char *certReqFileName, + char *certFileName, + char *issuerNameStr, + CERTCertDBHandle *certHandle, + const char *nickNameStr, + char *trustStr, + unsigned int serialNumber, + PRBool selfsign, + PRBool ascii) + { + SECStatus rv; + SECKEYPrivateKey *privkey = NULL; + SECKEYPublicKey *pubkey = NULL; + SECOidTag hashAlgTag = SEC_OID_UNKNOWN; + + if (PR_Access(certFileName, PR_ACCESS_EXISTS) == PR_FAILURE) { + rv = CreateCert(certHandle, slot, issuerNameStr, + certReqFileName, certFileName, &privkey, &pwdata, hashAlgTag, + serialNumber, 0, 3, NULL, ascii, selfsign); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed to create Certificate\n"); + goto cleanup; + } + } + rv = AddCert(slot, certHandle, nickNameStr, + trustStr, certFileName, ascii, 0, &pwdata); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed to add Certificate\n"); + } + cleanup: + return rv; + } + + /* + * Finds the certificate using nickname and saves it to the header file + */ + SECStatus AddCertificateToHeader(PK11SlotInfo *slot, + secuPWData *pwdata, + const char *headerFileName, + CERTCertDBHandle *certHandle, + const char *nickNameStr, + PRBool sigVerify) + + { + SECStatus rv = SECSuccess; + PRFileDesc *headerFile = NULL; + CERTCertificate *cert = NULL; + HeaderType hType = CERTENC; + + /* If the intermediate header file already exists, delete it */ + if (PR_Access(headerFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { + PR_Delete(headerFileName); + } + headerFile = PR_Open(headerFileName, PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660); + if (!headerFile) { + PR_fprintf(PR_STDERR, + "unable to open \"%s\" for writing (%ld, %ld).\n", + headerFileName, PR_GetError(), PR_GetOSError()); + rv = SECFailure; + goto cleanup; + } + cert = CERT_FindCertByNicknameOrEmailAddr(certHandle, nickNameStr); + if (!cert) { + PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); + rv = SECFailure; + goto cleanup; + } + if (sigVerify) { + hType = CERTVFY; + } + WriteToHeaderFile(cert->derCert.data, cert->derCert.len, hType, headerFile); + cleanup: + if (headerFile) { + PR_Close(headerFile); + } + if (cert) { + CERT_DestroyCertificate(cert); + } + return rv; + } + + /* + * Finds the public key from the certificate saved in the header file + * and encrypts with it the contents of inFileName to encryptedFileName. + */ + SECStatus FindKeyAndEncrypt(PK11SlotInfo *slot, + secuPWData *pwdata, + const char *headerFileName, + const char *encryptedFileName, + const char *inFileName) + { + SECStatus rv; + PRFileDesc *headerFile = NULL; + PRFileDesc *encFile = NULL; + PRFileDesc *inFile = NULL; + CERTCertificate *cert = NULL; + SECItem data; + unsigned char ptext[MODBLOCKSIZE]; + unsigned char encBuf[MODBLOCKSIZE]; + unsigned int ptextLen; + int index; + unsigned int nWritten; + unsigned int pad[1]; + SECItem padItem; + unsigned int paddingLength = 0; + SECKEYPublicKey *pubkey = NULL; + + /* If the intermediate encrypted file already exists, delete it*/ + if (PR_Access(encryptedFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { + PR_Delete(encryptedFileName); + } + + /* Read certificate from header file */ + rv = ReadFromHeaderFile(headerFileName, CERTENC, &data, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not read certificate from header file\n"); + goto cleanup; + } + /* Read in an ASCII cert and return a CERTCertificate */ + cert = CERT_DecodeCertFromPackage((char *)data.data, data.len); + if (!cert) { + PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); + rv = SECFailure; + goto cleanup; + } + /* Extract the public key from certificate */ + pubkey = CERT_ExtractPublicKey(cert); + if (!pubkey) { + PR_fprintf(PR_STDERR, "could not get key from certificate\n"); + rv = SECFailure; + goto cleanup; + } + + /* Open the encrypted file for writing */ + encFile = PR_Open(encryptedFileName, + PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); + if (!encFile) { + PR_fprintf(PR_STDERR, + "Unable to open \"%s\" for writing.\n", + encryptedFileName); + rv = SECFailure; + goto cleanup; + } + + /* Open the input file for reading */ + inFile = PR_Open(inFileName, PR_RDONLY, 0); + if (!inFile) { + PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", + inFileName); + rv = SECFailure; + goto cleanup; + } + + /* Open the header file to write padding */ + headerFile = PR_Open(headerFileName, PR_CREATE_FILE | PR_RDWR | PR_APPEND, 00660); + if (!headerFile) { + PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", + headerFileName); + rv = SECFailure; + goto cleanup; + } + + /* Read input file */ + while ((ptextLen = PR_Read(inFile, ptext, sizeof(ptext))) > 0) { + if (ptextLen != MODBLOCKSIZE) { + paddingLength = MODBLOCKSIZE - ptextLen; + for ( index=0; index < paddingLength; index++) { + ptext[ptextLen+index] = (unsigned char)paddingLength; + } + ptextLen = MODBLOCKSIZE; + } + rv = PK11_PubEncryptRaw(pubkey, encBuf, ptext, ptextLen, NULL); + nWritten = PR_Write(encFile, encBuf, ptextLen); + } + + /* Write the padding to header file */ + pad[0] = paddingLength; + padItem.type = siBuffer; + padItem.data = (unsigned char *)pad; + padItem.len = sizeof(pad[0]); + WriteToHeaderFile(padItem.data, padItem.len, PAD, headerFile); + + cleanup: + if (headerFile) { + PR_Close(headerFile); + } + if (encFile) { + PR_Close(encFile); + } + if (inFile) { + PR_Close(inFile); + } + if (pubkey) { + SECKEY_DestroyPublicKey(pubkey); + } + if (cert) { + CERT_DestroyCertificate(cert); + } + return rv; + } + + /* + * Finds the private key from db and signs the contents + * of inFileName and writes to signatureFileName + */ + SECStatus FindKeyAndSign(PK11SlotInfo *slot, + CERTCertDBHandle* certHandle, + secuPWData *pwdata, + const char *nickNameStr, + const char *headerFileName, + const char *inFileName) + { + SECStatus rv; + PRFileDesc *headerFile = NULL; + PRFileDesc *inFile = NULL; + CERTCertificate *cert = NULL; + unsigned int signatureLen = 0; + SECKEYPrivateKey *privkey = NULL; + SECItem sigItem; + SECOidTag hashOIDTag; + + /* Open the header file to write padding */ + headerFile = PR_Open(headerFileName, PR_CREATE_FILE | PR_RDWR | PR_APPEND, 00660); + if (!headerFile) { + PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", + headerFileName); + rv = SECFailure; + goto cleanup; + } + + /* Get the certificate by nick name and write to header file */ + cert = CERT_FindCertByNicknameOrEmailAddr(certHandle, nickNameStr); + if (!cert) { + PR_fprintf(PR_STDERR, "could not obtain certificate by name - %s\n", nickNameStr); + rv = SECFailure; + goto cleanup; + } + WriteToHeaderFile(cert->derCert.data, cert->derCert.len, CERTVFY, headerFile); + + + /* Find private key from certificate */ + privkey = PK11_FindKeyByAnyCert(cert, NULL); + if (privkey == NULL) { + fprintf(stderr, "Couldn't find private key for cert\n"); + rv = SECFailure; + goto cleanup; + } + + /* Sign the contents of the input file */ + rv = SignData(inFileName, privkey, &sigItem); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "could not sign the contents from file - %s \n", inFileName); + goto cleanup; + } + + /* write signature to header file */ + WriteToHeaderFile(sigItem.data, sigItem.len, SIG, headerFile); + + cleanup: + if (headerFile) { + PR_Close(headerFile); + } + if (privkey) { + SECKEY_DestroyPrivateKey(privkey); + } + if (cert) { + CERT_DestroyCertificate(cert); + } + return rv; + } + + /* + * Finds the public key from certificate and verifies signature + */ + SECStatus FindKeyAndVerify(PK11SlotInfo *slot, + CERTCertDBHandle* certHandle, + secuPWData *pwdata, + const char *headerFileName, + const char *inFileName) + { + SECStatus rv = SECFailure; + PRFileDesc *headerFile = NULL; + PRFileDesc *inFile = NULL; + CERTCertificate *cert = NULL; + SECKEYPublicKey *pubkey = NULL; + SECItem sigItem; + SECItem certData; + + + /* Open the input file */ + inFile = PR_Open(inFileName, PR_RDONLY, 0); + if (!inFile) { + PR_fprintf(PR_STDERR, + "Unable to open \"%s\" for reading.\n", + inFileName); + rv = SECFailure; + goto cleanup; + } + + /* Open the header file to read the certificate and signature */ + headerFile = PR_Open(headerFileName, PR_RDONLY, 0); + if (!headerFile) { + PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", + headerFileName); + rv = SECFailure; + goto cleanup; + } + + /* Read certificate from header file */ + rv = ReadFromHeaderFile(headerFileName, CERTVFY, &certData, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not read certificate from header file\n"); + goto cleanup; + } + + /* Read in an ASCII cert and return a CERTCertificate */ + cert = CERT_DecodeCertFromPackage((char *)certData.data, certData.len); + if (!cert) { + PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); + rv = SECFailure; + goto cleanup; + } + + /* Extract the public key from certificate */ + pubkey = CERT_ExtractPublicKey(cert); + if (!pubkey) { + PR_fprintf(PR_STDERR, "Could not get key from certificate\n"); + rv = SECFailure; + goto cleanup; + } + + /* Read signature from header file */ + rv = ReadFromHeaderFile(headerFileName, SIG, &sigItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not read signature from header file\n"); + goto cleanup; + } + + /* Verify with the public key */ + rv = VerifyData(inFileName, pubkey, &sigItem, pwdata); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Couldn't verify the signature for file - %s\n", inFileName); + goto cleanup; + } + + cleanup: + if (headerFile) { + PR_Close(headerFile); + } + if (pubkey) { + SECKEY_DestroyPublicKey(pubkey); + } + if (cert) { + CERT_DestroyCertificate(cert); + } + return rv; + } + + /* + * Finds the private key corresponding to the certificate saved in the header file + * and decrypts with it the contents of encryptedFileName to outFileName. + */ + SECStatus FindKeyAndDecrypt(PK11SlotInfo *slot, + secuPWData *pwdata, + const char *headerFileName, + const char *encryptedFileName, + const char *outFileName) + { + SECStatus rv; + PRFileDesc *encFile = NULL; + PRFileDesc *outFile = NULL; + SECKEYPrivateKey *pvtkey = NULL; + unsigned int inFileLength = 0; + unsigned int paddingLength = 0; + unsigned int count = 0; + unsigned int temp = 0; + unsigned char ctext[MODBLOCKSIZE]; + unsigned char decBuf[MODBLOCKSIZE]; + unsigned int ctextLen; + unsigned int decBufLen; + SECItem padItem; + SECItem data; + SECItem signature; + CERTCertificate *cert = NULL; + + /* Read certificate from header file */ + rv = ReadFromHeaderFile(headerFileName, CERTENC, &data, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not read certificate from header file\n"); + goto cleanup; + } + + /* Read padding from header file */ + rv = ReadFromHeaderFile(headerFileName, PAD, &padItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not retrieve PAD detail from header file\n"); + goto cleanup; + } + paddingLength = (unsigned int)padItem.data[0]; + inFileLength = FileSize(encryptedFileName); + + /* Read in an ASCII cert and return a CERTCertificate */ + cert = CERT_DecodeCertFromPackage((char *)data.data, data.len); + if (!cert) { + PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); + rv = SECFailure; + goto cleanup; + } + + /* Find private key from certificate */ + pvtkey = PK11_FindKeyByAnyCert(cert, NULL); + if (pvtkey == NULL) { + fprintf(stderr, "Couldn't find private key for cert\n"); + rv = SECFailure; + goto cleanup; + } + + /* Open the out file to write */ + outFile = PR_Open(outFileName, + PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); + if (!outFile) { + PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", + outFileName); + rv = SECFailure; + goto cleanup; + } + /* Open the encrypted file for reading */ + encFile = PR_Open(encryptedFileName, PR_RDONLY, 0); + if (!encFile) { + PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", + encryptedFileName); + rv = SECFailure; + goto cleanup; + } + /* Read the encrypt file, decrypt and write to out file */ + while ((ctextLen = PR_Read(encFile, ctext, sizeof(ctext))) > 0) { + count += ctextLen; + rv = PK11_PubDecryptRaw(pvtkey, decBuf, &decBufLen, sizeof(decBuf), ctext, ctextLen); + if (rv != SECSuccess) { + fprintf(stderr, "Couldn't decrypt\n"); + goto cleanup; + } + if (decBufLen == 0) { + break; + } + if (count == inFileLength) { + decBufLen = decBufLen - paddingLength; + } + /* write the plain text to out file */ + temp = PR_Write(outFile, decBuf, decBufLen); + if (temp != decBufLen) { + PR_fprintf(PR_STDERR, "write error\n"); + rv = SECFailure; + break; + } + } + cleanup: + if (encFile) { + PR_Close(encFile); + } + if (outFile) { + PR_Close(outFile); + } + if (pvtkey) { + SECKEY_DestroyPrivateKey(pvtkey); + } + if (cert) { + CERT_DestroyCertificate(cert); + } + return rv; + } + + /* Map option letter to command */ + static CommandType option2Command(char c) + { + switch (c) { + case 'G': return GENERATE_CSR; + case 'A': return ADD_CERT_TO_DB; + case 'H': return SAVE_CERT_TO_HEADER; + case 'E': return ENCRYPT; + case 'D': return DECRYPT; + case 'S': return SIGN; + case 'V': return VERIFY; + default: return UNKNOWN; + } + } + + /* + * This example illustrates basic encryption/decryption and MACing + * Generates the RSA key pair as token object and outputs public key as cert request. + * Reads cert request file and stores certificate in DB. + * Input, store and trust CA certificate. + * Write certificate to intermediate header file + * Extract public key from certificate, encrypts the input file and write to external file. + * Finds the matching private key, decrypts and write to external file + * + * How this sample is different from sample 5 ? + * + * 1. As in sample 5, output is a PKCS#10 CSR + * 2. Input and store a cert in cert DB and also used to input, store and trust CA cert. + * 3. Like sample 5, but puts cert in header + * 4. Like sample 5, but finds key matching cert in header + */ + int + main(int argc, char **argv) + { + SECStatus rv; + PLOptState *optstate; + PLOptStatus status; + PRBool initialized = PR_FALSE; + + CommandType cmd = UNKNOWN; + const char *dbdir = NULL; + secuPWData pwdata = { PW_NONE, 0 }; + + char *subjectStr = NULL; + CERTName *subject = 0; + + unsigned int serialNumber = 0; + char *serialNumberStr = NULL; + char *trustStr = NULL; + CERTCertDBHandle *certHandle; + const char *nickNameStr = NULL; + char *issuerNameStr = NULL; + PRBool selfsign = PR_FALSE; + PRBool ascii = PR_FALSE; + PRBool sigVerify = PR_FALSE; + + const char *headerFileName = NULL; + const char *encryptedFileName = NULL; + const char *inFileName = NULL; + const char *outFileName = NULL; + char *certReqFileName = NULL; + char *certFileName = NULL; + const char *noiseFileName = NULL; + PK11SlotInfo *slot = NULL; + + char * progName = strrchr(argv[0], '/'); + progName = progName ? progName + 1 : argv[0]; + + /* Parse command line arguments */ + optstate = PL_CreateOptState(argc, argv, "GAHEDSVad:i:o:f:p:z:s:r:n:x:m:t:c:u:e:b:v:"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + switch (optstate->option) { + case 'a': + ascii = PR_TRUE; + break; + case 'G': /* Generate a CSR */ + case 'A': /* Add cert to database */ + case 'H': /* Save cert to the header file */ + case 'E': /* Encrypt with public key from cert in header file */ + case 'S': /* Sign with private key */ + case 'D': /* Decrypt with the matching private key */ + case 'V': /* Verify with the matching public key */ + cmd = option2Command(optstate->option); + break; + case 'd': + dbdir = strdup(optstate->value); + break; + case 'f': + pwdata.source = PW_FROMFILE; + pwdata.data = strdup(optstate->value); + break; + case 'p': + pwdata.source = PW_PLAINTEXT; + pwdata.data = strdup(optstate->value); + break; + case 'i': + inFileName = strdup(optstate->value); + break; + case 'b': + headerFileName = strdup(optstate->value); + break; + case 'e': + encryptedFileName = strdup(optstate->value); + break; + case 'o': + outFileName = strdup(optstate->value); + break; + case 'z': + noiseFileName = strdup(optstate->value); + break; + case 's': + subjectStr = strdup(optstate->value); + subject = CERT_AsciiToName(subjectStr); + break; + case 'r': + certReqFileName = strdup(optstate->value); + break; + case 'c': + certFileName = strdup(optstate->value); + break; + case 'u': + issuerNameStr = strdup(optstate->value); + break; + case 'n': + nickNameStr = strdup(optstate->value); + break; + case 'x': + selfsign = PR_TRUE; + break; + case 'm': + serialNumberStr = strdup(optstate->value); + serialNumber = atoi(serialNumberStr); + break; + case 't': + trustStr = strdup(optstate->value); + break; + case 'v': + sigVerify = PR_TRUE; + break; + default: + Usage(progName); + break; + } + } + PL_DestroyOptState(optstate); + + if (cmd == UNKNOWN || !dbdir) + Usage(progName); + + /* Open DB for read/write and authenticate to it */ + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + initialized = PR_TRUE; + rv = NSS_InitReadWrite(dbdir); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "NSS_InitReadWrite Failed\n"); + goto cleanup; + } + + PK11_SetPasswordFunc(GetModulePassword); + slot = PK11_GetInternalKeySlot(); + if (PK11_NeedLogin(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, &pwdata); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", + PK11_GetTokenName(slot)); + goto cleanup; + } + } + + switch (cmd) { + case GENERATE_CSR: + ValidateGenerateCSRCommand(progName, dbdir, subject, subjectStr, + certReqFileName); + /* Generate a CSR */ + rv = CreateCertRequest(slot, &pwdata, subject, + certReqFileName, ascii); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Create Certificate Request: Failed\n"); + goto cleanup; + } + break; + case ADD_CERT_TO_DB: + ValidateAddCertToDBCommand(progName, dbdir, nickNameStr, trustStr, + certFileName, certReqFileName, + issuerNameStr, serialNumberStr, selfsign); + /* Add cert to database */ + rv = AddCertificateToDB(slot, &pwdata, certReqFileName, certFileName, + issuerNameStr, certHandle, nickNameStr, + trustStr, serialNumber, selfsign, ascii); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Add Certificate to DB: Failed\n"); + goto cleanup; + } + break; + case SAVE_CERT_TO_HEADER: + ValidateSaveCertToHeaderCommand(progName, dbdir, nickNameStr, headerFileName); + /* Save cert to the header file */ + rv = AddCertificateToHeader(slot, &pwdata, headerFileName, certHandle, nickNameStr, sigVerify); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Saving Certificate to header: Failed\n"); + goto cleanup; + } + break; + case ENCRYPT: + ValidateEncryptCommand(progName, dbdir, nickNameStr, headerFileName, inFileName, encryptedFileName); + /* Encrypt with public key from cert in header file */ + rv = FindKeyAndEncrypt(slot, &pwdata, headerFileName, encryptedFileName, inFileName); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Find public key and Encrypt : Failed\n"); + goto cleanup; + } + break; + case SIGN: + ValidateSignCommand(progName, dbdir, nickNameStr, headerFileName, inFileName); + /* Sign with private key */ + rv = FindKeyAndSign(slot, certHandle, &pwdata, nickNameStr, headerFileName, inFileName); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Find private key and sign : Failed\n"); + goto cleanup; + } + break; + case DECRYPT: + ValidateDecryptCommand(progName, dbdir, headerFileName, encryptedFileName, outFileName); + /* Decrypt with the matching private key */ + rv = FindKeyAndDecrypt(slot, &pwdata, headerFileName, encryptedFileName, outFileName); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Find private key and Decrypt : Failed\n"); + } + break; + case VERIFY: + ValidateVerifyCommand(progName, dbdir, headerFileName, inFileName); + /* Verify with the matching public key */ + rv = FindKeyAndVerify(slot, certHandle, &pwdata, headerFileName, inFileName); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Find public key and verify signature : Failed\n"); + goto cleanup; + } + } + cleanup: + if (slot) { + PK11_FreeSlot(slot); + } + if (initialized) { + SECStatus rvShutdown = NSS_Shutdown(); + if (rvShutdown != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed : NSS_Shutdown() - %s", + PORT_ErrorToString(rvShutdown)); + rv = SECFailure; + } + PR_Cleanup(); + } + return rv; + } \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/encrypt_decrypt_mac_using_token/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/encrypt_decrypt_mac_using_token/index.rst new file mode 100644 index 0000000000..712823698d --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/encrypt_decrypt_mac_using_token/index.rst @@ -0,0 +1,1206 @@ +.. _mozilla_projects_nss_nss_sample_code_encrypt_decrypt_mac_using_token: + +Encrypt Decrypt_MAC_Using Token +=============================== + +.. _nss_sample_code_3_encryptiondecryption_and_mac_using_token_object.: + +`NSS Sample Code 3: Encryption/Decryption and MAC Using Token Object. <#nss_sample_code_3_encryptiondecryption_and_mac_using_token_object.>`__ +---------------------------------------------------------------------------------------------------------------------------------------------- + +.. container:: + + Generates encryption/mac keys and uses token for storing. + + .. code:: c + + /* 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 https://mozilla.org/MPL/2.0/. */ + + /* NSPR Headers */ + #include + #include + #include + #include + #include + #include + #include + + /* NSS headers */ + #include + #include + + /* Our samples utilities */ + #include "util.h" + + #define BUFFERSIZE 80 + #define DIGESTSIZE 16 + #define PTEXT_MAC_BUFFER_SIZE 96 + #define CIPHERSIZE 96 + #define BLOCKSIZE 32 + + #define CIPHER_HEADER "-----BEGIN CIPHER-----" + #define CIPHER_TRAILER "-----END CIPHER-----" + #define ENCKEY_HEADER "-----BEGIN AESKEY CKAID-----" + #define ENCKEY_TRAILER "-----END AESKEY CKAID-----" + #define MACKEY_HEADER "-----BEGIN MACKEY CKAID-----" + #define MACKEY_TRAILER "-----END MACKEY CKAID-----" + #define IV_HEADER "-----BEGIN IV-----" + #define IV_TRAILER "-----END IV-----" + #define MAC_HEADER "-----BEGIN MAC-----" + #define MAC_TRAILER "-----END MAC-----" + #define PAD_HEADER "-----BEGIN PAD-----" + #define PAD_TRAILER "-----END PAD-----" + + typedef enum { + ENCRYPT, + DECRYPT, + UNKNOWN + } CommandType; + + typedef enum { + SYMKEY = 0, + MACKEY = 1, + IV = 2, + MAC = 3, + PAD = 4 + } HeaderType; + + + /* + * Print usage message and exit. + */ + static void + Usage(const char *progName) + { + fprintf(stderr, "\nUsage: %s -c -d [-z ] " + "[-p | -f ] -i -o \n\n", + progName); + fprintf(stderr, "%-20s Specify 'a' for encrypt operation\n\n", + "-c "); + fprintf(stderr, "%-20s Specify 'b' for decrypt operation\n\n", + " "); + fprintf(stderr, "%-20s Specify db directory path\n\n", + "-d "); + fprintf(stderr, "%-20s Specify db password [optional]\n\n", + "-p "); + fprintf(stderr, "%-20s Specify db password file [optional]\n\n", + "-f "); + fprintf(stderr, "%-20s Specify noise file name [optional]\n\n", + "-z "); + fprintf(stderr, "%-21s Specify an input file name\n\n", + "-i "); + fprintf(stderr, "%-21s Specify an output file name\n\n", + "-o "); + fprintf(stderr, "%-7s For encrypt, it takes as an input file and produces\n", + "Note :"); + fprintf(stderr, "%-7s .enc and .header as intermediate output files.\n\n", + ""); + fprintf(stderr, "%-7s For decrypt, it takes .enc and .header\n", + ""); + fprintf(stderr, "%-7s as input files and produces as a final output file.\n\n", + ""); + exit(-1); + } + + /* + * Gather a CKA_ID. + */ + SECStatus + GatherCKA_ID(PK11SymKey* key, SECItem* buf) + { + SECStatus rv = PK11_ReadRawAttribute(PK11_TypeSymKey, key, CKA_ID, buf); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "PK11_ReadRawAttribute returned (%d)\n", rv); + PR_fprintf(PR_STDERR, "Could not read SymKey CKA_ID attribute\n"); + return rv; + } + return rv; + } + + /* + * Generate a Symmetric Key. + */ + PK11SymKey * + GenerateSYMKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE mechanism, + int keySize, SECItem *keyID, secuPWData *pwdata) + { + SECStatus rv; + PK11SymKey *key; + + if (PK11_NeedLogin(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, pwdata); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", + PK11_GetTokenName(slot)); + return NULL; + } + } + + /* Generate the symmetric key. */ + key = PK11_TokenKeyGen(slot, mechanism, + NULL, keySize, keyID, PR_TRUE, pwdata); + + if (!key) { + PR_fprintf(PR_STDERR, "Symmetric Key Generation Failed \n"); + } + + return key; + } + + /* + * MacInit + */ + SECStatus + MacInit(PK11Context *ctx) + { + SECStatus rv = PK11_DigestBegin(ctx); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Compute MAC Failed : PK11_DigestBegin()\n"); + } + return rv; + } + + /* + * MacUpdate + */ + SECStatus + MacUpdate(PK11Context *ctx, + unsigned char *msg, unsigned int msgLen) + { + SECStatus rv = PK11_DigestOp(ctx, msg, msgLen); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Compute MAC Failed : DigestOp()\n"); + } + return rv; + } + + /* + * Finalize MACing. + */ + SECStatus + MacFinal(PK11Context *ctx, + unsigned char *mac, unsigned int *macLen, unsigned int maxLen) + { + SECStatus rv = PK11_DigestFinal(ctx, mac, macLen, maxLen); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Compute MAC Failed : PK11_DigestFinal()\n"); + } + return SECSuccess; + } + + /* + * Compute Mac. + */ + SECStatus + ComputeMac(PK11Context *ctxmac, + unsigned char *ptext, unsigned int ptextLen, + unsigned char *mac, unsigned int *macLen, + unsigned int maxLen) + { + SECStatus rv = MacInit(ctxmac); + if (rv != SECSuccess) return rv; + rv = MacUpdate(ctxmac, ptext, ptextLen); + if (rv != SECSuccess) return rv; + rv = MacFinal(ctxmac, mac, macLen, maxLen); + return rv; + } + + /* + * WriteToHeaderFile + */ + SECStatus + WriteToHeaderFile(const char *buf, unsigned int len, HeaderType type, + PRFileDesc *outFile) + { + SECStatus rv; + char header[40]; + char trailer[40]; + char *outString = NULL; + + switch (type) { + case SYMKEY: + strcpy(header, ENCKEY_HEADER); + strcpy(trailer, ENCKEY_TRAILER); + break; + case MACKEY: + strcpy(header, MACKEY_HEADER); + strcpy(trailer, MACKEY_TRAILER); + break; + case IV: + strcpy(header, IV_HEADER); + strcpy(trailer, IV_TRAILER); + break; + case MAC: + strcpy(header, MAC_HEADER); + strcpy(trailer, MAC_TRAILER); + break; + case PAD: + strcpy(header, PAD_HEADER); + strcpy(trailer, PAD_TRAILER); + break; + } + + PR_fprintf(outFile, "%s\n", header); + PrintAsHex(outFile, buf, len); + PR_fprintf(outFile, "%s\n\n", trailer); + return SECSuccess; + } + + /* + * Initialize for encryption or decryption - common code. + */ + PK11Context * + CryptInit(PK11SymKey *key, + unsigned char *iv, unsigned int ivLen, + CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation) + { + SECItem ivItem = { siBuffer, iv, ivLen }; + PK11Context *ctx = NULL; + + SECItem *secParam = PK11_ParamFromIV(CKM_AES_CBC, &ivItem); + if (secParam == NULL) { + PR_fprintf(PR_STDERR, "Crypt Failed : secParam NULL\n"); + return NULL; + } + ctx = PK11_CreateContextBySymKey(CKM_AES_CBC, operation, key, secParam); + if (ctx == NULL) { + PR_fprintf(PR_STDERR, "Crypt Failed : can't create a context\n"); + goto cleanup; + + } + cleanup: + if (secParam) { + SECITEM_FreeItem(secParam, PR_TRUE); + } + return ctx; + } + + /* + * Common encryption and decryption code. + */ + SECStatus + Crypt(PK11Context *ctx, + unsigned char *out, unsigned int *outLen, unsigned int maxOut, + unsigned char *in, unsigned int inLen) + { + SECStatus rv; + + rv = PK11_CipherOp(ctx, out, outLen, maxOut, in, inLen); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Crypt Failed : PK11_CipherOp returned %d\n", rv); + goto cleanup; + } + + cleanup: + if (rv != SECSuccess) { + return rv; + } + return SECSuccess; + } + + /* + * Decrypt + */ + SECStatus + Decrypt(PK11Context *ctx, + unsigned char *out, unsigned int *outLen, unsigned int maxout, + unsigned char *in, unsigned int inLen) + { + return Crypt(ctx, out, outLen, maxout, in, inLen); + } + + /* + * Encrypt + */ + SECStatus + Encrypt(PK11Context* ctx, + unsigned char *out, unsigned int *outLen, unsigned int maxout, + unsigned char *in, unsigned int inLen) + { + return Crypt(ctx, out, outLen, maxout, in, inLen); + } + + /* + * EncryptInit + */ + PK11Context * + EncryptInit(PK11SymKey *ek, unsigned char *iv, unsigned int ivLen, + CK_MECHANISM_TYPE type) + { + return CryptInit(ek, iv, ivLen, type, CKA_ENCRYPT); + } + + /* + * DecryptInit + */ + PK11Context * + DecryptInit(PK11SymKey *dk, unsigned char *iv, unsigned int ivLen, + CK_MECHANISM_TYPE type) + { + return CryptInit(dk, iv, ivLen, type, CKA_DECRYPT); + } + + /* + * Read cryptographic parameters from the header file. + */ + SECStatus + ReadFromHeaderFile(const char *fileName, HeaderType type, + SECItem *item, PRBool isHexData) + { + SECStatus rv; + PRFileDesc* file; + SECItem filedata; + SECItem outbuf; + unsigned char *nonbody; + unsigned char *body; + char header[40]; + char trailer[40]; + + outbuf.type = siBuffer; + file = PR_Open(fileName, PR_RDONLY, 0); + if (!file) { + PR_fprintf(PR_STDERR, "Failed to open %s\n", fileName); + return SECFailure; + } + switch (type) { + case SYMKEY: + strcpy(header, ENCKEY_HEADER); + strcpy(trailer, ENCKEY_TRAILER); + break; + case MACKEY: + strcpy(header, MACKEY_HEADER); + strcpy(trailer, MACKEY_TRAILER); + break; + case IV: + strcpy(header, IV_HEADER); + strcpy(trailer, IV_TRAILER); + break; + case MAC: + strcpy(header, MAC_HEADER); + strcpy(trailer, MAC_TRAILER); + break; + case PAD: + strcpy(header, PAD_HEADER); + strcpy(trailer, PAD_TRAILER); + break; + } + + rv = FileToItem(&filedata, file); + nonbody = (char *)filedata.data; + if (!nonbody) { + PR_fprintf(PR_STDERR, "unable to read data from input file\n"); + rv = SECFailure; + goto cleanup; + } + + /* Check for headers and trailers and remove them. */ + if ((body = strstr(nonbody, header)) != NULL) { + char *trail = NULL; + nonbody = body; + body = PORT_Strchr(body, '\n'); + if (!body) + body = PORT_Strchr(nonbody, '\r'); /* Maybe this is a MAC file. */ + if (body) + trail = strstr(++body, trailer); + if (trail != NULL) { + *trail = '\0'; + } else { + PR_fprintf(PR_STDERR, "input has header but no trailer\n"); + PORT_Free(filedata.data); + return SECFailure; + } + } else { + body = nonbody; + } + + cleanup: + PR_Close(file); + HexToBuf(body, item, isHexData); + return SECSuccess; + } + + /* + * EncryptAndMac + */ + SECStatus + EncryptAndMac(PRFileDesc *inFile, + PRFileDesc *headerFile, + PRFileDesc *encFile, + PK11SymKey *ek, + PK11SymKey *mk, + unsigned char *iv, unsigned int ivLen, + PRBool ascii) + { + SECStatus rv; + unsigned char ptext[BLOCKSIZE]; + unsigned int ptextLen; + unsigned char mac[DIGESTSIZE]; + unsigned int macLen; + unsigned int nwritten; + unsigned char encbuf[BLOCKSIZE]; + unsigned int encbufLen; + SECItem noParams = { siBuffer, NULL, 0 }; + PK11Context *ctxmac = NULL; + PK11Context *ctxenc = NULL; + unsigned int pad[1]; + SECItem padItem; + unsigned int paddingLength; + + static unsigned int firstTime = 1; + int j; + + ctxmac = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, mk, &noParams); + if (ctxmac == NULL) { + PR_fprintf(PR_STDERR, "Can't create MAC context\n"); + rv = SECFailure; + goto cleanup; + } + rv = MacInit(ctxmac); + if (rv != SECSuccess) { + goto cleanup; + } + + ctxenc = EncryptInit(ek, iv, ivLen, CKM_AES_CBC); + + /* Read a buffer of plaintext from input file. */ + while ((ptextLen = PR_Read(inFile, ptext, sizeof(ptext))) > 0) { + + /* Encrypt using it using CBC, using previously created IV. */ + if (ptextLen != BLOCKSIZE) { + paddingLength = BLOCKSIZE - ptextLen; + for ( j=0; j < paddingLength; j++) { + ptext[ptextLen+j] = (unsigned char)paddingLength; + } + ptextLen = BLOCKSIZE; + } + rv = Encrypt(ctxenc, + encbuf, &encbufLen, sizeof(encbuf), + ptext, ptextLen); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Encrypt Failure\n"); + goto cleanup; + } + + /* Save the last block of ciphertext as the next IV. */ + iv = encbuf; + ivLen = encbufLen; + + /* Write the cipher text to intermediate file. */ + nwritten = PR_Write(encFile, encbuf, encbufLen); + /* PR_Assert(nwritten == encbufLen); */ + + rv = MacUpdate(ctxmac, ptext, ptextLen); + } + + rv = MacFinal(ctxmac, mac, &macLen, DIGESTSIZE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "MacFinal Failure\n"); + goto cleanup; + } + if (macLen == 0) { + PR_fprintf(PR_STDERR, "Bad MAC length\n"); + rv = SECFailure; + goto cleanup; + } + WriteToHeaderFile(mac, macLen, MAC, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Write MAC Failure\n"); + goto cleanup; + } + + pad[0] = paddingLength; + padItem.type = siBuffer; + padItem.data = (unsigned char *)pad; + padItem.len = sizeof(pad[0]); + + WriteToHeaderFile(padItem.data, padItem.len, PAD, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Write PAD Failure\n"); + goto cleanup; + } + + rv = SECSuccess; + + cleanup: + if (ctxmac != NULL) { + PK11_DestroyContext(ctxmac, PR_TRUE); + } + if (ctxenc != NULL) { + PK11_DestroyContext(ctxenc, PR_TRUE); + } + + return rv; + } + + /* + * Find the Key for the given mechanism. + */ + PK11SymKey* + FindKey(PK11SlotInfo *slot, + CK_MECHANISM_TYPE mechanism, + SECItem *keyBuf, secuPWData *pwdata) + { + SECStatus rv; + PK11SymKey *key; + + if (PK11_NeedLogin(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, pwdata); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not authenticate to token %s.\n", + PK11_GetTokenName(slot)); + if (slot) { + PK11_FreeSlot(slot); + } + return NULL; + } + } + + key = PK11_FindFixedKey(slot, mechanism, keyBuf, 0); + if (!key) { + PR_fprintf(PR_STDERR, + "PK11_FindFixedKey failed (err %d)\n", + PR_GetError()); + PK11_FreeSlot(slot); + return NULL; + } + return key; + } + + /* + * Decrypt and Verify MAC. + */ + SECStatus + DecryptAndVerifyMac(const char* outFileName, + char *encryptedFileName, + SECItem *cItem, SECItem *macItem, + PK11SymKey* ek, PK11SymKey* mk, SECItem *ivItem, SECItem *padItem) + { + SECStatus rv; + PRFileDesc* inFile; + PRFileDesc* outFile; + + unsigned char decbuf[64]; + unsigned int decbufLen; + + unsigned char ptext[BLOCKSIZE]; + unsigned int ptextLen = 0; + unsigned char ctext[64]; + unsigned int ctextLen; + unsigned char newmac[DIGESTSIZE]; + unsigned int newmacLen = 0; + unsigned int newptextLen = 0; + unsigned int count = 0; + unsigned int temp = 0; + unsigned int blockNumber = 0; + SECItem noParams = { siBuffer, NULL, 0 }; + PK11Context *ctxmac = NULL; + PK11Context *ctxenc = NULL; + + unsigned char iv[BLOCKSIZE]; + unsigned int ivLen = ivItem->len; + unsigned int fileLength; + unsigned int paddingLength; + int j; + + memcpy(iv, ivItem->data, ivItem->len); + paddingLength = (unsigned int)padItem->data[0]; + + ctxmac = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, mk, &noParams); + if (ctxmac == NULL) { + PR_fprintf(PR_STDERR, "Can't create MAC context\n"); + rv = SECFailure; + goto cleanup; + } + + /* Open the input file. */ + inFile = PR_Open(encryptedFileName, PR_RDONLY , 0); + if (!inFile) { + PR_fprintf(PR_STDERR, + "Unable to open \"%s\" for writing.\n", + encryptedFileName); + return SECFailure; + } + /* Open the output file. */ + outFile = PR_Open(outFileName, + PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR , 00660); + if (!outFile) { + PR_fprintf(PR_STDERR, + "Unable to open \"%s\" for writing.\n", + outFileName); + return SECFailure; + } + + rv = MacInit(ctxmac); + if (rv != SECSuccess) goto cleanup; + + ctxenc = DecryptInit(ek, iv, ivLen, CKM_AES_CBC); + fileLength = FileSize(encryptedFileName); + + while ((ctextLen = PR_Read(inFile, ctext, sizeof(ctext))) > 0) { + + count += ctextLen; + + /* Decrypt cipher text buffer using CBC and IV. */ + + rv = Decrypt(ctxenc, decbuf, &decbufLen, sizeof(decbuf), + ctext, ctextLen); + + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Decrypt Failure\n"); + goto cleanup; + } + + if (decbufLen == 0) break; + + rv = MacUpdate(ctxmac, decbuf, decbufLen); + if (rv != SECSuccess) { goto cleanup; } + if (count == fileLength) { + decbufLen = decbufLen-paddingLength; + } + + /* Write the plain text to out file. */ + temp = PR_Write(outFile, decbuf, decbufLen); + if (temp != decbufLen) { + PR_fprintf(PR_STDERR, "write error\n"); + rv = SECFailure; + break; + } + + /* Save last block of ciphertext. */ + memcpy(iv, decbuf, decbufLen); + ivLen = decbufLen; + blockNumber++; + } + + if (rv != SECSuccess) { goto cleanup; } + + rv = MacFinal(ctxmac, newmac, &newmacLen, sizeof(newmac)); + if (rv != SECSuccess) { goto cleanup; } + + if (PORT_Memcmp(macItem->data, newmac, newmacLen) == 0) { + rv = SECSuccess; + } else { + PR_fprintf(PR_STDERR, "Check MAC : Failure\n"); + PR_fprintf(PR_STDERR, "Extracted : "); + PrintAsHex(PR_STDERR, macItem->data, macItem->len); + PR_fprintf(PR_STDERR, "Computed : "); + PrintAsHex(PR_STDERR, newmac, newmacLen); + rv = SECFailure; + } + cleanup: + if (ctxmac) { + PK11_DestroyContext(ctxmac, PR_TRUE); + } + if (ctxenc) { + PK11_DestroyContext(ctxenc, PR_TRUE); + } + if (outFile) { + PR_Close(outFile); + } + + return rv; + } + + /* + * Gets IV and CKAIDS from Header File. + */ + SECStatus + GetIVandCKAIDSFromHeader(const char *cipherFileName, + SECItem *ivItem, SECItem *encKeyItem, SECItem *macKeyItem) + { + SECStatus rv; + + /* Open intermediate file, read in header, get IV and CKA_IDs of two keys + * from it. + */ + rv = ReadFromHeaderFile(cipherFileName, IV, ivItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not retrieve IV from cipher file\n"); + goto cleanup; + } + + rv = ReadFromHeaderFile(cipherFileName, SYMKEY, encKeyItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not retrieve AES CKA_ID from cipher file\n"); + goto cleanup; + } + rv = ReadFromHeaderFile(cipherFileName, MACKEY, macKeyItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not retrieve MAC CKA_ID from cipher file\n"); + goto cleanup; + } + cleanup: + return rv; + } + + /* + * DecryptFile + */ + SECStatus + DecryptFile(PK11SlotInfo *slot, + const char *dbdir, + const char *outFileName, + const char *headerFileName, + char *encryptedFileName, + secuPWData *pwdata, + PRBool ascii) + { + /* + * The DB is open read only and we have authenticated to it: + * Open input file, read in header, get IV and CKA_IDs of two keys from it. + * Find those keys in the DB token. + * Open output file. + * Loop until EOF(input): + * Read a buffer of ciphertext from input file. + * Save last block of ciphertext. + * Decrypt ciphertext buffer using CBC and IV. + * Compute and check MAC, then remove MAC from plaintext. + * Replace IV with saved last block of ciphertext. + * Write the plain text to output file. + * Close files. + * Report success. + */ + + SECStatus rv; + SECItem ivItem; + SECItem encKeyItem; + SECItem macKeyItem; + SECItem cipherItem; + SECItem macItem; + SECItem padItem; + PK11SymKey *encKey = NULL; + PK11SymKey *macKey = NULL; + + + /* Open intermediate file, read in header, get IV and CKA_IDs of two keys + * from it. + */ + rv = GetIVandCKAIDSFromHeader(headerFileName, + &ivItem, &encKeyItem, &macKeyItem); + if (rv != SECSuccess) { + goto cleanup; + } + + /* Find those keys in the DB token. */ + encKey = FindKey(slot, CKM_AES_CBC, &encKeyItem, pwdata); + if (encKey == NULL) { + PR_fprintf(PR_STDERR, "Can't find the encryption key\n"); + rv = SECFailure; + goto cleanup; + } + /* CKM_MD5_HMAC or CKM_EXTRACT_KEY_FROM_KEY */ + macKey = FindKey(slot, CKM_MD5_HMAC, &macKeyItem, pwdata); + if (macKey == NULL) { + rv = SECFailure; + goto cleanup; + } + + /* Read in the Mac into item from the intermediate file. */ + rv = ReadFromHeaderFile(headerFileName, MAC, &macItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not retrieve MAC from cipher file\n"); + goto cleanup; + } + if (macItem.data == NULL) { + PR_fprintf(PR_STDERR, "MAC has NULL data\n"); + rv = SECFailure; + goto cleanup; + } + if (macItem.len == 0) { + PR_fprintf(PR_STDERR, "MAC has data has 0 length\n"); + /*rv = SECFailure; + goto cleanup;*/ + } + + rv = ReadFromHeaderFile(headerFileName, PAD, &padItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not retrieve PAD detail from header file\n"); + goto cleanup; + } + + if (rv == SECSuccess) { + /* Decrypt and Remove Mac */ + rv = DecryptAndVerifyMac(outFileName, encryptedFileName, + &cipherItem, &macItem, encKey, macKey, &ivItem, &padItem); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed while decrypting and removing MAC\n"); + } + } + + cleanup: + if (slot) { + PK11_FreeSlot(slot); + } + if (encKey) { + PK11_FreeSymKey(encKey); + } + if (macKey) { + PK11_FreeSymKey(macKey); + } + + return rv; + } + + /* + * EncryptFile + */ + SECStatus + EncryptFile(PK11SlotInfo *slot, + const char *dbdir, + const char *inFileName, + const char *headerFileName, + const char *encryptedFileName, + const char *noiseFileName, + secuPWData *pwdata, + PRBool ascii) + { + /* + * The DB is open for read/write and we have authenticated to it. + * generate a symmetric AES key as a token object. + * generate a second key to use for MACing, also a token object. + * get their CKA_IDs + * generate a random value to use as IV for AES CBC + * open an input file and an output file, + * write a header to the output that identifies the two keys by + * their CKA_IDs, May include original file name and length. + * loop until EOF(input) + * read a buffer of plaintext from input file, + * MAC it, append the MAC to the plaintext + * encrypt it using CBC, using previously created IV, + * store the last block of ciphertext as the new IV, + * write the cipher text to intermediate file + * close files + * report success + */ + SECStatus rv; + PRFileDesc *inFile; + PRFileDesc *headerFile; + PRFileDesc *encFile; + + unsigned char *encKeyId = (unsigned char *) "Encrypt Key"; + unsigned char *macKeyId = (unsigned char *) "MAC Key"; + SECItem encKeyID = { siAsciiString, encKeyId, PL_strlen(encKeyId) }; + SECItem macKeyID = { siAsciiString, macKeyId, PL_strlen(macKeyId) }; + + SECItem encCKAID; + SECItem macCKAID; + unsigned char iv[BLOCKSIZE]; + SECItem ivItem; + PK11SymKey *encKey = NULL; + PK11SymKey *macKey = NULL; + SECItem temp; + unsigned char c; + + /* Generate a symmetric AES key as a token object. */ + encKey = GenerateSYMKey(slot, CKM_AES_KEY_GEN, 128/8, &encKeyID, pwdata); + if (encKey == NULL) { + PR_fprintf(PR_STDERR, "GenerateSYMKey for AES returned NULL.\n"); + rv = SECFailure; + goto cleanup; + } + + /* Generate a second key to use for MACing, also a token object. */ + macKey = GenerateSYMKey(slot, CKM_GENERIC_SECRET_KEY_GEN, 160/8, + &macKeyID, pwdata); + if (macKey == NULL) { + PR_fprintf(PR_STDERR, "GenerateSYMKey for MACing returned NULL.\n"); + rv = SECFailure; + goto cleanup; + } + + /* Get the encrypt key CKA_ID */ + rv = GatherCKA_ID(encKey, &encCKAID); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Error while wrapping encrypt key\n"); + goto cleanup; + } + + /* Get the MAC key CKA_ID */ + rv = GatherCKA_ID(macKey, &macCKAID); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Can't get the MAC key CKA_ID.\n"); + goto cleanup; + } + + if (noiseFileName) { + rv = SeedFromNoiseFile(noiseFileName); + if (rv != SECSuccess) { + PORT_SetError(PR_END_OF_FILE_ERROR); + return SECFailure; + } + rv = PK11_GenerateRandom(iv, BLOCKSIZE); + if (rv != SECSuccess) { + goto cleanup; + } + + } else { + /* Generate a random value to use as IV for AES CBC. */ + GenerateRandom(iv, BLOCKSIZE); + } + + headerFile = PR_Open(headerFileName, + PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); + if (!headerFile) { + PR_fprintf(PR_STDERR, + "Unable to open \"%s\" for writing.\n", + headerFileName); + return SECFailure; + } + encFile = PR_Open(encryptedFileName, + PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); + if (!encFile) { + PR_fprintf(PR_STDERR, + "Unable to open \"%s\" for writing.\n", + encryptedFileName); + return SECFailure; + } + /* Write to a header file the IV and the CKA_IDs + * identifying the two keys. + */ + ivItem.type = siBuffer; + ivItem.data = iv; + ivItem.len = BLOCKSIZE; + + rv = WriteToHeaderFile(iv, BLOCKSIZE, IV, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Error writing IV to cipher file - %s\n", + headerFileName); + goto cleanup; + } + + rv = WriteToHeaderFile(encCKAID.data, encCKAID.len, SYMKEY, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Error writing AES CKA_ID to cipher file - %s\n", + encryptedFileName); + goto cleanup; + } + rv = WriteToHeaderFile(macCKAID.data, macCKAID.len, MACKEY, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Error writing MAC CKA_ID to cipher file - %s\n", + headerFileName); + goto cleanup; + } + + /* Open the input file. */ + inFile = PR_Open(inFileName, PR_RDONLY, 0); + if (!inFile) { + PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", + inFileName); + return SECFailure; + } + + /* Macing and Encryption */ + if (rv == SECSuccess) { + rv = EncryptAndMac(inFile, headerFile, encFile, + encKey, macKey, ivItem.data, ivItem.len, ascii); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed : Macing and Encryption\n"); + goto cleanup; + } + } + + cleanup: + if (inFile) { + PR_Close(inFile); + } + if (headerFile) { + PR_Close(headerFile); + } + if (encFile) { + PR_Close(encFile); + } + if (slot) { + PK11_FreeSlot(slot); + } + if (encKey) { + PK11_FreeSymKey(encKey); + } + if (macKey) { + PK11_FreeSymKey(macKey); + } + + return rv; + } + + /* + * This example illustrates basic encryption/decryption and MACing. + * Generates the encryption/mac keys and uses token for storing. + * Encrypts the input file and appends MAC before storing in intermediate + * header file. + * Writes the CKA_IDs of the encryption keys into intermediate header file. + * Reads the intermediate headerfile for CKA_IDs and encrypted + * contents and decrypts into output file. + */ + int + main(int argc, char **argv) + { + SECStatus rv; + SECStatus rvShutdown; + PK11SlotInfo *slot = NULL; + PLOptState *optstate; + PLOptStatus status; + char headerFileName[50]; + char encryptedFileName[50]; + PRFileDesc *inFile; + PRFileDesc *outFile; + PRBool ascii = PR_FALSE; + CommandType cmd = UNKNOWN; + const char *command = NULL; + const char *dbdir = NULL; + const char *inFileName = NULL; + const char *outFileName = NULL; + const char *noiseFileName = NULL; + secuPWData pwdata = { PW_NONE, 0 }; + + char * progName = strrchr(argv[0], '/'); + progName = progName ? progName + 1 : argv[0]; + + /* Parse command line arguments */ + optstate = PL_CreateOptState(argc, argv, "c:d:i:o:f:p:z:a"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + switch (optstate->option) { + case 'a': + ascii = PR_TRUE; + break; + case 'c': + command = strdup(optstate->value); + break; + case 'd': + dbdir = strdup(optstate->value); + break; + case 'f': + pwdata.source = PW_FROMFILE; + pwdata.data = strdup(optstate->value); + break; + case 'p': + pwdata.source = PW_PLAINTEXT; + pwdata.data = strdup(optstate->value); + break; + case 'i': + inFileName = strdup(optstate->value); + break; + case 'o': + outFileName = strdup(optstate->value); + break; + case 'z': + noiseFileName = strdup(optstate->value); + break; + default: + Usage(progName); + break; + } + } + PL_DestroyOptState(optstate); + + if (!command || !dbdir || !inFileName || !outFileName) + Usage(progName); + if (PL_strlen(command)==0) + Usage(progName); + + cmd = command[0] == 'a' ? ENCRYPT : command[0] == 'b' ? DECRYPT : UNKNOWN; + + /* Open the input file. */ + inFile = PR_Open(inFileName, PR_RDONLY, 0); + if (!inFile) { + PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", + inFileName); + return SECFailure; + } + PR_Close(inFile); + + /* For intermediate header file, choose filename as inputfile name + with extension ".header" */ + strcpy(headerFileName, inFileName); + strcat(headerFileName, ".header"); + + /* For intermediate encrypted file, choose filename as inputfile name + with extension ".enc" */ + strcpy(encryptedFileName, inFileName); + strcat(encryptedFileName, ".enc"); + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + switch (cmd) { + case ENCRYPT: + /* If the intermediate header file already exists, delete it. */ + if (PR_Access(headerFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { + PR_Delete(headerFileName); + } + /* If the intermediate encrypted already exists, delete it. */ + if (PR_Access(encryptedFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { + PR_Delete(encryptedFileName); + } + + /* Open DB for read/write and authenticate to it. */ + rv = NSS_InitReadWrite(dbdir); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "NSS_InitReadWrite Failed\n"); + goto cleanup; + } + + PK11_SetPasswordFunc(GetModulePassword); + slot = PK11_GetInternalKeySlot(); + if (PK11_NeedLogin(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, &pwdata); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", + PK11_GetTokenName(slot)); + goto cleanup; + } + } + rv = EncryptFile(slot, dbdir, + inFileName, headerFileName, encryptedFileName, + noiseFileName, &pwdata, ascii); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "EncryptFile : Failed\n"); + return SECFailure; + } + break; + case DECRYPT: + /* Open DB read only, authenticate to it. */ + PK11_SetPasswordFunc(GetModulePassword); + + rv = NSS_Init(dbdir); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "NSS_Init Failed\n"); + return SECFailure; + } + + slot = PK11_GetInternalKeySlot(); + if (PK11_NeedLogin(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, &pwdata); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", + PK11_GetTokenName(slot)); + goto cleanup; + } + } + + rv = DecryptFile(slot, dbdir, + outFileName, headerFileName, + encryptedFileName, &pwdata, ascii); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "DecryptFile : Failed\n"); + return SECFailure; + } + break; + } + + cleanup: + rvShutdown = NSS_Shutdown(); + if (rvShutdown != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed : NSS_Shutdown()\n"); + rv = SECFailure; + } + + PR_Cleanup(); + + return rv; + } \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/index.rst new file mode 100644 index 0000000000..88f1e7bdab --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/index.rst @@ -0,0 +1,31 @@ +.. _mozilla_projects_nss_nss_sample_code: + +NSS Sample Code +=============== + +.. container:: + + The collection of sample code here demonstrates how NSS can be used for cryptographic operations, + certificate handling, SSL, etc. It also demonstrates some best practices in the application of + cryptography. + + Old samples in the process of being replaced. + + #. :ref:`mozilla_projects_nss_sample_code_sample1` + #. :ref:`mozilla_projects_nss_sample_code_sample2` + #. :ref:`mozilla_projects_nss_sample_code_sample3` + #. :ref:`mozilla_projects_nss_sample_code_sample4` + #. :ref:`mozilla_projects_nss_sample_code_sample5` + #. :ref:`mozilla_projects_nss_sample_code_sample6` + + | + | These are very old examples in need of replacement. See + https://bugzilla.mozilla.org/show_bug.cgi?id=490238 + + You are welcome to download the new samples via: + + .. code:: sh + + hg clone https://hg.mozilla.org/projects/nss; cd nss; hg update SAMPLES_BRANCH + + The new samples: :ref:`mozilla_projects_nss_new_nss_samples` \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample1/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample1/index.rst new file mode 100644 index 0000000000..10926903af --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample1/index.rst @@ -0,0 +1,713 @@ +.. _mozilla_projects_nss_nss_sample_code_nss_sample_code_sample1: + +NSS Sample Code Sample1 +======================= + +.. _nss_sample_code_1_key_generation_and_transport_between_servers.: + +`NSS Sample Code 1: Key Generation and Transport Between Servers. <#nss_sample_code_1_key_generation_and_transport_between_servers.>`__ +--------------------------------------------------------------------------------------------------------------------------------------- + +.. container:: + + This is an example program that demonstrates how to do key generation and transport between + cooperating servers. This program shows the following: + + - RSA key pair generation + - Naming RSA key pairs + - Looking up a previously generated key pair by name + - Creating AES and MAC keys (or encryption and MAC keys in general) + - Wrapping symmetric keys using your own RSA key pair so that they can be stored on disk or in a + database. + + - As an alternative to TOKEN symmetric keys + + - As a way to store large numbers of symmetric keys + + - Wrapping symmetric keys using an RSA key from another server + - Unwrapping keys using your own RSA key pair + + | The main part of the program shows a typical sequence of events for two servers that are trying + to extablish a shared key pair. + | We will add message protection (encryption and MACing) examples to this program in the future. + +.. _sample_code: + +`Sample Code <#sample_code>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. container:: + + .. code:: + + #include + #include "pk11pub.h" + #include "keyhi.h" + #include "nss.h" + + // Key management for keys share among multiple hosts + // + // This example shows how to use NSS functions to create and + // distribute keys that need to be shared among multiple servers + // or hosts. + // + // The management scheme assumes that one host is PRIMARY. It + // generates the secret keys that will be used by all participating + // hosts. The other hosts (SECONDARY) request keys from the + // primary host. As an alternative, new keys may be sent to the + // current set of SECONDARY hosts when they are generated by the + // PRIMARY. In this case, the PRIMARY maintains a list of the + // secondary hosts. + // + // The sequence of events is: + // 1. The primary host generates a new symmetric key. This key + // may be used for an encryption mechanism (DES or AES) or for + // integrity (MD5_HMAC or SHA1_HMAC). This key needs to be + // permanent, since it may be used during several runs of the + // server. (Currently NSS doesn't store persistant keys. Steps + // 1a through 1x show how to do this). + // 1a. The primary host generates an RSA keypair that will be used + // store keys locally. + // 1b. The primary host wraps the newly generated key using the + // RSA key and stores the wrapped key data in a local file. + // 1c. The primary host unwraps the key using the RSA key each time + // access to the key is required, such as at server startup. + // 2. The secondary host generates an RSA keypair that will be used + // to transport keys between the primary host and itself. This + // key needs to exist long enough to be used to process the + // response to a key transport request that is made to the primary + // server. The example here shows how to create a permanent (token) + // RSA key for this purpose. (This key will also be used for + // storage of the keys, since NSS does not support permanent symmetric + // keys at the current time.) + // 3. The secondary host sends its RSA public key to the primary host as + // part of a request for a particular key, or to be added to a list + // of secondary hosts. + // 4. The administrator of the primary host verifies that the RSA key + // that was received belongs to a valid secondary host. The adminstrator + // may do this by checking that the key was received in a signed email + // message, or by checking a digest value with the adminstrator of the + // secondary host. [Need support for digest check values] + // 5. The primary host exports (wraps) the symmetric key using the + // secondary host's RSA key. The wrapped value is sent back to + // the secondary host. + // 6. The administrator of the secondary host verifies that the wrapped + // key data came from the primary host. The same methods outlined + // in step 4 may be used here. + // 7. The secondary host unwraps the key using its own RSA private key. + // NOTE: currently NSS does not support permanent symmetric keys. + // The secondary host may store the wrapped value that was received + // from the primary in a file, and unwrap it each time the key is required + // (such as at server startup). + + // NSS actually has some support for permanent symmetric keys. However this + // example will need to be modified somewhat in order to demonstrate it. + + // Utility function to print hex data + static void + printBuffer(unsigned char *digest, unsigned int len) + { + int i; + + cout << "length: " << len << endl; + for(i = 0;i < len;i++) printf("%02x ", digest[i]); + cout << endl; + } + + // XXX Data protection + // - takes an input buffer, applies the encryption + // and MAC, and generates a buffer with the result. + // - the application sends or uses the result (possibly + // after base64 encoding it. + + // + // Server - an instance of a server that is part of a + // cluster of servers that are sharing a common set + // of encryption and MACing keys. + // + class Server + { + public: + // Initializes the server instance. In particular, this + // creates the key pair that is used for wrapping keys + int Init(); + + // Generates keys for encryption (AES) and MACing. The + // wrapped keys are stored in data files. + int GenerateKeys(); + + // Gets the server's public key (wrapping key) to + // send to another server. This becomes the input to + // the ExportKeys method on the remote server. + int ExportPublicKey(SECItem **pubKeyData); + + // Export the encryption and key using the key + // provided. The key should come from another server + // in the cluster. (The admin should verify this.) + // + // In this example, the server must be started to perform + // this function (see Start()) + int ExportKeys(SECItem *pubKey, SECItem **wrappedEncKey, + SECItem **wrappedMacKey); + + // Import the keys received from another server in the + // cluster. The admin should make sure the keys actually + // came from the correct source. + int ImportKeys(SECItem *wrappedEncKey, SECItem *wrappedMacKey); + + // Start the server, loading the encryption and MACing keys + // from files + int Start(); + + // Shut down the server. (For completeness) + int Shutdown(); + + // Compare keys in two server instances. Use this in the + // example to make sure the keys are transferred correctly. + // This will not work in real life! + // + // The servers must be started + int CompareKeys(Server *peer); + + // Create a server - the name distiguish the keys in the + // shared database in this example + Server(const char *serverName); + ~Server(); + + private: + int getPrivateKey(SECKEYPrivateKey **prvKey); + int getPublicKey(SECKEYPublicKey **pubKey); + int wrapKey(PK11SymKey *key, SECKEYPublicKey *pubKey, SECItem **data); + + // export raw key (unwrapped) DO NOT USE + int rawExportKey(PK11SymKey *key, SECItem **data); + + char *mServerName; + + // These items represent data that might be stored + // in files or in a configuration file + SECItem *mWrappedEncKey; + SECItem *mWrappedMacKey; + + // These are the runtime keys as loaded from the files + PK11SymKey *mEncKey; + PK11SymKey *mMacKey; + }; + + Server::Server(const char *serverName) + : mServerName(0), mWrappedEncKey(0), mWrappedMacKey(0), + mEncKey(0), mMacKey(0) + { + // Copy the server name + mServerName = PL_strdup(serverName); + } + + Server::~Server() + { + if (mServerName) PL_strfree(mServerName); + if (mWrappedEncKey) SECITEM_FreeItem(mWrappedEncKey, PR_TRUE); + if (mWrappedMacKey) SECITEM_FreeItem(mWrappedMacKey, PR_TRUE); + if (mEncKey) PK11_FreeSymKey(mEncKey); + if (mMacKey) PK11_FreeSymKey(mMacKey); + } + + int + Server::Init() + { + int rv = 0; + SECKEYPrivateKey *prvKey = 0; + SECKEYPublicKey *pubKey = 0; + PK11SlotInfo *slot = 0; + PK11RSAGenParams rsaParams; + SECStatus s; + + // See if there is already a private key with this name. + // If there is one, no further action is required. + rv = getPrivateKey(&prvKey); + if (rv == 0 && prvKey) goto done; + + rv = 0; + + // These could be parameters to the Init function + rsaParams.keySizeInBits = 1024; + rsaParams.pe = 65537; + + slot = PK11_GetInternalKeySlot(); + if (!slot) { rv = 1; goto done; } + + prvKey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaParams, + &pubKey, PR_TRUE, PR_TRUE, 0); + if (!prvKey) { rv = 1; goto done; } + + // Set the nickname on the private key so that it + // can be found later. + s = PK11_SetPrivateKeyNickname(prvKey, mServerName); + if (s != SECSuccess) { rv = 1; goto done; } + + done: + if (slot) PK11_FreeSlot(slot); + if (pubKey) SECKEY_DestroyPublicKey(pubKey); + if (prvKey) SECKEY_DestroyPrivateKey(prvKey); + + return rv; + } + + int + Server::GenerateKeys() + { + int rv = 0; + SECKEYPublicKey *pubKey = 0; + PK11SlotInfo *slot = 0; + + // Choose a slot to use + slot = PK11_GetInternalKeySlot(); + if (!slot) { rv = 1; goto done; } + + // Get our own public key to use for wrapping + rv = getPublicKey(&pubKey); + if (rv) goto done; + + // Do the Encryption (AES) key + if (!mWrappedEncKey) + { + PK11SymKey *key = 0; + + // The key size is 128 bits (16 bytes) + key = PK11_KeyGen(slot, CKM_AES_KEY_GEN, 0, 128/8, 0); + if (!key) { rv = 1; goto aes_done; } + + rv = wrapKey(key, pubKey, &mWrappedEncKey); + + aes_done: + if (key) PK11_FreeSymKey(key); + + if (rv) goto done; + } + + // Do the Mac key + if (!mWrappedMacKey) + { + PK11SymKey *key = 0; + + // The key size is 160 bits (20 bytes) + key = PK11_KeyGen(slot, CKM_GENERIC_SECRET_KEY_GEN, 0, 160/8, 0); + if (!key) { rv = 1; goto mac_done; } + + rv = wrapKey(key, pubKey, &mWrappedMacKey); + + mac_done: + if (key) PK11_FreeSymKey(key); + } + + done: + if (slot) PK11_FreeSlot(slot); + + return rv; + } + + int + Server::ExportPublicKey(SECItem **pubKeyData) + { + int rv = 0; + SECKEYPublicKey *pubKey = 0; + + rv = getPublicKey(&pubKey); + if (rv) goto done; + + *pubKeyData = SECKEY_EncodeDERSubjectPublicKeyInfo(pubKey); + if (!*pubKeyData) { rv = 1; goto done; } + + done: + if (pubKey) SECKEY_DestroyPublicKey(pubKey); + + return rv; + } + + int + Server::ExportKeys(SECItem *pubKeyData, SECItem **wrappedEncKey, + SECItem **wrappedMacKey) + { + int rv; + CERTSubjectPublicKeyInfo *keyInfo = 0; + SECKEYPublicKey *pubKey = 0; + SECItem *data = 0; + + // Make sure the keys are available (server running) + if (!mEncKey || !mMacKey) { rv = 1; goto done; } + + // Import the public key of the other server + keyInfo = SECKEY_DecodeDERSubjectPublicKeyInfo(pubKeyData); + if (!keyInfo) { rv = 1; goto done; } + + pubKey = SECKEY_ExtractPublicKey(keyInfo); + if (!pubKey) { rv = 1; goto done; } + + // Export the encryption key + rv = wrapKey(mEncKey, pubKey, &data); + if (rv) goto done; + + // Export the MAC key + rv = wrapKey(mMacKey, pubKey, wrappedMacKey); + if (rv) goto done; + + // Commit the rest of the operation + *wrappedEncKey = data; + data = 0; + + done: + if (data) SECITEM_FreeItem(data, PR_TRUE); + if (pubKey) SECKEY_DestroyPublicKey(pubKey); + if (keyInfo) SECKEY_DestroySubjectPublicKeyInfo(keyInfo); + + return rv; + } + + int + Server::ImportKeys(SECItem *wrappedEncKey, SECItem *wrappedMacKey) + { + int rv = 0; + + if (mWrappedEncKey || mWrappedMacKey) { rv = 1; goto done; } + + mWrappedEncKey = SECITEM_DupItem(wrappedEncKey); + if (!mWrappedEncKey) { rv = 1; goto done; } + + mWrappedMacKey = SECITEM_DupItem(wrappedMacKey); + if (!mWrappedMacKey) { rv = 1; goto done; } + + done: + return rv; + } + + int + Server::Start() + { + int rv; + SECKEYPrivateKey *prvKey = 0; + + rv = getPrivateKey(&prvKey); + if (rv) goto done; + + if (!mEncKey) + { + // Unwrap the encryption key from the "file" + // This function uses a mechanism rather than a key type + // Does this need to be "WithFlags"?? + mEncKey = PK11_PubUnwrapSymKey(prvKey, mWrappedEncKey, + CKM_AES_CBC_PAD, CKA_ENCRYPT, 0); + if (!mEncKey) { rv = 1; goto done; } + } + + if (!mMacKey) + { + // Unwrap the MAC key from the "file" + // This function uses a mechanism rather than a key type + // Does this need to be "WithFlags"?? + mMacKey = PK11_PubUnwrapSymKey(prvKey, mWrappedMacKey, + CKM_MD5_HMAC, CKA_SIGN, 0); + if (!mMacKey) { rv = 1; goto done; } + } + + done: + if (prvKey) SECKEY_DestroyPrivateKey(prvKey); + + return rv; + } + + int + Server::Shutdown() + { + if (mEncKey) PK11_FreeSymKey(mEncKey); + if (mMacKey) PK11_FreeSymKey(mMacKey); + + mEncKey = 0; + mMacKey = 0; + + return 0; + } + + int + Server::CompareKeys(Server *peer) + { + int rv; + SECItem *macKey1 = 0; + SECItem *macKey2 = 0; + SECItem *encKey1 = 0; + SECItem *encKey2 = 0; + + // Export each of the keys in raw form + rv = rawExportKey(mMacKey, &macKey1); + if (rv) goto done; + + rv = rawExportKey(peer->mMacKey, &macKey2); + if (rv) goto done; + + rv = rawExportKey(mEncKey, &encKey1); + if (rv) goto done; + + rv = rawExportKey(peer->mEncKey, &encKey2); + if (rv) goto done; + + if (!SECITEM_ItemsAreEqual(macKey1, macKey2)) { rv = 1; goto done; } + if (!SECITEM_ItemsAreEqual(encKey1, encKey2)) { rv = 1; goto done; } + + done: + if (macKey1) SECITEM_ZfreeItem(macKey1, PR_TRUE); + if (macKey2) SECITEM_ZfreeItem(macKey2, PR_TRUE); + if (encKey1) SECITEM_ZfreeItem(encKey1, PR_TRUE); + if (encKey2) SECITEM_ZfreeItem(encKey2, PR_TRUE); + + return rv; + } + + // Private helper, retrieves the private key for the server + // from the database. Free the key using SECKEY_DestroyPrivateKey + int + Server::getPrivateKey(SECKEYPrivateKey **prvKey) + { + int rv = 0; + PK11SlotInfo *slot = 0; + SECKEYPrivateKeyList *list = 0; + SECKEYPrivateKeyListNode *n; + char *nickname; + + slot = PK11_GetInternalKeySlot(); + if (!slot) goto done; + + // ListPrivKeysInSlot looks like it should check the + // nickname and only return keys that match. However, + // that doesn't seem to work at the moment. + // BUG: XXXXX + list = PK11_ListPrivKeysInSlot(slot, mServerName, 0); + cout << "getPrivateKey: list = " << list << endl; + if (!list) { rv = 1; goto done; } + + for(n = PRIVKEY_LIST_HEAD(list); + !PRIVKEY_LIST_END(n, list); + n = PRIVKEY_LIST_NEXT(n)) + { + nickname = PK11_GetPrivateKeyNickname(n->key); + if (PL_strcmp(nickname, mServerName) == 0) break; + } + if (PRIVKEY_LIST_END(n, list)) { rv = 1; goto done; } + + *prvKey = SECKEY_CopyPrivateKey(n->key); + + done: + if (list) SECKEY_DestroyPrivateKeyList(list); + + return rv; + } + + int + Server::getPublicKey(SECKEYPublicKey **pubKey) + { + int rv; + SECKEYPrivateKey *prvKey = 0; + + rv = getPrivateKey(&prvKey); + if (rv) goto done; + + *pubKey = SECKEY_ConvertToPublicKey(prvKey); + if (!*pubKey) { rv = 1; goto done; } + + done: + if (prvKey) SECKEY_DestroyPrivateKey(prvKey); + + return rv; + } + + int + Server::wrapKey(PK11SymKey *key, SECKEYPublicKey *pubKey, SECItem **ret) + { + int rv = 0; + SECItem *data; + SECStatus s; + + data = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); + if (!data) { rv = 1; goto done; } + + // Allocate space for output of wrap + data->len = SECKEY_PublicKeyStrength(pubKey); + data->data = new unsigned char[data->len]; + if (!data->data) { rv = 1; goto done; } + + s = PK11_PubWrapSymKey(CKM_RSA_PKCS, pubKey, key, data); + if (s != SECSuccess) { rv = 1; goto done; } + + *ret = data; + data = 0; + + done: + if (data) SECITEM_FreeItem(data, PR_TRUE); + + return rv; + } + + // Example of how to do a raw export (no wrapping of a key) + // This should not be used. Use the RSA-based wrapping + // methods instead. + int + Server::rawExportKey(PK11SymKey *key, SECItem **res) + { + int rv = 0; + SECItem *data; + SECStatus s; + + s = PK11_ExtractKeyValue(key); + if (s != SECSuccess) { rv = 1; goto done; } + + data = PK11_GetKeyData(key); + + *res = SECITEM_DupItem(data); + if (!*res) { rv = 1; goto done; } + + done: + return rv; + } + + // Initialize the NSS library. Normally this + // would be done as part of each server's startup. + // However, this example uses the same databases + // to store keys for server in the "cluster" so + // it is done once. + int + InitNSS() + { + int rv = 0; + SECStatus s; + + s = NSS_InitReadWrite("."); + if (s != SECSuccess) rv = 1; // Error + + // For this example, we don't use database passwords + PK11_InitPin(PK11_GetInternalKeySlot(), "", ""); + + return rv; + } + + int + main(int argc, char *argv[]) + { + int rv; + Server *server1 = 0; + Server *server2 = 0; + + // Initialize NSS + rv = InitNSS(); + if (rv) { cout << "InitNSS failed" << endl; goto done; } + + // Create the first "server" + server1 = new Server("Server1"); + if (!server1 || server1->Init()) + { + cout << "Server1 could not be created" << endl; + rv = 1; + goto done; + } + + // Generate encryption and mac keys. These keys will + // be used by all the servers in the cluster. + rv = server1->GenerateKeys(); + if (rv) { cout << "GenerateKeys failed" << endl; goto done; } + + // Now that everything is ready, start server1. This loads + // the encryption and MAC keys from the "files" + rv = server1->Start(); + if (rv) { cout << "Cannot start server 1" << endl; goto done; } + + // Create a second server in the cluster. We will need + // to transfer the keys from the first server to this + // one + server2 = new Server("Server2"); + if (!server2 || server2->Init()) + { + cout << "Server2 could not be created" << endl; + rv = 1; // Error + goto done; + } + + // Transfer the keys from server1 + { + SECItem *wrappedEncKey = 0; + SECItem *wrappedMacKey = 0; + SECItem *pubKeyData = 0; + + // Get the public key for server 2 so that it can + // be sent to server 1 + rv = server2->ExportPublicKey(&pubKeyData); + if (rv) { cout << "ExportPublicKey failed" << endl; goto trans_done; } + + // Send the public key to server 1 and get back the + // wrapped key values + rv = server1->ExportKeys(pubKeyData, &wrappedEncKey, &wrappedMacKey); + if (rv) { cout << "ExportKeys failed" << endl; goto trans_done; } + + // Print - for information + cout << "Wrapped Encryption Key" << endl; + printBuffer(wrappedEncKey->data, wrappedEncKey->len); + cout << "Wrapped MAC Key" << endl; + printBuffer(wrappedMacKey->data, wrappedMacKey->len); + + // Import the keys into server 2 - this just puts the wrapped + // values into the "files" + rv = server2->ImportKeys(wrappedEncKey, wrappedMacKey); + if (rv) { cout << "ImportKeys failed" << endl; goto trans_done; } + + trans_done: + if (wrappedEncKey) SECITEM_FreeItem(wrappedEncKey, PR_TRUE); + if (wrappedMacKey) SECITEM_FreeItem(wrappedMacKey, PR_TRUE); + if (pubKeyData) SECITEM_FreeItem(pubKeyData, PR_TRUE); + } + if (rv) goto done; + + // Start server 2 - this unwraps the encryption and MAC keys + // so that they can be used + rv = server2->Start(); + if (rv) { cout << "Cannot start server 2" << endl; goto done; } + + // List keys in the token - informational + { + PK11SlotInfo *slot = 0; + SECKEYPrivateKeyList *list = 0; + SECKEYPrivateKeyListNode *n; + + slot = PK11_GetInternalKeySlot(); + if (!slot) goto list_done; + + cout << "List Private Keys" << endl; + + list = PK11_ListPrivKeysInSlot(slot, 0, 0); + if (!list) goto list_done; + + for(n = PRIVKEY_LIST_HEAD(list); + !PRIVKEY_LIST_END(n, list); + n = PRIVKEY_LIST_NEXT(n)) + { + char *name; + + name = PK11_GetPrivateKeyNickname(n->key); + cout << "Key: " << name << endl; + } + list_done: + if (slot) PK11_FreeSlot(slot); + if (list) SECKEY_DestroyPrivateKeyList(list); + + cout << "Done" << endl; + } + + // Let's see if the keys are the same + rv = server1->CompareKeys(server2); + if (rv) { cout << "Key Comparison failed" << endl; } + + server1->Shutdown(); + server2->Shutdown(); + + done: + if (server1) delete server1; + if (server2) delete server2; + + NSS_Shutdown(); + + return rv; + } \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample2/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample2/index.rst new file mode 100644 index 0000000000..76f6c21d8f --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample2/index.rst @@ -0,0 +1,166 @@ +.. _mozilla_projects_nss_nss_sample_code_nss_sample_code_sample2: + +NSS Sample Code sample2 +======================= + +.. _nss_sample_code_2_symmetric_encryption: + +`NSS Sample Code 2: Symmetric Encryption <#nss_sample_code_2_symmetric_encryption>`__ +------------------------------------------------------------------------------------- + +.. container:: + + .. code:: + + /* Example code to illustrate DES enccryption/decryption using NSS. + * The example skips the details of obtaining the Key & IV to use, and + * just uses a hardcoded Key & IV. + * Note: IV is only needed if Cipher Blocking Chaining (CBC) mode of encryption + * is used + * + * The recommended approach is to store and transport WRAPPED (encrypted) + * DES Keys (IVs can be in the clear). However, it is a common (and dangerous) + * practice to use raw DES Keys. This example shows the use of a RAW key. + */ + + + #include "nss.h" + #include "pk11pub.h" + + /* example Key & IV */ + unsigned char gKey[] = {0xe8, 0xa7, 0x7c, 0xe2, 0x05, 0x63, 0x6a, 0x31}; + unsigned char gIV[] = {0xe4, 0xbb, 0x3b, 0xd3, 0xc3, 0x71, 0x2e, 0x58}; + + int main(int argc, char **argv) + { + CK_MECHANISM_TYPE cipherMech; + PK11SlotInfo* slot = NULL; + PK11SymKey* SymKey = NULL; + SECItem* SecParam = NULL; + PK11Context* EncContext = NULL; + SECItem keyItem, ivItem; + SECStatus rv, rv1, rv2; + unsigned char data[1024], buf1[1024], buf2[1024]; + int i, result_len, tmp1_outlen, tmp2_outlen; + + /* Initialize NSS + * If your application code has already initialized NSS, you can skip it + * here. + * This code uses the simplest of the Init functions, which does not + * require a NSS database to exist + */ + rv = NSS_NoDB_Init("."); + if (rv != SECSuccess) + { + fprintf(stderr, "NSS initialization failed (err %d)\n", + PR_GetError()); + goto out; + } + + /* choose mechanism: CKM_DES_CBC_PAD, CKM_DES3_ECB, CKM_DES3_CBC..... + * Note that some mechanisms (*_PAD) imply the padding is handled for you + * by NSS. If you choose something else, then data padding is the + * application's responsibility + */ + cipherMech = CKM_DES_CBC_PAD; + slot = PK11_GetBestSlot(cipherMech, NULL); + /* slot = PK11_GetInternalKeySlot(); is a simpler alternative but in + * theory, it *may not* return the optimal slot for the operation. For + * DES ops, Internal slot is typically the best slot + */ + if (slot == NULL) + { + fprintf(stderr, "Unable to find security device (err %d)\n", + PR_GetError()); + goto out; + } + + /* NSS passes blobs around as SECItems. These contain a pointer to + * data and a length. Turn the raw key into a SECItem. */ + keyItem.type = siBuffer; + keyItem.data = gKey; + keyItem.len = sizeof(gKey); + + /* Turn the raw key into a key object. We use PK11_OriginUnwrap + * to indicate the key was unwrapped - which is what should be done + * normally anyway - using raw keys isn't a good idea */ + SymKey = PK11_ImportSymKey(slot, cipherMech, PK11_OriginUnwrap, CKA_ENCRYPT, + &keyItem, NULL); + if (SymKey == NULL) + { + fprintf(stderr, "Failure to import key into NSS (err %d)\n", + PR_GetError()); + goto out; + } + + /* set up the PKCS11 encryption parameters. + * when not using CBC mode, ivItem.data and ivItem.len can be 0, or you + * can simply pass NULL for the iv parameter in PK11_ParamFromIV func + */ + ivItem.type = siBuffer; + ivItem.data = gIV; + ivItem.len = sizeof(gIV); + SecParam = PK11_ParamFromIV(cipherMech, &ivItem); + if (SecParam == NULL) + { + fprintf(stderr, "Failure to set up PKCS11 param (err %d)\n", + PR_GetError()); + goto out; + } + + /* sample data we'll encrypt and decrypt */ + strcpy(data, "Encrypt me!"); + fprintf(stderr, "Clear Data: %s\n", data); + + /* ========================= START SECTION ============================= */ + /* If using the same key and iv over and over, stuff before this */ + /* section and after this section needs to be done only ONCE */ + + /* ENCRYPT data into buf1. buf1 len must be atleast (data len + 8) */ + tmp1_outlen = tmp2_outlen = 0; + + /* Create cipher context */ + EncContext = PK11_CreateContextBySymKey(cipherMech, CKA_ENCRYPT, + SymKey, SecParam); + rv1 = PK11_CipherOp(EncContext, buf1, &tmp1_outlen, sizeof(buf1), + data, strlen(data)+1); + rv2 = PK11_DigestFinal(EncContext, buf1+tmp1_outlen, &tmp2_outlen, + sizeof(buf1)-tmp1_outlen); + PK11_DestroyContext(EncContext, PR_TRUE); + result_len = tmp1_outlen + tmp2_outlen; + if (rv1 != SECSuccess || rv2 != SECSuccess) + goto out; + + fprintf(stderr, "Encrypted Data: "); + for (i=0; i`__ +------------------------------------------------------------ + +.. container:: + + .. code:: + + /* + * Demonstration program for hashing and MACs + */ + + #include + + #include "pk11pub.h" + #include "nss.h" + + static void + printDigest(unsigned char *digest, unsigned int len) + { + int i; + + cout << "length: " << len << endl; + for(i = 0;i < len;i++) printf("%02x ", digest[i]); + cout << endl; + } + + /* + * main + */ + int + main(int argc, const char *argv[]) + { + int status = 0; + PK11SlotInfo *slot = 0; + PK11SymKey *key = 0; + PK11Context *context = 0; + unsigned char data[80]; + unsigned char digest[20]; /*Is there a way to tell how large the output is?*/ + unsigned int len; + SECStatus s; + + /* Initialize NSS + * If your application code has already initialized NSS, you can skip it + * here. + * This code uses the simplest of the Init functions, which does not + * require a NSS database to exist + */ + NSS_NoDB_Init("."); + + /* Get a slot to use for the crypto operations */ + slot = PK11_GetInternalKeySlot(); + if (!slot) + { + cout << "GetInternalKeySlot failed" << endl; + status = 1; + goto done; + } + + /* + * Part 1 - Simple hashing + */ + cout << "Part 1 -- Simple hashing" << endl; + + /* Initialize data */ + memset(data, 0xbc, sizeof data); + + /* Create a context for hashing (digesting) */ + context = PK11_CreateDigestContext(SEC_OID_MD5); + if (!context) { cout << "CreateDigestContext failed" << endl; goto done; } + + s = PK11_DigestBegin(context); + if (s != SECSuccess) { cout << "DigestBegin failed" << endl; goto done; } + + s = PK11_DigestOp(context, data, sizeof data); + if (s != SECSuccess) { cout << "DigestUpdate failed" << endl; goto done; } + + s = PK11_DigestFinal(context, digest, &len, sizeof digest); + if (s != SECSuccess) { cout << "DigestFinal failed" << endl; goto done; } + + /* Print digest */ + printDigest(digest, len); + + PK11_DestroyContext(context, PR_TRUE); + context = 0; + + /* + * Part 2 - Hashing with included secret key + */ + cout << "Part 2 -- Hashing with included secret key" << endl; + + /* Initialize data */ + memset(data, 0xbc, sizeof data); + + /* Create a Key */ + key = PK11_KeyGen(slot, CKM_GENERIC_SECRET_KEY_GEN, 0, 128, 0); + if (!key) { cout << "Create key failed" << endl; goto done; } + + cout << (void *)key << endl; + + /* Create parameters for crypto context */ + /* NOTE: params must be provided, but may be empty */ + SECItem noParams; + noParams.type = siBuffer; + noParams.data = 0; + noParams.len = 0; + + /* Create context using the same slot as the key */ + // context = PK11_CreateDigestContext(SEC_OID_MD5); + context = PK11_CreateContextBySymKey(CKM_MD5, CKA_DIGEST, key, &noParams); + if (!context) { cout << "CreateDigestContext failed" << endl; goto done; } + + s = PK11_DigestBegin(context); + if (s != SECSuccess) { cout << "DigestBegin failed" << endl; goto done; } + + s = PK11_DigestKey(context, key); + if (s != SECSuccess) { cout << "DigestKey failed" << endl; goto done; } + + s = PK11_DigestOp(context, data, sizeof data); + if (s != SECSuccess) { cout << "DigestUpdate failed" << endl; goto done; } + + s = PK11_DigestFinal(context, digest, &len, sizeof digest); + if (s != SECSuccess) { cout << "DigestFinal failed" << endl; goto done; } + + /* Print digest */ + printDigest(digest, len); + + PK11_DestroyContext(context, PR_TRUE); + context = 0; + + /* + * Part 3 - MAC (with secret key) + */ + cout << "Part 3 -- MAC (with secret key)" << endl; + + /* Initialize data */ + memset(data, 0xbc, sizeof data); + + context = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, key, &noParams); + if (!context) { cout << "CreateContextBySymKey failed" << endl; goto done; } + + s = PK11_DigestBegin(context); + if (s != SECSuccess) { cout << "DigestBegin failed" << endl; goto done; } + + s = PK11_DigestOp(context, data, sizeof data); + if (s != SECSuccess) { cout << "DigestOp failed" << endl; goto done; } + + s = PK11_DigestFinal(context, digest, &len, sizeof digest); + if (s != SECSuccess) { cout << "DigestFinal failed" << endl; goto done; } + + /* Print digest */ + printDigest(digest, len); + + PK11_DestroyContext(context, PR_TRUE); + context = 0; + + done: + if (context) PK11_DestroyContext(context, PR_TRUE); /* freeit ?? */ + if (key) PK11_FreeSymKey(key); + if (slot) PK11_FreeSlot(slot); + + return status; + } \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample4/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample4/index.rst new file mode 100644 index 0000000000..1d5cba11ce --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample4/index.rst @@ -0,0 +1,158 @@ +.. _mozilla_projects_nss_nss_sample_code_nss_sample_code_sample4: + +NSS Sample Code sample4 +======================= + +.. _nss_sample_code_4_pki_encryption: + +`NSS Sample Code 4: PKI Encryption <#nss_sample_code_4_pki_encryption>`__ +------------------------------------------------------------------------- + +.. container:: + + .. code:: + + /* Example code to illustrate PKI crypto ops (encrypt with public key, + * decrypt with private key) + * + * Code assumes that you have set up a NSS database with a certificate + * and a private key. The db password should be "test" and the cert + * nickname should be "TestCA" + * Here is one way of doing it: + * # create CA cert db, if -f not provided, prompts for passwd + * $ certutil -N -d . + * # create CA cert, self-signed, generates key-pair, prompts for key + * # type, cert type etc + * # answers for prompts: 5,9,n,y,-1,n,5,6,7,9,n + * $ certutil -S -s "CN=Test CA, O=BOGUS Inc, L=Mtn View, ST=CA, C=US" \ + * -n TestCA -t CTu,CTu,CTu -v 60 -x -d . -1 -2 -5 + * + * There are many ways to setup a public/private key to use - this + * example shows one of them. + * + * This example does not do any padding. It simply encrypts/decrypts a block + * of length equal to modulus length of the public/private key. + */ + + + #include "nss.h" + #include "pk11pub.h" + + + /* this callback is responsible for returning the password to the NSS + * key database. for example purposes, this function hardcodes the password. + * In a real app, this function should obtain the password using secure means + * such as prompting an operator, or retrieving it over a secure communication + * channel + */ + char *passwdcb(PK11SlotInfo *info, PRBool retry, void *arg); + + + int main(int argc, char **argv) + { + SECStatus rv; + CERTCertificate *cert = NULL; + SECKEYPublicKey *pubkey = NULL; + SECKEYPrivateKey *pvtkey = NULL; + int modulus_len, i, outlen; + char *buf1 = NULL; + char *buf2 = NULL; + + /* Initialize NSS */ + PK11_SetPasswordFunc(passwdcb); + rv = NSS_Init("."); + if (rv != SECSuccess) + { + fprintf(stderr, "NSS initialization failed (err %d)\n", + PR_GetError()); + goto cleanup; + } + + cert = PK11_FindCertFromNickname("TestCA", NULL); + if (cert == NULL) + { + fprintf(stderr, "Couldn't find cert TestCA in NSS db (err %d)\n", + PR_GetError()); + goto cleanup; + } + + pubkey = CERT_ExtractPublicKey(cert); + if (pubkey == NULL) + { + fprintf(stderr, "Couldn't extract public key from cert TestCA (err %d)\n", + PR_GetError()); + goto cleanup; + } + + modulus_len = SECKEY_PublicKeyStrength(pubkey); + fprintf(stderr, "Public Key Modulus %d bytes\n", modulus_len); + buf1 = (char *)malloc(modulus_len); + buf2 = (char *)malloc(modulus_len); + + /* initialize buf1 */ + for (i=0;i`__ +------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +.. container:: + + .. code:: + + /* Example code to illustrate PKI crypto ops (encrypt with public key, + * decrypt with private key) + * + * No NSS db needed. The Public Key & Private Key to use are + * sourced from a base64-encoded DER SubjectPublicKeyInfo structure, + * and a base64-encoded DER PrivateKeyInfo structure. + * + * There is no attempt to link the public & private key together + * + * This example does not do any padding. It simply encrypts/decrypts a block + * of length equal to modulus length of the public/private key. + */ + + + #include "nss.h" + #include "pk11pub.h" + + #define BASE64_ENCODED_SUBJECTPUBLICKEYINFO "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL3F6TIc3JEYsugo+a2fPU3W+Epv/FeIX21DC86WYnpFtW4srFtz2oNUzyLUzDHZdb+k//8dcT3IAOzUUi3R2eMCAwEAAQ==" + + #define BASE64_ENCODED_PRIVATEKEYINFO "MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAvcXpMhzckRiy6Cj5rZ89Tdb4Sm/8V4hfbUMLzpZiekW1biysW3Pag1TPItTMMdl1v6T//x1xPcgA7NRSLdHZ4wIDAQABAkEAjh8+4qncwcmGivnM6ytbpQT+k/jEOeXG2bQhjojvnXN3FazGCEFXvpuIBcJVfaIJS9YBCMOzzrAtO0+k2hWnOQIhAOC4NVbo8FQhZS4yXM1M86kMl47FA9ui//OUfbhlAdw1AiEA2DBmIXnsboKB+OHver69p0gNeWlvcJc9bjDVfdLVsLcCIQCPtV3vGYJv2vdwxqZQaHC+YB4gIGAqOqBCbmjD3lyFLQIgA+VTYdUNoqwtZWvE4gRf7IzK2V5CCNhg3gR5RGwxN58CIGCcafoRrUKsM66ISg0ITI04G9V/w+wMx91wjEEB+QBz" + + + int main(int argc, char **argv) + { + SECStatus rv; + CERTCertificate *cert = NULL; + SECKEYPublicKey *pubkey = NULL; + CERTSubjectPublicKeyInfo *spki = NULL; + SECKEYPrivateKey *pvtkey = NULL; + int modulus_len, i, outlen; + char *buf1 = NULL; + char *buf2 = NULL; + char *pubkstr = BASE64_ENCODED_SUBJECTPUBLICKEYINFO; + char *pvtkstr = BASE64_ENCODED_PRIVATEKEYINFO; + SECItem der; + SECItem nickname; + PK11SlotInfo *slot = NULL; + + /* Initialize NSS + * You need to explicitly authenticate to the internal token if you use + * NSS_Init insteadf of NSS_NoDB_Init + * Invoke this after getting the internal token handle + * PK11_Authenticate(slot, PR_FALSE, NULL); + */ + rv = NSS_NoDB_Init("."); + if (rv != SECSuccess) + { + fprintf(stderr, "NSS initialization failed (err %d)\n", + PR_GetError()); + goto cleanup; + } + + /* get internal slot */ + slot = PK11_GetInternalKeySlot(); + if (slot == NULL) + { + fprintf(stderr, "Couldn't find slot (err %d)\n", PR_GetError()); + goto cleanup; + } + + rv = ATOB_ConvertAsciiToItem(&der, pubkstr); + if (rv!= SECSuccess) + { + fprintf(stderr, "ATOB_ConvertAsciiToItem failed %d\n", PR_GetError()); + goto cleanup; + } + spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&der); + SECITEM_FreeItem(&der, PR_FALSE); + pubkey = SECKEY_ExtractPublicKey(spki); + + if (pubkey == NULL) + { + fprintf(stderr, "Couldn't extract public key (err %d)\n", PR_GetError()); + goto cleanup; + } + + modulus_len = SECKEY_PublicKeyStrength(pubkey); + fprintf(stderr, "Public Key Modulus %d bytes\n", modulus_len); + buf1 = (char *)malloc(modulus_len); + buf2 = (char *)malloc(modulus_len); + + /* initialize buf1 */ + for (i=0;i`__ +------------------------------------------------------------------------------------------------------------------------------- + +.. container:: + + .. code:: + + /* Example code to illustrate generation of a secret symmetric key ring + * that PERSISTS in the NSS database. The symmetric keys can then be used + * without ever exposing them in the clear. + * + * To encrypt, you need the id of the key to use. + * To decrypt, you need the ciphertext and the id of the key that was used + * to encrypt + * + * Before running this example, create the NSS database + * certutil -N -d . + * (enter "test" when prompted for password) + */ + + + #include "nss.h" + #include "pk11pub.h" + + /* the key id can be any sequence of bytes. this example happens to use an + * integer */ + void genkey(int id); + + /* this callback is responsible for returning the password to the NSS + * key database. for example purposes, this function hardcodes the password. + * In a real app, this function should obtain the password using secure means + * such as prompting an operator, or retrieving it over a secure communication + * channel + */ + char *passwdcb(PK11SlotInfo *info, PRBool retry, void *arg); + + + int main(int argc, char **argv) + { + SECStatus rv; + + /* Initialize NSS */ + PK11_SetPasswordFunc(passwdcb); + + /* The NSS db must be initialized read-write since we'll be creating + * keys in it. Once keys are generated, it can be opened without read-write + * subsequently (NSS_Init). + */ + rv = NSS_InitReadWrite("."); + if (rv != SECSuccess) + { + fprintf(stderr, "NSS initialization failed (err %d)\n", + PR_GetError()); + exit(1); + } + + /* generate a key with id 1. should succeed on first run on a fresh db, + * should fail on successive runs because key with that id already exists */ + genkey(1); + + /* generate a key with id 2. should succeed on first run on a fresh db, + * should fail on successive runs because key with that id already exists */ + genkey(2); + + /* generate a key with id 1 - this will fail because key with that id + * already exists */ + genkey(1); + } + + + void genkey(int id) + { + PK11SlotInfo* slot = NULL; + PK11SymKey* key = NULL; + SECItem keyiditem; + int keyid[1]; + CK_MECHANISM_TYPE cipherMech; + + /* using CKM_AES_CBC_PAD mechanism for example */ + cipherMech = CKM_AES_CBC_PAD; + + slot = PK11_GetInternalKeySlot(); + /* slot = PK11_GetBestSlot(cipherMech, NULL); didn't work. + * Error code: token is read-only. ?? + */ + if (slot == NULL) + { + fprintf(stderr, "Unable to find security device (err %d)\n", + PR_GetError()); + return; + } + + keyid[0] = id; + keyiditem.type = siBuffer; + keyiditem.data = (void *)keyid; + keyiditem.len = sizeof(keyid[0]); + + /* Note: keysize must be 0 for fixed key-length algorithms like DES. + * Since we're using AES in this example, we're specifying + * one of the valid keysizes (16, 24, 32) + */ + key = PK11_TokenKeyGen(slot, cipherMech, 0, 32 /*keysize*/, + &keyiditem, PR_TRUE, 0); + if (key == NULL) + { + fprintf(stderr, "PK11_TokenKeyGen failed (err %d)\n", + PR_GetError()); + PK11_FreeSlot(slot); + return; + } + + fprintf(stderr, "key length of generated key is %d\n", + PK11_GetKeyLength(key)); + fprintf(stderr, "mechanism of key is %d (asked for %d)\n", + PK11_GetMechanism(key), cipherMech); + + PK11_FreeSymKey(key); + + + key = PK11_FindFixedKey(slot, cipherMech, &keyiditem, 0); + if (key == NULL) + { + fprintf(stderr, "PK11_FindFixedKey failed (err %d)\n", + PR_GetError()); + PK11_FreeSlot(slot); + return; + } + + fprintf(stderr, "Found key!\n"); + fprintf(stderr, "key length of generated key is %d\n", + PK11_GetKeyLength(key)); + fprintf(stderr, "mechanism of key is %d (asked for %d)\n", + PK11_GetMechanism(key), cipherMech); + + PK11_FreeSymKey(key); + + PK11_FreeSlot(slot); + } + + char *passwdcb(PK11SlotInfo *info, PRBool retry, void *arg) + { + if (!retry) + return PL_strdup("test"); + else + return NULL; + } \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample_1_hashing/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample_1_hashing/index.rst new file mode 100644 index 0000000000..5ea9c8cadd --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample_1_hashing/index.rst @@ -0,0 +1,253 @@ +.. _mozilla_projects_nss_nss_sample_code_nss_sample_code_sample_1_hashing: + +NSS Sample Code Sample_1_Hashing +================================ + +.. _nss_sample_code_1_hashing_a_file.: + +`NSS Sample Code 1: Hashing a file. <#nss_sample_code_1_hashing_a_file.>`__ +--------------------------------------------------------------------------- + +.. container:: + + This is an example program that demonstrates how to compute the hash of a file and save it to + another file. This program illustrates the use of NSS message APIs. + +.. _sample_code_1: + +`Sample Code 1 <#sample_code_1>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. container:: + + .. code:: c + + /* NSPR Headers */ + #include + #include + #include + #include + + /* NSS headers */ + #include + #include + #include + + typedef struct { + const char *hashName; + SECOidTag oid; + } NameTagPair; + + /* The hash algorithms supported */ + static const NameTagPair HASH_NAMES[] = { + { "MD2", SEC_OID_MD2 }, + { "MD5", SEC_OID_MD5 }, + { "SHA1", SEC_OID_SHA1 }, + { "SHA256", SEC_OID_SHA256 }, + { "SHA384", SEC_OID_SHA384 }, + { "SHA512", SEC_OID_SHA512 } + }; + + /* + * Maps a hash name to a SECOidTag. + * Returns NULL if the name is not a supported algorithm + */ + static SECOidTag HashNameToOIDTag(const char *hashName) + { + int i, nhashes = sizeof(HASH_NAMES); + SECOidTag hashtag = SEC_OID_UNKNOWN; + + for (i = 0; i < nhashes; i++) { + if (PORT_Strcasecmp(hashName, HASH_NAMES[i].hashName) == 0) { + hashtag = HASH_NAMES[i].oid; + break; + } + } + return hashtag; + } + + /* + * Newline + */ + static void Newline(PRFileDesc* out) { PR_fprintf(out, "\n"); } + + /* + * PrintAsHex + */ + void PrintAsHex(PRFileDesc* out, unsigned char *data, unsigned int len) + { + unsigned i; + int column; + unsigned int limit = 15; + unsigned int level = 1; + + column = level; + if (!len) { + PR_fprintf(out, "(empty)\n"); + return; + } + + for (i = 0; i < len; i++) { + if (i != len - 1) { + PR_fprintf(out, "%02x:", data[i]); + column += 3; + } else { + PR_fprintf(out, "%02x", data[i]); + column += 2; + break; + } + if (column > 76 || (i % 16 == limit)) { + Newline(out); + column = level; + limit = i % 16; + } + } + if (column != level) { + Newline(out); + } + } + + /* + * Prints a usage message and exits + */ + static void Usage(const char *progName) + { + int htype; + int HASH_AlgTOTAL = sizeof(HASH_NAMES) / sizeof(HASH_NAMES[0]); + + fprintf(stderr, "Usage: %s -t type [ < input ] [ > output ]\n", progName); + fprintf(stderr, "%-20s Specify the digest method (must be one of\n", + "-t type"); + fprintf(stderr, "%-20s ", ""); + for (htype = 0; htype < HASH_AlgTOTAL; htype++) { + fprintf(stderr, HASH_NAMES[htype].hashName); + if (htype == (HASH_AlgTOTAL - 2)) + fprintf(stderr, " or "); + else if (htype != (HASH_AlgTOTAL - 1)) + fprintf(stderr, ", "); + } + fprintf(stderr, " (case ignored))\n"); + fprintf(stderr, "%-20s Define an input file to use (default is stdin)\n", + "< input"); + fprintf(stderr, "%-20s Define an output file to use (default is stdout)\n", + "> output"); + exit(-1); + } + + /* + * Check for the missing arguments + */ + static void PrintMsgAndExit(const char *progName, char opt) + { + fprintf(stderr, "%s: option -%c requires an argument\n", progName, opt); + Usage(progName); + } + + #define REQUIRE_ARG(opt,value) if (!(value)) PrintMsgAndExit(progName, opt) + + /* + * Digests a file according to the specified algorithm. + * It writes out the digest as a hexadecimal string. + */ + static int DigestFile(PRFileDesc *outFile, PRFileDesc *inFile, SECOidTag hashOIDTag) + { + unsigned int nb; + unsigned char ibuf[4096]; + unsigned char digest[64]; + unsigned int len; + unsigned int digestLen; + HASH_HashType hashType; + HASHContext *hashContext = NULL; + + hashType = HASH_GetHashTypeByOidTag(hashOIDTag); + hashContext = HASH_Create(hashType); + if (hashContext == NULL) { + return SECFailure; + } + + do { + + HASH_Begin(hashContext); + + /* Incrementally hash the file contents */ + while ((nb = PR_Read(inFile, ibuf, sizeof(ibuf))) > 0) { + HASH_Update(hashContext, ibuf, nb); + } + + HASH_End(hashContext, digest, &len, 64); + + /* Normally we would write it out in binary with + * nb = PR_Write(outFile, digest, len); + * but for illustration let's print it in hex. + */ + PrintAsHex(outFile, digest, len); + + } while (0); + + /* cleanup */ + if (hashContext != NULL) + HASH_Destroy(hashContext); + + return SECSuccess; + } + + /* + * This sample computes the hash of a file and saves it + * to another file. It illustrates the use of NSS message + * APIs. + */ + int main(int argc, char **argv) + { + SECOidTag hashOIDTag; + PLOptState *optstate; + PLOptStatus status; + SECStatus rv; + char *hashName = NULL; + char *progName = strrchr(argv[0], '/'); + + progName = progName ? progName + 1 : argv[0]; + + rv = NSS_NoDB_Init("/tmp"); + if (rv != SECSuccess) { + fprintf(stderr, "%s: NSS_Init failed in directory %s\n", + progName, "/tmp"); + return -1; + } + + /* Parse command line arguments */ + optstate = PL_CreateOptState(argc, argv, "t:"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + switch (optstate->option) { + case 't': + REQUIRE_ARG(optstate->option, optstate->value); + hashName = strdup(optstate->value); + break; + } + } + + if (!hashName) + Usage(progName); + + /* convert and validate */ + hashOIDTag = HashNameToOIDTag(hashName); + if (hashOIDTag == SEC_OID_UNKNOWN) { + fprintf(stderr, "%s: invalid digest type - %s\n", progName, hashName); + Usage(progName); + } + + /* Digest it and print the result */ + rv = DigestFile(PR_STDOUT, PR_STDIN, hashOIDTag); + if (rv != SECSuccess) { + fprintf(stderr, "%s: problem digesting data (%d)\n", + progName, PORT_GetError()); + } + + rv = NSS_Shutdown(); + if (rv != SECSuccess) { + exit(-1); + } + + return 0; + } + + \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample_2_initialization_of_nss/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample_2_initialization_of_nss/index.rst new file mode 100644 index 0000000000..a8ad843b49 --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample_2_initialization_of_nss/index.rst @@ -0,0 +1,257 @@ +.. _mozilla_projects_nss_nss_sample_code_nss_sample_code_sample_2_initialization_of_nss: + +NSS Sample Code Sample_2_Initialization of NSS +============================================== + +.. _nss_sample_code_2_initializing_nss: + +`NSS Sample Code 2: Initializing NSS <#nss_sample_code_2_initializing_nss>`__ +----------------------------------------------------------------------------- + +.. container:: + + This example program demonstrates how to initialize the NSS Database. This program illustrates + password handling. + +.. _sample_code_1: + +`Sample Code 1 <#sample_code_1>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. container:: + + .. code:: c + + /* NSPR Headers */ + #include + #include + #include + + /* NSS headers */ + #include + #include + + #include "util.h" + + /* Print a usage message and exit */ + static void Usage(const char *progName) + { + fprintf(stderr, "\nUsage: %s -d [-p ]" + " [-f ]\n\n", + progName); + fprintf(stderr, "%-15s Specify a DB directory path\n\n", + "-d "); + fprintf(stderr, "%-15s Specify a plaintext password\n\n", + "-p "); + fprintf(stderr, "%-15s Specify a password file\n\n", + "-f "); + exit(-1); + } + + /* Initialize the slot password */ + char *InitSlotPassword(PK11SlotInfo *slot, PRBool retry, void *arg) + { + FILE *input; + FILE *output; + char *p0 = NULL; + char *p1 = NULL; + secuPWData *pwdata = (secuPWData *) arg; + + if (pwdata->source == PW_FROMFILE) { + return FilePasswd(slot, retry, pwdata->data); + } + if (pwdata->source == PW_PLAINTEXT) { + return PL_strdup(pwdata->data); + } + + /* open terminal */ + input = fopen("/dev/tty", "r"); + if (input == NULL) { + PR_fprintf(PR_STDERR, "Error opening input terminal for read\n"); + return NULL; + } + + /* we have no password, so initialize database with one */ + PR_fprintf(PR_STDERR, + "Enter a password which will be used to encrypt your keys.\n" + "The password should be at least 8 characters long,\n" + "and should contain at least one non-alphabetic character.\n\n"); + + output = fopen("/dev/tty", "w"); + if (output == NULL) { + PR_fprintf(PR_STDERR, "Error opening output terminal for write\n"); + return NULL; + } + + for (;;) { + if (p0) + PORT_Free(p0); + p0 = GetPassword(input, output, "Enter new password: ", + CheckPassword); + if (p1) + PORT_Free(p1); + p1 = GetPassword(input, output, "Re-enter password: ", + CheckPassword); + if (p0 && p1 && !PORT_Strcmp(p0, p1)) { + break; + } + PR_fprintf(PR_STDERR, "Passwords do not match. Try again.\n"); + } + + /* clear out the duplicate password string */ + if (p1) { + PORT_Memset(p1, 0, PORT_Strlen(p1)); + PORT_Free(p1); + } + fclose(input); + fclose(output); + + return p0; + } + + /* Change the password */ + SECStatus ChangePW(PK11SlotInfo *slot, char *oldPass, char *newPass, + char *oldPwFile, char *newPwFile) + { + SECStatus rv; + secuPWData pwdata; + secuPWData newpwdata; + char *oldpw = NULL; + char *newpw = NULL; + + if (oldPass) { + pwdata.source = PW_PLAINTEXT; + pwdata.data = oldPass; + } else if (oldPwFile) { + pwdata.source = PW_FROMFILE; + pwdata.data = oldPwFile; + } else { + pwdata.source = PW_NONE; + pwdata.data = NULL; + } + + if (newPass) { + newpwdata.source = PW_PLAINTEXT; + newpwdata.data = newPass; + } else if (newPwFile) { + newpwdata.source = PW_FROMFILE; + newpwdata.data = NULL; + } else { + newpwdata.source = PW_NONE; + newpwdata.data = NULL; + } + + if (PK11_NeedUserInit(slot)) { + newpw = InitSlotPassword(slot, PR_FALSE, &pwdata); + rv = PK11_InitPin(slot, (char*)NULL, newpw); + } + else { + for (;;) { + oldpw = GetModulePassword(slot, PR_FALSE, &pwdata); + + if (PK11_CheckUserPassword(slot, oldpw) != SECSuccess) { + if (pwdata.source == PW_NONE) { + PR_fprintf(PR_STDERR, "Invalid password. Try again.\n"); + } else { + PR_fprintf(PR_STDERR, "Invalid password.\n"); + PORT_Memset(oldpw, 0, PL_strlen(oldpw)); + PORT_Free(oldpw); + return SECFailure; + } + } else { + break; + } + PORT_Free(oldpw); + } + newpw = InitSlotPassword(slot, PR_FALSE, &newpwdata); + + if (PK11_ChangePW(slot, oldpw, newpw) != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed to change password.\n"); + return SECFailure; + } + PORT_Memset(oldpw, 0, PL_strlen(oldpw)); + PORT_Free(oldpw); + PR_fprintf(PR_STDOUT, "Password changed successfully.\n"); + } + PORT_Memset(newpw, 0, PL_strlen(newpw)); + PORT_Free(newpw); + return SECSuccess; + } + + /* + * This example illustrates initialization of the NSS database. + * It creates an nss configuration directory with empty databases + * and initializes the databases. It also illustrates techniques for + * password handling. + */ + int main(int argc, char **argv) + { + PLOptState *optstate; + PLOptStatus status; + SECStatus rv; + SECStatus rvShutdown; + char *slotname = "internal"; + PK11SlotInfo *slot = NULL; + char *dbdir = NULL; + char *plainPass = NULL; + char *pwFile = NULL; + + char * progName = strrchr(argv[0], '/'); + progName = progName ? progName + 1 : argv[0]; + + /* Parse command line arguments */ + optstate = PL_CreateOptState(argc, argv, "d:p:q:f:g:"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + switch (optstate->option) { + case 'd': + dbdir = strdup(optstate->value); + break; + case 'p': + plainPass = strdup(optstate->value); + break; + case 'f': + pwFile = strdup(optstate->value); + break; + default: + Usage(progName); + break; + } + } + PL_DestroyOptState(optstate); + + if (!dbdir) + Usage(progName); + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + /* Create the database */ + rv = NSS_InitReadWrite(dbdir); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "NSS_Initialize Failed"); + PR_Cleanup(); + exit(rv); + } + + if (PL_strcmp(slotname, "internal") == 0) + slot = PK11_GetInternalKeySlot(); + + /* If creating new database, initialize the password. */ + rv = ChangePW(slot, plainPass, 0, pwFile, 0); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed to change password\n"); + } + + if (slot) { + PK11_FreeSlot(slot); + } + rvShutdown = NSS_Shutdown(); + if (rvShutdown != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed : NSS_Shutdown()\n"); + rv = SECFailure; + } + + PR_Cleanup(); + + return rv; + } + \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample_3_basic_encryption_and_maci/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample_3_basic_encryption_and_maci/index.rst new file mode 100644 index 0000000000..059c6861b8 --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_sample_3_basic_encryption_and_maci/index.rst @@ -0,0 +1,1221 @@ +.. _mozilla_projects_nss_nss_sample_code_nss_sample_code_sample_3_basic_encryption_and_maci: + +NSS Sample Code Sample_3_Basic Encryption and MACing +==================================================== + +.. _nss_sample_code_3_basic_encryption_and_macing: + +`NSS Sample Code 3: Basic Encryption and MACing <#nss_sample_code_3_basic_encryption_and_macing>`__ +--------------------------------------------------------------------------------------------------- + +.. container:: + + This example program demonstrates how to encrypt and MAC a file. + +.. _sample_code_3: + +`Sample Code 3 <#sample_code_3>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. container:: + + .. code:: c + + /* NSPR Headers */ + #include + #include + #include + #include + #include + #include + #include + + /* NSS headers */ + #include + #include + + /* our samples utilities */ + #include "util.h" + + #define BUFFERSIZE 80 + #define DIGESTSIZE 16 + #define PTEXT_MAC_BUFFER_SIZE 96 + #define CIPHERSIZE 96 + #define BLOCKSIZE 32 + + #define CIPHER_HEADER "-----BEGIN CIPHER-----" + #define CIPHER_TRAILER "-----END CIPHER-----" + #define ENCKEY_HEADER "-----BEGIN AESKEY CKAID-----" + #define ENCKEY_TRAILER "-----END AESKEY CKAID-----" + #define MACKEY_HEADER "-----BEGIN MACKEY CKAID-----" + #define MACKEY_TRAILER "-----END MACKEY CKAID-----" + #define IV_HEADER "-----BEGIN IV-----" + #define IV_TRAILER "-----END IV-----" + #define MAC_HEADER "-----BEGIN MAC-----" + #define MAC_TRAILER "-----END MAC-----" + #define PAD_HEADER "-----BEGIN PAD-----" + #define PAD_TRAILER "-----END PAD-----" + + typedef enum { + ENCRYPT, + DECRYPT, + UNKNOWN + } CommandType; + + typedef enum { + SYMKEY = 0, + MACKEY = 1, + IV = 2, + MAC = 3, + PAD = 4 + } HeaderType; + + + /* Print a usage message and exit */ + static void Usage(const char *progName) + { + fprintf(stderr, "\nUsage: %s -c -d [-z ] " + "[-p | -f ] -i -o \n\n", + progName); + fprintf(stderr, "%-20s Specify 'a' for encrypt operation\n\n", + "-c "); + fprintf(stderr, "%-20s Specify 'b' for decrypt operation\n\n", + " "); + fprintf(stderr, "%-20s Specify db directory path\n\n", + "-d "); + fprintf(stderr, "%-20s Specify db password [optional]\n\n", + "-p "); + fprintf(stderr, "%-20s Specify db password file [optional]\n\n", + "-f "); + fprintf(stderr, "%-20s Specify noise file name [optional]\n\n", + "-z "); + fprintf(stderr, "%-21s Specify an input file name\n\n", + "-i "); + fprintf(stderr, "%-21s Specify an output file name\n\n", + "-o "); + fprintf(stderr, "%-7s For encrypt, it takes as an input file and produces\n", + "Note :"); + fprintf(stderr, "%-7s .enc and .header as intermediate output files.\n\n", + ""); + fprintf(stderr, "%-7s For decrypt, it takes .enc and .header\n", + ""); + fprintf(stderr, "%-7s as input files and produces as a final output file.\n\n", + ""); + exit(-1); + } + + /* 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 https://mozilla.org/MPL/2.0/. */ + + /* NSPR Headers */ + #include + #include + #include + #include + #include + #include + #include + + /* + * Gather a CKA_ID + */ + SECStatus + GatherCKA_ID(PK11SymKey* key, SECItem* buf) + { + SECStatus rv = PK11_ReadRawAttribute(PK11_TypeSymKey, key, CKA_ID, buf); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "PK11_ReadRawAttribute returned (%d)\n", rv); + PR_fprintf(PR_STDERR, "Could not read SymKey CKA_ID attribute\n"); + return rv; + } + return rv; + } + + /* + * Generate a Symmetric Key + */ + PK11SymKey * + GenerateSYMKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE mechanism, + int keySize, SECItem *keyID, secuPWData *pwdata) + { + SECStatus rv; + PK11SymKey *key; + + if (PK11_NeedLogin(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, pwdata); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", + PK11_GetTokenName(slot)); + return NULL; + } + } + + /* Generate the symmetric key */ + key = PK11_TokenKeyGen(slot, mechanism, + NULL, keySize, keyID, PR_TRUE, pwdata); + + if (!key) { + PR_fprintf(PR_STDERR, "Symmetric Key Generation Failed \n"); + } + + return key; + } + + /* + * MacInit + */ + SECStatus + MacInit(PK11Context *ctx) + { + SECStatus rv = PK11_DigestBegin(ctx); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Compute MAC Failed : PK11_DigestBegin()\n"); + } + return rv; + } + + /* + * MacUpdate + */ + SECStatus + MacUpdate(PK11Context *ctx, + unsigned char *msg, unsigned int msgLen) + { + SECStatus rv = PK11_DigestOp(ctx, msg, msgLen); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Compute MAC Failed : DigestOp()\n"); + } + return rv; + } + + /* + * Finalize MACing + */ + SECStatus + MacFinal(PK11Context *ctx, + unsigned char *mac, unsigned int *macLen, unsigned int maxLen) + { + SECStatus rv = PK11_DigestFinal(ctx, mac, macLen, maxLen); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Compute MAC Failed : PK11_DigestFinal()\n"); + } + return SECSuccess; + } + + /* + * Compute Mac + */ + SECStatus + ComputeMac(PK11Context *ctxmac, + unsigned char *ptext, unsigned int ptextLen, + unsigned char *mac, unsigned int *macLen, + unsigned int maxLen) + { + SECStatus rv = MacInit(ctxmac); + if (rv != SECSuccess) return rv; + rv = MacUpdate(ctxmac, ptext, ptextLen); + if (rv != SECSuccess) return rv; + rv = MacFinal(ctxmac, mac, macLen, maxLen); + return rv; + } + + /* + * Write To Header File + */ + SECStatus + WriteToHeaderFile(const char *buf, unsigned int len, HeaderType type, + PRFileDesc *outFile) + { + SECStatus rv; + char header[40]; + char trailer[40]; + char *outString = NULL; + + switch (type) { + case SYMKEY: + strcpy(header, ENCKEY_HEADER); + strcpy(trailer, ENCKEY_TRAILER); + break; + case MACKEY: + strcpy(header, MACKEY_HEADER); + strcpy(trailer, MACKEY_TRAILER); + break; + case IV: + strcpy(header, IV_HEADER); + strcpy(trailer, IV_TRAILER); + break; + case MAC: + strcpy(header, MAC_HEADER); + strcpy(trailer, MAC_TRAILER); + break; + case PAD: + strcpy(header, PAD_HEADER); + strcpy(trailer, PAD_TRAILER); + break; + } + + PR_fprintf(outFile, "%s\n", header); + PrintAsHex(outFile, buf, len); + PR_fprintf(outFile, "%s\n\n", trailer); + return SECSuccess; + } + + /* + * Initialize for encryption or decryption - common code + */ + PK11Context * + CryptInit(PK11SymKey *key, + unsigned char *iv, unsigned int ivLen, + CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation) + { + SECItem ivItem = { siBuffer, iv, ivLen }; + PK11Context *ctx = NULL; + + SECItem *secParam = PK11_ParamFromIV(CKM_AES_CBC, &ivItem); + if (secParam == NULL) { + PR_fprintf(PR_STDERR, "Crypt Failed : secParam NULL\n"); + return NULL; + } + ctx = PK11_CreateContextBySymKey(CKM_AES_CBC, operation, key, secParam); + if (ctx == NULL) { + PR_fprintf(PR_STDERR, "Crypt Failed : can't create a context\n"); + goto cleanup; + + } + cleanup: + if (secParam) { + SECITEM_FreeItem(secParam, PR_TRUE); + } + return ctx; + } + + /* + * Common encryption and decryption code + */ + SECStatus + Crypt(PK11Context *ctx, + unsigned char *out, unsigned int *outLen, unsigned int maxOut, + unsigned char *in, unsigned int inLen) + { + SECStatus rv; + + rv = PK11_CipherOp(ctx, out, outLen, maxOut, in, inLen); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Crypt Failed : PK11_CipherOp returned %d\n", rv); + goto cleanup; + } + + cleanup: + if (rv != SECSuccess) { + return rv; + } + return SECSuccess; + } + + /* + * Decrypt + */ + SECStatus + Decrypt(PK11Context *ctx, + unsigned char *out, unsigned int *outLen, unsigned int maxout, + unsigned char *in, unsigned int inLen) + { + return Crypt(ctx, out, outLen, maxout, in, inLen); + } + + /* + * Encrypt + */ + SECStatus + Encrypt(PK11Context* ctx, + unsigned char *out, unsigned int *outLen, unsigned int maxout, + unsigned char *in, unsigned int inLen) + { + return Crypt(ctx, out, outLen, maxout, in, inLen); + } + + /* + * EncryptInit + */ + PK11Context * + EncryptInit(PK11SymKey *ek, unsigned char *iv, unsigned int ivLen, + CK_MECHANISM_TYPE type) + { + return CryptInit(ek, iv, ivLen, type, CKA_ENCRYPT); + } + + /* + * DecryptInit + */ + PK11Context * + DecryptInit(PK11SymKey *dk, unsigned char *iv, unsigned int ivLen, + CK_MECHANISM_TYPE type) + { + return CryptInit(dk, iv, ivLen, type, CKA_DECRYPT); + } + + /* + * Read cryptographic parameters from the header file + */ + SECStatus + ReadFromHeaderFile(const char *fileName, HeaderType type, + SECItem *item, PRBool isHexData) + { + SECStatus rv; + PRFileDesc* file; + SECItem filedata; + SECItem outbuf; + unsigned char *nonbody; + unsigned char *body; + char header[40]; + char trailer[40]; + + outbuf.type = siBuffer; + file = PR_Open(fileName, PR_RDONLY, 0); + if (!file) { + PR_fprintf(PR_STDERR, "Failed to open %s\n", fileName); + return SECFailure; + } + switch (type) { + case SYMKEY: + strcpy(header, ENCKEY_HEADER); + strcpy(trailer, ENCKEY_TRAILER); + break; + case MACKEY: + strcpy(header, MACKEY_HEADER); + strcpy(trailer, MACKEY_TRAILER); + break; + case IV: + strcpy(header, IV_HEADER); + strcpy(trailer, IV_TRAILER); + break; + case MAC: + strcpy(header, MAC_HEADER); + strcpy(trailer, MAC_TRAILER); + break; + case PAD: + strcpy(header, PAD_HEADER); + strcpy(trailer, PAD_TRAILER); + break; + } + + rv = FileToItem(&filedata, file); + nonbody = (char *)filedata.data; + if (!nonbody) { + PR_fprintf(PR_STDERR, "unable to read data from input file\n"); + rv = SECFailure; + goto cleanup; + } + + /* check for headers and trailers and remove them */ + if ((body = strstr(nonbody, header)) != NULL) { + char *trail = NULL; + nonbody = body; + body = PORT_Strchr(body, '\n'); + if (!body) + body = PORT_Strchr(nonbody, '\r'); /* maybe this is a MAC file */ + if (body) + trail = strstr(++body, trailer); + if (trail != NULL) { + *trail = '\0'; + } else { + PR_fprintf(PR_STDERR, "input has header but no trailer\n"); + PORT_Free(filedata.data); + return SECFailure; + } + } else { + body = nonbody; + } + + cleanup: + PR_Close(file); + HexToBuf(body, item, isHexData); + return SECSuccess; + } + + /* + * EncryptAndMac + */ + SECStatus + EncryptAndMac(PRFileDesc *inFile, + PRFileDesc *headerFile, + PRFileDesc *encFile, + PK11SymKey *ek, + PK11SymKey *mk, + unsigned char *iv, unsigned int ivLen, + PRBool ascii) + { + SECStatus rv; + unsigned char ptext[BLOCKSIZE]; + unsigned int ptextLen; + unsigned char mac[DIGESTSIZE]; + unsigned int macLen; + unsigned int nwritten; + unsigned char encbuf[BLOCKSIZE]; + unsigned int encbufLen; + SECItem noParams = { siBuffer, NULL, 0 }; + PK11Context *ctxmac = NULL; + PK11Context *ctxenc = NULL; + unsigned int pad[1]; + SECItem padItem; + unsigned int paddingLength; + + static unsigned int firstTime = 1; + int j; + + ctxmac = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, mk, &noParams); + if (ctxmac == NULL) { + PR_fprintf(PR_STDERR, "Can't create MAC context\n"); + rv = SECFailure; + goto cleanup; + } + rv = MacInit(ctxmac); + if (rv != SECSuccess) { + goto cleanup; + } + + ctxenc = EncryptInit(ek, iv, ivLen, CKM_AES_CBC); + + /* read a buffer of plaintext from input file */ + while ((ptextLen = PR_Read(inFile, ptext, sizeof(ptext))) > 0) { + + /* Encrypt using it using CBC, using previously created IV */ + if (ptextLen != BLOCKSIZE) { + paddingLength = BLOCKSIZE - ptextLen; + for ( j=0; j < paddingLength; j++) { + ptext[ptextLen+j] = (unsigned char)paddingLength; + } + ptextLen = BLOCKSIZE; + } + rv = Encrypt(ctxenc, + encbuf, &encbufLen, sizeof(encbuf), + ptext, ptextLen); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Encrypt Failure\n"); + goto cleanup; + } + + /* save the last block of ciphertext as the next IV */ + iv = encbuf; + ivLen = encbufLen; + + /* write the cipher text to intermediate file */ + nwritten = PR_Write(encFile, encbuf, encbufLen); + /*PR_Assert(nwritten == encbufLen);*/ + + rv = MacUpdate(ctxmac, ptext, ptextLen); + } + + rv = MacFinal(ctxmac, mac, &macLen, DIGESTSIZE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "MacFinal Failure\n"); + goto cleanup; + } + if (macLen == 0) { + PR_fprintf(PR_STDERR, "Bad MAC length\n"); + rv = SECFailure; + goto cleanup; + } + WriteToHeaderFile(mac, macLen, MAC, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Write MAC Failure\n"); + goto cleanup; + } + + pad[0] = paddingLength; + padItem.type = siBuffer; + padItem.data = (unsigned char *)pad; + padItem.len = sizeof(pad[0]); + + WriteToHeaderFile(padItem.data, padItem.len, PAD, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Write PAD Failure\n"); + goto cleanup; + } + + rv = SECSuccess; + + cleanup: + if (ctxmac != NULL) { + PK11_DestroyContext(ctxmac, PR_TRUE); + } + if (ctxenc != NULL) { + PK11_DestroyContext(ctxenc, PR_TRUE); + } + + return rv; + } + + /* + * Find the Key for the given mechanism + */ + PK11SymKey* + FindKey(PK11SlotInfo *slot, + CK_MECHANISM_TYPE mechanism, + SECItem *keyBuf, secuPWData *pwdata) + { + SECStatus rv; + PK11SymKey *key; + + if (PK11_NeedLogin(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, pwdata); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not authenticate to token %s.\n", + PK11_GetTokenName(slot)); + if (slot) { + PK11_FreeSlot(slot); + } + return NULL; + } + } + + key = PK11_FindFixedKey(slot, mechanism, keyBuf, 0); + if (!key) { + PR_fprintf(PR_STDERR, + "PK11_FindFixedKey failed (err %d)\n", + PR_GetError()); + PK11_FreeSlot(slot); + return NULL; + } + return key; + } + + /* + * Decrypt and Verify MAC + */ + SECStatus + DecryptAndVerifyMac( + const char* outFileName, + char *encryptedFileName, + SECItem *cItem, SECItem *macItem, + PK11SymKey* ek, PK11SymKey* mk, SECItem *ivItem, SECItem *padItem) + { + SECStatus rv; + PRFileDesc* inFile; + PRFileDesc* outFile; + + unsigned char decbuf[64]; + unsigned int decbufLen; + + unsigned char ptext[BLOCKSIZE]; + unsigned int ptextLen = 0; + unsigned char ctext[64]; + unsigned int ctextLen; + unsigned char newmac[DIGESTSIZE]; + unsigned int newmacLen = 0; + unsigned int newptextLen = 0; + unsigned int count = 0; + unsigned int temp = 0; + unsigned int blockNumber = 0; + SECItem noParams = { siBuffer, NULL, 0 }; + PK11Context *ctxmac = NULL; + PK11Context *ctxenc = NULL; + + unsigned char iv[BLOCKSIZE]; + unsigned int ivLen = ivItem->len; + unsigned int fileLength; + unsigned int paddingLength; + int j; + + memcpy(iv, ivItem->data, ivItem->len); + paddingLength = (unsigned int)padItem->data[0]; + + ctxmac = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, mk, &noParams); + if (ctxmac == NULL) { + PR_fprintf(PR_STDERR, "Can't create MAC context\n"); + rv = SECFailure; + goto cleanup; + } + + /* Open the input file. */ + inFile = PR_Open(encryptedFileName, PR_RDONLY , 0); + if (!inFile) { + PR_fprintf(PR_STDERR, + "Unable to open \"%s\" for writing.\n", + encryptedFileName); + return SECFailure; + } + /* Open the output file. */ + outFile = PR_Open(outFileName, + PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR , 00660); + if (!outFile) { + PR_fprintf(PR_STDERR, + "Unable to open \"%s\" for writing.\n", + outFileName); + return SECFailure; + } + + rv = MacInit(ctxmac); + if (rv != SECSuccess) goto cleanup; + + ctxenc = DecryptInit(ek, iv, ivLen, CKM_AES_CBC); + fileLength = FileSize(encryptedFileName); + + while ((ctextLen = PR_Read(inFile, ctext, sizeof(ctext))) > 0) { + + count += ctextLen; + + /* decrypt cipher text buffer using CBC and IV */ + + rv = Decrypt(ctxenc, decbuf, &decbufLen, sizeof(decbuf), + ctext, ctextLen); + + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Decrypt Failure\n"); + goto cleanup; + } + + if (decbufLen == 0) break; + + rv = MacUpdate(ctxmac, decbuf, decbufLen); + if (rv != SECSuccess) { goto cleanup; } + if (count == fileLength) { + decbufLen = decbufLen-paddingLength; + } + + /* write the plain text to out file */ + temp = PR_Write(outFile, decbuf, decbufLen); + if (temp != decbufLen) { + PR_fprintf(PR_STDERR, "write error\n"); + rv = SECFailure; + break; + } + + /* save last block of ciphertext */ + memcpy(iv, decbuf, decbufLen); + ivLen = decbufLen; + blockNumber++; + } + + if (rv != SECSuccess) { goto cleanup; } + + rv = MacFinal(ctxmac, newmac, &newmacLen, sizeof(newmac)); + if (rv != SECSuccess) { goto cleanup; } + + if (PORT_Memcmp(macItem->data, newmac, newmacLen) == 0) { + rv = SECSuccess; + } else { + PR_fprintf(PR_STDERR, "Check MAC : Failure\n"); + PR_fprintf(PR_STDERR, "Extracted : "); + PrintAsHex(PR_STDERR, macItem->data, macItem->len); + PR_fprintf(PR_STDERR, "Computed : "); + PrintAsHex(PR_STDERR, newmac, newmacLen); + rv = SECFailure; + } + cleanup: + if (ctxmac) { + PK11_DestroyContext(ctxmac, PR_TRUE); + } + if (ctxenc) { + PK11_DestroyContext(ctxenc, PR_TRUE); + } + if (outFile) { + PR_Close(outFile); + } + + return rv; + } + + /* + * Gets IV and CKAIDs From Header File + */ + SECStatus + GetIVandCKAIDSFromHeader(const char *cipherFileName, + SECItem *ivItem, SECItem *encKeyItem, SECItem *macKeyItem) + { + SECStatus rv; + + /* open intermediate file, read in header, get IV and CKA_IDs of two keys + * from it + */ + rv = ReadFromHeaderFile(cipherFileName, IV, ivItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not retrieve IV from cipher file\n"); + goto cleanup; + } + + rv = ReadFromHeaderFile(cipherFileName, SYMKEY, encKeyItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not retrieve AES CKA_ID from cipher file\n"); + goto cleanup; + } + rv = ReadFromHeaderFile(cipherFileName, MACKEY, macKeyItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not retrieve MAC CKA_ID from cipher file\n"); + goto cleanup; + } + cleanup: + return rv; + } + + /* + * Decrypt a File + */ + SECStatus + DecryptFile(PK11SlotInfo *slot, + const char *dbdir, + const char *outFileName, + const char *headerFileName, + char *encryptedFileName, + secuPWData *pwdata, + PRBool ascii) + { + /* + * The DB is open read only and we have authenticated to it + * open input file, read in header, get IV and CKA_IDs of two keys from it + * find those keys in the DB token + * Open output file + * loop until EOF(input): + * read a buffer of ciphertext from input file + * save last block of ciphertext + * decrypt ciphertext buffer using CBC and IV + * compute and check MAC, then remove MAC from plaintext + * replace IV with saved last block of ciphertext + * write the plain text to output file + * close files + * report success + */ + + SECStatus rv; + SECItem ivItem; + SECItem encKeyItem; + SECItem macKeyItem; + SECItem cipherItem; + SECItem macItem; + SECItem padItem; + PK11SymKey *encKey = NULL; + PK11SymKey *macKey = NULL; + + + /* open intermediate file, read in header, get IV and CKA_IDs of two keys + * from it + */ + rv = GetIVandCKAIDSFromHeader(headerFileName, + &ivItem, &encKeyItem, &macKeyItem); + if (rv != SECSuccess) { + goto cleanup; + } + + /* find those keys in the DB token */ + encKey = FindKey(slot, CKM_AES_CBC, &encKeyItem, pwdata); + if (encKey == NULL) { + PR_fprintf(PR_STDERR, "Can't find the encryption key\n"); + rv = SECFailure; + goto cleanup; + } + /* CKM_MD5_HMAC or CKM_EXTRACT_KEY_FROM_KEY */ + macKey = FindKey(slot, CKM_MD5_HMAC, &macKeyItem, pwdata); + if (macKey == NULL) { + rv = SECFailure; + goto cleanup; + } + + /* Read in the Mac into item from the intermediate file */ + rv = ReadFromHeaderFile(headerFileName, MAC, &macItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not retrieve MAC from cipher file\n"); + goto cleanup; + } + if (macItem.data == NULL) { + PR_fprintf(PR_STDERR, "MAC has NULL data\n"); + rv = SECFailure; + goto cleanup; + } + if (macItem.len == 0) { + PR_fprintf(PR_STDERR, "MAC has data has 0 length\n"); + /*rv = SECFailure; + goto cleanup;*/ + } + + rv = ReadFromHeaderFile(headerFileName, PAD, &padItem, PR_TRUE); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, + "Could not retrieve PAD detail from header file\n"); + goto cleanup; + } + + if (rv == SECSuccess) { + /* Decrypt and Remove Mac */ + rv = DecryptAndVerifyMac(outFileName, encryptedFileName, + &cipherItem, &macItem, encKey, macKey, &ivItem, &padItem); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed while decrypting and removing MAC\n"); + } + } + + cleanup: + if (slot) { + PK11_FreeSlot(slot); + } + if (encKey) { + PK11_FreeSymKey(encKey); + } + if (macKey) { + PK11_FreeSymKey(macKey); + } + + return rv; + } + + /* + * Encrypt a File + */ + SECStatus + EncryptFile( + PK11SlotInfo *slot, + const char *dbdir, + const char *inFileName, + const char *headerFileName, + const char *encryptedFileName, + const char *noiseFileName, + secuPWData *pwdata, + PRBool ascii) + { + /* + * The DB is open for read/write and we have authenticated to it. + * generate a symmetric AES key as a token object. + * generate a second key to use for MACing, also a token object. + * get their CKA_IDs + * generate a random value to use as IV for AES CBC + * open an input file and an output file, + * write a header to the output that identifies the two keys by + * their CKA_IDs, May include original file name and length. + * loop until EOF(input) + * read a buffer of plaintext from input file + * MAC it, append the MAC to the plaintext + * encrypt it using CBC, using previously created IV + * store the last block of ciphertext as the new IV + * write the cipher text to intermediate file + * close files + * report success + */ + SECStatus rv; + PRFileDesc *inFile; + PRFileDesc *headerFile; + PRFileDesc *encFile; + + unsigned char *encKeyId = (unsigned char *) "Encrypt Key"; + unsigned char *macKeyId = (unsigned char *) "MAC Key"; + SECItem encKeyID = { siAsciiString, encKeyId, PL_strlen(encKeyId) }; + SECItem macKeyID = { siAsciiString, macKeyId, PL_strlen(macKeyId) }; + + SECItem encCKAID; + SECItem macCKAID; + unsigned char iv[BLOCKSIZE]; + SECItem ivItem; + PK11SymKey *encKey = NULL; + PK11SymKey *macKey = NULL; + SECItem temp; + unsigned char c; + + /* generate a symmetric AES key as a token object. */ + encKey = GenerateSYMKey(slot, CKM_AES_KEY_GEN, 128/8, &encKeyID, pwdata); + if (encKey == NULL) { + PR_fprintf(PR_STDERR, "GenerateSYMKey for AES returned NULL.\n"); + rv = SECFailure; + goto cleanup; + } + + /* generate a second key to use for MACing, also a token object. */ + macKey = GenerateSYMKey(slot, CKM_GENERIC_SECRET_KEY_GEN, 160/8, + &macKeyID, pwdata); + if (macKey == NULL) { + PR_fprintf(PR_STDERR, "GenerateSYMKey for MACing returned NULL.\n"); + rv = SECFailure; + goto cleanup; + } + + /* get the encrypt key CKA_ID */ + rv = GatherCKA_ID(encKey, &encCKAID); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Error while wrapping encrypt key\n"); + goto cleanup; + } + + /* get the MAC key CKA_ID */ + rv = GatherCKA_ID(macKey, &macCKAID); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Can't get the MAC key CKA_ID.\n"); + goto cleanup; + } + + if (noiseFileName) { + rv = SeedFromNoiseFile(noiseFileName); + if (rv != SECSuccess) { + PORT_SetError(PR_END_OF_FILE_ERROR); + return SECFailure; + } + rv = PK11_GenerateRandom(iv, BLOCKSIZE); + if (rv != SECSuccess) { + goto cleanup; + } + + } else { + /* generate a random value to use as IV for AES CBC */ + GenerateRandom(iv, BLOCKSIZE); + } + + headerFile = PR_Open(headerFileName, + PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); + if (!headerFile) { + PR_fprintf(PR_STDERR, + "Unable to open \"%s\" for writing.\n", + headerFileName); + return SECFailure; + } + encFile = PR_Open(encryptedFileName, + PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); + if (!encFile) { + PR_fprintf(PR_STDERR, + "Unable to open \"%s\" for writing.\n", + encryptedFileName); + return SECFailure; + } + /* write to a header file the IV and the CKA_IDs + * identifying the two keys + */ + ivItem.type = siBuffer; + ivItem.data = iv; + ivItem.len = BLOCKSIZE; + + rv = WriteToHeaderFile(iv, BLOCKSIZE, IV, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Error writing IV to cipher file - %s\n", + headerFileName); + goto cleanup; + } + + rv = WriteToHeaderFile(encCKAID.data, encCKAID.len, SYMKEY, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Error writing AES CKA_ID to cipher file - %s\n", + encryptedFileName); + goto cleanup; + } + rv = WriteToHeaderFile(macCKAID.data, macCKAID.len, MACKEY, headerFile); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Error writing MAC CKA_ID to cipher file - %s\n", + headerFileName); + goto cleanup; + } + + /* Open the input file. */ + inFile = PR_Open(inFileName, PR_RDONLY, 0); + if (!inFile) { + PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", + inFileName); + return SECFailure; + } + + /* Macing and Encryption */ + if (rv == SECSuccess) { + rv = EncryptAndMac(inFile, headerFile, encFile, + encKey, macKey, ivItem.data, ivItem.len, ascii); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed : MACing and Encryption\n"); + goto cleanup; + } + } + + cleanup: + if (inFile) { + PR_Close(inFile); + } + if (headerFile) { + PR_Close(headerFile); + } + if (encFile) { + PR_Close(encFile); + } + if (slot) { + PK11_FreeSlot(slot); + } + if (encKey) { + PK11_FreeSymKey(encKey); + } + if (macKey) { + PK11_FreeSymKey(macKey); + } + + return rv; + } + + /* + * This example illustrates basic encryption/decryption and MACing. + * Generates the encryption/mac keys and uses token for storing. + * Encrypts the input file and appends MAC before storing in intermediate + * header file. + * Writes the CKA_IDs of the encryption keys into intermediate header file. + * Reads the intermediate header file for CKA_IDs and encrypted + * contents and decrypts into output file. + */ + int main(int argc, char **argv) + { + SECStatus rv; + SECStatus rvShutdown; + PK11SlotInfo *slot = NULL; + PLOptState *optstate; + PLOptStatus status; + char headerFileName[50]; + char encryptedFileName[50]; + PRFileDesc *inFile; + PRFileDesc *outFile; + PRBool ascii = PR_FALSE; + CommandType cmd = UNKNOWN; + const char *command = NULL; + const char *dbdir = NULL; + const char *inFileName = NULL; + const char *outFileName = NULL; + const char *noiseFileName = NULL; + secuPWData pwdata = { PW_NONE, 0 }; + + char * progName = strrchr(argv[0], '/'); + progName = progName ? progName + 1 : argv[0]; + + /* Parse command line arguments */ + optstate = PL_CreateOptState(argc, argv, "c:d:i:o:f:p:z:a"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + switch (optstate->option) { + case 'a': + ascii = PR_TRUE; + break; + case 'c': + command = strdup(optstate->value); + break; + case 'd': + dbdir = strdup(optstate->value); + break; + case 'f': + pwdata.source = PW_FROMFILE; + pwdata.data = strdup(optstate->value); + break; + case 'p': + pwdata.source = PW_PLAINTEXT; + pwdata.data = strdup(optstate->value); + break; + case 'i': + inFileName = strdup(optstate->value); + break; + case 'o': + outFileName = strdup(optstate->value); + break; + case 'z': + noiseFileName = strdup(optstate->value); + break; + default: + Usage(progName); + break; + } + } + PL_DestroyOptState(optstate); + + if (!command || !dbdir || !inFileName || !outFileName) + Usage(progName); + if (PL_strlen(command)==0) + Usage(progName); + + cmd = command[0] == 'a' ? ENCRYPT : command[0] == 'b' ? DECRYPT : UNKNOWN; + + /* Open the input file. */ + inFile = PR_Open(inFileName, PR_RDONLY, 0); + if (!inFile) { + PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", + inFileName); + return SECFailure; + } + PR_Close(inFile); + + /* For intermediate header file, choose filename as inputfile name + with extension ".header" */ + strcpy(headerFileName, inFileName); + strcat(headerFileName, ".header"); + + /* For intermediate encrypted file, choose filename as inputfile name + with extension ".enc" */ + strcpy(encryptedFileName, inFileName); + strcat(encryptedFileName, ".enc"); + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + switch (cmd) { + case ENCRYPT: + /* If the intermediate header file already exists, delete it */ + if (PR_Access(headerFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { + PR_Delete(headerFileName); + } + /* If the intermediate encrypted already exists, delete it */ + if (PR_Access(encryptedFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { + PR_Delete(encryptedFileName); + } + + /* Open DB for read/write and authenticate to it. */ + rv = NSS_InitReadWrite(dbdir); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "NSS_InitReadWrite Failed\n"); + goto cleanup; + } + + PK11_SetPasswordFunc(GetModulePassword); + slot = PK11_GetInternalKeySlot(); + if (PK11_NeedLogin(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, &pwdata); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", + PK11_GetTokenName(slot)); + goto cleanup; + } + } + rv = EncryptFile(slot, dbdir, + inFileName, headerFileName, encryptedFileName, + noiseFileName, &pwdata, ascii); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "EncryptFile : Failed\n"); + return SECFailure; + } + break; + case DECRYPT: + /* Open DB read only, authenticate to it */ + PK11_SetPasswordFunc(GetModulePassword); + + rv = NSS_Init(dbdir); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "NSS_Init Failed\n"); + return SECFailure; + } + + slot = PK11_GetInternalKeySlot(); + if (PK11_NeedLogin(slot)) { + rv = PK11_Authenticate(slot, PR_TRUE, &pwdata); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", + PK11_GetTokenName(slot)); + goto cleanup; + } + } + + rv = DecryptFile(slot, dbdir, + outFileName, headerFileName, + encryptedFileName, &pwdata, ascii); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "DecryptFile : Failed\n"); + return SECFailure; + } + break; + } + + cleanup: + rvShutdown = NSS_Shutdown(); + if (rvShutdown != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed : NSS_Shutdown()\n"); + rv = SECFailure; + } + + PR_Cleanup(); + + return rv; + } + \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_utililies_1/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_utililies_1/index.rst new file mode 100644 index 0000000000..261b587f9f --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/nss_sample_code_utililies_1/index.rst @@ -0,0 +1,553 @@ +.. _mozilla_projects_nss_nss_sample_code_nss_sample_code_utililies_1: + +NSS Sample Code Utilities_1 +=========================== + +.. _nss_sample_code_common_utilities: + +`NSS Sample Code Common: Utilities <#nss_sample_code_common_utilities>`__ +------------------------------------------------------------------------- + +.. container:: + + This is a library of utilities used by many of the samples. This code shows the following: + + - Extract seed from noise file + - Read DER encoding from a file + - Extract the password from a text file + - Get the module password + - Print as ascii or hexadecimal + +.. _sample_code: + +`Sample Code <#sample_code>`__ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. container:: + + .. code:: + + #include + #include + #include + #include + #include + #include + #include "util.h" + + + /* + * These utility functions are adapted from those found in + * the sectool library used by the NSS security tools and + * other NSS test applications. + */ + + /* + * Newline + */ + static void Newline(PRFileDesc* out) { + PR_fprintf(out, "\n"); + } + + /* + * PrintAsAscii + */ + void PrintAsAscii(PRFileDesc* out, const unsigned char *data, unsigned int len) + { + char *b64Data = NULL; + + b64Data = BTOA_DataToAscii(data, len); + PR_fprintf(out, "%s", b64Data); + PR_fprintf(out, "\n"); + if (b64Data) { + PORT_Free(b64Data); + } + } + + /* + * PrintAsHex + */ + void PrintAsHex(PRFileDesc* out, const unsigned char *data, unsigned int len) + { + unsigned i; + int column; + unsigned int limit = 15; + unsigned int level = 1; + + column = level; + if (!len) { + PR_fprintf(out, "(empty)\n"); + return; + } + + for (i = 0; i < len; i++) { + if (i != len - 1) { + PR_fprintf(out, "%02x:", data[i]); + column += 3; + } else { + PR_fprintf(out, "%02x", data[i]); + column += 2; + break; + } + if (column > 76 || (i % 16 == limit)) { + Newline(out); + column = level; + limit = i % 16; + } + } + if (column != level) { + Newline(out); + } + } + + /* + * GetDigit + */ + int GetDigit(char c) + { + if (c == 0) { + return -1; + } + if (c <= '9' && c >= '0') { + return c - '0'; + } + if (c <= 'f' && c >= 'a') { + return c - 'a' + 0xa; + } + if (c <= 'F' && c >= 'A') { + return c - 'A' + 0xa; + } + return -1; + } + + /* + * HexToBuf + */ + int HexToBuf(unsigned char *inString, SECItem *outbuf, PRBool isHexData) + { + int len = strlen(inString); + int outLen = len+1/2; + int trueLen = 0; + int digit1, digit2; + + outbuf->data = isHexData + ? PORT_Alloc(outLen) + : PORT_Alloc(len); + if (!outbuf->data) { + return -1; + } + if (isHexData) { + while (*inString) { + if ((*inString == '\n') || (*inString == ':')) { + inString++; + continue; + } + digit1 = GetDigit(*inString++); + digit2 = GetDigit(*inString++); + if ((digit1 == -1) || (digit2 == -1)) { + PORT_Free(outbuf->data); + outbuf->data = NULL; + return -1; + } + outbuf->data[trueLen++] = digit1 << 4 | digit2; + } + } else { + while (*inString) { + if (*inString == '\n') { + inString++; + continue; + } + outbuf->data[trueLen++] = *inString++; + } + outbuf->data[trueLen] = '\0'; + trueLen = trueLen-1; + } + outbuf->len = trueLen; + return 0; + } + + /* + * FileToItem + */ + SECStatus FileToItem(SECItem *dst, PRFileDesc *src) + { + PRFileInfo info; + PRInt32 numBytes; + PRStatus prStatus; + + prStatus = PR_GetOpenFileInfo(src, &info); + + if (prStatus != PR_SUCCESS) { + return SECFailure; + } + + dst->data = 0; + if (SECITEM_AllocItem(NULL, dst, info.size)) { + numBytes = PR_Read(src, dst->data, info.size); + if (numBytes == info.size) { + return SECSuccess; + } + } + SECITEM_FreeItem(dst, PR_FALSE); + dst->data = NULL; + return SECFailure; + } + + /* + * echoOff + */ + static void echoOff(int fd) + { + if (isatty(fd)) { + struct termios tio; + tcgetattr(fd, &tio); + tio.c_lflag &= ~ECHO; + tcsetattr(fd, TCSAFLUSH, &tio); + } + } + + /* + * echoOn + */ + static void echoOn(int fd) + { + if (isatty(fd)) { + struct termios tio; + tcgetattr(fd, &tio); + tio.c_lflag |= ECHO; + tcsetattr(fd, TCSAFLUSH, &tio); + } + } + + /* + * CheckPassword + */ + PRBool CheckPassword(char *cp) + { + int len; + char *end; + len = PORT_Strlen(cp); + if (len < 8) { + return PR_FALSE; + } + end = cp + len; + while (cp < end) { + unsigned char ch = *cp++; + if (!((ch >= 'A') && (ch <= 'Z')) && + !((ch >= 'a') && (ch <= 'z'))) { + return PR_TRUE; + } + } + return PR_FALSE; + } + + /* + * GetPassword + */ + char* GetPassword(FILE *input, FILE *output, char *prompt, + PRBool (*ok)(char *)) + { + char phrase[200] = {'\0'}; + int infd = fileno(input); + int isTTY = isatty(infd); + + for (;;) { + /* Prompt for password */ + if (isTTY) { + fprintf(output, "%s", prompt); + fflush (output); + echoOff(infd); + } + fgets(phrase, sizeof(phrase), input); + if (isTTY) { + fprintf(output, "\n"); + echoOn(infd); + } + /* stomp on newline */ + phrase[PORT_Strlen(phrase)-1] = 0; + /* Validate password */ + if (!(*ok)(phrase)) { + if (!isTTY) return 0; + fprintf(output, "Password must be at least 8 characters long with one or more\n"); + fprintf(output, "non-alphabetic characters\n"); + continue; + } + return (char*) PORT_Strdup(phrase); + } + } + + /* + * FilePasswd extracts the password from a text file + * + * Storing passwords is often used with server environments + * where prompting the user for a password or requiring it + * to be entered in the command line is not a feasible option. + * + * This function supports password extraction from files with + * multiple passwords, one for each token. In the single password + * case a line would just have the password whereas in the multi- + * password variant they could be of the form + * + * token_1_name:its_password + * token_2_name:its_password + * + */ + char *FilePasswd(PK11SlotInfo *slot, PRBool retry, void *arg) + { + char* phrases, *phrase; + PRFileDesc *fd; + PRInt32 nb; + char *pwFile = arg; + int i; + const long maxPwdFileSize = 4096; + char* tokenName = NULL; + int tokenLen = 0; + + if (!pwFile) + return 0; + + if (retry) { + return 0; /* no good retrying - the file contents will be the same */ + } + + phrases = PORT_ZAlloc(maxPwdFileSize); + + if (!phrases) { + return 0; /* out of memory */ + } + + fd = PR_Open(pwFile, PR_RDONLY, 0); + if (!fd) { + fprintf(stderr, "No password file \"%s\" exists.\n", pwFile); + PORT_Free(phrases); + return NULL; + } + + nb = PR_Read(fd, phrases, maxPwdFileSize); + + PR_Close(fd); + + if (nb == 0) { + fprintf(stderr,"password file contains no data\n"); + PORT_Free(phrases); + return NULL; + } + + if (slot) { + tokenName = PK11_GetTokenName(slot); + if (tokenName) { + tokenLen = PORT_Strlen(tokenName); + } + } + i = 0; + do { + int startphrase = i; + int phraseLen; + + /* handle the Windows EOL case */ + while (phrases[i] != '\r' && phrases[i] != '\n' && i < nb) i++; + + /* terminate passphrase */ + phrases[i++] = '\0'; + /* clean up any EOL before the start of the next passphrase */ + while ( (isource != PW_NONE) { + PR_fprintf(PR_STDERR, "Incorrect password/PIN entered.\n"); + return NULL; + } + + switch (pwdata->source) { + case PW_NONE: + sprintf(prompt, "Enter Password or Pin for \"%s\":", + PK11_GetTokenName(slot)); + return GetPassword(stdin, stdout, prompt, CheckPassword); + case PW_FROMFILE: + pw = FilePasswd(slot, retry, pwdata->data); + pwdata->source = PW_PLAINTEXT; + pwdata->data = PL_strdup(pw); + return pw; + case PW_PLAINTEXT: + return PL_strdup(pwdata->data); + default: + break; + } + PR_fprintf(PR_STDERR, "Password check failed: No password found.\n"); + return NULL; + } + + /* + * GenerateRandom + */ + SECStatus GenerateRandom(unsigned char *rbuf, int rsize) + { + char meter[] = { + "\r| |" }; + int fd, count; + int c; + SECStatus rv = SECSuccess; + cc_t orig_cc_min; + cc_t orig_cc_time; + tcflag_t orig_lflag; + struct termios tio; + + fprintf(stderr, "To generate random numbers, " + "continue typing until the progress meter is full:\n\n"); + fprintf(stderr, "%s", meter); + fprintf(stderr, "\r|"); + + /* turn off echo on stdin & return on 1 char instead of NL */ + fd = fileno(stdin); + + tcgetattr(fd, &tio); + orig_lflag = tio.c_lflag; + orig_cc_min = tio.c_cc[VMIN]; + orig_cc_time = tio.c_cc[VTIME]; + tio.c_lflag &= ~ECHO; + tio.c_lflag &= ~ICANON; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + tcsetattr(fd, TCSAFLUSH, &tio); + /* Get random noise from keyboard strokes */ + count = 0; + while (count < rsize) { + c = getc(stdin); + if (c == EOF) { + rv = SECFailure; + break; + } + *(rbuf + count) = c; + if (count == 0 || c != *(rbuf + count -1)) { + count++; + fprintf(stderr, "*"); + } + } + rbuf[count] = '\0'; + + fprintf(stderr, "\n\nFinished. Press enter to continue: "); + while ((c = getc(stdin)) != '\n' && c != EOF) + ; + if (c == EOF) + rv = SECFailure; + fprintf(stderr, "\n"); + + /* set back termio the way it was */ + tio.c_lflag = orig_lflag; + tio.c_cc[VMIN] = orig_cc_min; + tio.c_cc[VTIME] = orig_cc_time; + tcsetattr(fd, TCSAFLUSH, &tio); + return rv; + } + + /* + * SeedFromNoiseFile + */ + SECStatus SeedFromNoiseFile(const char *noiseFileName) + { + char buf[2048]; + PRFileDesc *fd; + PRInt32 count; + + fd = PR_Open(noiseFileName, PR_RDONLY, 0); + if (!fd) { + fprintf(stderr, "failed to open noise file."); + return SECFailure; + } + + do { + count = PR_Read(fd,buf,sizeof(buf)); + if (count > 0) { + PK11_RandomUpdate(buf,count); + } + } while (count > 0); + + PR_Close(fd); + return SECSuccess; + } + + /* + * FileSize + */ + long FileSize(const char* filename) + { + struct stat stbuf; + stat(filename, &stbuf); + return stbuf.st_size; + } + + /* + * ReadDERFromFile + */ + SECStatus ReadDERFromFile(SECItem *der, const char *inFileName, PRBool ascii) + { + SECStatus rv = SECSuccess; + PRFileDesc *inFile = NULL; + + inFile = PR_Open(inFileName, PR_RDONLY, 0); + if (!inFile) { + PR_fprintf(PR_STDERR, "Failed to open file \"%s\" (%ld, %ld).\n", + inFileName, PR_GetError(), PR_GetOSError()); + rv = SECFailure; + goto cleanup; + } + + if (ascii) { + /* First convert ascii to binary */ + SECItem filedata; + char *asc, *body; + + /* Read in ascii data */ + rv = FileToItem(&filedata, inFile); + asc = (char *)filedata.data; + if (!asc) { + PR_fprintf(PR_STDERR, "unable to read data from input file\n"); + rv = SECFailure; + goto cleanup; + } + + /* check for headers and trailers and remove them */ + if ((body = strstr(asc, "-----BEGIN")) != NULL) { + char *trailer = NULL; + asc = body; + body = PORT_Strchr(body, '\n'); + if (!body) + body = PORT_Strchr(asc, '\r'); /* maybe this is a MAC file */ + if (body) + trailer = strstr(++body, "-----END"); + if (trailer != NULL) { + *trailer = '\0'; + } else { + PR_fprintf(PR_STDERR, "input has header but no trailer\n"); + PORT_Free(filedata.data); + rv = SECFailure; + goto cleanup; + } + } else { + body = asc; + } + + /* Convert to binary */ + rv = ATOB_ConvertAsciiToItem(der, body); + if (rv) { + PR_fprintf(PR_STDERR, "error converting ascii to binary %s\n", + PORT_GetError()); + PORT_Free(filedata.data); + rv = SECFailure; + goto cleanup; + } + + PORT_Free(filedata.data); + } else { + /* Read in binary der */ + rv = FileToItem(der, inFile); + if (rv) { + PR_fprintf(PR_STDERR, "error converting der \n"); + rv = SECFailure; + } + } + cleanup: + if (inFile) { + PR_Close(inFile); + } + return rv; + } + + \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/sample1/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/sample1/index.rst new file mode 100644 index 0000000000..ee1bb2dcf1 --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/sample1/index.rst @@ -0,0 +1,230 @@ +.. _mozilla_projects_nss_nss_sample_code_sample1: + +sample1 +======= + +.. container:: + + 1. A program to compute the hash of a file and save it to another file. + + .. code:: c + + /* NSPR Headers */ + #include + #include + #include + #include + #include + + /* NSS headers */ + #include + #include + #include + + typedef struct { + const char *hashName; + SECOidTag oid; + } NameTagPair; + + /* The hash algorithms supported */ + static const NameTagPair HASH_NAMES[] = { + { "MD2", SEC_OID_MD2 }, + { "MD5", SEC_OID_MD5 }, + { "SHA1", SEC_OID_SHA1 }, + { "SHA256", SEC_OID_SHA256 }, + { "SHA384", SEC_OID_SHA384 }, + { "SHA512", SEC_OID_SHA512 } + }; + + /* Maps a hash name to a SECOidTag. + * Returns NULL if the name is not a supported algorithm + */ + static SECOidTag HashNameToOIDTag(const char *hashName) + { + int i, nhashes = sizeof(HASH_NAMES); + SECOidTag hashtag = SEC_OID_UNKNOWN; + + for (i = 0; i < nhashes; i++) { + if (PORT_Strcasecmp(hashName, HASH_NAMES[i].hashName) == 0) { + hashtag = HASH_NAMES[i].oid; + break; + } + } + return hashtag; + } + + /* Newline */ + static void Newline(PRFileDesc* out) + { + PR_fprintf(out, "\n"); + } + + /* PrintAsHex */ + void PrintAsHex(PRFileDesc* out, unsigned char *data, unsigned int len) + { + unsigned i; + int column; + unsigned int limit = 15; + unsigned int level = 1; + + column = level; + if (!len) { + PR_fprintf(out, "(empty)\n"); + return; + } + + for (i = 0; i < len; i++) { + if (i != len - 1) { + PR_fprintf(out, "%02x:", data[i]); + column += 3; + } else { + PR_fprintf(out, "%02x", data[i]); + column += 2; + break; + } + if (column > 76 || (i % 16 == limit)) { + Newline(out); + column = level; + limit = i % 16; + } + } + if (column != level) { + Newline(out); + } + } + + + /* Prints a usage message and exits */ + static void Usage(const char *progName) + { + int htype; + int HASH_AlgTOTAL = sizeof(HASH_NAMES) / sizeof(HASH_NAMES[0]); + + fprintf(stderr, "Usage: %s -t type [ < input ] [ > output ]\n", progName); + fprintf(stderr, "%-20s Specify the digest method (must be one of\n", + "-t type"); + fprintf(stderr, "%-20s ", ""); + for (htype = 0; htype < HASH_AlgTOTAL; htype++) { + fprintf(stderr, HASH_NAMES[htype].hashName); + if (htype == (HASH_AlgTOTAL - 2)) + fprintf(stderr, " or "); + else if (htype != (HASH_AlgTOTAL - 1)) + fprintf(stderr, ", "); + } + fprintf(stderr, " (case ignored))\n"); + fprintf(stderr, "%-20s Define an input file to use (default is stdin)\n", + "< input"); + fprintf(stderr, "%-20s Define an output file to use (default is stdout)\n", + "> output"); + exit(-1); + } + + /* Check for the missing arguments */ + static void + PrintMsgAndExit(const char *progName, char opt) + { + fprintf(stderr, "%s: option -%c requires an argument\n", progName, opt); + Usage(progName); + } + + #define REQUIRE_ARG(opt,value) if (!(value)) PrintMsgAndExit(progName, opt) + + /* Digests a file according to the specified algorithm. + * It writes out the digest as a hexadecimal string. + */ + static int + DigestFile(PRFileDesc *outFile, PRFileDesc *inFile, SECOidTag hashOIDTag) + { + unsigned int nb; + unsigned char ibuf[4096]; + unsigned char digest[64]; + unsigned int len; + unsigned int digestLen; + HASH_HashType hashType; + HASHContext *hashContext = NULL; + + hashType = HASH_GetHashTypeByOidTag(hashOIDTag); + hashContext = HASH_Create(hashType); + if (hashContext == NULL) { + return SECFailure; + } + + do { + HASH_Begin(hashContext); + + /* Incrementally hash the file contents */ + while ((nb = PR_Read(inFile, ibuf, sizeof(ibuf))) > 0) { + HASH_Update(hashContext, ibuf, nb); + } + + HASH_End(hashContext, digest, &len, 64); + + /* Normally we would write it out in binary with + * nb = PR_Write(outFile, digest, len); + * but for illustration let's print it in hex. + */ + PrintAsHex(outFile, digest, len); + + } while (0); + + /* cleanup */ + if (hashContext != NULL) + HASH_Destroy(hashContext); + + return SECSuccess; + } + + /* + * This sample computes the hash of a file and saves it to another file. It illustrates the use of NSS message APIs. + */ + int main(int argc, char **argv) + { + SECOidTag hashOIDTag; + PLOptState *optstate; + PLOptStatus status; + SECStatus rv; + char *hashName = NULL; + char *progName = strrchr(argv[0], '/'); + + progName = progName ? progName + 1 : argv[0]; + + rv = NSS_NoDB_Init("/tmp"); + if (rv != SECSuccess) { + fprintf(stderr, "%s: NSS_Init failed in directory %s\n", progName, "/tmp"); + return -1; + } + + /* Parse command line arguments */ + optstate = PL_CreateOptState(argc, argv, "t:"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + switch (optstate->option) { + case 't': + REQUIRE_ARG(optstate->option, optstate->value); + hashName = strdup(optstate->value); + break; + } + } + + if (!hashName) + Usage(progName); + + /* convert and validate */ + hashOIDTag = HashNameToOIDTag(hashName); + if (hashOIDTag == SEC_OID_UNKNOWN) { + fprintf(stderr, "%s: invalid digest type - %s\n", progName, hashName); + Usage(progName); + } + + /* Digest it and print the result */ + rv = DigestFile(PR_STDOUT, PR_STDIN, hashOIDTag); + if (rv != SECSuccess) { + fprintf(stderr, "%s: problem digesting data (%d)\n", progName, PORT_GetError()); + } + + rv = NSS_Shutdown(); + if (rv != SECSuccess) { + exit(-1); + } + + return 0; + } \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/sample1_-_hashing/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/sample1_-_hashing/index.rst new file mode 100644 index 0000000000..3fe6f2fb17 --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/sample1_-_hashing/index.rst @@ -0,0 +1,257 @@ +.. _mozilla_projects_nss_nss_sample_code_sample1_-_hashing: + +Hashing - sample 1 +================== + +.. _nss_sample_code_1_hashing.: + +`NSS sample code 1: hashing. <#nss_sample_code_1_hashing.>`__ +------------------------------------------------------------- + +.. container:: + + The NSS same code below computes the hash of a file and saves it to another file, this + illustrates the use of NSS message APIs. + + .. code:: c + + /* 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 https://mozilla.org/MPL/2.0/. */ + + /* NSPR Headers */ + #include + #include + #include + #include + + /* NSS headers */ + #include + #include + #include + #include + + typedef struct { + const char *hashName; + SECOidTag oid; + } NameTagPair; + + /* The hash algorithms supported */ + static const NameTagPair HASH_NAMES[] = { + { "MD2", SEC_OID_MD2 }, + { "MD5", SEC_OID_MD5 }, + { "SHA1", SEC_OID_SHA1 }, + { "SHA256", SEC_OID_SHA256 }, + { "SHA384", SEC_OID_SHA384 }, + { "SHA512", SEC_OID_SHA512 } + }; + + /* + * Maps a hash name to a SECOidTag. + * Returns NULL if the name if not a supported algorithm + */ + static SECOidTag HashNameToOIDTag(const char *hashName) + { + int i, nhashes = sizeof(HASH_NAMES); + SECOidTag hashtag = SEC_OID_UNKNOWN; + + for (i = 0; i < nhashes; i++) { + if (PORT_Strcasecmp(hashName, HASH_NAMES[i].hashName) == 0) { + hashtag = HASH_NAMES[i].oid; + break; + } + } + return hashtag; + } + + /* + * Newline + */ + static void + Newline(PRFileDesc* out) + { + PR_fprintf(out, "\n"); + } + + /* + * PrintAsHex + */ + void + PrintAsHex(PRFileDesc* out, unsigned char *data, unsigned int len) + { + unsigned i; + int column; + unsigned int limit = 15; + unsigned int level = 1; + + column = level; + if (!len) { + PR_fprintf(out, "(empty)\n"); + return; + } + + for (i = 0; i < len; i++) { + if (i != len - 1) { + PR_fprintf(out, "%02x:", data[i]); + column += 3; + } else { + PR_fprintf(out, "%02x", data[i]); + column += 2; + break; + } + if (column > 76 || (i % 16 == limit)) { + Newline(out); + column = level; + limit = i % 16; + } + } + if (column != level) { + Newline(out); + } + } + + + /* + * Prints a usage message and exits + */ + static void + Usage(const char *progName) + { + int htype; + int HASH_AlgTOTAL = sizeof(HASH_NAMES) / sizeof(HASH_NAMES[0]); + + fprintf(stderr, "Usage: %s -t type [ < input ] [ > output ]\n", progName); + fprintf(stderr, "%-20s Specify the digest method (must be one of\n", + "-t type"); + fprintf(stderr, "%-20s ", ""); + for (htype = 0; htype < HASH_AlgTOTAL; htype++) { + fprintf(stderr, HASH_NAMES[htype].hashName); + if (htype == (HASH_AlgTOTAL - 2)) + fprintf(stderr, " or "); + else if (htype != (HASH_AlgTOTAL - 1)) + fprintf(stderr, ", "); + } + fprintf(stderr, " (case ignored))\n"); + fprintf(stderr, "%-20s Define an input file to use (default is stdin)\n", + "< input"); + fprintf(stderr, "%-20s Define an output file to use (default is stdout)\n", + "> output"); + exit(-1); + } + + /* + * Check for the missing arguments + */ + static void + PrintMsgAndExit(const char *progName, char opt) + { + fprintf(stderr, "%s: option -%c requires an argument\n", progName, opt); + Usage(progName); + } + + #define REQUIRE_ARG(opt,value) if (!(value)) PrintMsgAndExit(progName, opt) + + /* + * Digests a file according to the specified algorithm. + * It writes out the digest as a hexadecimal string. + */ + static int + DigestFile(PRFileDesc *outFile, PRFileDesc *inFile, SECOidTag hashOIDTag) + { + unsigned int nb; + unsigned char ibuf[4096]; + unsigned char digest[64]; + unsigned int len; + HASH_HashType hashType; + HASHContext *hashContext = NULL; + + hashType = HASH_GetHashTypeByOidTag(hashOIDTag); + hashContext = HASH_Create(hashType); + if (hashContext == NULL) { + return SECFailure; + } + + do { + + HASH_Begin(hashContext); + + /* Incrementally hash the file contents */ + while ((nb = PR_Read(inFile, ibuf, sizeof(ibuf))) > 0) { + HASH_Update(hashContext, ibuf, nb); + } + + HASH_End(hashContext, digest, &len, 64); + + /* Normally we would write it out in binary with + * nb = PR_Write(outFile, digest, len); + * but for illustration let's print it in hex. + */ + PrintAsHex(outFile, digest, len); + + } while (0); + + /* cleanup */ + if (hashContext != NULL) + HASH_Destroy(hashContext); + + return SECSuccess; + } + + /* + * This sample computes the hash of a file and saves it + * to another file. It illustrates the use of NSS message + * APIs. + */ + int main(int argc, char **argv) + { + SECOidTag hashOIDTag; + PLOptState *optstate; + PLOptStatus status; + SECStatus rv; + char *hashName = NULL; + char *progName = strrchr(argv[0], '/'); + + progName = progName ? progName + 1 : argv[0]; + + rv = NSS_NoDB_Init("/tmp"); + if (rv != SECSuccess) { + fprintf(stderr, "%s: NSS_Init failed in directory %s\n", + progName, "/tmp"); + return -1; + } + + /* Parse command line arguments */ + optstate = PL_CreateOptState(argc, argv, "t:"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + switch (optstate->option) { + case 't': + REQUIRE_ARG(optstate->option, optstate->value); + hashName = strdup(optstate->value); + break; + } + } + + if (!hashName) + Usage(progName); + + /* convert and validate */ + hashOIDTag = HashNameToOIDTag(hashName); + if (hashOIDTag == SEC_OID_UNKNOWN) { + fprintf(stderr, "%s: invalid digest type - %s\n", progName, hashName); + Usage(progName); + } + + /* Digest it and print the result */ + rv = DigestFile(PR_STDOUT, PR_STDIN, hashOIDTag); + if (rv != SECSuccess) { + fprintf(stderr, "%s: problem digesting data (%d)\n", + progName, PORT_GetError()); + } + + rv = NSS_Shutdown(); + if (rv != SECSuccess) { + exit(-1); + } + + return 0; + } \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/sample2/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/sample2/index.rst new file mode 100644 index 0000000000..5243b05e4d --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/sample2/index.rst @@ -0,0 +1,12 @@ +.. _mozilla_projects_nss_nss_sample_code_sample2: + +sample2 +======= + +.. container:: + + .. container:: summary + + .. code:: c + + /* 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 https://mozilla.org/MPL/2.0/. */ /* NSPR Headers */ #include #include #include #include #include #include #include /* NSS headers */ #include #include #include #include #include #include #include #include #include #include #include /* our samples utilities */ #include "util.h" /* Constants */ #define BLOCKSIZE 32 #define MODBLOCKSIZE 128 #define DEFAULT_KEY_BITS 1024 /* Header file Constants */ #define ENCKEY_HEADER "-----BEGIN WRAPPED ENCKEY-----" #define ENCKEY_TRAILER "-----END WRAPPED ENCKEY-----" #define MACKEY_HEADER "-----BEGIN WRAPPED MACKEY-----" #define MACKEY_TRAILER "-----END WRAPPED MACKEY-----" #define IV_HEADER "-----BEGIN IV-----" #define IV_TRAILER "-----END IV-----" #define MAC_HEADER "-----BEGIN MAC-----" #define MAC_TRAILER "-----END MAC-----" #define PAD_HEADER "-----BEGIN PAD-----" #define PAD_TRAILER "-----END PAD-----" #define LAB_HEADER "-----BEGIN KEY LABEL-----" #define LAB_TRAILER "-----END KEY LABEL-----" #define PUBKEY_HEADER "-----BEGIN PUB KEY -----" #define PUBKEY_TRAILER "-----END PUB KEY -----" #define NS_CERTREQ_HEADER "-----BEGIN NEW CERTIFICATE REQUEST-----" #define NS_CERTREQ_TRAILER "-----END NEW CERTIFICATE REQUEST-----" #define NS_CERT_ENC_HEADER "-----BEGIN CERTIFICATE FOR ENCRYPTION-----" #define NS_CERT_ENC_TRAILER "-----END CERTIFICATE FOR ENCRYPTION-----" #define NS_CERT_VFY_HEADER "-----BEGIN CERTIFICATE FOR SIGNATURE VERIFICATION-----" #define NS_CERT_VFY_TRAILER "-----END CERTIFICATE FOR SIGNATURE VERIFICATION-----" #define NS_SIG_HEADER "-----BEGIN SIGNATURE-----" #define NS_SIG_TRAILER "-----END SIGNATURE-----" #define NS_CERT_HEADER "-----BEGIN CERTIFICATE-----" #define NS_CERT_TRAILER "-----END CERTIFICATE-----" /* Missing publically from nss versions earlier than 3.13 */ #ifndef SEC_ERROR_BASE #define SEC_ERROR_BASE (-0x2000) typedef enum { SEC_ERROR_IO = SEC_ERROR_BASE + 0, SEC_ERROR_TOKEN_NOT_LOGGED_IN = (SEC_ERROR_BASE + 155), SEC_ERROR_END_OF_LIST } SECErrorCodes; #endif /* PORT_ErrorToString introduced in nss 3.13. On earlier versions of nss that * don't support error tables, PR_ErrorToString will return "Unknown code". */ #ifndef PORT_ErrorToString #define PORT_ErrorToString(err) PR_ErrorToString((err), PR_LANGUAGE_I_DEFAULT) #endif /* sample 6 commands */ typedef enum { GENERATE_CSR, ADD_CERT_TO_DB, SAVE_CERT_TO_HEADER, ENCRYPT, DECRYPT, SIGN, VERIFY, UNKNOWN } CommandType; typedef enum { SYMKEY = 0, MACKEY = 1, IV = 2, MAC = 3, PAD = 4, PUBKEY = 5, LAB = 6, CERTENC= 7, CERTVFY= 8, SIG = 9 } HeaderType; /* * Print usage message and exit */ static void Usage(const char *progName) { fprintf(stderr, "\nUsage: %s %s %s %s %s %s %s %s %s %s\n\n", progName, " - -d ", "[-p | -f ] [-z ] [-a <\"\">]", "-s -r | ", "-n -t -c [ -r -u [-x <\"\">] -m ] | ", "-n -b | ", "-b -i -e | ", "-b -i | ", "-b -i | ", "-b -e -o \n"); fprintf(stderr, "commands:\n\n"); fprintf(stderr, "%s %s\n --for generating cert request (for CA also)\n\n", progName, "-G -s -r "); fprintf(stderr, "%s %s\n --to input and store cert (for CA also)\n\n", progName, "-A -n -t -c [ -r -u [-x <\"\">] -m ]"); fprintf(stderr, "%s %s\n --to put cert in header\n\n", progName, "-H -n -b [-v <\"\">]"); fprintf(stderr, "%s %s\n --to find public key from cert in header and encrypt\n\n", progName, "-E -b -i -e "); fprintf(stderr, "%s %s\n --decrypt using corresponding private key \n\n", progName, "-D -b -e -o "); fprintf(stderr, "%s %s\n --Sign using private key \n\n", progName, "-S -b -i "); fprintf(stderr, "%s %s\n --Verify using public key \n\n", progName, "-V -b -i "); fprintf(stderr, "options:\n\n"); fprintf(stderr, "%-30s - db directory path\n\n", "-d "); fprintf(stderr, "%-30s - db password [optional]\n\n", "-p "); fprintf(stderr, "%-30s - db password file [optional]\n\n", "-f "); fprintf(stderr, "%-30s - noise file name [optional]\n\n", "-z "); fprintf(stderr, "%-30s - input file name\n\n", "-i "); fprintf(stderr, "%-30s - header file name\n\n", "-b "); fprintf(stderr, "%-30s - encrypt file name\n\n", "-e "); fprintf(stderr, "%-30s - output file name\n\n", "-o "); fprintf(stderr, "%-30s - certificate serial number\n\n", "-m "); fprintf(stderr, "%-30s - certificate nickname\n\n", "-n "); fprintf(stderr, "%-30s - certificate trust\n\n", "-t "); fprintf(stderr, "%-30s - certificate issuer nickname\n\n", "-u "); fprintf(stderr, "%-30s - certificate signing request \n\n", "-r "); fprintf(stderr, "%-30s - generate a self-signed cert [optional]\n\n", "-x"); fprintf(stderr, "%-30s - to enable ascii [optional]\n\n", "-a"); fprintf(stderr, "%-30s - to save certificate to header file as sig verification [optional]\n\n", "-v"); exit(-1); } /* * Validate the options used for Generate CSR command */ static void ValidateGenerateCSRCommand(const char *progName, const char *dbdir, CERTName *subject, const char *subjectStr, const char *certReqFileName) { PRBool validationFailed = PR_FALSE; if (!subject) { PR_fprintf(PR_STDERR, "%s -G -d %s -s: improperly formatted name: \"%s\"\n", progName, dbdir, subjectStr); validationFailed = PR_TRUE; } if (!certReqFileName) { PR_fprintf(PR_STDERR, "%s -G -d %s -s %s -r: certificate request file name not found\n", progName, dbdir, subjectStr); validationFailed = PR_TRUE; } if (validationFailed) { fprintf(stderr, "\nUsage: %s %s \n\n", progName, "-G -d -s -r \n"); exit(-1); } } /* * Validate the options used for Add Cert to DB command */ static void ValidateAddCertToDBCommand(const char *progName, const char *dbdir, const char *nickNameStr, const char *trustStr, const char *certFileName, const char *certReqFileName, const char *issuerNameStr, const char *serialNumberStr, PRBool selfsign) { PRBool validationFailed = PR_FALSE; if (!nickNameStr) { PR_fprintf(PR_STDERR, "%s -A -d %s -n : nick name is missing\n", progName, dbdir); validationFailed = PR_TRUE; } if (!trustStr) { PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t: trust flag is missing\n", progName, dbdir, nickNameStr); validationFailed = PR_TRUE; } if (!certFileName) { PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c: certificate file name not found\n", progName, dbdir, nickNameStr, trustStr, serialNumberStr, certReqFileName); validationFailed = PR_TRUE; } if (PR_Access(certFileName, PR_ACCESS_EXISTS) == PR_FAILURE) { if (!certReqFileName) { PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c %s -r: certificate file or certificate request file is not found\n", progName, dbdir, nickNameStr, trustStr, certFileName); validationFailed = PR_TRUE; } if (!selfsign && !issuerNameStr) { PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c %s -r %s -u : issuer name is missing\n", progName, dbdir, nickNameStr, trustStr, certFileName, certReqFileName); validationFailed = PR_TRUE; } if (!serialNumberStr) { PR_fprintf(PR_STDERR, "%s -A -d %s -n %s -t %s -c %s -r %s -u %s -m : serial number is missing\n", progName, dbdir, nickNameStr, trustStr, certFileName, certReqFileName, issuerNameStr); validationFailed = PR_TRUE; } } if (validationFailed) { fprintf(stderr, "\nUsage: %s %s \n\n", progName, " -A -d -n -t -c \n"); fprintf(stderr, " OR\n"); fprintf(stderr, "\nUsage: %s %s \n\n", progName, "-A -d -n -t -c -r -u -m [-x <\"\">] \n"); exit(-1); } } /* * Validate the options used for Save Cert To Header command */ static void ValidateSaveCertToHeaderCommand(const char *progName, const char *dbdir, const char *nickNameStr, const char *headerFileName) { PRBool validationFailed = PR_FALSE; if (!nickNameStr) { PR_fprintf(PR_STDERR, "%s -S -d %s -n : nick name is missing\n", progName, dbdir); validationFailed = PR_TRUE; } if (!headerFileName) { PR_fprintf(PR_STDERR, "%s -S -d %s -n %s -b : header file name is not found\n", progName, dbdir, nickNameStr); validationFailed = PR_TRUE; } if (validationFailed) { fprintf(stderr, "\nUsage: %s %s \n\n", progName, "-S -d -n -b [-v <\"\">]\n"); exit(-1); } } /* * Validate the options used for Encrypt command */ static void ValidateEncryptCommand(const char *progName, const char *dbdir, const char *nickNameStr, const char *headerFileName, const char *inFileName, const char *encryptedFileName) { PRBool validationFailed = PR_FALSE; if (!nickNameStr) { PR_fprintf(PR_STDERR, "%s -E -d %s -n : nick name is missing\n", progName, dbdir); validationFailed = PR_TRUE; } if (!headerFileName) { PR_fprintf(PR_STDERR, "%s -E -d %s -n %s -b : header file name is not found\n", progName, dbdir, nickNameStr); validationFailed = PR_TRUE; } if (!inFileName) { PR_fprintf(PR_STDERR, "%s -E -d %s -n %s -b %s -i : input file name is not found\n", progName, dbdir, nickNameStr, headerFileName); validationFailed = PR_TRUE; } if (!encryptedFileName) { PR_fprintf(PR_STDERR, "%s -E -d %s -n %s -b %s -i %s -e : encrypt file name is not found\n", progName, dbdir, nickNameStr, headerFileName, inFileName); validationFailed = PR_TRUE; } if (validationFailed) { fprintf(stderr, "\nUsage: %s %s \n\n", progName, "-E -d -b -i -e -n \n"); exit(-1); } } /* * Validate the options used for Sign command */ static void ValidateSignCommand(const char *progName, const char *dbdir, const char *nickNameStr, const char *headerFileName, const char *inFileName) { PRBool validationFailed = PR_FALSE; if (!nickNameStr) { PR_fprintf(PR_STDERR, "%s -I -d %s -n : nick name is missing\n", progName, dbdir); validationFailed = PR_TRUE; } if (!headerFileName) { PR_fprintf(PR_STDERR, "%s -I -d %s -n %s -b : header file name is not found\n", progName, dbdir, nickNameStr); validationFailed = PR_TRUE; } if (!inFileName) { PR_fprintf(PR_STDERR, "%s -I -d %s -n %s -b %s -i : input file name is not found\n", progName, dbdir, nickNameStr, headerFileName); validationFailed = PR_TRUE; } if (validationFailed) { fprintf(stderr, "\nUsage: %s %s \n\n", progName, "-I -d -b -i -n \n"); exit(-1); } } /* * Validate the options used for verify command */ static void ValidateVerifyCommand(const char *progName, const char *dbdir, const char *headerFileName, const char *inFileName) { PRBool validationFailed = PR_FALSE; if (!headerFileName) { PR_fprintf(PR_STDERR, "%s -V -d %s -b : header file name is not found\n", progName, dbdir); validationFailed = PR_TRUE; } if (!inFileName) { PR_fprintf(PR_STDERR, "%s -I -d %s -b %s -i : input file name is not found\n", progName, dbdir, headerFileName); validationFailed = PR_TRUE; } if (validationFailed) { fprintf(stderr, "\nUsage: %s %s \n\n", progName, "-I -d -b -i \n"); exit(-1); } } /* * Validate the options used for Decrypt command */ static void ValidateDecryptCommand(const char *progName, const char *dbdir, const char *headerFileName, const char *encryptedFileName, const char *outFileName) { PRBool validationFailed = PR_FALSE; if (!headerFileName) { PR_fprintf(PR_STDERR, "%s -D -d %s -b : header file name is not found\n", progName, dbdir); validationFailed = PR_TRUE; } if (!encryptedFileName) { PR_fprintf(PR_STDERR, "%s -D -d %s -b %s -e : encrypt file name is not found\n", progName, dbdir, headerFileName); validationFailed = PR_TRUE; } if (!outFileName) { PR_fprintf(PR_STDERR, "%s -D -d %s -b %s -e %s -o : output file name is not found\n", progName, dbdir, headerFileName, encryptedFileName); validationFailed = PR_TRUE; } if (validationFailed) { fprintf(stderr, "\nUsage: %s %s \n\n", progName, "-D -d -b -e -o \n"); exit(-1); } } /* * Sign the contents of input file using private key and * return result as SECItem */ SECStatus SignData(const char *inFileName, SECKEYPrivateKey *pk, SECItem *res) { SECStatus rv = SECFailure; unsigned int nb; unsigned char ibuf[4096]; PRFileDesc *inFile = NULL; SGNContext *sgn = NULL; /* Open the input file for reading */ inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", inFileName); rv = SECFailure; goto cleanup; } /* Sign using private key */ sgn = SGN_NewContext(SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, pk); if (!sgn) { PR_fprintf(PR_STDERR, "unable to create context for signing\n"); rv = SECFailure; goto cleanup; } rv = SGN_Begin(sgn); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "problem while SGN_Begin\n"); goto cleanup; } while ((nb = PR_Read(inFile, ibuf, sizeof(ibuf))) > 0) { rv = SGN_Update(sgn, ibuf, nb); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "problem while SGN_Update\n"); goto cleanup; } } rv = SGN_End(sgn, res); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "problem while SGN_End\n"); goto cleanup; } cleanup: if (inFile) { PR_Close(inFile); } if (sgn) { SGN_DestroyContext(sgn, PR_TRUE); } return rv; } /* * Verify the signature using public key */ SECStatus VerifyData(const char *inFileName, SECKEYPublicKey *pk, SECItem *sigItem, secuPWData *pwdata) { unsigned int nb; unsigned char ibuf[4096]; SECStatus rv = SECFailure; VFYContext *vfy = NULL; PRFileDesc *inFile = NULL; /* Open the input file for reading */ inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", inFileName); rv = SECFailure; goto cleanup; } vfy = VFY_CreateContext(pk, sigItem, SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION, pwdata); if (!vfy) { PR_fprintf(PR_STDERR, "unable to create context for verifying signature\n"); rv = SECFailure; goto cleanup; } rv = VFY_Begin(vfy); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "problem while VFY_Begin\n"); goto cleanup; } while ((nb = PR_Read(inFile, ibuf, sizeof(ibuf))) > 0) { rv = VFY_Update(vfy, ibuf, nb); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "problem while VFY_Update\n"); goto cleanup; } } rv = VFY_End(vfy); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "problem while VFY_End\n"); goto cleanup; } cleanup: if (inFile) { PR_Close(inFile); } if (vfy) { VFY_DestroyContext(vfy, PR_TRUE); } return rv; } /* * Write Cryptographic parameters to header file */ SECStatus WriteToHeaderFile(const char *buf, unsigned int len, HeaderType type, PRFileDesc *outFile) { SECStatus rv; const char *header; const char *trailer; switch (type) { case SYMKEY: header = ENCKEY_HEADER; trailer = ENCKEY_TRAILER; break; case MACKEY: header = MACKEY_HEADER; trailer = MACKEY_TRAILER; break; case IV: header = IV_HEADER; trailer = IV_TRAILER; break; case MAC: header = MAC_HEADER; trailer = MAC_TRAILER; break; case PAD: header = PAD_HEADER; trailer = PAD_TRAILER; break; case PUBKEY: header = PUBKEY_HEADER; trailer = PUBKEY_TRAILER; break; case CERTENC: header = NS_CERT_ENC_HEADER; trailer = NS_CERT_ENC_TRAILER; break; case CERTVFY: header = NS_CERT_VFY_HEADER; trailer = NS_CERT_VFY_TRAILER; break; case SIG: header = NS_SIG_HEADER; trailer = NS_SIG_TRAILER; break; case LAB: header = LAB_HEADER; trailer = LAB_TRAILER; PR_fprintf(outFile, "%s\n", header); PR_fprintf(outFile, "%s\n", buf); PR_fprintf(outFile, "%s\n\n", trailer); return SECSuccess; break; default: return SECFailure; } PR_fprintf(outFile, "%s\n", header); PrintAsHex(outFile, buf, len); PR_fprintf(outFile, "%s\n\n", trailer); return SECSuccess; } /* * Read cryptographic parameters from the header file */ SECStatus ReadFromHeaderFile(const char *fileName, HeaderType type, SECItem *item, PRBool isHexData) { SECStatus rv = SECSuccess; PRFileDesc* file = NULL; SECItem filedata; SECItem outbuf; unsigned char *nonbody; unsigned char *body; char *header; char *trailer; outbuf.type = siBuffer; file = PR_Open(fileName, PR_RDONLY, 0); if (!file) { PR_fprintf(PR_STDERR, "Failed to open %s\n", fileName); rv = SECFailure; goto cleanup; } switch (type) { case PUBKEY: header = PUBKEY_HEADER; trailer = PUBKEY_TRAILER; break; case SYMKEY: header = ENCKEY_HEADER; trailer = ENCKEY_TRAILER; break; case MACKEY: header = MACKEY_HEADER; trailer = MACKEY_TRAILER; break; case IV: header = IV_HEADER; trailer = IV_TRAILER; break; case MAC: header = MAC_HEADER; trailer = MAC_TRAILER; break; case PAD: header = PAD_HEADER; trailer = PAD_TRAILER; break; case LAB: header = LAB_HEADER; trailer = LAB_TRAILER; break; case CERTENC: header = NS_CERT_ENC_HEADER; trailer = NS_CERT_ENC_TRAILER; break; case CERTVFY: header = NS_CERT_VFY_HEADER; trailer = NS_CERT_VFY_TRAILER; break; case SIG: header = NS_SIG_HEADER; trailer = NS_SIG_TRAILER; break; default: rv = SECFailure; goto cleanup; } rv = FileToItem(&filedata, file); nonbody = (char *)filedata.data; if (!nonbody) { PR_fprintf(PR_STDERR, "unable to read data from input file\n"); rv = SECFailure; goto cleanup; } /* check for headers and trailers and remove them */ if ((body = strstr(nonbody, header)) != NULL) { char *trail = NULL; nonbody = body; body = PORT_Strchr(body, '\n'); if (!body) body = PORT_Strchr(nonbody, '\r'); /* maybe this is a MAC file */ if (body) trail = strstr(++body, trailer); if (trail != NULL) { *trail = '\0'; } else { PR_fprintf(PR_STDERR, "input has header but no trailer\n"); PORT_Free(filedata.data); rv = SECFailure; goto cleanup; } } else { /* headers didn't exist */ char *trail = NULL; body = nonbody; if (body) { trail = strstr(++body, trailer); if (trail != NULL) { PR_fprintf(PR_STDERR, "input has no header but has trailer\n"); PORT_Free(filedata.data); rv = SECFailure; goto cleanup; } } } HexToBuf(body, item, isHexData); cleanup: if (file) { PR_Close(file); } return rv; } /* * Generate the private key */ SECKEYPrivateKey * GeneratePrivateKey(KeyType keytype, PK11SlotInfo *slot, int size, int publicExponent, const char *noise, SECKEYPublicKey **pubkeyp, const char *pqgFile, secuPWData *pwdata) { CK_MECHANISM_TYPE mechanism; SECOidTag algtag; PK11RSAGenParams rsaparams; void *params; SECKEYPrivateKey *privKey = NULL; SECStatus rv; unsigned char randbuf[BLOCKSIZE + 1]; rv = GenerateRandom(randbuf, BLOCKSIZE); if (rv != SECSuccess) { fprintf(stderr, "Error while generating the random numbers : %s\n", PORT_ErrorToString(rv)); goto cleanup; } PK11_RandomUpdate(randbuf, BLOCKSIZE); switch (keytype) { case rsaKey: rsaparams.keySizeInBits = size; rsaparams.pe = publicExponent; mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN; algtag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; params = &rsaparams; break; default: goto cleanup; } fprintf(stderr, "\n\n"); fprintf(stderr, "Generating key. This may take a few moments...\n\n"); privKey = PK11_GenerateKeyPair(slot, mechanism, params, pubkeyp, PR_TRUE /*isPerm*/, PR_TRUE /*isSensitive*/, pwdata); cleanup: return privKey; } /* * Get the certificate request from CSR */ static CERTCertificateRequest * GetCertRequest(char *inFileName, PRBool ascii) { CERTSignedData signedData; SECItem reqDER; CERTCertificateRequest *certReq = NULL; SECStatus rv = SECSuccess; PRArenaPool *arena = NULL; reqDER.data = NULL; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (arena == NULL) { rv = SECFailure; goto cleanup; } rv = ReadDERFromFile(&reqDER, inFileName, ascii); if (rv) { rv = SECFailure; goto cleanup; } certReq = (CERTCertificateRequest*) PORT_ArenaZAlloc (arena, sizeof(CERTCertificateRequest)); if (!certReq) { rv = SECFailure; goto cleanup; } certReq->arena = arena; /* Since cert request is a signed data, must decode to get the inner data */ PORT_Memset(&signedData, 0, sizeof(signedData)); rv = SEC_ASN1DecodeItem(arena, &signedData, SEC_ASN1_GET(CERT_SignedDataTemplate), &reqDER); if (rv) { rv = SECFailure; goto cleanup; } rv = SEC_ASN1DecodeItem(arena, certReq, SEC_ASN1_GET(CERT_CertificateRequestTemplate), &signedData.data); if (rv) { rv = SECFailure; goto cleanup; } rv = CERT_VerifySignedDataWithPublicKeyInfo(&signedData, &certReq->subjectPublicKeyInfo, NULL /* wincx */); if (reqDER.data) { SECITEM_FreeItem(&reqDER, PR_FALSE); } cleanup: if (rv) { PR_fprintf(PR_STDERR, "bad certificate request\n"); if (arena) { PORT_FreeArena(arena, PR_FALSE); } certReq = NULL; } return certReq; } /* * Sign Cert */ static SECItem * SignCert(CERTCertDBHandle *handle, CERTCertificate *cert, PRBool selfsign, SECOidTag hashAlgTag, SECKEYPrivateKey *privKey, char *issuerNickName, void *pwarg) { SECItem der; SECStatus rv; SECOidTag algID; void *dummy; PRArenaPool *arena = NULL; SECItem *result = NULL; SECKEYPrivateKey *caPrivateKey = NULL; if (!selfsign) { CERTCertificate *issuer = PK11_FindCertFromNickname(issuerNickName, pwarg); if ((CERTCertificate *)NULL == issuer) { PR_fprintf(PR_STDERR, "unable to find issuer with nickname %s\n", issuerNickName); goto cleanup; } privKey = caPrivateKey = PK11_FindKeyByAnyCert(issuer, pwarg); CERT_DestroyCertificate(issuer); if (caPrivateKey == NULL) { PR_fprintf(PR_STDERR, "unable to retrieve key %s\n", issuerNickName); goto cleanup; } } arena = cert->arena; algID = SEC_GetSignatureAlgorithmOidTag(privKey->keyType, hashAlgTag); if (algID == SEC_OID_UNKNOWN) { PR_fprintf(PR_STDERR, "Unknown key or hash type for issuer.\n"); goto cleanup; } rv = SECOID_SetAlgorithmID(arena, &cert->signature, algID, 0); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not set signature algorithm id.\n%s\n", PORT_ErrorToString(rv)); goto cleanup; } /* we only deal with cert v3 here */ *(cert->version.data) = 2; cert->version.len = 1; der.len = 0; der.data = NULL; dummy = SEC_ASN1EncodeItem (arena, &der, cert, SEC_ASN1_GET(CERT_CertificateTemplate)); if (!dummy) { PR_fprintf(PR_STDERR, "Could not encode certificate.\n"); goto cleanup; } result = (SECItem *) PORT_ArenaZAlloc (arena, sizeof (SECItem)); if (result == NULL) { PR_fprintf(PR_STDERR, "Could not allocate item for certificate data.\n"); goto cleanup; } rv = SEC_DerSignData(arena, result, der.data, der.len, privKey, algID); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not sign encoded certificate data : %s\n", PORT_ErrorToString(rv)); /* result allocated out of the arena, it will be freed * when the arena is freed */ result = NULL; goto cleanup; } cert->derCert = *result; cleanup: if (caPrivateKey) { SECKEY_DestroyPrivateKey(caPrivateKey); } return result; } /* * MakeV1Cert */ static CERTCertificate * MakeV1Cert(CERTCertDBHandle *handle, CERTCertificateRequest *req, char * issuerNickName, PRBool selfsign, unsigned int serialNumber, int warpmonths, int validityMonths) { PRExplodedTime printableTime; PRTime now; PRTime after; CERTValidity *validity = NULL; CERTCertificate *issuerCert = NULL; CERTCertificate *cert = NULL; if ( !selfsign ) { issuerCert = CERT_FindCertByNicknameOrEmailAddr(handle, issuerNickName); if (!issuerCert) { PR_fprintf(PR_STDERR, "could not find certificate named %s\n", issuerNickName); goto cleanup; } } now = PR_Now(); PR_ExplodeTime (now, PR_GMTParameters, &printableTime); if ( warpmonths ) { printableTime.tm_month += warpmonths; now = PR_ImplodeTime (&printableTime); PR_ExplodeTime (now, PR_GMTParameters, &printableTime); } printableTime.tm_month += validityMonths; after = PR_ImplodeTime (&printableTime); /* note that the time is now in micro-second unit */ validity = CERT_CreateValidity (now, after); if (validity) { cert = CERT_CreateCertificate(serialNumber, (selfsign ? &req->subject : &issuerCert->subject), validity, req); CERT_DestroyValidity(validity); } cleanup: if ( issuerCert ) { CERT_DestroyCertificate (issuerCert); } return cert; } /* * Add a certificate to the nss database */ SECStatus AddCert(PK11SlotInfo *slot, CERTCertDBHandle *handle, const char *name, char *trusts, char *inFileName, PRBool ascii, PRBool emailcert, void *pwdata) { SECItem certDER; SECStatus rv; CERTCertTrust *trust = NULL; CERTCertificate *cert = NULL; certDER.data = NULL; /* Read in the entire file specified with the -i argument */ rv = ReadDERFromFile(&certDER, inFileName, ascii); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "unable to read input file %s : %s\n", inFileName, PORT_ErrorToString(rv)); goto cleanup; } /* Read in an ASCII cert and return a CERTCertificate */ cert = CERT_DecodeCertFromPackage((char *)certDER.data, certDER.len); if (!cert) { PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); rv = SECFailure; goto cleanup; } /* Create a cert trust */ trust = (CERTCertTrust *)PORT_ZAlloc(sizeof(CERTCertTrust)); if (!trust) { PR_fprintf(PR_STDERR, "unable to allocate cert trust\n"); rv = SECFailure; goto cleanup; } rv = CERT_DecodeTrustString(trust, trusts); if (rv) { PR_fprintf(PR_STDERR, "unable to decode trust string\n"); rv = SECFailure; goto cleanup; } rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, name, PR_FALSE); if (rv != SECSuccess) { /* sigh, PK11_Import Cert and CERT_ChangeCertTrust should have * been coded to take a password arg. */ if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) { rv = PK11_Authenticate(slot, PR_TRUE, pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "could not authenticate to token %s : %s\n", PK11_GetTokenName(slot), PORT_ErrorToString(rv)); rv = SECFailure; goto cleanup; } rv = PK11_ImportCert(slot, cert, CK_INVALID_HANDLE, name, PR_FALSE); } if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "could not add certificate to token or database : %s\n", PORT_ErrorToString(rv)); rv = SECFailure; goto cleanup; } } rv = CERT_ChangeCertTrust(handle, cert, trust); if (rv != SECSuccess) { if (PORT_GetError() == SEC_ERROR_TOKEN_NOT_LOGGED_IN) { rv = PK11_Authenticate(slot, PR_TRUE, pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "could not authenticate to token %s : %s\n", PK11_GetTokenName(slot), PORT_ErrorToString(rv)); rv = SECFailure; goto cleanup; } rv = CERT_ChangeCertTrust(handle, cert, trust); } if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "could not change trust on certificate : %s\n", PORT_ErrorToString(rv)); rv = SECFailure; goto cleanup; } } if (emailcert) { CERT_SaveSMimeProfile(cert, NULL, pwdata); } cleanup: if (cert) { CERT_DestroyCertificate (cert); } if (trust) { PORT_Free(trust); } if (certDER.data) { PORT_Free(certDER.data); } return rv; } /* * Create a certificate */ static SECStatus CreateCert( CERTCertDBHandle *handle, PK11SlotInfo *slot, char * issuerNickName, char *inFileName, char *outFileName, SECKEYPrivateKey **selfsignprivkey, void *pwarg, SECOidTag hashAlgTag, unsigned int serialNumber, int warpmonths, int validityMonths, const char *dnsNames, PRBool ascii, PRBool selfsign) { void *extHandle; SECItem reqDER; CERTCertExtension **CRexts; SECStatus rv = SECSuccess; CERTCertificate *subjectCert = NULL; CERTCertificateRequest *certReq = NULL; PRFileDesc *outFile = NULL; SECItem *certDER = NULL; reqDER.data = NULL; outFile = PR_Open(outFileName, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, 00660); /* Create a cert request object from the input cert request der */ certReq = GetCertRequest(inFileName, ascii); if (certReq == NULL) { rv = SECFailure; goto cleanup; } subjectCert = MakeV1Cert(handle, certReq, issuerNickName, selfsign, serialNumber, warpmonths, validityMonths); if (subjectCert == NULL) { rv = SECFailure; goto cleanup; } extHandle = CERT_StartCertExtensions (subjectCert); if (extHandle == NULL) { rv = SECFailure; goto cleanup; } if (certReq->attributes != NULL && certReq->attributes[0] != NULL && certReq->attributes[0]->attrType.data != NULL && certReq->attributes[0]->attrType.len > 0 && SECOID_FindOIDTag(&certReq->attributes[0]->attrType) == SEC_OID_PKCS9_EXTENSION_REQUEST) { rv = CERT_GetCertificateRequestExtensions(certReq, &CRexts); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "%s\n", PORT_ErrorToString(rv)); goto cleanup; } rv = CERT_MergeExtensions(extHandle, CRexts); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "%s\n", PORT_ErrorToString(rv)); goto cleanup; } } CERT_FinishExtensions(extHandle); /* self-signing a cert request, find the private key */ if (*selfsignprivkey == NULL) { *selfsignprivkey = PK11_FindKeyByDERCert(slot, subjectCert, pwarg); if (!*selfsignprivkey) { PR_fprintf(PR_STDERR, "Failed to locate private key.\n"); rv = SECFailure; goto cleanup; } } certDER = SignCert(handle, subjectCert, selfsign, hashAlgTag, *selfsignprivkey, issuerNickName,pwarg); if (certDER) { if (ascii) { PR_fprintf(outFile, "%s\n%s\n%s\n", NS_CERT_HEADER, BTOA_DataToAscii(certDER->data, certDER->len), NS_CERT_TRAILER); } else { PR_Write(outFile, certDER->data, certDER->len); } } if (rv != SECSuccess) { PRErrorCode perr = PR_GetError(); PR_fprintf(PR_STDERR, "unable to create cert %s\n", perr); } cleanup: if (outFile) { PR_Close(outFile); } if (*selfsignprivkey) { SECKEY_DestroyPrivateKey(*selfsignprivkey); } if (certReq) { CERT_DestroyCertificateRequest(certReq); } if (subjectCert) { CERT_DestroyCertificate(subjectCert); } return rv; } /* * Generate the certificate request with subject */ static SECStatus CertReq(SECKEYPrivateKey *privk, SECKEYPublicKey *pubk, KeyType keyType, SECOidTag hashAlgTag, CERTName *subject, PRBool ascii, const char *certReqFileName) { SECOidTag signAlgTag; SECItem result; PRInt32 numBytes; SECStatus rv = SECSuccess; PRArenaPool *arena = NULL; void *extHandle = NULL; PRFileDesc *outFile = NULL; CERTSubjectPublicKeyInfo *spki = NULL; CERTCertificateRequest *cr = NULL; SECItem *encoding = NULL; /* If the certificate request file already exists, delete it */ if (PR_Access(certReqFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { PR_Delete(certReqFileName); } /* Open the certificate request file to write */ outFile = PR_Open(certReqFileName, PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660); if (!outFile) { PR_fprintf(PR_STDERR, "unable to open \"%s\" for writing (%ld, %ld).\n", certReqFileName, PR_GetError(), PR_GetOSError()); goto cleanup; } /* Create info about public key */ spki = SECKEY_CreateSubjectPublicKeyInfo(pubk); if (!spki) { PR_fprintf(PR_STDERR, "unable to create subject public key\n"); rv = SECFailure; goto cleanup; } /* Generate certificate request */ cr = CERT_CreateCertificateRequest(subject, spki, NULL); if (!cr) { PR_fprintf(PR_STDERR, "unable to make certificate request\n"); rv = SECFailure; goto cleanup; } arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (!arena) { fprintf(stderr, "out of memory"); rv = SECFailure; goto cleanup; } extHandle = CERT_StartCertificateRequestAttributes(cr); if (extHandle == NULL) { PORT_FreeArena (arena, PR_FALSE); rv = SECFailure; goto cleanup; } CERT_FinishExtensions(extHandle); CERT_FinishCertificateRequestAttributes(cr); /* Der encode the request */ encoding = SEC_ASN1EncodeItem(arena, NULL, cr, SEC_ASN1_GET(CERT_CertificateRequestTemplate)); if (encoding == NULL) { PR_fprintf(PR_STDERR, "der encoding of request failed\n"); rv = SECFailure; goto cleanup; } /* Sign the request */ signAlgTag = SEC_GetSignatureAlgorithmOidTag(keyType, hashAlgTag); if (signAlgTag == SEC_OID_UNKNOWN) { PR_fprintf(PR_STDERR, "unknown Key or Hash type\n"); rv = SECFailure; goto cleanup; } rv = SEC_DerSignData(arena, &result, encoding->data, encoding->len, privk, signAlgTag); if (rv) { PR_fprintf(PR_STDERR, "signing of data failed\n"); rv = SECFailure; goto cleanup; } /* Encode request in specified format */ if (ascii) { char *obuf; char *name, *email, *org, *state, *country; SECItem *it; int total; it = &result; obuf = BTOA_ConvertItemToAscii(it); total = PL_strlen(obuf); name = CERT_GetCommonName(subject); if (!name) { name = strdup("(not specified)"); } email = CERT_GetCertEmailAddress(subject); if (!email) email = strdup("(not specified)"); org = CERT_GetOrgName(subject); if (!org) org = strdup("(not specified)"); state = CERT_GetStateName(subject); if (!state) state = strdup("(not specified)"); country = CERT_GetCountryName(subject); if (!country) country = strdup("(not specified)"); PR_fprintf(outFile, "\nCertificate request generated by Netscape certutil\n"); PR_fprintf(outFile, "Common Name: %s\n", name); PR_fprintf(outFile, "Email: %s\n", email); PR_fprintf(outFile, "Organization: %s\n", org); PR_fprintf(outFile, "State: %s\n", state); PR_fprintf(outFile, "Country: %s\n\n", country); PR_fprintf(outFile, "%s\n", NS_CERTREQ_HEADER); numBytes = PR_Write(outFile, obuf, total); if (numBytes != total) { PR_fprintf(PR_STDERR, "write error\n"); return SECFailure; } PR_fprintf(outFile, "\n%s\n", NS_CERTREQ_TRAILER); } else { numBytes = PR_Write(outFile, result.data, result.len); if (numBytes != (int)result.len) { PR_fprintf(PR_STDERR, "write error\n"); rv = SECFailure; goto cleanup; } } cleanup: if (outFile) { PR_Close(outFile); } if (privk) { SECKEY_DestroyPrivateKey(privk); } if (pubk) { SECKEY_DestroyPublicKey(pubk); } return rv; } /* * Create certificate request with subject */ SECStatus CreateCertRequest(PK11SlotInfo *slot, secuPWData *pwdata, CERTName *subject, char *certReqFileName, PRBool ascii) { SECStatus rv; SECKEYPrivateKey *privkey = NULL; SECKEYPublicKey *pubkey = NULL; KeyType keytype = rsaKey; int keysize = DEFAULT_KEY_BITS; int publicExponent = 0x010001; SECOidTag hashAlgTag = SEC_OID_UNKNOWN; privkey = GeneratePrivateKey(keytype, slot, keysize, publicExponent, NULL, &pubkey, NULL, pwdata); if (privkey == NULL) { PR_fprintf(PR_STDERR, "unable to generate key(s)\n"); rv = SECFailure; goto cleanup; } privkey->wincx = pwdata; PORT_Assert(pubkey != NULL); rv = CertReq(privkey, pubkey, keytype, hashAlgTag, subject, ascii, certReqFileName); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Failed to create Certificate Request\n"); } cleanup: return rv; } /* * Creates the certificate using CSR and adds the certificate to DB */ SECStatus AddCertificateToDB(PK11SlotInfo *slot, secuPWData *pwdata, char *certReqFileName, char *certFileName, char *issuerNameStr, CERTCertDBHandle *certHandle, const char *nickNameStr, char *trustStr, unsigned int serialNumber, PRBool selfsign, PRBool ascii) { SECStatus rv; SECKEYPrivateKey *privkey = NULL; SECKEYPublicKey *pubkey = NULL; SECOidTag hashAlgTag = SEC_OID_UNKNOWN; if (PR_Access(certFileName, PR_ACCESS_EXISTS) == PR_FAILURE) { rv = CreateCert(certHandle, slot, issuerNameStr, certReqFileName, certFileName, &privkey, &pwdata, hashAlgTag, serialNumber, 0, 3, NULL, ascii, selfsign); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Failed to create Certificate\n"); goto cleanup; } } rv = AddCert(slot, certHandle, nickNameStr, trustStr, certFileName, ascii, 0, &pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Failed to add Certificate\n"); } cleanup: return rv; } /* * Finds the certificate using nickname and saves it to the header file */ SECStatus AddCertificateToHeader(PK11SlotInfo *slot, secuPWData *pwdata, const char *headerFileName, CERTCertDBHandle *certHandle, const char *nickNameStr, PRBool sigVerify) { SECStatus rv = SECSuccess; PRFileDesc *headerFile = NULL; CERTCertificate *cert = NULL; HeaderType hType = CERTENC; /* If the intermediate header file already exists, delete it */ if (PR_Access(headerFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { PR_Delete(headerFileName); } headerFile = PR_Open(headerFileName, PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660); if (!headerFile) { PR_fprintf(PR_STDERR, "unable to open \"%s\" for writing (%ld, %ld).\n", headerFileName, PR_GetError(), PR_GetOSError()); rv = SECFailure; goto cleanup; } cert = CERT_FindCertByNicknameOrEmailAddr(certHandle, nickNameStr); if (!cert) { PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); rv = SECFailure; goto cleanup; } if (sigVerify) { hType = CERTVFY; } WriteToHeaderFile(cert->derCert.data, cert->derCert.len, hType, headerFile); cleanup: if (headerFile) { PR_Close(headerFile); } if (cert) { CERT_DestroyCertificate(cert); } return rv; } /* * Finds the public key from the certificate saved in the header file * and encrypts with it the contents of inFileName to encryptedFileName. */ SECStatus FindKeyAndEncrypt(PK11SlotInfo *slot, secuPWData *pwdata, const char *headerFileName, const char *encryptedFileName, const char *inFileName) { SECStatus rv; PRFileDesc *headerFile = NULL; PRFileDesc *encFile = NULL; PRFileDesc *inFile = NULL; CERTCertificate *cert = NULL; SECItem data; unsigned char ptext[MODBLOCKSIZE]; unsigned char encBuf[MODBLOCKSIZE]; unsigned int ptextLen; int index; unsigned int nWritten; unsigned int pad[1]; SECItem padItem; unsigned int paddingLength = 0; SECKEYPublicKey *pubkey = NULL; /* If the intermediate encrypted file already exists, delete it*/ if (PR_Access(encryptedFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { PR_Delete(encryptedFileName); } /* Read certificate from header file */ rv = ReadFromHeaderFile(headerFileName, CERTENC, &data, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not read certificate from header file\n"); goto cleanup; } /* Read in an ASCII cert and return a CERTCertificate */ cert = CERT_DecodeCertFromPackage((char *)data.data, data.len); if (!cert) { PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); rv = SECFailure; goto cleanup; } /* Extract the public key from certificate */ pubkey = CERT_ExtractPublicKey(cert); if (!pubkey) { PR_fprintf(PR_STDERR, "could not get key from certificate\n"); rv = SECFailure; goto cleanup; } /* Open the encrypted file for writing */ encFile = PR_Open(encryptedFileName, PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); if (!encFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", encryptedFileName); rv = SECFailure; goto cleanup; } /* Open the input file for reading */ inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", inFileName); rv = SECFailure; goto cleanup; } /* Open the header file to write padding */ headerFile = PR_Open(headerFileName, PR_CREATE_FILE | PR_RDWR | PR_APPEND, 00660); if (!headerFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", headerFileName); rv = SECFailure; goto cleanup; } /* Read input file */ while ((ptextLen = PR_Read(inFile, ptext, sizeof(ptext))) > 0) { if (ptextLen != MODBLOCKSIZE) { paddingLength = MODBLOCKSIZE - ptextLen; for ( index=0; index < paddingLength; index++) { ptext[ptextLen+index] = (unsigned char)paddingLength; } ptextLen = MODBLOCKSIZE; } rv = PK11_PubEncryptRaw(pubkey, encBuf, ptext, ptextLen, NULL); nWritten = PR_Write(encFile, encBuf, ptextLen); } /* Write the padding to header file */ pad[0] = paddingLength; padItem.type = siBuffer; padItem.data = (unsigned char *)pad; padItem.len = sizeof(pad[0]); WriteToHeaderFile(padItem.data, padItem.len, PAD, headerFile); cleanup: if (headerFile) { PR_Close(headerFile); } if (encFile) { PR_Close(encFile); } if (inFile) { PR_Close(inFile); } if (pubkey) { SECKEY_DestroyPublicKey(pubkey); } if (cert) { CERT_DestroyCertificate(cert); } return rv; } /* * Finds the private key from db and signs the contents * of inFileName and writes to signatureFileName */ SECStatus FindKeyAndSign(PK11SlotInfo *slot, CERTCertDBHandle* certHandle, secuPWData *pwdata, const char *nickNameStr, const char *headerFileName, const char *inFileName) { SECStatus rv; PRFileDesc *headerFile = NULL; PRFileDesc *inFile = NULL; CERTCertificate *cert = NULL; unsigned int signatureLen = 0; SECKEYPrivateKey *privkey = NULL; SECItem sigItem; SECOidTag hashOIDTag; /* Open the header file to write padding */ headerFile = PR_Open(headerFileName, PR_CREATE_FILE | PR_RDWR | PR_APPEND, 00660); if (!headerFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", headerFileName); rv = SECFailure; goto cleanup; } /* Get the certificate by nick name and write to header file */ cert = CERT_FindCertByNicknameOrEmailAddr(certHandle, nickNameStr); if (!cert) { PR_fprintf(PR_STDERR, "could not obtain certificate by name - %s\n", nickNameStr); rv = SECFailure; goto cleanup; } WriteToHeaderFile(cert->derCert.data, cert->derCert.len, CERTVFY, headerFile); /* Find private key from certificate */ privkey = PK11_FindKeyByAnyCert(cert, NULL); if (privkey == NULL) { fprintf(stderr, "Couldn't find private key for cert\n"); rv = SECFailure; goto cleanup; } /* Sign the contents of the input file */ rv = SignData(inFileName, privkey, &sigItem); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "could not sign the contents from file - %s \n", inFileName); goto cleanup; } /* write signature to header file */ WriteToHeaderFile(sigItem.data, sigItem.len, SIG, headerFile); cleanup: if (headerFile) { PR_Close(headerFile); } if (privkey) { SECKEY_DestroyPrivateKey(privkey); } if (cert) { CERT_DestroyCertificate(cert); } return rv; } /* * Finds the public key from certificate and verifies signature */ SECStatus FindKeyAndVerify(PK11SlotInfo *slot, CERTCertDBHandle* certHandle, secuPWData *pwdata, const char *headerFileName, const char *inFileName) { SECStatus rv = SECFailure; PRFileDesc *headerFile = NULL; PRFileDesc *inFile = NULL; CERTCertificate *cert = NULL; SECKEYPublicKey *pubkey = NULL; SECItem sigItem; SECItem certData; /* Open the input file */ inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", inFileName); rv = SECFailure; goto cleanup; } /* Open the header file to read the certificate and signature */ headerFile = PR_Open(headerFileName, PR_RDONLY, 0); if (!headerFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", headerFileName); rv = SECFailure; goto cleanup; } /* Read certificate from header file */ rv = ReadFromHeaderFile(headerFileName, CERTVFY, &certData, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not read certificate from header file\n"); goto cleanup; } /* Read in an ASCII cert and return a CERTCertificate */ cert = CERT_DecodeCertFromPackage((char *)certData.data, certData.len); if (!cert) { PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); rv = SECFailure; goto cleanup; } /* Extract the public key from certificate */ pubkey = CERT_ExtractPublicKey(cert); if (!pubkey) { PR_fprintf(PR_STDERR, "Could not get key from certificate\n"); rv = SECFailure; goto cleanup; } /* Read signature from header file */ rv = ReadFromHeaderFile(headerFileName, SIG, &sigItem, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not read signature from header file\n"); goto cleanup; } /* Verify with the public key */ rv = VerifyData(inFileName, pubkey, &sigItem, pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Couldn't verify the signature for file - %s\n", inFileName); goto cleanup; } cleanup: if (headerFile) { PR_Close(headerFile); } if (pubkey) { SECKEY_DestroyPublicKey(pubkey); } if (cert) { CERT_DestroyCertificate(cert); } return rv; } /* * Finds the private key corresponding to the certificate saved in the header file * and decrypts with it the contents of encryptedFileName to outFileName. */ SECStatus FindKeyAndDecrypt(PK11SlotInfo *slot, secuPWData *pwdata, const char *headerFileName, const char *encryptedFileName, const char *outFileName) { SECStatus rv; PRFileDesc *encFile = NULL; PRFileDesc *outFile = NULL; SECKEYPrivateKey *pvtkey = NULL; unsigned int inFileLength = 0; unsigned int paddingLength = 0; unsigned int count = 0; unsigned int temp = 0; unsigned char ctext[MODBLOCKSIZE]; unsigned char decBuf[MODBLOCKSIZE]; unsigned int ctextLen; unsigned int decBufLen; SECItem padItem; SECItem data; SECItem signature; CERTCertificate *cert = NULL; /* Read certificate from header file */ rv = ReadFromHeaderFile(headerFileName, CERTENC, &data, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not read certificate from header file\n"); goto cleanup; } /* Read padding from header file */ rv = ReadFromHeaderFile(headerFileName, PAD, &padItem, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not retrieve PAD detail from header file\n"); goto cleanup; } paddingLength = (unsigned int)padItem.data[0]; inFileLength = FileSize(encryptedFileName); /* Read in an ASCII cert and return a CERTCertificate */ cert = CERT_DecodeCertFromPackage((char *)data.data, data.len); if (!cert) { PR_fprintf(PR_STDERR, "could not obtain certificate from file\n"); rv = SECFailure; goto cleanup; } /* Find private key from certificate */ pvtkey = PK11_FindKeyByAnyCert(cert, NULL); if (pvtkey == NULL) { fprintf(stderr, "Couldn't find private key for cert\n"); rv = SECFailure; goto cleanup; } /* Open the out file to write */ outFile = PR_Open(outFileName, PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); if (!outFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", outFileName); rv = SECFailure; goto cleanup; } /* Open the encrypted file for reading */ encFile = PR_Open(encryptedFileName, PR_RDONLY, 0); if (!encFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", encryptedFileName); rv = SECFailure; goto cleanup; } /* Read the encrypt file, decrypt and write to out file */ while ((ctextLen = PR_Read(encFile, ctext, sizeof(ctext))) > 0) { count += ctextLen; rv = PK11_PubDecryptRaw(pvtkey, decBuf, &decBufLen, sizeof(decBuf), ctext, ctextLen); if (rv != SECSuccess) { fprintf(stderr, "Couldn't decrypt\n"); goto cleanup; } if (decBufLen == 0) { break; } if (count == inFileLength) { decBufLen = decBufLen - paddingLength; } /* write the plain text to out file */ temp = PR_Write(outFile, decBuf, decBufLen); if (temp != decBufLen) { PR_fprintf(PR_STDERR, "write error\n"); rv = SECFailure; break; } } cleanup: if (encFile) { PR_Close(encFile); } if (outFile) { PR_Close(outFile); } if (pvtkey) { SECKEY_DestroyPrivateKey(pvtkey); } if (cert) { CERT_DestroyCertificate(cert); } return rv; } /* Map option letter to command */ static CommandType option2Command(char c) { switch (c) { case 'G': return GENERATE_CSR; case 'A': return ADD_CERT_TO_DB; case 'H': return SAVE_CERT_TO_HEADER; case 'E': return ENCRYPT; case 'D': return DECRYPT; case 'S': return SIGN; case 'V': return VERIFY; default: return UNKNOWN; } } /* * This example illustrates basic encryption/decryption and MACing * Generates the RSA key pair as token object and outputs public key as cert request. * Reads cert request file and stores certificate in DB. * Input, store and trust CA certificate. * Write certificate to intermediate header file * Extract public key from certificate, encrypts the input file and write to external file. * Finds the matching private key, decrypts and write to external file * * How this sample is different from sample 5 ? * * 1. As in sample 5, output is a PKCS#10 CSR * 2. Input and store a cert in cert DB and also used to input, store and trust CA cert. * 3. Like sample 5, but puts cert in header * 4. Like sample 5, but finds key matching cert in header */ int main(int argc, char **argv) { SECStatus rv; PLOptState *optstate; PLOptStatus status; PRBool initialized = PR_FALSE; CommandType cmd = UNKNOWN; const char *dbdir = NULL; secuPWData pwdata = { PW_NONE, 0 }; char *subjectStr = NULL; CERTName *subject = 0; unsigned int serialNumber = 0; char *serialNumberStr = NULL; char *trustStr = NULL; CERTCertDBHandle *certHandle; const char *nickNameStr = NULL; char *issuerNameStr = NULL; PRBool selfsign = PR_FALSE; PRBool ascii = PR_FALSE; PRBool sigVerify = PR_FALSE; const char *headerFileName = NULL; const char *encryptedFileName = NULL; const char *inFileName = NULL; const char *outFileName = NULL; char *certReqFileName = NULL; char *certFileName = NULL; const char *noiseFileName = NULL; PK11SlotInfo *slot = NULL; char * progName = strrchr(argv[0], '/'); progName = progName ? progName + 1 : argv[0]; /* Parse command line arguments */ optstate = PL_CreateOptState(argc, argv, "GAHEDSVad:i:o:f:p:z:s:r:n:x:m:t:c:u:e:b:v:"); while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { case 'a': ascii = PR_TRUE; break; case 'G': /* Generate a CSR */ case 'A': /* Add cert to database */ case 'H': /* Save cert to the header file */ case 'E': /* Encrypt with public key from cert in header file */ case 'S': /* Sign with private key */ case 'D': /* Decrypt with the matching private key */ case 'V': /* Verify with the matching public key */ cmd = option2Command(optstate->option); break; case 'd': dbdir = strdup(optstate->value); break; case 'f': pwdata.source = PW_FROMFILE; pwdata.data = strdup(optstate->value); break; case 'p': pwdata.source = PW_PLAINTEXT; pwdata.data = strdup(optstate->value); break; case 'i': inFileName = strdup(optstate->value); break; case 'b': headerFileName = strdup(optstate->value); break; case 'e': encryptedFileName = strdup(optstate->value); break; case 'o': outFileName = strdup(optstate->value); break; case 'z': noiseFileName = strdup(optstate->value); break; case 's': subjectStr = strdup(optstate->value); subject = CERT_AsciiToName(subjectStr); break; case 'r': certReqFileName = strdup(optstate->value); break; case 'c': certFileName = strdup(optstate->value); break; case 'u': issuerNameStr = strdup(optstate->value); break; case 'n': nickNameStr = strdup(optstate->value); break; case 'x': selfsign = PR_TRUE; break; case 'm': serialNumberStr = strdup(optstate->value); serialNumber = atoi(serialNumberStr); break; case 't': trustStr = strdup(optstate->value); break; case 'v': sigVerify = PR_TRUE; break; default: Usage(progName); break; } } PL_DestroyOptState(optstate); if (cmd == UNKNOWN || !dbdir) Usage(progName); /* Open DB for read/write and authenticate to it */ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); initialized = PR_TRUE; rv = NSS_InitReadWrite(dbdir); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "NSS_InitReadWrite Failed\n"); goto cleanup; } PK11_SetPasswordFunc(GetModulePassword); slot = PK11_GetInternalKeySlot(); if (PK11_NeedLogin(slot)) { rv = PK11_Authenticate(slot, PR_TRUE, &pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", PK11_GetTokenName(slot)); goto cleanup; } } switch (cmd) { case GENERATE_CSR: ValidateGenerateCSRCommand(progName, dbdir, subject, subjectStr, certReqFileName); /* Generate a CSR */ rv = CreateCertRequest(slot, &pwdata, subject, certReqFileName, ascii); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Create Certificate Request: Failed\n"); goto cleanup; } break; case ADD_CERT_TO_DB: ValidateAddCertToDBCommand(progName, dbdir, nickNameStr, trustStr, certFileName, certReqFileName, issuerNameStr, serialNumberStr, selfsign); /* Add cert to database */ rv = AddCertificateToDB(slot, &pwdata, certReqFileName, certFileName, issuerNameStr, certHandle, nickNameStr, trustStr, serialNumber, selfsign, ascii); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Add Certificate to DB: Failed\n"); goto cleanup; } break; case SAVE_CERT_TO_HEADER: ValidateSaveCertToHeaderCommand(progName, dbdir, nickNameStr, headerFileName); /* Save cert to the header file */ rv = AddCertificateToHeader(slot, &pwdata, headerFileName, certHandle, nickNameStr, sigVerify); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Saving Certificate to header: Failed\n"); goto cleanup; } break; case ENCRYPT: ValidateEncryptCommand(progName, dbdir, nickNameStr, headerFileName, inFileName, encryptedFileName); /* Encrypt with public key from cert in header file */ rv = FindKeyAndEncrypt(slot, &pwdata, headerFileName, encryptedFileName, inFileName); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Find public key and Encrypt : Failed\n"); goto cleanup; } break; case SIGN: ValidateSignCommand(progName, dbdir, nickNameStr, headerFileName, inFileName); /* Sign with private key */ rv = FindKeyAndSign(slot, certHandle, &pwdata, nickNameStr, headerFileName, inFileName); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Find private key and sign : Failed\n"); goto cleanup; } break; case DECRYPT: ValidateDecryptCommand(progName, dbdir, headerFileName, encryptedFileName, outFileName); /* Decrypt with the matching private key */ rv = FindKeyAndDecrypt(slot, &pwdata, headerFileName, encryptedFileName, outFileName); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Find private key and Decrypt : Failed\n"); } break; case VERIFY: ValidateVerifyCommand(progName, dbdir, headerFileName, inFileName); /* Verify with the matching public key */ rv = FindKeyAndVerify(slot, certHandle, &pwdata, headerFileName, inFileName); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Find public key and verify signature : Failed\n"); goto cleanup; } } cleanup: if (slot) { PK11_FreeSlot(slot); } if (initialized) { SECStatus rvShutdown = NSS_Shutdown(); if (rvShutdown != SECSuccess) { PR_fprintf(PR_STDERR, "Failed : NSS_Shutdown() - %s", PORT_ErrorToString(rvShutdown)); rv = SECFailure; } PR_Cleanup(); } return rv; } diff --git a/security/nss/doc/rst/legacy/nss_sample_code/sample2_-_initialize_nss_database/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/sample2_-_initialize_nss_database/index.rst new file mode 100644 index 0000000000..fe1d83fc61 --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/sample2_-_initialize_nss_database/index.rst @@ -0,0 +1,250 @@ +.. _mozilla_projects_nss_nss_sample_code_sample2_-_initialize_nss_database: + +Initialize NSS database - sample 2 +================================== + +.. _nss_sample_code_2_initialize_the_nss_database.: + +`NSS sample code 2: initialize the NSS database. <#nss_sample_code_2_initialize_the_nss_database.>`__ +----------------------------------------------------------------------------------------------------- + +.. container:: + + The NSS sample code below demonstrates how to initialize the NSS database. + + .. code:: c + + /* + * Print a usage message and exit + */ + static void + Usage(const char *progName) + { + fprintf(stderr, "\nUsage: %s -d [-p ]" + " [-f ]\n\n", + progName); + fprintf(stderr, "%-15s Specify a DB directory path\n\n", + "-d "); + fprintf(stderr, "%-15s Specify a plaintext password\n\n", + "-p "); + fprintf(stderr, "%-15s Specify a password file\n\n", + "-f "); + exit(-1); + } + + /* + * InitSlotPassword + */ + char * + InitSlotPassword(PK11SlotInfo *slot, PRBool retry, void *arg) + { + FILE *input; + FILE *output; + char *p0 = NULL; + char *p1 = NULL; + secuPWData *pwdata = (secuPWData *) arg; + + if (pwdata->source == PW_FROMFILE) { + return FilePasswd(slot, retry, pwdata->data); + } + if (pwdata->source == PW_PLAINTEXT) { + return PL_strdup(pwdata->data); + } + + /* open terminal */ + input = fopen("/dev/tty", "r"); + if (input == NULL) { + PR_fprintf(PR_STDERR, "Error opening input terminal for read\n"); + return NULL; + } + + /* we have no password, so initialize database with one */ + PR_fprintf(PR_STDERR, + "Enter a password which will be used to encrypt your keys.\n" + "The password should be at least 8 characters long,\n" + "and should contain at least one non-alphabetic character.\n\n"); + + output = fopen("/dev/tty", "w"); + if (output == NULL) { + PR_fprintf(PR_STDERR, "Error opening output terminal for write\n"); + return NULL; + } + + for (;;) { + if (p0) + PORT_Free(p0); + p0 = GetPassword(input, output, "Enter new password: ", + CheckPassword); + if (p1) + PORT_Free(p1); + p1 = GetPassword(input, output, "Re-enter password: ", + CheckPassword); + if (p0 && p1 && !PORT_Strcmp(p0, p1)) { + break; + } + PR_fprintf(PR_STDERR, "Passwords do not match. Try again.\n"); + } + + /* clear out the duplicate password string */ + if (p1) { + PORT_Memset(p1, 0, PORT_Strlen(p1)); + PORT_Free(p1); + } + fclose(input); + fclose(output); + + return p0; + } + + /* + * ChangePW + */ + SECStatus + ChangePW(PK11SlotInfo *slot, char *oldPass, char *newPass, + char *oldPwFile, char *newPwFile) + { + SECStatus rv; + secuPWData pwdata; + secuPWData newpwdata; + char *oldpw = NULL; + char *newpw = NULL; + + if (oldPass) { + pwdata.source = PW_PLAINTEXT; + pwdata.data = oldPass; + } else if (oldPwFile) { + pwdata.source = PW_FROMFILE; + pwdata.data = oldPwFile; + } else { + pwdata.source = PW_NONE; + pwdata.data = NULL; + } + + if (newPass) { + newpwdata.source = PW_PLAINTEXT; + newpwdata.data = newPass; + } else if (newPwFile) { + newpwdata.source = PW_FROMFILE; + newpwdata.data = NULL; + } else { + newpwdata.source = PW_NONE; + newpwdata.data = NULL; + } + + if (PK11_NeedUserInit(slot)) { + newpw = InitSlotPassword(slot, PR_FALSE, &pwdata); + rv = PK11_InitPin(slot, (char*)NULL, newpw); + if (rv == SECSuccess) { + PR_fprintf(PR_STDERR, "PK11_InitPin failed.\n"); + return SECFailure; + } + } + else { + for (;;) { + oldpw = GetModulePassword(slot, PR_FALSE, &pwdata); + + if (PK11_CheckUserPassword(slot, oldpw) != SECSuccess) { + if (pwdata.source == PW_NONE) { + PR_fprintf(PR_STDERR, "Invalid password. Try again.\n"); + } else { + PR_fprintf(PR_STDERR, "Invalid password.\n"); + PORT_Memset(oldpw, 0, PL_strlen(oldpw)); + PORT_Free(oldpw); + return SECFailure; + } + } else { + break; + } + PORT_Free(oldpw); + } + newpw = InitSlotPassword(slot, PR_FALSE, &newpwdata); + + if (PK11_ChangePW(slot, oldpw, newpw) != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed to change password.\n"); + return SECFailure; + } + PORT_Memset(oldpw, 0, PL_strlen(oldpw)); + PORT_Free(oldpw); + PR_fprintf(PR_STDOUT, "Password changed successfully.\n"); + } + PORT_Memset(newpw, 0, PL_strlen(newpw)); + PORT_Free(newpw); + return SECSuccess; + } + + /* + * This example illustrates initialization of the NSS database. + * It creates an nss configuration directory with empty databases + * and initializes the databases. It also illustrates techniques for + * password handling. + */ + int main(int argc, char **argv) + { + PLOptState *optstate; + PLOptStatus status; + SECStatus rv; + SECStatus rvShutdown; + char *slotname = "internal"; + PK11SlotInfo *slot = NULL; + char *dbdir = NULL; + char *plainPass = NULL; + char *pwFile = NULL; + + char * progName = strrchr(argv[0], '/'); + progName = progName ? progName + 1 : argv[0]; + + /* Parse command line arguments */ + optstate = PL_CreateOptState(argc, argv, "d:p:q:f:g:"); + while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { + switch (optstate->option) { + case 'd': + dbdir = strdup(optstate->value); + break; + case 'p': + plainPass = strdup(optstate->value); + break; + case 'f': + pwFile = strdup(optstate->value); + break; + default: + Usage(progName); + break; + } + } + PL_DestroyOptState(optstate); + + if (!dbdir) + Usage(progName); + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + /* Create the database */ + rv = NSS_InitReadWrite(dbdir); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "NSS_Initialize Failed"); + PR_Cleanup(); + exit(rv); + } + + if (PL_strcmp(slotname, "internal") == 0) + slot = PK11_GetInternalKeySlot(); + + /* If creating new database, initialize the password. */ + rv = ChangePW(slot, plainPass, 0, pwFile, 0); + if (rv != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed to change password\n"); + } + + if (slot) { + PK11_FreeSlot(slot); + } + rvShutdown = NSS_Shutdown(); + if (rvShutdown != SECSuccess) { + PR_fprintf(PR_STDERR, "Failed : NSS_Shutdown()\n"); + rv = SECFailure; + } + + PR_Cleanup(); + + return rv; + } \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/sample3_-_encdecmac_using_token_object/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/sample3_-_encdecmac_using_token_object/index.rst new file mode 100644 index 0000000000..ec6ff208fb --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/sample3_-_encdecmac_using_token_object/index.rst @@ -0,0 +1,30 @@ +.. _mozilla_projects_nss_nss_sample_code_sample3_-_encdecmac_using_token_object: + +EncDecMAC using token object - sample 3 +======================================= + +.. _encdecmac_using_token_object: + +`EncDecMAC using token object <#encdecmac_using_token_object>`__ +---------------------------------------------------------------- + +.. container:: + +`Example: <#example>`__ +~~~~~~~~~~~~~~~~~~~~~~~ + +.. container:: + +.. _nss_sample_code_3_hashing.: + +`NSS Sample Code 3: Enc/Dec/MAC Using Token Object ID. <#nss_sample_code_3_hashing.>`__ +--------------------------------------------------------------------------------------- + +.. container:: + + Computes the hash of a file and saves it to another file, illustrates the use of NSS message + APIs. + + .. code:: c + + /* 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 https://mozilla.org/MPL/2.0/. */ /* NSPR Headers */ #include #include #include #include #include #include #include /* NSS headers */ #include #include /* our samples utilities */ #include "util.h" #define BUFFERSIZE 80 #define DIGESTSIZE 16 #define PTEXT_MAC_BUFFER_SIZE 96 #define CIPHERSIZE 96 #define BLOCKSIZE 32 #define CIPHER_HEADER "-----BEGIN CIPHER-----" #define CIPHER_TRAILER "-----END CIPHER-----" #define ENCKEY_HEADER "-----BEGIN AESKEY CKAID-----" #define ENCKEY_TRAILER "-----END AESKEY CKAID-----" #define MACKEY_HEADER "-----BEGIN MACKEY CKAID-----" #define MACKEY_TRAILER "-----END MACKEY CKAID-----" #define IV_HEADER "-----BEGIN IV-----" #define IV_TRAILER "-----END IV-----" #define MAC_HEADER "-----BEGIN MAC-----" #define MAC_TRAILER "-----END MAC-----" #define PAD_HEADER "-----BEGIN PAD-----" #define PAD_TRAILER "-----END PAD-----" typedef enum { ENCRYPT, DECRYPT, UNKNOWN } CommandType; typedef enum { SYMKEY = 0, MACKEY = 1, IV = 2, MAC = 3, PAD = 4 } HeaderType; /* * Print usage message and exit */ static void Usage(const char *progName) { fprintf(stderr, "\nUsage: %s -c -d [-z ] " "[-p | -f ] -i -o \n\n", progName); fprintf(stderr, "%-20s Specify 'a' for encrypt operation\n\n", "-c "); fprintf(stderr, "%-20s Specify 'b' for decrypt operation\n\n", " "); fprintf(stderr, "%-20s Specify db directory path\n\n", "-d "); fprintf(stderr, "%-20s Specify db password [optional]\n\n", "-p "); fprintf(stderr, "%-20s Specify db password file [optional]\n\n", "-f "); fprintf(stderr, "%-20s Specify noise file name [optional]\n\n", "-z "); fprintf(stderr, "%-21s Specify an input file name\n\n", "-i "); fprintf(stderr, "%-21s Specify an output file name\n\n", "-o "); fprintf(stderr, "%-7s For encrypt, it takes as an input file and produces\n", "Note :"); fprintf(stderr, "%-7s .enc and .header as intermediate output files.\n\n", ""); fprintf(stderr, "%-7s For decrypt, it takes .enc and .header\n", ""); fprintf(stderr, "%-7s as input files and produces as a final output file.\n\n", ""); exit(-1); } /* * Gather a CKA_ID */ SECStatus GatherCKA_ID(PK11SymKey* key, SECItem* buf) { SECStatus rv = PK11_ReadRawAttribute(PK11_TypeSymKey, key, CKA_ID, buf); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "PK11_ReadRawAttribute returned (%d)\n", rv); PR_fprintf(PR_STDERR, "Could not read SymKey CKA_ID attribute\n"); return rv; } return rv; } /* * Generate a Symmetric Key */ PK11SymKey * GenerateSYMKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE mechanism, int keySize, SECItem *keyID, secuPWData *pwdata) { SECStatus rv; PK11SymKey *key; if (PK11_NeedLogin(slot)) { rv = PK11_Authenticate(slot, PR_TRUE, pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", PK11_GetTokenName(slot)); return NULL; } } /* Generate the symmetric key */ key = PK11_TokenKeyGen(slot, mechanism, NULL, keySize, keyID, PR_TRUE, pwdata); if (!key) { PR_fprintf(PR_STDERR, "Symmetric Key Generation Failed \n"); } return key; } /* * MacInit */ SECStatus MacInit(PK11Context *ctx) { SECStatus rv = PK11_DigestBegin(ctx); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Compute MAC Failed : PK11_DigestBegin()\n"); } return rv; } /* * MacUpdate */ SECStatus MacUpdate(PK11Context *ctx, unsigned char *msg, unsigned int msgLen) { SECStatus rv = PK11_DigestOp(ctx, msg, msgLen); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Compute MAC Failed : DigestOp()\n"); } return rv; } /* * Finalize MACing */ SECStatus MacFinal(PK11Context *ctx, unsigned char *mac, unsigned int *macLen, unsigned int maxLen) { SECStatus rv = PK11_DigestFinal(ctx, mac, macLen, maxLen); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Compute MAC Failed : PK11_DigestFinal()\n"); } return SECSuccess; } /* * Compute Mac */ SECStatus ComputeMac(PK11Context *ctxmac, unsigned char *ptext, unsigned int ptextLen, unsigned char *mac, unsigned int *macLen, unsigned int maxLen) { SECStatus rv = MacInit(ctxmac); if (rv != SECSuccess) return rv; rv = MacUpdate(ctxmac, ptext, ptextLen); if (rv != SECSuccess) return rv; rv = MacFinal(ctxmac, mac, macLen, maxLen); return rv; } /* * WriteToHeaderFile */ SECStatus WriteToHeaderFile(const char *buf, unsigned int len, HeaderType type, PRFileDesc *outFile) { SECStatus rv; char header[40]; char trailer[40]; char *outString = NULL; switch (type) { case SYMKEY: strcpy(header, ENCKEY_HEADER); strcpy(trailer, ENCKEY_TRAILER); break; case MACKEY: strcpy(header, MACKEY_HEADER); strcpy(trailer, MACKEY_TRAILER); break; case IV: strcpy(header, IV_HEADER); strcpy(trailer, IV_TRAILER); break; case MAC: strcpy(header, MAC_HEADER); strcpy(trailer, MAC_TRAILER); break; case PAD: strcpy(header, PAD_HEADER); strcpy(trailer, PAD_TRAILER); break; } PR_fprintf(outFile, "%s\n", header); PrintAsHex(outFile, buf, len); PR_fprintf(outFile, "%s\n\n", trailer); return SECSuccess; } /* * Initialize for encryption or decryption - common code */ PK11Context * CryptInit(PK11SymKey *key, unsigned char *iv, unsigned int ivLen, CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE operation) { SECItem ivItem = { siBuffer, iv, ivLen }; PK11Context *ctx = NULL; SECItem *secParam = PK11_ParamFromIV(CKM_AES_CBC, &ivItem); if (secParam == NULL) { PR_fprintf(PR_STDERR, "Crypt Failed : secParam NULL\n"); return NULL; } ctx = PK11_CreateContextBySymKey(CKM_AES_CBC, operation, key, secParam); if (ctx == NULL) { PR_fprintf(PR_STDERR, "Crypt Failed : can't create a context\n"); goto cleanup; } cleanup: if (secParam) { SECITEM_FreeItem(secParam, PR_TRUE); } return ctx; } /* * Common encryption and decryption code */ SECStatus Crypt(PK11Context *ctx, unsigned char *out, unsigned int *outLen, unsigned int maxOut, unsigned char *in, unsigned int inLen) { SECStatus rv; rv = PK11_CipherOp(ctx, out, outLen, maxOut, in, inLen); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Crypt Failed : PK11_CipherOp returned %d\n", rv); goto cleanup; } cleanup: if (rv != SECSuccess) { return rv; } return SECSuccess; } /* * Decrypt */ SECStatus Decrypt(PK11Context *ctx, unsigned char *out, unsigned int *outLen, unsigned int maxout, unsigned char *in, unsigned int inLen) { return Crypt(ctx, out, outLen, maxout, in, inLen); } /* * Encrypt */ SECStatus Encrypt(PK11Context* ctx, unsigned char *out, unsigned int *outLen, unsigned int maxout, unsigned char *in, unsigned int inLen) { return Crypt(ctx, out, outLen, maxout, in, inLen); } /* * EncryptInit */ PK11Context * EncryptInit(PK11SymKey *ek, unsigned char *iv, unsigned int ivLen, CK_MECHANISM_TYPE type) { return CryptInit(ek, iv, ivLen, type, CKA_ENCRYPT); } /* * DecryptInit */ PK11Context * DecryptInit(PK11SymKey *dk, unsigned char *iv, unsigned int ivLen, CK_MECHANISM_TYPE type) { return CryptInit(dk, iv, ivLen, type, CKA_DECRYPT); } /* * Read cryptographic parameters from the header file */ SECStatus ReadFromHeaderFile(const char *fileName, HeaderType type, SECItem *item, PRBool isHexData) { SECStatus rv; PRFileDesc* file; SECItem filedata; SECItem outbuf; unsigned char *nonbody; unsigned char *body; char header[40]; char trailer[40]; outbuf.type = siBuffer; file = PR_Open(fileName, PR_RDONLY, 0); if (!file) { PR_fprintf(PR_STDERR, "Failed to open %s\n", fileName); return SECFailure; } switch (type) { case SYMKEY: strcpy(header, ENCKEY_HEADER); strcpy(trailer, ENCKEY_TRAILER); break; case MACKEY: strcpy(header, MACKEY_HEADER); strcpy(trailer, MACKEY_TRAILER); break; case IV: strcpy(header, IV_HEADER); strcpy(trailer, IV_TRAILER); break; case MAC: strcpy(header, MAC_HEADER); strcpy(trailer, MAC_TRAILER); break; case PAD: strcpy(header, PAD_HEADER); strcpy(trailer, PAD_TRAILER); break; } rv = FileToItem(&filedata, file); nonbody = (char *)filedata.data; if (!nonbody) { PR_fprintf(PR_STDERR, "unable to read data from input file\n"); rv = SECFailure; goto cleanup; } /* check for headers and trailers and remove them */ if ((body = strstr(nonbody, header)) != NULL) { char *trail = NULL; nonbody = body; body = PORT_Strchr(body, '\n'); if (!body) body = PORT_Strchr(nonbody, '\r'); /* maybe this is a MAC file */ if (body) trail = strstr(++body, trailer); if (trail != NULL) { *trail = '\0'; } else { PR_fprintf(PR_STDERR, "input has header but no trailer\n"); PORT_Free(filedata.data); return SECFailure; } } else { body = nonbody; } cleanup: PR_Close(file); HexToBuf(body, item, isHexData); return SECSuccess; } /* * EncryptAndMac */ SECStatus EncryptAndMac(PRFileDesc *inFile, PRFileDesc *headerFile, PRFileDesc *encFile, PK11SymKey *ek, PK11SymKey *mk, unsigned char *iv, unsigned int ivLen, PRBool ascii) { SECStatus rv; unsigned char ptext[BLOCKSIZE]; unsigned int ptextLen; unsigned char mac[DIGESTSIZE]; unsigned int macLen; unsigned int nwritten; unsigned char encbuf[BLOCKSIZE]; unsigned int encbufLen; SECItem noParams = { siBuffer, NULL, 0 }; PK11Context *ctxmac = NULL; PK11Context *ctxenc = NULL; unsigned int pad[1]; SECItem padItem; unsigned int paddingLength; static unsigned int firstTime = 1; int j; ctxmac = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, mk, &noParams); if (ctxmac == NULL) { PR_fprintf(PR_STDERR, "Can't create MAC context\n"); rv = SECFailure; goto cleanup; } rv = MacInit(ctxmac); if (rv != SECSuccess) { goto cleanup; } ctxenc = EncryptInit(ek, iv, ivLen, CKM_AES_CBC); /* read a buffer of plaintext from input file */ while ((ptextLen = PR_Read(inFile, ptext, sizeof(ptext))) > 0) { /* Encrypt using it using CBC, using previously created IV */ if (ptextLen != BLOCKSIZE) { paddingLength = BLOCKSIZE - ptextLen; for ( j=0; j < paddingLength; j++) { ptext[ptextLen+j] = (unsigned char)paddingLength; } ptextLen = BLOCKSIZE; } rv = Encrypt(ctxenc, encbuf, &encbufLen, sizeof(encbuf), ptext, ptextLen); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Encrypt Failure\n"); goto cleanup; } /* save the last block of ciphertext as the next IV */ iv = encbuf; ivLen = encbufLen; /* write the cipher text to intermediate file */ nwritten = PR_Write(encFile, encbuf, encbufLen); /*PR_Assert(nwritten == encbufLen);*/ rv = MacUpdate(ctxmac, ptext, ptextLen); } rv = MacFinal(ctxmac, mac, &macLen, DIGESTSIZE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "MacFinal Failure\n"); goto cleanup; } if (macLen == 0) { PR_fprintf(PR_STDERR, "Bad MAC length\n"); rv = SECFailure; goto cleanup; } WriteToHeaderFile(mac, macLen, MAC, headerFile); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Write MAC Failure\n"); goto cleanup; } pad[0] = paddingLength; padItem.type = siBuffer; padItem.data = (unsigned char *)pad; padItem.len = sizeof(pad[0]); WriteToHeaderFile(padItem.data, padItem.len, PAD, headerFile); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Write PAD Failure\n"); goto cleanup; } rv = SECSuccess; cleanup: if (ctxmac != NULL) { PK11_DestroyContext(ctxmac, PR_TRUE); } if (ctxenc != NULL) { PK11_DestroyContext(ctxenc, PR_TRUE); } return rv; } /* * Find the Key for the given mechanism */ PK11SymKey* FindKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE mechanism, SECItem *keyBuf, secuPWData *pwdata) { SECStatus rv; PK11SymKey *key; if (PK11_NeedLogin(slot)) { rv = PK11_Authenticate(slot, PR_TRUE, pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", PK11_GetTokenName(slot)); if (slot) { PK11_FreeSlot(slot); } return NULL; } } key = PK11_FindFixedKey(slot, mechanism, keyBuf, 0); if (!key) { PR_fprintf(PR_STDERR, "PK11_FindFixedKey failed (err %d)\n", PR_GetError()); PK11_FreeSlot(slot); return NULL; } return key; } /* * Decrypt and Verify MAC */ SECStatus DecryptAndVerifyMac(const char* outFileName, char *encryptedFileName, SECItem *cItem, SECItem *macItem, PK11SymKey* ek, PK11SymKey* mk, SECItem *ivItem, SECItem *padItem) { SECStatus rv; PRFileDesc* inFile; PRFileDesc* outFile; unsigned char decbuf[64]; unsigned int decbufLen; unsigned char ptext[BLOCKSIZE]; unsigned int ptextLen = 0; unsigned char ctext[64]; unsigned int ctextLen; unsigned char newmac[DIGESTSIZE]; unsigned int newmacLen = 0; unsigned int newptextLen = 0; unsigned int count = 0; unsigned int temp = 0; unsigned int blockNumber = 0; SECItem noParams = { siBuffer, NULL, 0 }; PK11Context *ctxmac = NULL; PK11Context *ctxenc = NULL; unsigned char iv[BLOCKSIZE]; unsigned int ivLen = ivItem->len; unsigned int fileLength; unsigned int paddingLength; int j; memcpy(iv, ivItem->data, ivItem->len); paddingLength = (unsigned int)padItem->data[0]; ctxmac = PK11_CreateContextBySymKey(CKM_MD5_HMAC, CKA_SIGN, mk, &noParams); if (ctxmac == NULL) { PR_fprintf(PR_STDERR, "Can't create MAC context\n"); rv = SECFailure; goto cleanup; } /* Open the input file. */ inFile = PR_Open(encryptedFileName, PR_RDONLY , 0); if (!inFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", encryptedFileName); return SECFailure; } /* Open the output file. */ outFile = PR_Open(outFileName, PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR , 00660); if (!outFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", outFileName); return SECFailure; } rv = MacInit(ctxmac); if (rv != SECSuccess) goto cleanup; ctxenc = DecryptInit(ek, iv, ivLen, CKM_AES_CBC); fileLength = FileSize(encryptedFileName); while ((ctextLen = PR_Read(inFile, ctext, sizeof(ctext))) > 0) { count += ctextLen; /* decrypt cipher text buffer using CBC and IV */ rv = Decrypt(ctxenc, decbuf, &decbufLen, sizeof(decbuf), ctext, ctextLen); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Decrypt Failure\n"); goto cleanup; } if (decbufLen == 0) break; rv = MacUpdate(ctxmac, decbuf, decbufLen); if (rv != SECSuccess) { goto cleanup; } if (count == fileLength) { decbufLen = decbufLen-paddingLength; } /* write the plain text to out file */ temp = PR_Write(outFile, decbuf, decbufLen); if (temp != decbufLen) { PR_fprintf(PR_STDERR, "write error\n"); rv = SECFailure; break; } /* save last block of ciphertext */ memcpy(iv, decbuf, decbufLen); ivLen = decbufLen; blockNumber++; } if (rv != SECSuccess) { goto cleanup; } rv = MacFinal(ctxmac, newmac, &newmacLen, sizeof(newmac)); if (rv != SECSuccess) { goto cleanup; } if (PORT_Memcmp(macItem->data, newmac, newmacLen) == 0) { rv = SECSuccess; } else { PR_fprintf(PR_STDERR, "Check MAC : Failure\n"); PR_fprintf(PR_STDERR, "Extracted : "); PrintAsHex(PR_STDERR, macItem->data, macItem->len); PR_fprintf(PR_STDERR, "Computed : "); PrintAsHex(PR_STDERR, newmac, newmacLen); rv = SECFailure; } cleanup: if (ctxmac) { PK11_DestroyContext(ctxmac, PR_TRUE); } if (ctxenc) { PK11_DestroyContext(ctxenc, PR_TRUE); } if (outFile) { PR_Close(outFile); } return rv; } /* * Gets IV and CKAIDS From Header File */ SECStatus GetIVandCKAIDSFromHeader(const char *cipherFileName, SECItem *ivItem, SECItem *encKeyItem, SECItem *macKeyItem) { SECStatus rv; /* open intermediate file, read in header, get IV and CKA_IDs of two keys * from it */ rv = ReadFromHeaderFile(cipherFileName, IV, ivItem, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not retrieve IV from cipher file\n"); goto cleanup; } rv = ReadFromHeaderFile(cipherFileName, SYMKEY, encKeyItem, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not retrieve AES CKA_ID from cipher file\n"); goto cleanup; } rv = ReadFromHeaderFile(cipherFileName, MACKEY, macKeyItem, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not retrieve MAC CKA_ID from cipher file\n"); goto cleanup; } cleanup: return rv; } /* * DecryptFile */ SECStatus DecryptFile(PK11SlotInfo *slot, const char *dbdir, const char *outFileName, const char *headerFileName, char *encryptedFileName, secuPWData *pwdata, PRBool ascii) { /* * The DB is open read only and we have authenticated to it * open input file, read in header, get IV and CKA_IDs of two keys from it * find those keys in the DB token * Open output file * loop until EOF(input): * read a buffer of ciphertext from input file, * Save last block of ciphertext * decrypt ciphertext buffer using CBC and IV, * compute and check MAC, then remove MAC from plaintext * replace IV with saved last block of ciphertext * write the plain text to output file * close files * report success */ SECStatus rv; SECItem ivItem; SECItem encKeyItem; SECItem macKeyItem; SECItem cipherItem; SECItem macItem; SECItem padItem; PK11SymKey *encKey = NULL; PK11SymKey *macKey = NULL; /* open intermediate file, read in header, get IV and CKA_IDs of two keys * from it */ rv = GetIVandCKAIDSFromHeader(headerFileName, &ivItem, &encKeyItem, &macKeyItem); if (rv != SECSuccess) { goto cleanup; } /* find those keys in the DB token */ encKey = FindKey(slot, CKM_AES_CBC, &encKeyItem, pwdata); if (encKey == NULL) { PR_fprintf(PR_STDERR, "Can't find the encryption key\n"); rv = SECFailure; goto cleanup; } /* CKM_MD5_HMAC or CKM_EXTRACT_KEY_FROM_KEY */ macKey = FindKey(slot, CKM_MD5_HMAC, &macKeyItem, pwdata); if (macKey == NULL) { rv = SECFailure; goto cleanup; } /* Read in the Mac into item from the intermediate file */ rv = ReadFromHeaderFile(headerFileName, MAC, &macItem, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not retrieve MAC from cipher file\n"); goto cleanup; } if (macItem.data == NULL) { PR_fprintf(PR_STDERR, "MAC has NULL data\n"); rv = SECFailure; goto cleanup; } if (macItem.len == 0) { PR_fprintf(PR_STDERR, "MAC has data has 0 length\n"); /*rv = SECFailure; goto cleanup;*/ } rv = ReadFromHeaderFile(headerFileName, PAD, &padItem, PR_TRUE); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not retrieve PAD detail from header file\n"); goto cleanup; } if (rv == SECSuccess) { /* Decrypt and Remove Mac */ rv = DecryptAndVerifyMac(outFileName, encryptedFileName, &cipherItem, &macItem, encKey, macKey, &ivItem, &padItem); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Failed while decrypting and removing MAC\n"); } } cleanup: if (slot) { PK11_FreeSlot(slot); } if (encKey) { PK11_FreeSymKey(encKey); } if (macKey) { PK11_FreeSymKey(macKey); } return rv; } /* * EncryptFile */ SECStatus EncryptFile(PK11SlotInfo *slot, const char *dbdir, const char *inFileName, const char *headerFileName, const char *encryptedFileName, const char *noiseFileName, secuPWData *pwdata, PRBool ascii) { /* * The DB is open for read/write and we have authenticated to it. * generate a symmetric AES key as a token object. * generate a second key to use for MACing, also a token object. * get their CKA_IDs * generate a random value to use as IV for AES CBC * open an input file and an output file, * write a header to the output that identifies the two keys by * their CKA_IDs, May include original file name and length. * loop until EOF(input) * read a buffer of plaintext from input file, * MAC it, append the MAC to the plaintext * encrypt it using CBC, using previously created IV, * store the last block of ciphertext as the new IV, * write the cipher text to intermediate file * close files * report success */ SECStatus rv; PRFileDesc *inFile; PRFileDesc *headerFile; PRFileDesc *encFile; unsigned char *encKeyId = (unsigned char *) "Encrypt Key"; unsigned char *macKeyId = (unsigned char *) "MAC Key"; SECItem encKeyID = { siAsciiString, encKeyId, PL_strlen(encKeyId) }; SECItem macKeyID = { siAsciiString, macKeyId, PL_strlen(macKeyId) }; SECItem encCKAID; SECItem macCKAID; unsigned char iv[BLOCKSIZE]; SECItem ivItem; PK11SymKey *encKey = NULL; PK11SymKey *macKey = NULL; SECItem temp; unsigned char c; /* generate a symmetric AES key as a token object. */ encKey = GenerateSYMKey(slot, CKM_AES_KEY_GEN, 128/8, &encKeyID, pwdata); if (encKey == NULL) { PR_fprintf(PR_STDERR, "GenerateSYMKey for AES returned NULL.\n"); rv = SECFailure; goto cleanup; } /* generate a second key to use for MACing, also a token object. */ macKey = GenerateSYMKey(slot, CKM_GENERIC_SECRET_KEY_GEN, 160/8, &macKeyID, pwdata); if (macKey == NULL) { PR_fprintf(PR_STDERR, "GenerateSYMKey for MACing returned NULL.\n"); rv = SECFailure; goto cleanup; } /* get the encrypt key CKA_ID */ rv = GatherCKA_ID(encKey, &encCKAID); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Error while wrapping encrypt key\n"); goto cleanup; } /* get the MAC key CKA_ID */ rv = GatherCKA_ID(macKey, &macCKAID); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Can't get the MAC key CKA_ID.\n"); goto cleanup; } if (noiseFileName) { rv = SeedFromNoiseFile(noiseFileName); if (rv != SECSuccess) { PORT_SetError(PR_END_OF_FILE_ERROR); return SECFailure; } rv = PK11_GenerateRandom(iv, BLOCKSIZE); if (rv != SECSuccess) { goto cleanup; } } else { /* generate a random value to use as IV for AES CBC */ GenerateRandom(iv, BLOCKSIZE); } headerFile = PR_Open(headerFileName, PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); if (!headerFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", headerFileName); return SECFailure; } encFile = PR_Open(encryptedFileName, PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, 00660); if (!encFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for writing.\n", encryptedFileName); return SECFailure; } /* write to a header file the IV and the CKA_IDs * identifying the two keys */ ivItem.type = siBuffer; ivItem.data = iv; ivItem.len = BLOCKSIZE; rv = WriteToHeaderFile(iv, BLOCKSIZE, IV, headerFile); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Error writing IV to cipher file - %s\n", headerFileName); goto cleanup; } rv = WriteToHeaderFile(encCKAID.data, encCKAID.len, SYMKEY, headerFile); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Error writing AES CKA_ID to cipher file - %s\n", encryptedFileName); goto cleanup; } rv = WriteToHeaderFile(macCKAID.data, macCKAID.len, MACKEY, headerFile); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Error writing MAC CKA_ID to cipher file - %s\n", headerFileName); goto cleanup; } /* Open the input file. */ inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", inFileName); return SECFailure; } /* Macing and Encryption */ if (rv == SECSuccess) { rv = EncryptAndMac(inFile, headerFile, encFile, encKey, macKey, ivItem.data, ivItem.len, ascii); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Failed : Macing and Encryption\n"); goto cleanup; } } cleanup: if (inFile) { PR_Close(inFile); } if (headerFile) { PR_Close(headerFile); } if (encFile) { PR_Close(encFile); } if (slot) { PK11_FreeSlot(slot); } if (encKey) { PK11_FreeSymKey(encKey); } if (macKey) { PK11_FreeSymKey(macKey); } return rv; } /* * This example illustrates basic encryption/decryption and MACing * Generates the encryption/mac keys and uses token for storing. * Encrypts the input file and appends MAC before storing in intermediate * header file. * Writes the CKA_IDs of the encryption keys into intermediate header file. * Reads the intermediate headerfile for CKA_IDs and encrypted * contents and decrypts into output file. */ int main(int argc, char **argv) { SECStatus rv; SECStatus rvShutdown; PK11SlotInfo *slot = NULL; PLOptState *optstate; PLOptStatus status; char headerFileName[50]; char encryptedFileName[50]; PRFileDesc *inFile; PRFileDesc *outFile; PRBool ascii = PR_FALSE; CommandType cmd = UNKNOWN; const char *command = NULL; const char *dbdir = NULL; const char *inFileName = NULL; const char *outFileName = NULL; const char *noiseFileName = NULL; secuPWData pwdata = { PW_NONE, 0 }; char * progName = strrchr(argv[0], '/'); progName = progName ? progName + 1 : argv[0]; /* Parse command line arguments */ optstate = PL_CreateOptState(argc, argv, "c:d:i:o:f:p:z:a"); while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) { switch (optstate->option) { case 'a': ascii = PR_TRUE; break; case 'c': command = strdup(optstate->value); break; case 'd': dbdir = strdup(optstate->value); break; case 'f': pwdata.source = PW_FROMFILE; pwdata.data = strdup(optstate->value); break; case 'p': pwdata.source = PW_PLAINTEXT; pwdata.data = strdup(optstate->value); break; case 'i': inFileName = strdup(optstate->value); break; case 'o': outFileName = strdup(optstate->value); break; case 'z': noiseFileName = strdup(optstate->value); break; default: Usage(progName); break; } } PL_DestroyOptState(optstate); if (!command || !dbdir || !inFileName || !outFileName) Usage(progName); if (PL_strlen(command)==0) Usage(progName); cmd = command[0] == 'a' ? ENCRYPT : command[0] == 'b' ? DECRYPT : UNKNOWN; /* Open the input file. */ inFile = PR_Open(inFileName, PR_RDONLY, 0); if (!inFile) { PR_fprintf(PR_STDERR, "Unable to open \"%s\" for reading.\n", inFileName); return SECFailure; } PR_Close(inFile); /* For intermediate header file, choose filename as inputfile name with extension ".header" */ strcpy(headerFileName, inFileName); strcat(headerFileName, ".header"); /* For intermediate encrypted file, choose filename as inputfile name with extension ".enc" */ strcpy(encryptedFileName, inFileName); strcat(encryptedFileName, ".enc"); PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); switch (cmd) { case ENCRYPT: /* If the intermediate header file already exists, delete it */ if (PR_Access(headerFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { PR_Delete(headerFileName); } /* If the intermediate encrypted already exists, delete it */ if (PR_Access(encryptedFileName, PR_ACCESS_EXISTS) == PR_SUCCESS) { PR_Delete(encryptedFileName); } /* Open DB for read/write and authenticate to it. */ rv = NSS_InitReadWrite(dbdir); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "NSS_InitReadWrite Failed\n"); goto cleanup; } PK11_SetPasswordFunc(GetModulePassword); slot = PK11_GetInternalKeySlot(); if (PK11_NeedLogin(slot)) { rv = PK11_Authenticate(slot, PR_TRUE, &pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", PK11_GetTokenName(slot)); goto cleanup; } } rv = EncryptFile(slot, dbdir, inFileName, headerFileName, encryptedFileName, noiseFileName, &pwdata, ascii); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "EncryptFile : Failed\n"); return SECFailure; } break; case DECRYPT: /* Open DB read only, authenticate to it */ PK11_SetPasswordFunc(GetModulePassword); rv = NSS_Init(dbdir); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "NSS_Init Failed\n"); return SECFailure; } slot = PK11_GetInternalKeySlot(); if (PK11_NeedLogin(slot)) { rv = PK11_Authenticate(slot, PR_TRUE, &pwdata); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "Could not authenticate to token %s.\n", PK11_GetTokenName(slot)); goto cleanup; } } rv = DecryptFile(slot, dbdir, outFileName, headerFileName, encryptedFileName, &pwdata, ascii); if (rv != SECSuccess) { PR_fprintf(PR_STDERR, "DecryptFile : Failed\n"); return SECFailure; } break; } cleanup: rvShutdown = NSS_Shutdown(); if (rvShutdown != SECSuccess) { PR_fprintf(PR_STDERR, "Failed : NSS_Shutdown()\n"); rv = SECFailure; } PR_Cleanup(); return rv; } \ No newline at end of file diff --git a/security/nss/doc/rst/legacy/nss_sample_code/utiltiies_for_nss_samples/index.rst b/security/nss/doc/rst/legacy/nss_sample_code/utiltiies_for_nss_samples/index.rst new file mode 100644 index 0000000000..a1df68437e --- /dev/null +++ b/security/nss/doc/rst/legacy/nss_sample_code/utiltiies_for_nss_samples/index.rst @@ -0,0 +1,747 @@ +.. _mozilla_projects_nss_nss_sample_code_utiltiies_for_nss_samples: + +Utilities for nss samples +========================= + +.. _nss_sample_code_0_utilities.: + +`NSS Sample Code 0: Utilities. <#nss_sample_code_0_utilities.>`__ +----------------------------------------------------------------- + +.. container:: + + These utility functions are adapted from those found in the sectool library used by the NSS + security tools and other NSS test applications. + + It shows the following: + + - Read DER from a file. + - Compile file size. + - Get seed From a noise gile. + - Generate random numbers. + - Get a module password. + - Extract the password from a text file. + - Print data as hexadecimal. + +`util.h <#util.h>`__ +~~~~~~~~~~~~~~~~~~~~ + +.. container:: + + .. code:: c + + /* 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 https://mozilla.org/MPL/2.0/. */ + + #ifndef _UTIL_H + #define _UTIL_H + + #include + #include + #include + #include + #include + #include "util.h" + #include + #include + #include + #include + + /* + * These utility functions are adapted from those found in + * the sectool library used by the NSS security tools and + * other NSS test applications. + */ + + typedef struct { + enum { + PW_NONE = 0, /* no password */ + PW_FROMFILE = 1, /* password stored in a file */ + PW_PLAINTEXT = 2 /* plain-text password passed in buffer */ + /* PW_EXTERNAL = 3 */ + } source; + char *data; + /* depending on source this can be the actual + * password or the file to read it from + */ + } secuPWData; + + /* + * PrintAsAscii + */ + extern void + PrintAsAscii(PRFileDesc* out, const unsigned char *data, unsigned int len); + + /* + * PrintAsHex + */ + extern void + PrintAsHex(PRFileDesc* out, const unsigned char *data, unsigned int len); + + /* + * GetDigit + */ + extern int + GetDigit(char c); + + /* + * HexToBuf + */ + extern int + HexToBuf(unsigned char *inString, SECItem *outbuf, PRBool isHexData); + + /* + * FileToItem + */ + extern SECStatus + FileToItem(SECItem *dst, PRFileDesc *src); + + /* + * CheckPassword + */ + extern PRBool + CheckPassword(char *cp); + + /* + * GetPassword + */ + extern char * + GetPassword(FILE *input, + FILE *output, + char *prompt, + PRBool (*ok)(char *)); + + /* + * FilePasswd extracts the password from a text file + * + * Storing passwords is often used with server environments + * where prompting the user for a password or requiring it + * to be entered in the commnd line is not a feasible option. + * + * This function supports password extraction from files with + * multipe passwords, one for each token. In the single password + * case a line would just have the passord whereas in the multi- + * password variant they could be of the form + * + * token_1_name:its_password + * token_2_name:its_password + * + */ + extern char * + FilePasswd(PK11SlotInfo * + slot, PRBool retry, void *arg); + + /* + * GetModulePassword + */ + extern char * + GetModulePassword(PK11SlotInfo *slot, + int retry, + void *pwdata); + + /* + * GenerateRandom + */ + extern SECStatus + GenerateRandom(unsigned char *rbuf, + int rsize); + + /* + * FileToItem + */ + extern SECStatus + FileToItem(SECItem *dst, + PRFileDesc *src); + + /* + * SeedFromNoiseFile + */ + extern SECStatus + SeedFromNoiseFile(const char *noiseFileName); + + /* + * FileSize + */ + extern long + FileSize(const char* filename); + + /* + * ReadDERFromFile + */ + extern SECStatus + ReadDERFromFile(SECItem *der, const char *inFileName, PRBool ascii); + + #endif /* _UTIL_H */ + +`Util.c <#util.c>`__ +~~~~~~~~~~~~~~~~~~~~ + +.. container:: + + .. code:: c + + /* 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 https://mozilla.org/MPL/2.0/. */ + + #include "util.h" + + /* + * These utility functions are adapted from those found in + * the sectool library used by the NSS security tools and + * other NSS test applications. + */ + + /* + * Newline + */ + static void + Newline(PRFileDesc* out) + { + PR_fprintf(out, "\n"); + } + + /* + * PrintAsAscii + */ + void + PrintAsAscii(PRFileDesc* out, const unsigned char *data, unsigned int len) + { + char *b64Data = NULL; + + b64Data = BTOA_DataToAscii(data, len); + PR_fprintf(out, "%s", b64Data); + PR_fprintf(out, "\n"); + if (b64Data) { + PORT_Free(b64Data); + } + } + + /* + * PrintAsHex + */ + void + PrintAsHex(PRFileDesc* out, const unsigned char *data, unsigned int len) + { + unsigned i; + int column; + unsigned int limit = 15; + unsigned int level = 1; + + column = level; + if (!len) { + PR_fprintf(out, "(empty)\n"); + return; + } + + for (i = 0; i < len; i++) { + if (i != len - 1) { + PR_fprintf(out, "%02x:", data[i]); + column += 3; + } else { + PR_fprintf(out, "%02x", data[i]); + column += 2; + break; + } + if (column > 76 || (i % 16 == limit)) { + Newline(out); + column = level; + limit = i % 16; + } + } + if (column != level) { + Newline(out); + } + } + + /* + * GetDigit + */ + int + GetDigit(char c) + { + if (c == 0) { + return -1; + } + if (c <= '9' && c >= '0') { + return c - '0'; + } + if (c <= 'f' && c >= 'a') { + return c - 'a' + 0xa; + } + if (c <= 'F' && c >= 'A') { + return c - 'A' + 0xa; + } + return -1; + } + + /* + * HexToBuf + */ + int + HexToBuf(unsigned char *inString, SECItem *outbuf, PRBool isHexData) + { + int len = strlen((const char *)inString); + int outLen = len+1/2; + int trueLen = 0; + int digit1, digit2; + + outbuf->data = isHexData + ? PORT_Alloc(outLen) + : PORT_Alloc(len); + if (!outbuf->data) { + return -1; + } + if (isHexData) { + while (*inString) { + if ((*inString == '\n') || (*inString == ':')) { + inString++; + continue; + } + digit1 = GetDigit(*inString++); + digit2 = GetDigit(*inString++); + if ((digit1 == -1) || (digit2 == -1)) { + PORT_Free(outbuf->data); + outbuf->data = NULL; + return -1; + } + outbuf->data[trueLen++] = digit1 << 4 | digit2; + } + } else { + while (*inString) { + if (*inString == '\n') { + inString++; + continue; + } + outbuf->data[trueLen++] = *inString++; + } + outbuf->data[trueLen] = '\0'; + trueLen = trueLen-1; + } + outbuf->len = trueLen; + return 0; + } + + /* + * FileToItem + */ + SECStatus + FileToItem(SECItem *dst, PRFileDesc *src) + { + PRFileInfo info; + PRInt32 numBytes; + PRStatus prStatus; + + prStatus = PR_GetOpenFileInfo(src, &info); + + if (prStatus != PR_SUCCESS) { + return SECFailure; + } + + dst->data = 0; + if (SECITEM_AllocItem(NULL, dst, info.size)) { + numBytes = PR_Read(src, dst->data, info.size); + if (numBytes == info.size) { + return SECSuccess; + } + } + SECITEM_FreeItem(dst, PR_FALSE); + dst->data = NULL; + return SECFailure; + } + + /* + * echoOff + */ + static void echoOff(int fd) + { + if (isatty(fd)) { + struct termios tio; + tcgetattr(fd, &tio); + tio.c_lflag &= ~ECHO; + tcsetattr(fd, TCSAFLUSH, &tio); + } + } + + /* + * echoOn + */ + static void echoOn(int fd) + { + if (isatty(fd)) { + struct termios tio; + tcgetattr(fd, &tio); + tio.c_lflag |= ECHO; + tcsetattr(fd, TCSAFLUSH, &tio); + } + } + + /* + * CheckPassword + */ + PRBool CheckPassword(char *cp) + { + int len; + char *end; + len = PORT_Strlen(cp); + if (len < 8) { + return PR_FALSE; + } + end = cp + len; + while (cp < end) { + unsigned char ch = *cp++; + if (!((ch >= 'A') && (ch <= 'Z')) && + !((ch >= 'a') && (ch <= 'z'))) { + return PR_TRUE; + } + } + return PR_FALSE; + } + + /* + * GetPassword + */ + char* GetPassword(FILE *input, FILE *output, char *prompt, + PRBool (*ok)(char *)) + { + char phrase[200] = {'\0'}; + int infd = fileno(input); + int isTTY = isatty(infd); + + for (;;) { + /* Prompt for password */ + if (isTTY) { + fprintf(output, "%s", prompt); + fflush (output); + echoOff(infd); + } + fgets(phrase, sizeof(phrase), input); + if (isTTY) { + fprintf(output, "\n"); + echoOn(infd); + } + /* stomp on newline */ + phrase[PORT_Strlen(phrase)-1] = 0; + /* Validate password */ + if (!(*ok)(phrase)) { + if (!isTTY) return 0; + fprintf(output, "Password must be at least 8 characters long with one or more\n"); + fprintf(output, "non-alphabetic characters\n"); + continue; + } + return (char*) PORT_Strdup(phrase); + } + } + + /* + * FilePasswd extracts the password from a text file + * + * Storing passwords is often used with server environments + * where prompting the user for a password or requiring it + * to be entered in the commnd line is not a feasible option. + * + * This function supports password extraction from files with + * multipe passwords, one for each token. In the single password + * case a line would just have the passord whereas in the multi- + * password variant they could be of the form + * + * token_1_name:its_password + * token_2_name:its_password + * + */ + char * + FilePasswd(PK11SlotInfo *slot, PRBool retry, void *arg) + { + char* phrases, *phrase; + PRFileDesc *fd; + PRInt32 nb; + char *pwFile = arg; + int i; + const long maxPwdFileSize = 4096; + char* tokenName = NULL; + int tokenLen = 0; + + if (!pwFile) + return 0; + + if (retry) { + return 0; /* no good retrying - the files contents will be the same */ + } + + phrases = PORT_ZAlloc(maxPwdFileSize); + + if (!phrases) { + return 0; /* out of memory */ + } + + fd = PR_Open(pwFile, PR_RDONLY, 0); + if (!fd) { + fprintf(stderr, "No password file \"%s\" exists.\n", pwFile); + PORT_Free(phrases); + return NULL; + } + + nb = PR_Read(fd, phrases, maxPwdFileSize); + + PR_Close(fd); + + if (nb == 0) { + fprintf(stderr,"password file contains no data\n"); + PORT_Free(phrases); + return NULL; + } + + if (slot) { + tokenName = PK11_GetTokenName(slot); + if (tokenName) { + tokenLen = PORT_Strlen(tokenName); + } + } + i = 0; + do { + int startphrase = i; + int phraseLen; + + /* handle the Windows EOL case */ + while (phrases[i] != '\r' && phrases[i] != '\n' && i < nb) i++; + + /* terminate passphrase */ + phrases[i++] = '\0'; + /* clean up any EOL before the start of the next passphrase */ + while ( (isource != PW_NONE) { + PR_fprintf(PR_STDERR, "Incorrect password/PIN entered.\n"); + return NULL; + } + + switch (pwdata->source) { + case PW_NONE: + sprintf(prompt, "Enter Password or Pin for \"%s\":", + PK11_GetTokenName(slot)); + return GetPassword(stdin, stdout, prompt, CheckPassword); + case PW_FROMFILE: + pw = FilePasswd(slot, retry, pwdata->data); + pwdata->source = PW_PLAINTEXT; + pwdata->data = PL_strdup(pw); + return pw; + case PW_PLAINTEXT: + return PL_strdup(pwdata->data); + default: + break; + } + PR_fprintf(PR_STDERR, "Password check failed: No password found.\n"); + return NULL; + } + + /* + * GenerateRandom + */ + SECStatus + GenerateRandom(unsigned char *rbuf, int rsize) + { + char meter[] = { + "\r| |" }; + int fd, count; + int c; + SECStatus rv = SECSuccess; + cc_t orig_cc_min; + cc_t orig_cc_time; + tcflag_t orig_lflag; + struct termios tio; + + fprintf(stderr, "To generate random numbers, " + "continue typing until the progress meter is full:\n\n"); + fprintf(stderr, "%s", meter); + fprintf(stderr, "\r|"); + + /* turn off echo on stdin & return on 1 char instead of NL */ + fd = fileno(stdin); + + tcgetattr(fd, &tio); + orig_lflag = tio.c_lflag; + orig_cc_min = tio.c_cc[VMIN]; + orig_cc_time = tio.c_cc[VTIME]; + tio.c_lflag &= ~ECHO; + tio.c_lflag &= ~ICANON; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + tcsetattr(fd, TCSAFLUSH, &tio); + /* Get random noise from keyboard strokes */ + count = 0; + while (count < rsize) { + c = getc(stdin); + if (c == EOF) { + rv = SECFailure; + break; + } + *(rbuf + count) = c; + if (count == 0 || c != *(rbuf + count -1)) { + count++; + fprintf(stderr, "*"); + } + } + rbuf[count] = '\0'; + + fprintf(stderr, "\n\nFinished. Press enter to continue: "); + while ((c = getc(stdin)) != '\n' && c != EOF) + ; + if (c == EOF) + rv = SECFailure; + fprintf(stderr, "\n"); + + /* set back termio the way it was */ + tio.c_lflag = orig_lflag; + tio.c_cc[VMIN] = orig_cc_min; + tio.c_cc[VTIME] = orig_cc_time; + tcsetattr(fd, TCSAFLUSH, &tio); + return rv; + } + + /* + * SeedFromNoiseFile + */ + SECStatus + SeedFromNoiseFile(const char *noiseFileName) + { + char buf[2048]; + PRFileDesc *fd; + PRInt32 count; + + fd = PR_Open(noiseFileName, PR_RDONLY, 0); + if (!fd) { + fprintf(stderr, "failed to open noise file."); + return SECFailure; + } + + do { + count = PR_Read(fd,buf,sizeof(buf)); + if (count > 0) { + PK11_RandomUpdate(buf,count); + } + } while (count > 0); + + PR_Close(fd); + return SECSuccess; + } + + /* + * FileSize + */ + long FileSize(const char* filename) + { + struct stat stbuf; + stat(filename, &stbuf); + return stbuf.st_size; + } + + /* + * ReadDERFromFile + */ + SECStatus + ReadDERFromFile(SECItem *der, const char *inFileName, PRBool ascii) + { + SECStatus rv = SECSuccess; + PRFileDesc *inFile = NULL; + + inFile = PR_Open(inFileName, PR_RDONLY, 0); + if (!inFile) { + PR_fprintf(PR_STDERR, "Failed to open file \"%s\" (%ld, %ld).\n", + inFileName, PR_GetError(), PR_GetOSError()); + rv = SECFailure; + goto cleanup; + } + + if (ascii) { + /* First convert ascii to binary */ + SECItem filedata; + char *asc, *body; + + /* Read in ascii data */ + rv = FileToItem(&filedata, inFile); + asc = (char *)filedata.data; + if (!asc) { + PR_fprintf(PR_STDERR, "unable to read data from input file\n"); + rv = SECFailure; + goto cleanup; + } + + /* check for headers and trailers and remove them */ + if ((body = strstr(asc, "-----BEGIN")) != NULL) { + char *trailer = NULL; + asc = body; + body = PORT_Strchr(body, '\n'); + if (!body) + body = PORT_Strchr(asc, '\r'); /* maybe this is a MAC file */ + if (body) + trailer = strstr(++body, "-----END"); + if (trailer != NULL) { + *trailer = '\0'; + } else { + PR_fprintf(PR_STDERR, "input has header but no trailer\n"); + PORT_Free(filedata.data); + rv = SECFailure; + goto cleanup; + } + } else { + body = asc; + } + + /* Convert to binary */ + rv = ATOB_ConvertAsciiToItem(der, body); + if (rv) { + PR_fprintf(PR_STDERR, "error converting ascii to binary %s\n", + PORT_GetError()); + PORT_Free(filedata.data); + rv = SECFailure; + goto cleanup; + } + + PORT_Free(filedata.data); + } else { + /* Read in binary der */ + rv = FileToItem(der, inFile); + if (rv) { + PR_fprintf(PR_STDERR, "error converting der \n"); + rv = SECFailure; + } + } + cleanup: + if (inFile) { + PR_Close(inFile); + } + return rv; + } \ No newline at end of file -- cgit v1.2.3