random technical thoughts from the Nominet technical team

40K signatures / second on fips 140-2 level 3 hardware.

1 Star2 Stars3 Stars4 Stars5 Stars (2 votes, average: 4.5 out of 5)
Loading ... Loading ...
Posted by roy on Jun 2nd, 2008

Vendors use different terminology to specify the performance of their Hardware Security Modules (HSMs). Regular terms are transactions, exponentiations, encryptions or signatures per second, or microsecond per transaction, exponentiation, etc. Performance statistics that use different units are incomparable. We’re trying to overcome that by using common unit. This post elaborates further on an small application for performance measurement.

Performance depends on algorithm and size of the key. Mostly, 1024-bit RSA private key operations are used, but that is often not specified. Using units like “encryption” or “verification” is biased as well, as both encryption/verification are public key operations (and thus small exponents), which are much faster than “decrypting/signing”. Using “exponentiations” is sometimes used to amplify the statistics. For example, a 1024 bit RSA key implies 512 exponentiations for a single “transaction” (the performance numbers are blown up by a factor of 2^9 …. on paper).

Performance is only comparable when using the same standard measurement unit. Since most vendors use 1024 bit RSA key signatures per second (sig/sec), let’s use that for a performance specification conformance test (or… lets check the marketing on the box).

For this test we’re using a Sun Fire T2000 with 3 SCA6000 cards. The technical specification promises “Up to 13,000 RSA operations per second with 1,024-bit keys”. All three combined should get a nice performance of about 39,000 RSA signatures/second…. in theory.

An often used method to measure performance is the OpenSSL speed test. However, it is not possible to specify keys that are located on the HSM. Also, an engine is needed to let OpenSSL use the pkcs11 interface. The well known OpenSC PKCS11 engine assumes that keys are on the HSM, while the RSA speed test generates its own key causing the speed test to fail. Sun’s PKCS11 engine is fully supported (thanks for Darren J. Moffat for pointing that out, see his comment below), the patches for OpenSSL are not supported by Sun. Lastly, the OpenSSL speed test uses fork/wait/pipe (using the undocumented -multi and -elapsed for proper timing), where we want to use threads (less overhead, no IPC). So it was time to write a small performance test application that uses native PKCS11 calls.

The result of that speed test is a whopping 39353 sig/sec for a 1024 RSA private key. This was verified independently by the unix time utility (for elapsed time) and Solaris kstat utility (for actual hardware transactions).

Or….. signing 7 million records in less than 3 minutes.

hsm-speed implementation notes

Download the hsm-speed package.

Simply creating a loop in which data is signed might not get the desired performance. A single loop performed at about 1600 sig/sec, while the specification promised 13000 sig/sec per card. A single loop (one process thread) did not get enough exposure to fill the bus fast enough. Creating multiple processing threads seems the obvious answer, especially since the T2000 uses an UltraSPARC T1 processor with 32 simultaneous processing threads. The speed-test is made multi threaded (using pthreads for portability, not the Solaris native threads), and gets about 13200 sig/sec on a single card. Note that there is also the option to fork processes, which effectively causes multithreading per forked process. Since forking has more overhead than threading, and threading has more overhead than looping, a straightforward way to maximize performance is to increase the loop iteration until it adds no more speed. Then increase the threads until it adds no more, then increase the forks.

Solaris Cryptographic Framework notes

The Solaris cryptographic framework allows different slot configurations. The “Metaslot” serves as a single virtual slot with all the combined capabilities of all the tokens and slots that have been installed. The “Keystore” slot groups only the crypto hardware together. The order in which multiple calls to C_FindObject returns objects from the metastore is reverse of that of the keystore. Hence, a search for a key without specifying the object class, will on the metaslot return the private key first, and on the keystore slot return the public key first. Effectively, when using the keystore slot, a C_SignInit that returns error “CKR_KEY_TYPE_INCONSISTENT” might be the result of not having specified CKO_PRIVATE_KEY in the search template for C_FindObjectsInit().

Another problem encountered with the Metaslot configuration is that it has a bug in meta_release_slot_session, used by C_CloseAllSessions, causing a nasty segmentation fault when trying to close a certain amount of idle sessions. This is circumvented by closing individual sessions one by one, though that is a tiny bit detrimental on the overall performance.

It is essential that the cards have the same firmware. Exporting the keystore information to another card requires the same firmware on both cards.

The PIN is a combination of the username and password, separated by a colon. When the password requirement for the SCA6000 is set to high, the password must be at least 8 characters long. However, the solaris getpass() call (from stdlib.h) only returns the first 8 characters, thus it leaves no room for the username to be specified. The GNU getpass() (libc) does not have this limitation. To circumvent this issue, use getpassphrase() on solaris. Note that this function is not portable.

Notes on PKCS11

Threads that share a single session might interfere each other between a C_SignInit and a C_Sign call. This will have unpredictable behavior. A thread safe way of sharing sessions is using mutex locks. This will significantly reduce the benefit of using threads. One way to avoid interference without having to use mutex locks is to create one session per thread. Since sessions can safely interleave and interfere, this is a very effective way to guarantee thread safety without locking.

PGP Desktop broken on Leopard

1 Star2 Stars3 Stars4 Stars5 Stars (2 votes, average: 3 out of 5)
Loading ... Loading ...
Posted by jason on Nov 2nd, 2007

We use PGP a lot here at Nominet, and I have been using PGP Desktop 9.6.3 on my Mac for quite some time. Unfortunately after upgrading to Leopard PGP Desktop is no longer working. I believe an update is due sometime in November, though this is in fact the beta program for PGP Desktop 9.7. Leopard has been available to developers for many, many months (with the iPhone delaying things as well) so I can’t really comprehend how they failed to test the product with Leopard.

Instead of waiting for this update I decided to look at GnuPGP, you can download and compile this very straightforwardly, though watch out if you see the following when compiling:

/usr/lib/libSystem.dylib unknown flags (type) of section 9 (__TEXT,__dof_plockstat)

