Ejemplo n.º 1
0
static bool pluto_init_nss(char *nssdb)
{
	SECStatus rv;

	/* little lie, lsw_nss_setup doesn't have logging */
	loglog(RC_LOG_SERIOUS, "NSS DB directory: sql:%s", nssdb);

	lsw_nss_buf_t err;
	if (!lsw_nss_setup(nssdb, LSW_NSS_READONLY, lsw_nss_get_password, err)) {
		loglog(RC_LOG_SERIOUS, "%s", err);
		return FALSE;
	}

	libreswan_log("NSS initialized");

	/*
	 * This exists purely to make the BSI happy.
	 * We do not inflict this on other users
	 */
	if (pluto_nss_seedbits != 0) {
		int seedbytes = BYTES_FOR_BITS(pluto_nss_seedbits);
		unsigned char *buf = alloc_bytes(seedbytes,"TLA seedmix");

		get_bsi_random(seedbytes, buf); /* much TLA, very blocking */
		rv = PK11_RandomUpdate(buf, seedbytes);
		libreswan_log("seeded %d bytes into the NSS PRNG", seedbytes);
		passert(rv == SECSuccess);
		messupn(buf, seedbytes);
		pfree(buf);
	}

	return TRUE;
}
Ejemplo n.º 2
0
/*
 * _import_crl <url> <der size>
 * the der blob is passed through STDIN from pluto's fork
 */
int main(int argc, char *argv[])
{
	char *url, *lenstr;
	unsigned char *buf, *tbuf;
	size_t len, tlen;
	ssize_t rd;
	int fin;

	if (argc != 3)
		exit(-1);

	progname = argv[0];
	url = argv[1];
	lenstr = argv[2];

	/* can't be 0 */
	if (*lenstr == '0' && strlen(lenstr) == 1)
		exit(-1);

	while (*lenstr != '\0') {
		if (!isalnum(*lenstr++)) {
			exit(-1);
		}
	}

	tlen = len = (size_t) atoi(argv[2]);
	tbuf = buf = (unsigned char *) malloc(len);

	if (tbuf == NULL)
		exit(-1);

	while (tlen != 0 && (rd = read(STDIN_FILENO, buf, len)) != 0) {
		if (rd == -1) {
			if (errno == EINTR)
				continue;
			exit(-1);
		}
		tlen -= rd;
		buf += rd;
	}

	if ((size_t)(buf - tbuf) != len)
		exit(-1);

	const struct lsw_conf_options *oco = lsw_init_options();
	lsw_nss_buf_t err;
	if (!lsw_nss_setup(oco->nssdir, 0, lsw_nss_get_password, err)) {
		fprintf(stderr, "%s: %s\n", progname, err);
		exit(1);
	}

	fin = import_crl(url, tbuf, len);

	if (tbuf != NULL)
		free(tbuf);

	NSS_Shutdown();

	return fin;
}
Ejemplo n.º 3
0
/*
 * generate an RSA signature key
 *
 * e is fixed at 3, without discussion.  That would not be wise if these
 * keys were to be used for encryption, but for signatures there are some
 * real speed advantages.
 * See also: https://www.imperialviolet.org/2012/03/16/rsae.html
 */
