...

Source file src/github.com/miekg/pkcs11/pkcs11_test.go

Documentation: github.com/miekg/pkcs11

     1  // Use of this source code is governed by a BSD-style
     2  // license that can be found in the LICENSE file.
     3  
     4  package pkcs11
     5  
     6  // These tests depend on SoftHSM and the library being in
     7  // in /usr/lib/softhsm/libsofthsm.so
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"log"
    13  	"math/big"
    14  	"os"
    15  	"testing"
    16  )
    17  
    18  /*
    19  This test supports the following environment variables:
    20  
    21  * SOFTHSM_LIB: complete path to libsofthsm.so
    22  * SOFTHSM_TOKENLABEL
    23  * SOFTHSM_PRIVKEYLABEL
    24  * SOFTHSM_PIN
    25  */
    26  
    27  var lib = "/usr/lib/softhsm/libsofthsm2.so"
    28  
    29  func setenv(t *testing.T) *Ctx {
    30  	if x := os.Getenv("SOFTHSM_LIB"); x != "" {
    31  		lib = x
    32  	}
    33  	t.Logf("loading %s", lib)
    34  	p := New(lib)
    35  	if p == nil {
    36  		t.Fatal("Failed to init lib")
    37  	}
    38  	return p
    39  }
    40  
    41  func TestSetenv(t *testing.T) {
    42  	wd, _ := os.Getwd()
    43  	os.Setenv("SOFTHSM_CONF", wd+"/softhsm.conf")
    44  
    45  	if x := os.Getenv("SOFTHSM_LIB"); x != "" {
    46  		lib = x
    47  	}
    48  	p := New(lib)
    49  	if p == nil {
    50  		t.Fatal("Failed to init pkcs11")
    51  	}
    52  	p.Destroy()
    53  	return
    54  }
    55  
    56  func getSession(p *Ctx, t *testing.T) SessionHandle {
    57  	if e := p.Initialize(); e != nil {
    58  		t.Fatalf("init error %s\n", e)
    59  	}
    60  	slots, e := p.GetSlotList(true)
    61  	if e != nil {
    62  		t.Fatalf("slots %s\n", e)
    63  	}
    64  	session, e := p.OpenSession(slots[0], CKF_SERIAL_SESSION|CKF_RW_SESSION)
    65  	if e != nil {
    66  		t.Fatalf("session %s\n", e)
    67  	}
    68  	if e := p.Login(session, CKU_USER, pin); e != nil {
    69  		t.Fatalf("user pin %s\n", e)
    70  	}
    71  	return session
    72  }
    73  
    74  func TestInitialize(t *testing.T) {
    75  	p := setenv(t)
    76  	if e := p.Initialize(); e != nil {
    77  		t.Fatalf("init error %s\n", e)
    78  	}
    79  	p.Finalize()
    80  	p.Destroy()
    81  }
    82  
    83  func TestNew(t *testing.T) {
    84  	if p := New(""); p != nil {
    85  		t.Fatalf("init should have failed, got %v\n", p)
    86  	}
    87  	if p := New("/does/not/exist"); p != nil {
    88  		t.Fatalf("init should have failed, got %v\n", p)
    89  	}
    90  }
    91  
    92  func finishSession(p *Ctx, session SessionHandle) {
    93  	p.Logout(session)
    94  	p.CloseSession(session)
    95  	p.Finalize()
    96  	p.Destroy()
    97  }
    98  
    99  func TestGetInfo(t *testing.T) {
   100  	p := setenv(t)
   101  	session := getSession(p, t)
   102  	defer finishSession(p, session)
   103  	info, err := p.GetInfo()
   104  	if err != nil {
   105  		t.Fatalf("non zero error %s\n", err)
   106  	}
   107  	if info.ManufacturerID != "SoftHSM" {
   108  		t.Fatal("ID should be SoftHSM")
   109  	}
   110  	t.Logf("%+v\n", info)
   111  }
   112  
   113  func TestFindObject(t *testing.T) {
   114  	p := setenv(t)
   115  	session := getSession(p, t)
   116  	defer finishSession(p, session)
   117  
   118  	tokenLabel := "TestFindObject"
   119  
   120  	// There are 2 keys in the db with this tag
   121  	generateRSAKeyPair(t, p, session, tokenLabel, false)
   122  
   123  	template := []*Attribute{NewAttribute(CKA_LABEL, tokenLabel)}
   124  	if e := p.FindObjectsInit(session, template); e != nil {
   125  		t.Fatalf("failed to init: %s\n", e)
   126  	}
   127  	obj, _, e := p.FindObjects(session, 2)
   128  	if e != nil {
   129  		t.Fatalf("failed to find: %s\n", e)
   130  	}
   131  	if e := p.FindObjectsFinal(session); e != nil {
   132  		t.Fatalf("failed to finalize: %s\n", e)
   133  	}
   134  	if len(obj) != 2 {
   135  		t.Fatal("should have found two objects")
   136  	}
   137  }
   138  
   139  func TestGetAttributeValue(t *testing.T) {
   140  	p := setenv(t)
   141  	session := getSession(p, t)
   142  	defer finishSession(p, session)
   143  
   144  	pbk, _ := generateRSAKeyPair(t, p, session, "GetAttributeValue", false)
   145  
   146  	template := []*Attribute{
   147  		NewAttribute(CKA_PUBLIC_EXPONENT, nil),
   148  		NewAttribute(CKA_MODULUS_BITS, nil),
   149  		NewAttribute(CKA_MODULUS, nil),
   150  		NewAttribute(CKA_LABEL, nil),
   151  	}
   152  	attr, err := p.GetAttributeValue(session, ObjectHandle(pbk), template)
   153  	if err != nil {
   154  		t.Fatalf("err %s\n", err)
   155  	}
   156  	for i, a := range attr {
   157  		t.Logf("attr %d, type %d, valuelen %d", i, a.Type, len(a.Value))
   158  		if a.Type == CKA_MODULUS {
   159  			mod := big.NewInt(0)
   160  			mod.SetBytes(a.Value)
   161  			t.Logf("modulus %s\n", mod.String())
   162  		}
   163  	}
   164  }
   165  
   166  func TestDigest(t *testing.T) {
   167  	p := setenv(t)
   168  	session := getSession(p, t)
   169  	defer finishSession(p, session)
   170  	t.Run("Simple", func(t *testing.T) {
   171  		// Teststring create with: echo -n "this is a string" | sha1sum
   172  		testDigest(t, p, session, []byte("this is a string"), "517592df8fec3ad146a79a9af153db2a4d784ec5")
   173  	})
   174  	t.Run("Empty", func(t *testing.T) {
   175  		// sha1sum < /dev/null
   176  		testDigest(t, p, session, []byte{}, "da39a3ee5e6b4b0d3255bfef95601890afd80709")
   177  	})
   178  }
   179  
   180  func testDigest(t *testing.T, p *Ctx, session SessionHandle, input []byte, expected string) {
   181  	e := p.DigestInit(session, []*Mechanism{NewMechanism(CKM_SHA_1, nil)})
   182  	if e != nil {
   183  		t.Fatalf("DigestInit: %s\n", e)
   184  	}
   185  
   186  	hash, e := p.Digest(session, input)
   187  	if e != nil {
   188  		t.Fatalf("digest: %s\n", e)
   189  	}
   190  	hex := ""
   191  	for _, d := range hash {
   192  		hex += fmt.Sprintf("%02x", d)
   193  	}
   194  	if hex != expected {
   195  		t.Fatalf("wrong digest: %s", hex)
   196  	}
   197  }
   198  
   199  func TestDigestUpdate(t *testing.T) {
   200  	p := setenv(t)
   201  	session := getSession(p, t)
   202  	defer finishSession(p, session)
   203  	t.Run("Simple", func(t *testing.T) {
   204  		// Teststring create with: echo -n "this is a string" | sha1sum
   205  		testDigestUpdate(t, p, session, [][]byte{[]byte("this is "), []byte("a string")}, "517592df8fec3ad146a79a9af153db2a4d784ec5")
   206  	})
   207  	t.Run("Empty", func(t *testing.T) {
   208  		// sha1sum < /dev/null
   209  		testDigestUpdate(t, p, session, [][]byte{{}}, "da39a3ee5e6b4b0d3255bfef95601890afd80709")
   210  	})
   211  }
   212  
   213  func testDigestUpdate(t *testing.T, p *Ctx, session SessionHandle, inputs [][]byte, expected string) {
   214  	if e := p.DigestInit(session, []*Mechanism{NewMechanism(CKM_SHA_1, nil)}); e != nil {
   215  		t.Fatalf("DigestInit: %s\n", e)
   216  	}
   217  	for _, input := range inputs {
   218  		if e := p.DigestUpdate(session, input); e != nil {
   219  			t.Fatalf("DigestUpdate: %s\n", e)
   220  		}
   221  	}
   222  	hash, e := p.DigestFinal(session)
   223  	if e != nil {
   224  		t.Fatalf("DigestFinal: %s\n", e)
   225  	}
   226  	hex := ""
   227  	for _, d := range hash {
   228  		hex += fmt.Sprintf("%02x", d)
   229  	}
   230  	if hex != expected {
   231  		t.Fatalf("wrong digest: %s", hex)
   232  	}
   233  }
   234  
   235  /*
   236  Purpose: Generate RSA keypair with a given name and persistence.
   237  Inputs: test object
   238  	context
   239  	session handle
   240  	tokenLabel: string to set as the token labels
   241  	tokenPersistent: boolean. Whether or not the token should be
   242  			session based or persistent. If false, the
   243  			token will not be saved in the HSM and is
   244  			destroyed upon termination of the session.
   245  Outputs: creates persistent or ephemeral tokens within the HSM.
   246  Returns: object handles for public and private keys. Fatal on error.
   247  */
   248  func generateRSAKeyPair(t *testing.T, p *Ctx, session SessionHandle, tokenLabel string, tokenPersistent bool) (ObjectHandle, ObjectHandle) {
   249  	/*
   250  		inputs: test object, context, session handle
   251  			tokenLabel: string to set as the token labels
   252  			tokenPersistent: boolean. Whether or not the token should be
   253  					session based or persistent. If false, the
   254  					token will not be saved in the HSM and is
   255  					destroyed upon termination of the session.
   256  		outputs: creates persistent or ephemeral tokens within the HSM.
   257  		returns: object handles for public and private keys.
   258  	*/
   259  
   260  	publicKeyTemplate := []*Attribute{
   261  		NewAttribute(CKA_CLASS, CKO_PUBLIC_KEY),
   262  		NewAttribute(CKA_KEY_TYPE, CKK_RSA),
   263  		NewAttribute(CKA_TOKEN, tokenPersistent),
   264  		NewAttribute(CKA_VERIFY, true),
   265  		NewAttribute(CKA_PUBLIC_EXPONENT, []byte{1, 0, 1}),
   266  		NewAttribute(CKA_MODULUS_BITS, 2048),
   267  		NewAttribute(CKA_LABEL, tokenLabel),
   268  	}
   269  	privateKeyTemplate := []*Attribute{
   270  		NewAttribute(CKA_TOKEN, tokenPersistent),
   271  		NewAttribute(CKA_SIGN, true),
   272  		NewAttribute(CKA_LABEL, tokenLabel),
   273  		NewAttribute(CKA_SENSITIVE, true),
   274  		NewAttribute(CKA_EXTRACTABLE, true),
   275  	}
   276  	pbk, pvk, e := p.GenerateKeyPair(session,
   277  		[]*Mechanism{NewMechanism(CKM_RSA_PKCS_KEY_PAIR_GEN, nil)},
   278  		publicKeyTemplate, privateKeyTemplate)
   279  	if e != nil {
   280  		t.Fatalf("failed to generate keypair: %s\n", e)
   281  	}
   282  
   283  	return pbk, pvk
   284  }
   285  
   286  func TestGenerateKeyPair(t *testing.T) {
   287  	p := setenv(t)
   288  	session := getSession(p, t)
   289  	defer finishSession(p, session)
   290  	tokenLabel := "TestGenerateKeyPair"
   291  	generateRSAKeyPair(t, p, session, tokenLabel, false)
   292  }
   293  
   294  func TestSign(t *testing.T) {
   295  	p := setenv(t)
   296  	session := getSession(p, t)
   297  	defer finishSession(p, session)
   298  
   299  	tokenLabel := "TestSign"
   300  	_, pvk := generateRSAKeyPair(t, p, session, tokenLabel, false)
   301  
   302  	p.SignInit(session, []*Mechanism{NewMechanism(CKM_SHA1_RSA_PKCS, nil)}, pvk)
   303  	_, e := p.Sign(session, []byte("Sign me!"))
   304  	if e != nil {
   305  		t.Fatalf("failed to sign: %s\n", e)
   306  	}
   307  }
   308  
   309  /* destroyObject
   310  Purpose: destroy and object from the HSM
   311  Inputs: test handle
   312  	session handle
   313  	searchToken: String containing the token label to search for.
   314  	class: Key type (CKO_PRIVATE_KEY or CKO_PUBLIC_KEY) to remove.
   315  Outputs: removes object from HSM
   316  Returns: Fatal error on failure.
   317  */
   318  func destroyObject(t *testing.T, p *Ctx, session SessionHandle, searchToken string, class uint) (err error) {
   319  	template := []*Attribute{
   320  		NewAttribute(CKA_LABEL, searchToken),
   321  		NewAttribute(CKA_CLASS, class)}
   322  
   323  	if e := p.FindObjectsInit(session, template); e != nil {
   324  		t.Fatalf("failed to init: %s\n", e)
   325  	}
   326  	obj, _, e := p.FindObjects(session, 1)
   327  	if e != nil || len(obj) == 0 {
   328  		t.Fatalf("failed to find objects")
   329  	}
   330  	if e := p.FindObjectsFinal(session); e != nil {
   331  		t.Fatalf("failed to finalize: %s\n", e)
   332  	}
   333  
   334  	if e := p.DestroyObject(session, obj[0]); e != nil {
   335  		t.Fatalf("DestroyObject failed: %s\n", e)
   336  	}
   337  	return
   338  }
   339  
   340  // Create and destroy persistent keys
   341  func TestDestroyObject(t *testing.T) {
   342  	p := setenv(t)
   343  	session := getSession(p, t)
   344  	defer finishSession(p, session)
   345  
   346  	generateRSAKeyPair(t, p, session, "TestDestroyKey", true)
   347  	if e := destroyObject(t, p, session, "TestDestroyKey", CKO_PUBLIC_KEY); e != nil {
   348  		t.Fatalf("Failed to destroy object: %s\n", e)
   349  	}
   350  	if e := destroyObject(t, p, session, "TestDestroyKey", CKO_PRIVATE_KEY); e != nil {
   351  		t.Fatalf("Failed to destroy object: %s\n", e)
   352  	}
   353  
   354  }
   355  
   356  func TestSymmetricEncryption(t *testing.T) {
   357  	p := setenv(t)
   358  	session := getSession(p, t)
   359  	defer finishSession(p, session)
   360  	if info, err := p.GetInfo(); err != nil {
   361  		t.Errorf("GetInfo: %v", err)
   362  		return
   363  	} else if info.ManufacturerID == "SoftHSM" && info.LibraryVersion.Major < 2 {
   364  		t.Skipf("AES not implemented on SoftHSM")
   365  	}
   366  	tokenLabel := "TestGenerateKey"
   367  	keyTemplate := []*Attribute{
   368  		NewAttribute(CKA_TOKEN, false),
   369  		NewAttribute(CKA_ENCRYPT, true),
   370  		NewAttribute(CKA_DECRYPT, true),
   371  		NewAttribute(CKA_LABEL, tokenLabel),
   372  		NewAttribute(CKA_SENSITIVE, true),
   373  		NewAttribute(CKA_EXTRACTABLE, true),
   374  		NewAttribute(CKA_VALUE_LEN, 16),
   375  	}
   376  	key, err := p.GenerateKey(session,
   377  		[]*Mechanism{NewMechanism(CKM_AES_KEY_GEN, nil)},
   378  		keyTemplate)
   379  	if err != nil {
   380  		t.Fatalf("failed to generate keypair: %s\n", err)
   381  	}
   382  	t.Run("Encrypt", func(t *testing.T) {
   383  		t.Run("ECB", func(t *testing.T) {
   384  			testEncrypt(t, p, session, key, CKM_AES_ECB, make([]byte, 32), nil)
   385  		})
   386  		t.Run("CBC", func(t *testing.T) {
   387  			testEncrypt(t, p, session, key, CKM_AES_CBC, make([]byte, 32), make([]byte, 16))
   388  		})
   389  		t.Run("CBC-PAD", func(t *testing.T) {
   390  			testEncrypt(t, p, session, key, CKM_AES_CBC_PAD, make([]byte, 31), make([]byte, 16))
   391  		})
   392  		/* Broken in SoftHSMv2
   393  		t.Run("Empty", func(t *testing.T) {
   394  			testEncrypt(t, p, session, key, CKM_AES_CBC, []byte{}, make([]byte, 16))
   395  		})
   396  		*/
   397  	})
   398  	t.Run("EncryptUpdate", func(t *testing.T) {
   399  		t.Run("ECB", func(t *testing.T) {
   400  			testEncryptUpdate(t, p, session, key, CKM_AES_ECB, [][]byte{make([]byte, 32), make([]byte, 16)}, nil)
   401  		})
   402  		t.Run("CBC", func(t *testing.T) {
   403  			testEncryptUpdate(t, p, session, key, CKM_AES_CBC, [][]byte{make([]byte, 32), make([]byte, 16)}, make([]byte, 16))
   404  		})
   405  		t.Run("CBC-PAD", func(t *testing.T) {
   406  			testEncryptUpdate(t, p, session, key, CKM_AES_CBC_PAD, [][]byte{make([]byte, 11), make([]byte, 20)}, make([]byte, 16))
   407  		})
   408  		/* Broken in SoftHSMv2
   409  		t.Run("Empty", func(t *testing.T) {
   410  			testEncryptUpdate(t, p, session, key, CKM_AES_CBC, [][]byte{make([]byte, 31), []byte{}}, make([]byte, 16))
   411  		})
   412  		*/
   413  	})
   414  }
   415  
   416  func testEncrypt(t *testing.T, p *Ctx, session SessionHandle, key ObjectHandle, mech uint, plaintext []byte, iv []byte) {
   417  	var err error
   418  	if err = p.EncryptInit(session, []*Mechanism{NewMechanism(mech, iv)}, key); err != nil {
   419  		t.Fatalf("EncryptInit: %s\n", err)
   420  	}
   421  	var ciphertext []byte
   422  	if ciphertext, err = p.Encrypt(session, plaintext); err != nil {
   423  		t.Fatalf("Encrypt: %s\n", err)
   424  	}
   425  	if err = p.DecryptInit(session, []*Mechanism{NewMechanism(mech, iv)}, key); err != nil {
   426  		t.Fatalf("DecryptInit: %s\n", err)
   427  	}
   428  	var decrypted []byte
   429  	if decrypted, err = p.Decrypt(session, ciphertext); err != nil {
   430  		t.Fatalf("Decrypt: %s\n", err)
   431  	}
   432  	if bytes.Compare(plaintext, decrypted) != 0 {
   433  		t.Fatalf("Plaintext mismatch")
   434  	}
   435  }
   436  
   437  func testEncryptUpdate(t *testing.T, p *Ctx, session SessionHandle, key ObjectHandle, mech uint, plaintexts [][]byte, iv []byte) {
   438  	var err error
   439  	if err = p.EncryptInit(session, []*Mechanism{NewMechanism(mech, iv)}, key); err != nil {
   440  		t.Fatalf("EncryptInit: %s\n", err)
   441  	}
   442  	var ciphertexts [][]byte
   443  	var output, plaintext []byte
   444  	for _, input := range plaintexts {
   445  		plaintext = append(plaintext, input...)
   446  		if output, err = p.EncryptUpdate(session, input); err != nil {
   447  			t.Fatalf("EncryptUpdate: %s\n", err)
   448  		}
   449  		ciphertexts = append(ciphertexts, output)
   450  	}
   451  	if output, err = p.EncryptFinal(session); err != nil {
   452  		t.Fatalf("EncryptFinal: %s\n", err)
   453  	}
   454  	ciphertexts = append(ciphertexts, output)
   455  	if err = p.DecryptInit(session, []*Mechanism{NewMechanism(mech, iv)}, key); err != nil {
   456  		t.Fatalf("DecryptInit: %s\n", err)
   457  	}
   458  	var decrypted []byte
   459  	for _, input := range ciphertexts {
   460  		if len(input) == 0 { // Broken in SoftHSMv2
   461  			continue
   462  		}
   463  		if output, err = p.DecryptUpdate(session, input); err != nil {
   464  			t.Fatalf("DecryptUpdate: %s\n", err)
   465  		}
   466  		decrypted = append(decrypted, output...)
   467  	}
   468  	if output, err = p.DecryptFinal(session); err != nil {
   469  		t.Fatalf("DecryptFinal: %s\n", err)
   470  	}
   471  	decrypted = append(decrypted, output...)
   472  	if bytes.Compare(plaintext, decrypted) != 0 {
   473  		t.Fatalf("Plaintext mismatch")
   474  	}
   475  }
   476  
   477  // ExampleSign shows how to sign some data with a private key.
   478  // Note: error correction is not implemented in this example.
   479  func ExampleCtx_Sign() {
   480  	if x := os.Getenv("SOFTHSM_LIB"); x != "" {
   481  		lib = x
   482  	}
   483  	p := New(lib)
   484  	if p == nil {
   485  		log.Fatal("Failed to init lib")
   486  	}
   487  
   488  	p.Initialize()
   489  	defer p.Destroy()
   490  	defer p.Finalize()
   491  	slots, _ := p.GetSlotList(true)
   492  	session, _ := p.OpenSession(slots[0], CKF_SERIAL_SESSION|CKF_RW_SESSION)
   493  	defer p.CloseSession(session)
   494  	p.Login(session, CKU_USER, "1234")
   495  	defer p.Logout(session)
   496  	publicKeyTemplate := []*Attribute{
   497  		NewAttribute(CKA_CLASS, CKO_PUBLIC_KEY),
   498  		NewAttribute(CKA_KEY_TYPE, CKK_RSA),
   499  		NewAttribute(CKA_TOKEN, false),
   500  		NewAttribute(CKA_ENCRYPT, true),
   501  		NewAttribute(CKA_PUBLIC_EXPONENT, []byte{3}),
   502  		NewAttribute(CKA_MODULUS_BITS, 1024),
   503  		NewAttribute(CKA_LABEL, "ExampleSign"),
   504  	}
   505  	privateKeyTemplate := []*Attribute{
   506  		NewAttribute(CKA_CLASS, CKO_PRIVATE_KEY),
   507  		NewAttribute(CKA_KEY_TYPE, CKK_RSA),
   508  		NewAttribute(CKA_TOKEN, false),
   509  		NewAttribute(CKA_PRIVATE, true),
   510  		NewAttribute(CKA_SIGN, true),
   511  		NewAttribute(CKA_LABEL, "ExampleSign"),
   512  	}
   513  	_, priv, err := p.GenerateKeyPair(session,
   514  		[]*Mechanism{NewMechanism(CKM_RSA_PKCS_KEY_PAIR_GEN, nil)},
   515  		publicKeyTemplate, privateKeyTemplate)
   516  	if err != nil {
   517  		log.Fatal(err)
   518  	}
   519  	p.SignInit(session, []*Mechanism{NewMechanism(CKM_SHA1_RSA_PKCS, nil)}, priv)
   520  	// Sign something with the private key.
   521  	data := []byte("Lets sign this data")
   522  
   523  	_, err = p.Sign(session, data)
   524  	if err != nil {
   525  		log.Fatal(err)
   526  	}
   527  
   528  	fmt.Printf("It works!")
   529  	// Output: It works!
   530  }
   531  
   532  // Copyright 2013 Miek Gieben. All rights reserved.
   533  

View as plain text