What you need to do is make sure you have installed the Xcode tools from the Leopard DVD, doing an upgrade from Tiger does not upgrade Xcode!

You can even pick up a dmg file from sourceforge.

Using Perl’s Inline::C to call OpenSSL’s EVP and ENGINE libraries.

1 Star2 Stars3 Stars4 Stars5 Stars (5 votes, average: 4.4 out of 5)
Loading ... Loading ...
Posted by roy on Apr 22nd, 2007

We’d like to use the SCA6000, a hardware security module from a Perl script.

The problem is that there is no perl package that combines OpenSSL’s ENGINE and EVP functionality. To route around this problem, we’ll call the necessary C library functions straight from perl, using Perl’s Inline C package. Inline::C is very useful to make add-hoc function calls to C libraries from Perl. We’ll try to give a brief explanation here.

Note that we don’t need to write any C code for this test, we just need to import C functions using prototypes from openssl/evp.h and openssl/engine.h. One thing is important to realize. In perl, there is no concept of types. A variable can transpose from integer to string to integer on the fly. This makes Perl powerful, or annoying, depending on your perspective. But, to include C code in your perl program, the concept of types rises to the surface. To call a C function from perl, the perl variable needs to be casted to a proper C type. This casting is done by using a typemap, which maps C types to perl, and vice versa.

For well known C types, such as int, short, char, etc, there exist a typemap file. A particular line in a typemap contains the C type on the left hand side, and a perl macro on the right hand side. The macro contains an INPUT section, to translate perl to C types, and an OUTPUT section to translate C types to perl. For unknown C types, like the EVP_PKEY struct, we’d have to write our own typemap. Luckily, most of these things are pointers to structs of which we don’t need to use the internals in perl, so we’ll map those to T_PTROBJ macros.

In short, it is trivial to include C code in your perl scripts if it is using well known C types. For other types, the typemap needs some extra work. That is it really. A proof of concept perl script (EVP_signer.pl) is included below. If you’re interested in what happens under the blanket, look at the example below, and move on to read perlguts, perlxs, etc.

I’ll give an example from an actual typemap file, included in any perl distro:

int	T_IV

INPUT
T_IV
	$var = (int)SvIV($arg)

OUTPUT
T_IV
	sv_setiv($arg, (IV)$var);

$var is the C variable, $arg is the perl variable.
In the INPUT section (turns perl variable to C) we see a function SvIV($arg). This gets the value of $arg. It is then casted to an integer (using (int)), and stored in $var.
The OUTPUT section (that turns C to perl) we see a function sv_setiv(). This turns an integer, stored in $var, into a perl variable in $arg.

The real typemap we use for this exercise (a perl program that uses the EVP and ENGINE function calls) is fairly straightforward:

EVP_MD_CTX *            T_PTROBJ
EVP_PKEY *              T_PTROBJ
ENGINE *                T_PTROBJ
UI_METHOD *             T_PTROBJ
EVP_MD *                T_PTROBJ
const EVP_MD *          T_PTROBJ
unsigned int*           T_PV
const void*             T_PV

The program itself is below. The code is commented inline:

#!/usr/bin/perl -w

use strict;
use subs qw/check_error_queue/;

# the SCA6000 constant indicates if we're using a SUN SCA6000 card, in which case we're using
# the pkcs11 engine, or if we're using the default openssl engine. We'll also base the path to the
# openssl library based on the SCA6000 value.

use constant SCA6000 => 0;
use constant OPENSSLPATH => SCA6000?'/opt/openssl-0.9.8d':'/usr/local/ssl';

sub main
    {
        # The user needs to specify a key identifier as argument.

    $#ARGV == 0 or die "usage: EVP_signer keyid\n";

        # all the parameters we'd like the user to specify, but we'll declare it here for now.

    my $message = "Hello World!"; # the message to be signed.
    my $key_id = $ARGV[0];        # the key identifier, derived from the argument
    my $engine_id = SCA6000?"pkcs11":"openssl"; # the engine

        # load human readable error strings.

    ERR_load_crypto_strings();

        # read the standard openssl config file

    OPENSSL_config(0); check_error_queue;

        # setup engine

    ENGINE_load_openssl(); check_error_queue;

    my $engine = ENGINE_by_id($engine_id); check_error_queue;

    ENGINE_init($engine) or check_error_queue;

        # To use the engine, we need to gain access first. The user is authenticated by a PIN.
        # This is only useful when the SCA6000 is used as an engine.

    SCA6000 and (ENGINE_ctrl_cmd_string($engine, 'PIN', 'nominet1:abc123', 0) or check_error_queue);

        # assign the private key

    my $key = ENGINE_load_private_key($engine, $key_id, UI_OpenSSL(), 0); check_error_queue;

        # create a digest context and assign the digest method

    my $ctx = EVP_MD_CTX_create(); check_error_queue;
    EVP_SignInit($ctx, EVP_sha1()) or check_error_queue;

        # hash the message into digest context

    EVP_SignUpdate($ctx, $message, length($message)) or check_error_queue;

        # setup a buffer to store the signature in. The buffer needs to be as long as the private key.

    my $sig_buflen = EVP_PKEY_size($key); check_error_queue;
    my $sig_buf = "\0" x $sig_buflen;

        # sign the hash in the digest context with our key

    EVP_SignFinal( $ctx, $sig_buf, $sig_buflen, $key) or check_error_queue;

        # print the signature in hexadecimal encoding.

    print "\n", unpack( "H*", $sig_buf), "\n";

         # clean up after us

    EVP_MD_CTX_destroy($ctx);check_error_queue;
    EVP_PKEY_free($key);check_error_queue;
    ENGINE_finish($engine);check_error_queue;
    ENGINE_free($engine);check_error_queue;
    }

main;

 #  The check_error_queue function is a wrapper around ERR_get_error() to print human
 #  readable error strings. If an error occurred, the program dies, printing
 #  the error.
 #