void rsasigkey(int nbits, int seedbits, const struct lsw_conf_options *oco)
{
	PK11RSAGenParams rsaparams = { nbits, (long) F4 };
	PK11SlotInfo *slot = NULL;
	SECKEYPrivateKey *privkey = NULL;
	SECKEYPublicKey *pubkey = NULL;
	realtime_t now = realnow();

	lsw_nss_buf_t err;
	if (!lsw_nss_setup(oco->nssdir, 0, lsw_nss_get_password, err)) {
		fprintf(stderr, "%s: %s\n", progname, err);
		exit(1);
	}

#ifdef FIPS_CHECK
	if (PK11_IsFIPS() && !FIPSCHECK_verify(NULL, NULL)) {
		fprintf(stderr,
			"FIPS HMAC integrity verification test failed.\n");
		exit(1);
	}
#endif

	/* Good for now but someone may want to use a hardware token */
	slot = lsw_nss_get_authenticated_slot(err);
	if (slot == NULL) {
		fprintf(stderr, "%s: %s\n", progname, err);
		lsw_nss_shutdown();
		exit(1);
	}

	/* Do some random-number initialization. */
	UpdateNSS_RNG(seedbits);
	privkey = PK11_GenerateKeyPair(slot,
				       CKM_RSA_PKCS_KEY_PAIR_GEN,
				       &rsaparams, &pubkey,
				       PR_TRUE,
				       PK11_IsFIPS() ? PR_TRUE : PR_FALSE,
				       lsw_return_nss_password_file_info());
	/* inTheToken, isSensitive, passwordCallbackFunction */
	if (privkey == NULL) {
		fprintf(stderr,
			"%s: key pair generation failed: \"%d\"\n", progname,
			PORT_GetError());
		return;
	}

	chunk_t public_modulus = {
		.ptr = pubkey->u.rsa.modulus.data,
		.len = pubkey->u.rsa.modulus.len,
	};
	chunk_t public_exponent = {
		.ptr = pubkey->u.rsa.publicExponent.data,
		.len = pubkey->u.rsa.publicExponent.len,
	};

	char *hex_ckaid;
	{
		SECItem *ckaid = PK11_GetLowLevelKeyIDForPrivateKey(privkey);
		if (ckaid == NULL) {
			fprintf(stderr, "%s: 'CKAID' calculation failed\n", progname);
			exit(1);
		}
		hex_ckaid = strdup(conv(ckaid->data, ckaid->len, 16));
		SECITEM_FreeItem(ckaid, PR_TRUE);
	}

	/*privkey->wincx = &pwdata;*/
	PORT_Assert(pubkey != NULL);
	fprintf(stderr, "Generated RSA key pair with CKAID %s was stored in the NSS database\n",
		hex_ckaid);

	/* and the output */
	libreswan_log("output...\n");  /* deliberate extra newline */
	printf("\t# RSA %d bits   %s   %s", nbits, outputhostname,
		ctime(&now.rt.tv_sec));
	/* ctime provides \n */
	printf("\t# for signatures only, UNSAFE FOR ENCRYPTION\n");

	printf("\t#ckaid=%s\n", hex_ckaid);

	/* RFC2537/RFC3110-ish format */
	{
		char *base64 = NULL;
		err_t err = rsa_pubkey_to_base64(public_exponent, public_modulus, &base64);
		if (err) {
			fprintf(stderr, "%s: unexpected error encoding RSA public key '%s'\n",
				progname, err);
			exit(1);
		}
		printf("\t#pubkey=%s\n", base64);
		pfree(base64);
	}

	printf("\tModulus: 0x%s\n", conv(public_modulus.ptr, public_modulus.len, 16));
	printf("\tPublicExponent: 0x%s\n", conv(public_exponent.ptr, public_exponent.len, 16));

	if (hex_ckaid != NULL)
		free(hex_ckaid);
	if (privkey != NULL)
		SECKEY_DestroyPrivateKey(privkey);
	if (pubkey != NULL)
		SECKEY_DestroyPublicKey(pubkey);

	lsw_nss_shutdown();
}

/*
 * lsw_random - get some random bytes from /dev/random (or wherever)
 * NOTE: This is only used for additional seeding of the NSS RNG
 */
void lsw_random(size_t nbytes, unsigned char *buf)
{
	size_t ndone;
	int dev;
	ssize_t got;

	dev = open(device, 0);
	if (dev < 0) {
		fprintf(stderr, "%s: could not open %s (%s)\n", progname,
			device, strerror(errno));
		exit(1);
	}

	ndone = 0;
	libreswan_log("getting %d random seed bytes for NSS from %s...\n",
		      (int) nbytes * BITS_PER_BYTE, device);
	while (ndone < nbytes) {
		got = read(dev, buf + ndone, nbytes - ndone);
		if (got < 0) {
			fprintf(stderr, "%s: read error on %s (%s)\n", progname,
				device, strerror(errno));
			exit(1);
		}
		if (got == 0) {
			fprintf(stderr, "%s: eof on %s!?!\n", progname, device);
			exit(1);
		}
		ndone += got;
	}

	close(dev);
}

