diff --git a/Gopkg.lock b/Gopkg.lock index de7da02..8cf432f 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -5,7 +5,7 @@ branch = "master" name = "github.com/miekg/pkcs11" packages = ["."] - revision = "7283ca79f35edb89bc1b4ecae7f86a3680ce737f" + revision = "287d9350987cc9334667882061e202e96cdfb4d0" [[projects]] name = "github.com/youtube/vitess" @@ -17,7 +17,7 @@ branch = "master" name = "golang.org/x/net" packages = ["context"] - revision = "afe8f62b1d6bbd81f31868121a50b06d8188e1f9" + revision = "f4c29de78a2a91c00474a2e689954305c350adf9" [[projects]] name = "vitess.io/vitess" diff --git a/README.md b/README.md index 6d52cdb..37e9023 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,8 @@ To protect keys with the module only, use the 'accelerator' token: "Pin" : "password" } +(At time of writing) GCM is not implemented, so expect test skips. + Testing with SoftHSM -------------------- diff --git a/symmetric.go b/symmetric.go index 95d64a7..0ec9044 100644 --- a/symmetric.go +++ b/symmetric.go @@ -51,6 +51,9 @@ type SymmetricCipher struct { // CBC mechanism (CKM_..._CBC) CBCMech uint + + // GCM mechanism (CKM_..._GCM) + GCMMech uint } // CipherAES describes the AES cipher. Use this with the @@ -63,6 +66,7 @@ var CipherAES = SymmetricCipher{ MAC: false, ECBMech: pkcs11.CKM_AES_ECB, CBCMech: pkcs11.CKM_AES_CBC, + GCMMech: pkcs11.CKM_AES_GCM, } // CipherDES3 describes the three-key triple-DES cipher. Use this with the @@ -75,6 +79,7 @@ var CipherDES3 = SymmetricCipher{ MAC: false, ECBMech: pkcs11.CKM_DES3_ECB, CBCMech: pkcs11.CKM_DES3_CBC, + GCMMech: 0, } // Ciphers is a map of PKCS#11 key types (CKK_...) to symmetric cipher information. @@ -216,6 +221,74 @@ func (key *PKCS11SecretKey) Encrypt(dst, src []byte) { } } +// cipher.AEAD ---------------------------------------------------------- + +type gcmAead struct { + key *PKCS11SecretKey +} + +// NewGCM returns a given cipher wrapped in Galois Counter Mode, with the standard +// nonce length. +// +// This depends on the HSM supporting the CKM_*_GCM mechanism. If it is not supported +// then you must use cipher.NewGCM; it will be slow. +func (key *PKCS11SecretKey) NewGCM() (g cipher.AEAD, err error) { + if key.Cipher.GCMMech == 0 { + err = fmt.Errorf("GCM not implemented for key type %#x", key.Cipher.KeyType) + return + } + g = gcmAead{key} + return +} + +func (g gcmAead) NonceSize() int { + return 12 +} + +func (g gcmAead) Overhead() int { + return 16 +} + +func (g gcmAead) Seal(dst, nonce, plaintext, additionalData []byte) []byte { + var result []byte + if err := withSession(g.key.Slot, func(session *PKCS11Session) (err error) { + params := pkcs11.NewGCMParams(nonce, additionalData, 16) + mech := []*pkcs11.Mechanism{pkcs11.NewMechanism(g.key.Cipher.GCMMech, params)} + if err = session.Ctx.EncryptInit(session.Handle, mech, g.key.Handle); err != nil { + return + } + if result, err = session.Ctx.Encrypt(session.Handle, plaintext); err != nil { + return + } + return + }); err != nil { + panic(err) + } else { + dst = append(dst, result...) + } + return dst +} + +func (g gcmAead) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { + var result []byte + if err := withSession(g.key.Slot, func(session *PKCS11Session) (err error) { + params := pkcs11.NewGCMParams(nonce, additionalData, 16) + mech := []*pkcs11.Mechanism{pkcs11.NewMechanism(g.key.Cipher.GCMMech, params)} + if err = session.Ctx.DecryptInit(session.Handle, mech, g.key.Handle); err != nil { + return + } + if result, err = session.Ctx.Decrypt(session.Handle, ciphertext); err != nil { + return + } + return + }); err != nil { + return nil, err + } else { + dst = append(dst, result...) + } + return dst, nil +} + // Stream encryption/decryption ----------------------------------------- // BlockModeCloser represents a block cipher running in a block-based mode (CBC, ECB etc). diff --git a/symmetric_test.go b/symmetric_test.go index 3e91cd7..e967e6b 100644 --- a/symmetric_test.go +++ b/symmetric_test.go @@ -87,7 +87,7 @@ func testHardSymmetric(t *testing.T, keytype int, bits int) { dec.Close() }) if bits == 128 { - t.Run("GCM", func(t *testing.T) { + t.Run("GCMSoft", func(t *testing.T) { aead, err := cipher.NewGCM(key2) if err != nil { t.Errorf("cipher.NewGCM: %v", err) @@ -95,6 +95,23 @@ func testHardSymmetric(t *testing.T, keytype int, bits int) { } testAEADMode(t, aead) }) + t.Run("GCMHard", func(t *testing.T) { + var info pkcs11.Info + if info, err = libHandle.GetInfo(); err != nil { + t.Errorf("GetInfo: %v", err) + return + } + if info.ManufacturerID == "nCipher Corp. Ltd" { + t.Skipf("nShield PKCS#11 does not have GCM yet") + } + aead, err := key2.NewGCM() + if err != nil { + t.Errorf("key2.NewGCM: %v", err) + return + } + testAEADMode(t, aead) + }) + // TODO check that hard/soft is consistent! } // TODO CFB // TODO OFB @@ -240,3 +257,5 @@ func BenchmarkCBC(b *testing.B) { }) Close() } + +// TODO BenchmarkGCM along the same lines as above