sub check_error_queue
    {
      my $errcode=ERR_get_error();
      my $errstr="\0"x256;
      if ($errcode)
         {
         ERR_error_string($errcode,$errstr);
         die($errstr,"\n");
         }
    }

use Inline C => DATA =>
  ENABLE => AUTOWRAP =>
  TYPEMAPS => './typemap' =>
  LIBS => '-L'.OPENSSLPATH.'/lib -lcrypto' =>
  INC => '-I'.OPENSSLPATH.'/include'

__END__
__C__
#include <openssl/evp.h>

/* Note that we only need to include the openssl/evp.h header file.
 * The others, like engine.h and err.h are included by the evp.h file
 */

void          OPENSSL_config(const char *config_name);

EVP_MD_CTX*   EVP_MD_CTX_create();
void          EVP_MD_CTX_destroy(EVP_MD_CTX* ctx);

const EVP_MD* EVP_sha1();

void          EVP_PKEY_free(EVP_PKEY* pkey);
int           EVP_PKEY_size(EVP_PKEY* pkey);

int           EVP_SignInit(EVP_MD_CTX* ctx,const EVP_MD* type);
int           EVP_SignUpdate(EVP_MD_CTX* ctx,const void* d,size_t cnt);
int           EVP_SignFinal(EVP_MD_CTX* ctx,unsigned char* md,unsigned int* s,EVP_PKEY* pkey);

int           ENGINE_init(ENGINE *e);
int           ENGINE_finish(ENGINE *e);
int           ENGINE_free(ENGINE *e);
void          ENGINE_load_openssl();

ENGINE*       ENGINE_by_id(const char *id);
EVP_PKEY*     ENGINE_load_private_key(ENGINE *e, const char *key_id, UI_METHOD *ui_method, void *callback_data);
int           ENGINE_ctrl_cmd_string(ENGINE *e, const char *cmd_name, const char *arg, int cmd_optional);
void          ERR_load_crypto_strings();
unsigned long ERR_get_error();
char*         ERR_error_string(unsigned long e,char *buf);

UI_METHOD*    UI_OpenSSL();

Installing Crypt::OpenSSL::RSA on Solaris 10

1 Star2 Stars3 Stars4 Stars5 Stars (2 votes, average: 4.5 out of 5)
Loading ... Loading ...
Posted by jad on Apr 13th, 2007

I just spent a frustrating hour trying to get the Crypt::OpenSSL::RSA perl module running on Solaris 10. I kept getting errors like

Note (probably harmless): No library found for -lssl
Note (probably harmless): No library found for -lcrypto

when running perlgcc Makefile.PL. Eventually I realized that you need to hack Makefile.PL and change the LIBS and INC lines so that they contain the paths to the openssl headers and libraries. What is more the order of the LIBS arguments is very important. the -L must come before -lssl -lcrypto. Like this:

    'LIBS'      => ['-L/opt/openssl-0.9.8d/lib -lssl -lcrypto'],
    'INC'       => '-I/opt/openssl-0.9.8d/include',

If anyone knows how to tell cpan to prepend the LIBS then please comment and let me know.

Controlling an Openssl engine

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...
Posted by jad on Apr 12th, 2007

OpenSSL provides a set of engine functions to allow you to access cryptographic modules. I have used these before to access a Sun SCA6000 via pkcs11. In those examples I always assumed the necessary configuration settings were in the openssl config file. However some settings would be better set on the fly. For example, you wouldn’t want the password required to access the keystore to be kept on disk in the config file.

To pass the password (pin) to an engine you can do something like this

 /* Send PIN to engine */
    if(!ENGINE_ctrl_cmd_string(e, "PIN", "nominet1:abc123", 0)){
        printf("Error sending PIN to engine");
        ENGINE_free(e);
        return;
    }

Thanks to Stephen Henson for pointing me in the correct direction.

Using a SCA6000 part 2

1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 5 out of 5)
Loading ... Loading ...
Posted by jad on Feb 13th, 2007

The other day I blogged about using a SCA6000. I showed an example that I hoped would create a RSA key in the keystore.

Further investigation revealed that I was wrong. There were two things wrong with the code. First, the code shown was missing a call to C_Login to login to the keystore. Second, both the public and private key templates used by the C_GenerateKeyPair function needed to have the CKA_Token attribute set to true.

Here is the corrected code.

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include <security/pkcs11.h>

#define BUFFERSIZ	8192