/*
   - conv - convert bits to output in specified datatot format
 * NOTE: result points into a STATIC buffer
 */
static const char *conv(const unsigned char *bits, size_t nbytes, int format)
{
	static char convbuf[MAXBITS / 4 + 50];  /* enough for hex */
	size_t n;

	n = datatot(bits, nbytes, format, convbuf, sizeof(convbuf));
	if (n == 0) {
		fprintf(stderr, "%s: can't-happen convert error\n", progname);
		exit(1);
	}
	if (n > sizeof(convbuf)) {
		fprintf(stderr,
			"%s: can't-happen convert overflow (need %d)\n",
			progname, (int) n);
		exit(1);
	}
	return convbuf;
}
Ejemplo n.º 4
0
int main(int argc UNUSED, char *argv[])
{
	tool_init_log(argv[0]);

	/*
	 * Need to ensure that NSS is initialized before calling
	 * ike_alg_init().  Some sanity checks require a working NSS.
	 */
	lsw_nss_buf_t err;
	if (!lsw_nss_setup(NULL, 0, NULL, err)) {
		fprintf(stderr, "unexpected %s\n", err);
		exit(1);
	}

	ike_alg_init();

	/* esp= */
	fprintf(stdout, "\n---- ESP tests that should succeed ----\n");

	do_test("aes_gcm_a-128-null", PROTO_IPSEC_ESP);
	do_test("3des-sha1;modp1024", PROTO_IPSEC_ESP);
	do_test("3des-sha1;modp1536", PROTO_IPSEC_ESP);
	do_test("3des-sha1;modp2048", PROTO_IPSEC_ESP);
	do_test("3des-sha1;dh22", PROTO_IPSEC_ESP);
	do_test("3des-sha1;dh23", PROTO_IPSEC_ESP);
	do_test("3des-sha1;dh24", PROTO_IPSEC_ESP);
	do_test("3des-sha1", PROTO_IPSEC_ESP);
	do_test("null-sha1", PROTO_IPSEC_ESP);
	do_test("aes", PROTO_IPSEC_ESP);
	do_test("aes_cbc", PROTO_IPSEC_ESP);
	do_test("aes-sha", PROTO_IPSEC_ESP);
	do_test("aes-sha1", PROTO_IPSEC_ESP);
	do_test("aes-sha2", PROTO_IPSEC_ESP);
	do_test("aes-sha256", PROTO_IPSEC_ESP);
	do_test("aes-sha384", PROTO_IPSEC_ESP);
	do_test("aes-sha512", PROTO_IPSEC_ESP);
	do_test("aes128-sha1", PROTO_IPSEC_ESP);
	do_test("aes128-aes_xcbc", PROTO_IPSEC_ESP);
	do_test("aes192-sha1", PROTO_IPSEC_ESP);
	do_test("aes256-sha1", PROTO_IPSEC_ESP);
	do_test("aes256-sha", PROTO_IPSEC_ESP);
	do_test("aes256-sha2", PROTO_IPSEC_ESP);
	do_test("aes256-sha2_256", PROTO_IPSEC_ESP);
	do_test("aes256-sha2_384", PROTO_IPSEC_ESP);
	do_test("aes256-sha2_512", PROTO_IPSEC_ESP);
	do_test("camellia", PROTO_IPSEC_ESP);
	do_test("camellia128", PROTO_IPSEC_ESP);
	do_test("camellia192", PROTO_IPSEC_ESP);
	do_test("camellia256", PROTO_IPSEC_ESP);
	do_test("aes_ccm_a-128-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_a-192-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_a-256-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_b-128-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_b-192-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_b-256-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_c-128-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_c-192-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_c-256-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_a-128-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_a-192-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_a-256-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_b-128-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_b-192-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_b-256-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_c-128-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_c-192-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_c-256-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm-256-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm-192-null", PROTO_IPSEC_ESP);
#if 0
	/* these are caught using "aliasing" and rewritten to the above syntax */
	do_test("aes_ccm_8-128-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_8-192-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_8-256-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_12-128-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_12-192-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_12-256-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_16-128-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_16-192-null", PROTO_IPSEC_ESP);
	do_test("aes_ccm_16-256-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_8-128-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_8-192-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_8-256-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_12-128-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_12-192-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_12-256-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_16-128-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_16-192-null", PROTO_IPSEC_ESP);
	do_test("aes_gcm_16-256-null", PROTO_IPSEC_ESP);
#endif
	/* other */
	do_test("aes_ctr", PROTO_IPSEC_ESP);
	do_test("aesctr", PROTO_IPSEC_ESP);
	do_test("aes_ctr128", PROTO_IPSEC_ESP);
	do_test("aes_ctr192", PROTO_IPSEC_ESP);
	do_test("aes_ctr256", PROTO_IPSEC_ESP);
	do_test("serpent", PROTO_IPSEC_ESP);
	do_test("twofish", PROTO_IPSEC_ESP);
	do_test("mars", PROTO_IPSEC_ESP);
	/*
	 * should this be supported - for now man page says not
	 * do_test("modp1536", PROTO_IPSEC_ESP);
	 */

	fprintf(stdout, "\n---- ESP tests that should fail----\n");

	do_test("3des168-sha1", PROTO_IPSEC_ESP); /* should get rejected */
	do_test("3des-null", PROTO_IPSEC_ESP); /* should get rejected */
	do_test("aes128-null", PROTO_IPSEC_ESP); /* should get rejected */
	do_test("aes224-sha1", PROTO_IPSEC_ESP); /* should get rejected */
	do_test("aes512-sha1", PROTO_IPSEC_ESP); /* should get rejected */
	do_test("aes-sha1555", PROTO_IPSEC_ESP); /* should get rejected */
	do_test("camellia666-sha1", PROTO_IPSEC_ESP); /* should get rejected */
	do_test("blowfish", PROTO_IPSEC_ESP); /* obsoleted */
	do_test("des-sha1", PROTO_IPSEC_ESP); /* obsoleted */
	do_test("aes_ctr666", PROTO_IPSEC_ESP); /* bad key size */
	do_test("aes128-sha2_128", PROTO_IPSEC_ESP); /* _128 does not exist */
	do_test("aes256-sha2_256-4096", PROTO_IPSEC_ESP); /* double keysize */
	do_test("aes256-sha2_256-128", PROTO_IPSEC_ESP); /* now what?? */
	do_test("vanitycipher", PROTO_IPSEC_ESP);
	do_test("ase-sah", PROTO_IPSEC_ESP); /* should get rejected */
	do_test("aes-sah1", PROTO_IPSEC_ESP); /* should get rejected */
	do_test("id3", PROTO_IPSEC_ESP); /* should be rejected; idXXX removed */
	do_test("aes-id3", PROTO_IPSEC_ESP); /* should be rejected; idXXX removed */
	do_test("aes_gcm-md5", PROTO_IPSEC_ESP); /* AEAD must have auth null */

	/* ah= */

	fprintf(stdout, "\n---- AH tests that should succeed ----\n");
	do_test("md5", PROTO_IPSEC_AH);
	do_test("sha", PROTO_IPSEC_AH);
	do_test("sha1", PROTO_IPSEC_AH);
	do_test("sha2", PROTO_IPSEC_AH);
	do_test("sha256", PROTO_IPSEC_AH);
	do_test("sha384", PROTO_IPSEC_AH);
	do_test("sha512", PROTO_IPSEC_AH);
	do_test("sha2_256", PROTO_IPSEC_AH);
	do_test("sha2_384", PROTO_IPSEC_AH);
	do_test("sha2_512", PROTO_IPSEC_AH);
	do_test("aes_xcbc", PROTO_IPSEC_AH);
	do_test("ripemd", PROTO_IPSEC_AH);

	fprintf(stdout, "\n---- AH tests that should fail ----\n");
	do_test("aes-sha1", PROTO_IPSEC_AH);
	do_test("vanityhash1", PROTO_IPSEC_AH);
	do_test("aes_gcm_c-256", PROTO_IPSEC_AH);
	do_test("id3", PROTO_IPSEC_AH); /* should be rejected; idXXX removed */
	do_test("3des", PROTO_IPSEC_AH);
	do_test("null", PROTO_IPSEC_AH);
	do_test("aes_gcm", PROTO_IPSEC_AH);
	do_test("aes_ccm", PROTO_IPSEC_AH);

	/* ike= */

	fprintf(stdout, "\n---- IKE tests that should succeed ----\n");
	do_test("3des-sha1", PROTO_ISAKMP);

	fprintf(stdout, "\n---- IKE tests that should fail ----\n");
	do_test("id2", PROTO_ISAKMP); /* should be rejected; idXXX removed */
	do_test("3des-id2", PROTO_ISAKMP); /* should be rejected; idXXX removed */

	fflush(NULL);
	report_leaks();

	lsw_nss_shutdown();
	tool_close_log();
	exit(0);
}
Ejemplo n.º 5
0
/*
 * generate an RSA signature key
 *
 * e is fixed at 3, without discussion.  That would not be wise if these
 * keys were to be used for encryption, but for signatures there are some
 * real speed advantages.
 * See also: https://www.imperialviolet.org/2012/03/16/rsae.html
 */
