1. A program to compute the hash of a file and save it to another file.
/* NSPR Headers */
#include <prprf.h>
#include <prtypes.h>
#include <plgetopt.h>
#include <prio.h>
#include <prprf.h>
/* NSS headers */
#include <secoid.h>
#include <secmodt.h>
#include <sechash.h>
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;
}