/* Define key template */
static CK_BBOOL truevalue = TRUE;
static CK_BBOOL falsevalue = FALSE;
static CK_ULONG modulusbits = 512;
static CK_BYTE public_exponent[] = {3};
static CK_BYTE myid[] = {123};
static CK_CHAR mylabel[] = "i am a key";
void
main(int argc, char **argv)
{
	CK_RV   rv;
	CK_MECHANISM genmech;
	CK_SESSION_HANDLE hSession;
	CK_SESSION_INFO sessInfo;
 	CK_UTF8CHAR pin[32], plabel;
	int error, i = 0;

	CK_OBJECT_HANDLE privatekey, publickey;

    /* Set public key. */
	CK_ATTRIBUTE publickey_template[] = {
		{CKA_VERIFY, &truevalue, sizeof (truevalue)},
		{CKA_MODULUS_BITS, &modulusbits, sizeof (modulusbits)},
		{CKA_TOKEN, &truevalue, sizeof (truevalue)},
		{CKA_PUBLIC_EXPONENT, &public_exponent,
		    sizeof (public_exponent)}
	};

    /* Set private key. */
	CK_ATTRIBUTE privatekey_template[] = {
		{CKA_SIGN, &truevalue, sizeof (truevalue)},
		{CKA_TOKEN, &truevalue, sizeof (truevalue)},
		{CKA_PRIVATE, &truevalue, sizeof (truevalue)},
		{CKA_SENSITIVE, &truevalue, sizeof (truevalue)},
		{CKA_LABEL, mylabel, sizeof (mylabel)},
		{CKA_ID, myid, sizeof (myid)}
	};

    /* Create sample message. */
	CK_ATTRIBUTE getattributes[] = {
		{CKA_MODULUS_BITS, NULL_PTR, 0},
		{CKA_MODULUS, NULL_PTR, 0},
		{CKA_PUBLIC_EXPONENT, NULL_PTR, 0}
	};

	CK_ULONG messagelen, slen, template_size, template_size1;

	uchar_t *message = (uchar_t *)"Simple message for signing & verifying.";
	uchar_t *modulus, *pub_exponent;
	char sign[BUFFERSIZ];
	slen = BUFFERSIZ;

	messagelen = strlen((char *)message);

	/* Set up mechanism for generating key pair */
	genmech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
	genmech.pParameter = NULL_PTR;
	genmech.ulParameterLen = 0;

	/* Initialize the CRYPTOKI library */
	rv = C_Initialize(NULL_PTR);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_Initialize: Error = 0x%.8Xn", rv);
		exit(1);
	}

	/* Open a session on the slot found */
	rv = C_OpenSession(0, CKF_RW_SESSION+CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR,
	    &hSession);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_OpenSession: rv = 0x%.8Xn", rv);
		error = 1;
		goto exit_program;
	}

	/* Login to the Nominet Token (Keystore) */
	strcpy (pin, "nominet1:abc123");
	rv = C_Login(hSession, CKU_USER, pin, sizeof (pin) );
	if (rv != CKR_OK) {
		fprintf(stderr, "C_Login: rv = 0x%.8Xn", rv);
                error = 1;
                goto exit_program;
        }

	fprintf(stdout, "Generating keypair....n");

	/* Generate Key pair for signing/verifying */
	rv = C_GenerateKeyPair(hSession, &genmech, publickey_template,
	    (sizeof (publickey_template) / sizeof (CK_ATTRIBUTE)),
	    privatekey_template,
	    (sizeof (privatekey_template) / sizeof (CK_ATTRIBUTE)),
	    &publickey, &privatekey);

	if (rv != CKR_OK) {
		fprintf(stderr, "C_GenerateKeyPair: rv = 0x%.8Xn", rv);
		error = 1;
		goto exit_session;
	}

	/* Display the publickey. */
	template_size = sizeof (getattributes) / sizeof (CK_ATTRIBUTE);

	rv = C_GetAttributeValue(hSession, publickey, getattributes,
	    template_size);

	if (rv != CKR_OK) {
		/* not fatal, we can still sign/verify if this failed */
		fprintf(stderr, "C_GetAttributeValue: rv = 0x%.8Xn", rv);
		error = 1;
	} else {
		/* Allocate memory to hold the data we want */
		for (i = 0; i < template_size; i++) {
			getattributes[i].pValue =
			    malloc (getattributes[i].ulValueLen *
				sizeof(CK_VOID_PTR));
			if (getattributes[i].pValue == NULL) {
				int j;
				for (j = 0; j < i; j++)
					free(getattributes[j].pValue);
				goto exit_session;
			}
		}

		/* Call again to get actual attributes */
		rv = C_GetAttributeValue(hSession, publickey, getattributes,
		    template_size);

		if (rv != CKR_OK) {
			/* not fatal, we can still sign/verify if failed */
			fprintf(stderr,
			    "C_GetAttributeValue: rv = 0x%.8Xn", rv);
			error = 1;
		} else {
			/* Display public key values */
			fprintf(stdout, "Public Key data:ntModulus bits: "
			    "%dn",
			    *((CK_ULONG_PTR)(getattributes[0].pValue)));

			fprintf(stdout, "tModulus: ");
			modulus = (uchar_t *)getattributes[1].pValue;
			for (i = 0; i < getattributes[1].ulValueLen; i++) {
				fprintf(stdout, "%.2x", modulus[i]);
			}

			fprintf(stdout, "ntPublic Exponent: ");
			pub_exponent = (uchar_t *)getattributes[2].pValue;
			for (i = 0; i< getattributes[2].ulValueLen; i++) {
				fprintf(stdout, "%.2x", pub_exponent[i]);
			}
			fprintf(stdout, "n");
		}
	}

exit_session:
	(void) C_CloseSession(hSession);

exit_program:
	(void) C_Finalize(NULL_PTR);

	for (i = 0; i < template_size; i++) {
		if (getattributes[i].pValue != NULL)
			free(getattributes[i].pValue);
	}

	exit(error);

}

To show that the key really was in the keystore I used pkcs11-tool. This is part of opensc.

-bash-3.00$ /opt/opensc/bin/pkcs11-tool --module=/usr/lib/libpkcs11.so -p nominet1:abc123 -O --slot 0
Private Key Object; RSA
  label:      i am a key
  ID:         7b
  Usage:      decrypt, sign, unwrap
Public Key Object; RSA 512 bits
  label:
  Usage:      encrypt, verify, wrap

Now how do I use this key?

Using a SCA6000

1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 5 out of 5)
Loading ... Loading ...
Posted by jad on Feb 9th, 2007

I have been playing with a Sun Cryptographic Accelerator 6000. This is a PCIe card that acts an Hardware Security Module (HSM) and a crypto accelerator. Here is a quick tutorial on starting to use one.

I installed the card in a Sun T2000 server. This machine already has a crypto accelerator but hopefully we will turn that one off and just use the SCA6000 during this testing.

Once the software, drivers and patches are installed, the card can be initialized using the scamgr utility. The allows you to set up a security officer, users and keystore as well as configuring various security settings. I created a security officer called “jad” and a keystore called “Nominet”.

The SCA6000 stores the keystore on disk. This keystore is protected by a master key that is held in the SCA6000’s firmware. By default the master key can be backed up by the security officer. However the master key can be locked to prevent backups by issuing the set lock command in the scamgr utility. Once you do this the master key can not be recovered and the lock can only be removed by zeroizing the board.