void rsasigkey(int nbits, int seedbits, char *configdir, char *password)
{
	SECStatus rv;
	PK11RSAGenParams rsaparams = { nbits, (long) E };
	secuPWData pwdata = { PW_NONE, NULL };
	PK11SlotInfo *slot = NULL;
	SECKEYPrivateKey *privkey = NULL;
	SECKEYPublicKey *pubkey = NULL;
	realtime_t now = realnow();

	if (password == NULL) {
		pwdata.source = PW_NONE;
	} else {
		/* check if passwd == configdir/nsspassword */
		size_t cdl = strlen(configdir);
		size_t pwl = strlen(password);
		static const char suf[] = "/nsspassword";

		if (pwl == cdl + sizeof(suf) - 1 &&
			memeq(password, configdir, cdl) &&
			memeq(password + cdl, suf, sizeof(suf)))
			pwdata.source = PW_FROMFILE;
		else
			pwdata.source = PW_PLAINTEXT;
	}
	pwdata.data = password;

	lsw_nss_buf_t err;
	if (!lsw_nss_setup(configdir, FALSE/*rw*/, GetModulePassword, err)) {
		fprintf(stderr, "%s: %s\n", me, err);
		exit(1);
	}

#ifdef FIPS_CHECK
	if (PK11_IsFIPS() && !FIPSCHECK_verify(NULL, NULL)) {
		fprintf(stderr,
			"FIPS HMAC integrity verification test failed.\n");
		exit(1);
	}
#endif

	if (PK11_IsFIPS() && password == NULL) {
		fprintf(stderr,
			"%s: On FIPS mode a password is required\n",
			me);
		exit(1);
	}

	/* Good for now but someone may want to use a hardware token */
	slot = PK11_GetInternalKeySlot();
	/* In which case this may be better */
	/* slot = PK11_GetBestSlot(CKM_RSA_PKCS_KEY_PAIR_GEN, password ? &pwdata : NULL); */
	/* or the user may specify the name of a token. */

#if 0
	if (PK11_IsFIPS() || !PK11_IsInternal(slot)) {
		rv = PK11_Authenticate(slot, PR_FALSE, &pwdata);
		if (rv != SECSuccess) {
			fprintf(stderr, "%s: could not authenticate to token '%s'\n",
				me, PK11_GetTokenName(slot));
			return;
		}
	}
#endif /* 0 */

	/* Do some random-number initialization. */
	UpdateNSS_RNG(seedbits);
	/* Log in to the token */
	if (password != NULL) {
		rv = PK11_Authenticate(slot, PR_FALSE, &pwdata);
		if (rv != SECSuccess) {
			fprintf(stderr,
				"%s: could not authenticate to token '%s'\n",
				me, PK11_GetTokenName(slot));
			return;
		}
	}
	privkey = PK11_GenerateKeyPair(slot,
				       CKM_RSA_PKCS_KEY_PAIR_GEN,
				       &rsaparams, &pubkey,
				       PR_TRUE,
				       password != NULL? PR_TRUE : PR_FALSE,
				       &pwdata);
	/* inTheToken, isSensitive, passwordCallbackFunction */
	if (privkey == NULL) {
		fprintf(stderr,
			"%s: key pair generation failed: \"%d\"\n", me,
			PORT_GetError());
		return;
	}

	chunk_t public_modulus = {
		.ptr = pubkey->u.rsa.modulus.data,
		.len = pubkey->u.rsa.modulus.len,
	};
	chunk_t public_exponent = {
		.ptr = pubkey->u.rsa.publicExponent.data,
		.len = pubkey->u.rsa.publicExponent.len,
	};

	char *hex_ckaid;
	{
		SECItem *ckaid = PK11_GetLowLevelKeyIDForPrivateKey(privkey);
		if (ckaid == NULL) {
			fprintf(stderr, "%s: 'CKAID' calculation failed\n", me);
			exit(1);
		}
		hex_ckaid = strdup(conv(ckaid->data, ckaid->len, 16));
		SECITEM_FreeItem(ckaid, PR_TRUE);
	}

	/*privkey->wincx = &pwdata;*/
	PORT_Assert(pubkey != NULL);
	fprintf(stderr, "Generated RSA key pair with CKAID %s was stored in the NSS database\n",
		hex_ckaid);

	/* and the output */
	report("output...\n");  /* deliberate extra newline */
	printf("\t# RSA %d bits   %s   %s", nbits, outputhostname,
		ctime(&now.real_secs));
	/* ctime provides \n */
	printf("\t# for signatures only, UNSAFE FOR ENCRYPTION\n");

	printf("\t#ckaid=%s\n", hex_ckaid);

	/* RFC2537/RFC3110-ish format */
	{
		char *bundle = base64_bundle(E, public_modulus);
		printf("\t#pubkey=%s\n", bundle);
		pfree(bundle);
	}

	printf("\tModulus: 0x%s\n", conv(public_modulus.ptr, public_modulus.len, 16));
	printf("\tPublicExponent: 0x%s\n", conv(public_exponent.ptr, public_exponent.len, 16));

	if (hex_ckaid != NULL)
		free(hex_ckaid);
	if (privkey != NULL)
		SECKEY_DestroyPrivateKey(privkey);
	if (pubkey != NULL)
		SECKEY_DestroyPublicKey(pubkey);

	lsw_nss_shutdown(LSW_NSS_CLEANUP);
}