The first problem that I came across was how to generate an RSA key directly in the keystore. As far as I can tell there is no utility for doing this. You are expected to write an application that does this using the libpkcs11 functions. Luckily I found some examples.

1. Slots and Tokens

A slot is the connecting point for applications that use cryptographic services. There are 4 types of slots available through the pkcs11 library.

  • Keystore slot
  • Sun Metaslot
  • Hardware slot
  • Sun Softtoken slot

In this example we will use the Sun Metaslot. See the SCA6000 user guide for an full explanation of what these slots are. The code shown here is a hacked version of the examples from Sun. Please check they really work before using them.

A token is the abstraction of a device that can perform cryptography. In addition, tokens can store information for use in cryptographic operations. A single token can support one or more mechanisms. Tokens can represent hardware, as in an accelerator board. A token can be plugged into a slot.

Configure the Sun Metaslot to use the SCA6000 keystore. (The Sun Metaslot can only access 1 keystore.)

% cryptoadm enable metaslot token=Nominet

Ensure that the keys can not be revealed on the host memory

% cryptoadm disable metaslot auto-key-migrate

You can view the metaslot like this

% cryptoadm list -v metaslot
System-wide Meta Slot Configuration:
------------------------------------
Status: enabled
Sensitive Token Object Automatic Migrate: disabled
Persistent object store token: Nominet

Detailed Meta Slot Information:
-------------------------------
actual status: enabled.
Description: Sun Metaslot
Token Present: True
Token Label: Sun Metaslot
Manufacturer ID: Sun Microsystems, Inc.
Model: 1.0
Serial Number:
Hardware Version: 0.0
Firmware Version: 0.0
UTC Time:
PIN Length: 0-253
Flags: CKF_RNG CKF_LOGIN_REQUIRED CKF_USER_PIN_INITIALIZED CKF_TOKEN_INITIALIZED CKF_SO_PIN_LOCKED

The following code will use libpkcs11 to display a list of slots

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include <security/pkcs11.h>

void
main(int argc, char **argv)
{
        CK_RV   rv;
        CK_MECHANISM genmech;
        CK_SESSION_HANDLE hSession;
        CK_SESSION_INFO sessInfo;
        CK_SLOT_ID_PTR pSlotList = NULL_PTR;
        CK_SLOT_ID SlotID;
        CK_ULONG ulSlotCount = 0;
        CK_MECHANISM_INFO mech_info;
        int i = 0;

        CK_OBJECT_HANDLE privatekey, publickey;

        /* Initialize the CRYPTOKI library */
        rv = C_Initialize(NULL_PTR);

        if (rv != CKR_OK) {
                fprintf(stderr, "C_Initialize: Error = 0x%.8Xn", rv);
                exit(1);
        }

        /* Get slot list for memory alloction */
        rv = C_GetSlotList(0, NULL_PTR, &ulSlotCount);

        if ((rv == CKR_OK) && (ulSlotCount > 0)) {
                fprintf(stdout, "slotCount = %dn", ulSlotCount);
                pSlotList = malloc(ulSlotCount * sizeof (CK_SLOT_ID));

                if (pSlotList == NULL) {
                        fprintf(stderr, "System error: unable to allocate "
                            "memoryn");
                        exit(1);
                }

                /* Get the slot list for processing */
                rv = C_GetSlotList(0, pSlotList, &ulSlotCount);
                if (rv != CKR_OK) {
                        fprintf(stderr, "GetSlotList failed: unable to get "
                            "slot count.n");
                        goto cleanup;
                }
        } else {
                fprintf(stderr, "GetSlotList failed: unable to get slot "
                    "list.n");
                exit(1);
        }

        /* Print slot info */
        for (i = 0; i < ulSlotCount; i++) {
                SlotID = pSlotList[i];
                fprintf(stdout, "Slot found: %d - ", SlotID);
                CK_SLOT_INFO Info;
                CK_TOKEN_INFO TInfo;
                rv = C_GetSlotInfo(SlotID, &Info);
                fprintf(stdout, "Slot Description: %sn", Info.slotDescription);
                fprintf(stdout, "Slot Flags: 0x%.8Xn", Info.flags);
                /* Get Token info for each slot */
                rv = C_GetTokenInfo(SlotID, &TInfo);
                fprintf(stdout, "Token Label: %sn", TInfo.label);
                fprintf(stdout, "Token Flags: 0x%.8Xn", TInfo.flags);
                fprintf(stdout, "Token manufacturerID: %sn", TInfo.manufacturerID);
                fprintf(stdout, "Token model: %sn", TInfo.model);
                fprintf(stdout, "Token serialNumber: %sn", TInfo.serialNumber);
                fprintf(stdout, "Token ulMaxSessionCount: %ldn", TInfo.ulMaxSessionCount);
                fprintf(stdout, "Token ulSessionCount: %ldn", TInfo.ulSessionCount);
                fprintf(stdout, "Token ulMaxRwSessionCount: %ldn", TInfo.ulMaxRwSessionCount);
                fprintf(stdout, "Token ulRwSessionCount: %ldn", TInfo.ulRwSessionCount);
                fprintf(stdout, "Token ulMaxPinLen: %ldn", TInfo.ulMaxPinLen);
                fprintf(stdout, "Token ulMinPinLen: %ldn", TInfo.ulMinPinLen);
                fprintf(stdout, "Token ulTotalPublicMemory: %ldn", TInfo.ulTotalPublicMemory);
                fprintf(stdout, "Token ulFreePublicMemory: %ldn", TInfo.ulFreePublicMemory);
                fprintf(stdout, "Token ulTotalPrivateMemory: %ldn", TInfo.ulTotalPrivateMemory);
                fprintf(stdout, "Token ulFreePrivateMemory: %ldn", TInfo.ulFreePrivateMemory);
                fprintf(stdout, "Token hardwareVersion: %dn", TInfo.hardwareVersion);
                fprintf(stdout, "Token firmwareVersion: %dn", TInfo.firmwareVersion);
                fprintf(stdout, "Token utcTime: %cn", TInfo.utcTime);          

                fprintf(stdout, "n");
        }

cleanup:
        if (pSlotList)
                free(pSlotList);

}

Compile like this

gcc -lpkcs11 getslot.c

and run

% ./a.out
slotCount = 3
Slot found: 0 - Slot Description: Sun Metaslot                                       Sun Microsystems, Inc.
Slot Flags: 0x00000001
Token Label: Sun Metaslot                    Sun Microsystems, Inc.          1.0
Token Flags: 0x0040040D
Token manufacturerID: Sun Microsystems, Inc.          1.0
Token model: 1.0
Token serialNumber:
Token ulMaxSessionCount: 0
Token ulSessionCount: 0
Token ulMaxRwSessionCount: 0
Token ulRwSessionCount: 0
Token ulMaxPinLen: 253
Token ulMinPinLen: 0
Token ulTotalPublicMemory: -1
Token ulFreePublicMemory: -1
Token ulTotalPrivateMemory: -1
Token ulFreePrivateMemory: -1
Token hardwareVersion: -4195448
Token firmwareVersion: -4195456
Token utcTime:  

Slot found: 1 - Slot Description: ncp/0 Crypto Accel Asym 1.0                         SUNWncp
Slot Flags: 0x00000005
Token Label: ncp/0 Crypto Accel Asym 1.0     SUNWncp                         NCP1
Token Flags: 0x00000002
Token manufacturerID: SUNWncp                         NCP1
Token model: NCP1
Token serialNumber:
Token ulMaxSessionCount: -1
Token ulSessionCount: -1
Token ulMaxRwSessionCount: -1
Token ulRwSessionCount: -1
Token ulMaxPinLen: -1
Token ulMinPinLen: -1
Token ulTotalPublicMemory: -1
Token ulFreePublicMemory: -1
Token ulTotalPrivateMemory: -1
Token ulFreePrivateMemory: -1
Token hardwareVersion: -4195448
Token firmwareVersion: -4195456
Token utcTime:  

Slot found: 2 - Slot Description: Sun Crypto Softtoken                                Sun Microsystems, Inc.
Slot Flags: 0x00000001
Token Label: Sun Software PKCS#11 softtoken  Sun Microsystems, Inc.          1.0
Token Flags: 0x0008062D
Token manufacturerID: Sun Microsystems, Inc.          1.0
Token model: 1.0
Token serialNumber:
Token ulMaxSessionCount: 0
Token ulSessionCount: 0
Token ulMaxRwSessionCount: 0
Token ulRwSessionCount: 0
Token ulMaxPinLen: 256
Token ulMinPinLen: 1
Token ulTotalPublicMemory: -1
Token ulFreePublicMemory: -1
Token ulTotalPrivateMemory: -1
Token ulFreePrivateMemory: -1
Token hardwareVersion: -4195448
Token firmwareVersion: -4195456
Token utcTime:

So we want to put our key in slot 0, the Sun Metaslot. The following code generates a 2048 bit RSA SHA1 key with a public exponent of 5 in the Metaslot.

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <security/cryptoki.h>
#include <security/pkcs11.h>

#define BUFFERSIZ       8192

/* Define key template */
static CK_BBOOL truevalue = TRUE;
static CK_BBOOL falsevalue = FALSE;
static CK_ULONG modulusbits = 2048;
static CK_BYTE public_exponent[] = {5};