/*
 * getrandom - get some random bytes from /dev/random (or wherever)
 * NOTE: This is only used for additional seeding of the NSS RNG
 */
void getrandom(size_t nbytes, unsigned char *buf)
{
	size_t ndone;
	int dev;
	ssize_t got;

	dev = open(device, 0);
	if (dev < 0) {
		fprintf(stderr, "%s: could not open %s (%s)\n", me,
			device, strerror(errno));
		exit(1);
	}

	ndone = 0;
	if (verbose) {
		fprintf(stderr, "getting %d random seed bytes for NSS from %s...\n",
			(int) nbytes * BITS_PER_BYTE,
			device);
	}
	while (ndone < nbytes) {
		got = read(dev, buf + ndone, nbytes - ndone);
		if (got < 0) {
			fprintf(stderr, "%s: read error on %s (%s)\n", me,
				device, strerror(errno));
			exit(1);
		}
		if (got == 0) {
			fprintf(stderr, "%s: eof on %s!?!\n", me, device);
			exit(1);
		}
		ndone += got;
	}

	close(dev);
}

/*
   - conv - convert bits to output in specified datatot format
 * NOTE: result points into a STATIC buffer
 */
static const char *conv(const unsigned char *bits, size_t nbytes, int format)
{
	static char convbuf[MAXBITS / 4 + 50];  /* enough for hex */
	size_t n;

	n = datatot(bits, nbytes, format, convbuf, sizeof(convbuf));
	if (n == 0) {
		fprintf(stderr, "%s: can't-happen convert error\n", me);
		exit(1);
	}
	if (n > sizeof(convbuf)) {
		fprintf(stderr,
			"%s: can't-happen convert overflow (need %d)\n",
			me, (int) n);
		exit(1);
	}
	return convbuf;
}

/*
   - report - report progress, if indicated
 */
void report(msg)
char *msg;
{
	if (!verbose)
		return;

	fprintf(stderr, "%s\n", msg);
}