void
main(int argc, char **argv)
{
        CK_RV   rv;
        CK_MECHANISM genmech;
        CK_SESSION_HANDLE hSession;
        CK_SESSION_INFO sessInfo;
        int error, i = 0;

        CK_OBJECT_HANDLE privatekey, publickey;

    /* Set public key. */
        CK_ATTRIBUTE publickey_template[] = {
                {CKA_VERIFY, &truevalue, sizeof (truevalue)},
                {CKA_MODULUS_BITS, &modulusbits, sizeof (modulusbits)},
                {CKA_PUBLIC_EXPONENT, &public_exponent,
                    sizeof (public_exponent)}
        };

    /* Set private key. */
        CK_ATTRIBUTE privatekey_template[] = {
                {CKA_SIGN, &truevalue, sizeof (truevalue)},
                {CKA_TOKEN, &falsevalue, sizeof (falsevalue)},
                {CKA_SENSITIVE, &truevalue, sizeof (truevalue)},
                {CKA_EXTRACTABLE, &truevalue, sizeof (truevalue)}
        };

    /* Create sample message. */
        CK_ATTRIBUTE getattributes[] = {
                {CKA_MODULUS_BITS, NULL_PTR, 0},
                {CKA_MODULUS, NULL_PTR, 0},
                {CKA_PUBLIC_EXPONENT, NULL_PTR, 0}
        };

        CK_ULONG messagelen, slen, template_size;

        uchar_t *message = (uchar_t *)"Simple message for signing & verifying.";
        uchar_t *modulus, *pub_exponent;
        char sign[BUFFERSIZ];
        slen = BUFFERSIZ;

        messagelen = strlen((char *)message);

        /* Set up mechanism for generating key pair */
        genmech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
        genmech.pParameter = NULL_PTR;
        genmech.ulParameterLen = 0;

        /* Initialize the CRYPTOKI library */
        rv = C_Initialize(NULL_PTR);

        if (rv != CKR_OK) {
                fprintf(stderr, "C_Initialize: Error = 0x%.8Xn", rv);
                exit(1);
        }

        /* Open a session on the slot found */
        rv = C_OpenSession(0, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR,
            &hSession);

        if (rv != CKR_OK) {
                fprintf(stderr, "C_OpenSession: rv = 0x%.8Xn", rv);
                error = 1;
                goto exit_program;
        }

        fprintf(stdout, "Generating keypair....n");

        /* Generate Key pair for signing/verifying */
        rv = C_GenerateKeyPair(hSession, &genmech, publickey_template,
            (sizeof (publickey_template) / sizeof (CK_ATTRIBUTE)),
            privatekey_template,
            (sizeof (privatekey_template) / sizeof (CK_ATTRIBUTE)),
            &publickey, &privatekey);

        if (rv != CKR_OK) {
                fprintf(stderr, "C_GenerateKeyPair: rv = 0x%.8Xn", rv);
                error = 1;
                goto exit_session;
        }

        /* Display the publickey. */
        template_size = sizeof (getattributes) / sizeof (CK_ATTRIBUTE);

        rv = C_GetAttributeValue(hSession, publickey, getattributes,
            template_size);

        if (rv != CKR_OK) {
                /* not fatal, we can still sign/verify if this failed */
                fprintf(stderr, "C_GetAttributeValue: rv = 0x%.8Xn", rv);
                error = 1;
        } else {
                /* Allocate memory to hold the data we want */
                for (i = 0; i < template_size; i++) {
                        getattributes[i].pValue =
                            malloc (getattributes[i].ulValueLen *
                                sizeof(CK_VOID_PTR));
                        if (getattributes[i].pValue == NULL) {
                                int j;
                                for (j = 0; j < i; j++)
                                        free(getattributes[j].pValue);
                                goto exit_session;
                        }
                }

                /* Call again to get actual attributes */
                rv = C_GetAttributeValue(hSession, publickey, getattributes,
                    template_size);

                if (rv != CKR_OK) {
                        /* not fatal, we can still sign/verify if failed */
                        fprintf(stderr,
                            "C_GetAttributeValue: rv = 0x%.8Xn", rv);
                        error = 1;
                } else {
                        /* Display public key values */
                        fprintf(stdout, "Public Key data:ntModulus bits: "
                            "%dn",
                            *((CK_ULONG_PTR)(getattributes[0].pValue)));

                        fprintf(stdout, "tModulus: ");
                        modulus = (uchar_t *)getattributes[1].pValue;
                        for (i = 0; i < getattributes[1].ulValueLen; i++) {
                                fprintf(stdout, "%.2x", modulus[i]);
                        }

                        fprintf(stdout, "ntPublic Exponent: ");
                        pub_exponent = (uchar_t *)getattributes[2].pValue;
                        for (i = 0; i< getattributes[2].ulValueLen; i++) {
                                fprintf(stdout, "%.2x", pub_exponent[i]);
                        }
                        fprintf(stdout, "n");
                }
        }

exit_session:
        (void) C_CloseSession(hSession);

exit_program:
        (void) C_Finalize(NULL_PTR);

        for (i = 0; i < template_size; i++) {
                if (getattributes[i].pValue != NULL)
                        free(getattributes[i].pValue);
        }

        exit(error);

                                                                                                                                                               }

Running it gives

% ./a.out
Generating keypair....
Public Key data:
        Modulus bits: 2048
        Modulus: a961e7d4c295334bec912f4aef7d86c5519ca1983b9d8258e7b8edd500dad4a267c7fd721e30c5dff9127eb5c55a6d98270cbe754c52fa8cad6927c863ad76acd2c4bdde19d41d745b7bd707f65c0d94b4ec8000664e31529e5086676672a635ad263daeb2e471ced8869da27a387406fa4d9df0aa24743629d5fbb803a1cef4dd505375e4a50b110744268a2f8b2ea3409bccc2b117eebea6f44a0584d331a2bb0cb9744e0361657cbd024014cf23f5cb7a3fe754bb591da5110340902bdd575ae27dd805dc3ab32ad1f19d2b5acc6728b84e57e668d3acb97271ac7f65b0881cae99b2f9e140f7527db2d6ae4799d8f0833976579dcf67cb9d7a820be50209
        Public Exponent: 05

So now I think we have a key in the keystore. Next I will try to figure out how we prove that the key really is in the keystore and how to use it from openssl…

Futureproofing the middlebox

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...
Posted by roy on Dec 15th, 2006

There are increasingly more applications which need to do more than simply have names resolved to addresses. There are methods needed to find zone-cuts (Mark Andrews’ SOA-discovery) for applications that want to update the DNS. DKIM uses DNS to store cryptographic material, etc etc. There is also DNSSEC, a whole layer of cryptographic data stored in the DNS to prove that the data stored in DNS is authentic. I’m sure there are many more.

The resolver needs to be as transparent as possible. It should not blindly or unknowingly restrict the application from functioning, and meanwhile it should have some ‘anti-spoof’ checking in place.

What follows is a small list of things needed for a resolver to be future-proof:

1) Support EDNS0 Maximum Payload Size.

DNS messages used to have a maximum payload size of 512 octets when transmitted over UDP. (This is the reason there are 13 root-servers in DNS, 14 would not fit in a response). If the server notices that the payload size is too small to store the necessary RRsets, it must set the TC (truncated) bit in the response. At this point, the resolver should re-query over TCP.

To avoid the extra latency involved in this fall-back scenario, the IETF standardized a method to advertise larger payload size. This advertising is done using EDNS0. EDNS0 defines a whole range of extentions for DNS, one of them is the ability to advertise maximum payload size. Technically, the resolver adds an record (OPT) to the additional section in a request or response. The payload size is stored in the CLASS field of the record.

This is a tradeoff. If the receiving side (a legacy nameserver) does not understand or expect OPT records or strange CLASS fields, the transaction will fail, causing the resolver to fallback to requery without the OPT record. Note that there might be a fallback yet again if the server responds with the TC bit.

Early deployment of EDNS (the standard which defines the OPT record) has seen a lot of these ‘fallbacks’: resolvers that use OPT, servers that do not understand them. But, currently the DNS is in a state where it is safe to assume that the server does. On the server side: Microsoft’s DNS server, ISC’s BIND and NLNetLabs’ NSD understand it. Older versions of some of these do not. Two other well known servers, PowerDNS (PDNS) and Dan Bernstein’s tinyDNS silently ignore the OPT record in requests, and respond anyway, so this won’t introduce a fallback either.

2) Ensure transmission of DNSSEC data.

DNSSEC provides for origin authentication and integrity cheking of DNS data using strong cryptography. All the cryptographic functionality resides in the zone-signer (code that signs data) and the validator (code that verifies responses). To show why this is, it’s useful to understand that the components of the resolver may be decomposed into five distinct and effectively independent functions: client (sending requests), server (receiving requests), resolver, cache, and validator. The resolver has in principal nothing to do with the cryptographic data. It just resolves and serves it. The server however must do some effort to find the proper data in its database (zonefiles or the like), but that is not too complex.

The resolver algorithm needs to do nothing at all with the cryptographic processing. This is done by the validator, preferably independent of the resolver algorithm. This allows an application to do its own DNSSEC validation. Note that this validator does not need to known anything about the resolving process nor the location of authoritative servers (NS, A and AAAA records). It has a trust anchor for some domain, and only cares about the DNSSEC data involved. It can query a resolver for DS and DNSKEY records, while the RRSIG and NSEC records are send back automatically.

To protect legacy resolvers, i.e., resolvers that cannot handle unknown data (more on that later), the request must include a signal to indicate to the server that it understands the presence of DNSSEC resource records. “understand” does not mean ‘able to validate’, merely that it will not be confused by these records.

This signal is called the DNSSEC-OKAY (DO) bit. It is allocated in the TTL field of the EDNS0 OPT record previously mentioned. It is basically the resolver signalling the server “send me DNSSEC data, if you have it”.

Since the resolver is now able to receive and store these DNSSEC records, applications can use it to validate DNS data.

3) Handle Unknown Data.

Without exception, all resource records have the same format: NAME, TYPE, CLASS, TTL, RDLEN and RDATA. There is no need for the resolver to understand that the RDATA of type 33 (SRV) has different semantics than the RDATA of type 44 (SSHFP). The syntax is of these records are identical. The type space is 16 bits wide, and only a handful of type codes have been assigned by IANA. There is only a tiny subset of types of which the resolver must know the RDATA semantics: SOA, NS, A, AAAA and CNAME.

4) Disregard and Remove Extraneous Data.

This section is about proper DNS hygiene. The resolver needs to protect client from potentially spoofed data. When a resolver queries a server, it is because it has learned that the server is authoratative for some domain in some class (typically the IN class). The resolver should therefore strip (not just ignore) all the resource records that have (1) a different class, and (2) a name not under the authority of the server. For example: a resolver connects to the authoritative nameserver for ‘.com’. When the resolver receives a response that contains names not ending with ‘.com’ (for instance: ns1.example.org A 127.0.0.1) it should remove them from the response before any other action. Note that the OPT record is only used to signal resolver and server capabilities and have no use outside of the actual transaction. After processing it, it should be removed as well.

Conclusion.

Some implementors sit on top of bleeding edge technology, others only implement if the demand for some technology is high. The latter approach is really a Catch 22. Applications can’t (and thus won’t) deploy new technology based on DNS if resolvers are not future proof. Resolvers won’t become future proof if there is no demand from the application side.

I believe it’s essential that all resolvers — especially the well-known ones — implement the principles listed above. I’d like to see organisations use future proof technology. This removes barriers to applications using DNS, DNSSEC, DKIM and whatever else is on the horizon.

Brief guide to ssh tunnels

1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 3 out of 5)
Loading ... Loading ...
Posted by jay on Dec 6th, 2006

Suppose you are away on a conference, say in Sao Paulo, and you haven’t set up VPN on your laptop but you need to access a server only accessible inside your corporate network. How do you do it?

Well all it takes is a host already inside your corporate network that you can ssh to because ssh has a clever facility built in to enable a tunnel through that computer.

Imagine I have an ssh host inside my network called ’ssh-host’ and the server I want to access is an intranet web server called ‘target’. Then all I need do from my laptop out in the wild is issue the following command

ssh -N -L 1234:target:80 myusername@ssh-host

And that will redirect port 1234 on my laptop to tunnel through to port 80 on the target server. To use it all I do is open up a web browser and go to http://localhost:1234 and hey presto the web page from the target server appears.

Just to talk through the command:

  • -N This tells ssh not to execute a command on the remote server. This does mean that the ssh command does not appear to complete after you execute it in a shell but just sits there doing nothing. However it has worked. You will need to control-C to quit the ssh command.
  • -L This tells ssh to create a tunnel.
  • 1234:target:80 This tells ssh that the tunnel should be from port 1234 on the localhost to port 80 on the machine called target.
  • myusername@ssh-host This is the username and host that sits inside the corporate network and provides the tunnel.

Testing randomness

1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...
Posted by jad on Dec 1st, 2006

I wrote about installing a Araneus Alea I the other day. Since then I have been reading about how you test random number generators.

There is a discussion of this subject at Hotbits. This a web based random number generator that uses radioactive decay to generate the numbers. They have a program called ENT for testing binary data sequences.

Generating 1000000 random bytes with the Alea I can be done like this.

time ./randomfile -b 1000000 > /tmp/myrandom

real    1m21.073s
user    0m0.066s
sys     0m0.134s

Testing with the ENT program gives these results

./ent /tmp/myrandom
Entropy = 7.999817 bits per byte.

Optimum compression would reduce the size
of this 1000000 byte file by 0 percent.

Chi square distribution for 1000000 samples is 252.96, and randomly
would exceed this value 50.00 percent of the times.

Arithmetic mean value of data bytes is 127.5287 (127.5 = random).
Monte Carlo value for Pi is 3.144636579 (error 0.10 percent).
Serial correlation coefficient is 0.000713 (totally uncorrelated = 0.0).

According to the ENT manual this is very random data. Next I will be looking at what the NIST random number test tools have to say.

Next »

Recent Posts

Highest Rated

Categories

Archives

Meta: