Skip to content

Commit

Permalink
HMAC implementation
Browse files Browse the repository at this point in the history
re #7
  • Loading branch information
Richard Kettlewell authored and Richard Kettlewell committed Oct 2, 2018
1 parent b3302aa commit 102e275
Show file tree
Hide file tree
Showing 4 changed files with 470 additions and 29 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ The configuration looks like this:
"Pin" : "password"
}

(At time of writing) OAEP is only partial, so expect test skips.
(At time of writing) OAEP is only partial and HMAC is unsupported, so expect test skips.

Limitations
===========
Expand Down
174 changes: 174 additions & 0 deletions hmac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
// Copyright 2018 Thales e-Security, Inc
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package crypto11

import (
"context"
"fmt"
"github.com/miekg/pkcs11"
"github.com/youtube/vitess/go/pools"
"hash"
)

const (
NFCK_VENDOR_NCIPHER = 0xde436972
CKM_NCIPHER = (pkcs11.CKM_VENDOR_DEFINED | NFCK_VENDOR_NCIPHER)

// CKM_NC_MD5_HMAC_KEY_GEN is the nShield-specific HMACMD5 key-generation mechanism
CKM_NC_MD5_HMAC_KEY_GEN = (CKM_NCIPHER + 0x6)

// CKM_NC_SHA_1_HMAC_KEY_GEN is the nShield-specific HMACSHA1 key-generation mechanism
CKM_NC_SHA_1_HMAC_KEY_GEN = (CKM_NCIPHER + 0x3)

// CKM_NC_SHA224_HMAC_KEY_GEN is the nShield-specific HMACSHA224 key-generation mechanism
CKM_NC_SHA224_HMAC_KEY_GEN = (CKM_NCIPHER + 0x24)

// CKM_NC_SHA256_HMAC_KEY_GEN is the nShield-specific HMACSHA256 key-generation mechanism
CKM_NC_SHA256_HMAC_KEY_GEN = (CKM_NCIPHER + 0x25)

// CKM_NC_SHA384_HMAC_KEY_GEN is the nShield-specific HMACSHA384 key-generation mechanism
CKM_NC_SHA384_HMAC_KEY_GEN = (CKM_NCIPHER + 0x26)

// CKM_NC_SHA512_HMAC_KEY_GEN is the nShield-specific HMACSHA512 key-generation mechanism
CKM_NC_SHA512_HMAC_KEY_GEN = (CKM_NCIPHER + 0x27)
)

type hmacImplementation struct {
// PKCS#11 session to use
session *PKCS11Session

size int

blockSize int

// Cleanup function
cleanup func()
}

type hmacInfo struct {
size int
blockSize int
general bool
}

var hmacInfos = map[int]*hmacInfo{
pkcs11.CKM_MD5_HMAC: {20, 64, false},
pkcs11.CKM_MD5_HMAC_GENERAL: {20, 64, true},
pkcs11.CKM_SHA_1_HMAC: {20, 64, false},
pkcs11.CKM_SHA_1_HMAC_GENERAL: {20, 64, true},
pkcs11.CKM_SHA224_HMAC: {28, 64, false},
pkcs11.CKM_SHA224_HMAC_GENERAL: {28, 64, true},
pkcs11.CKM_SHA256_HMAC: {32, 64, false},
pkcs11.CKM_SHA256_HMAC_GENERAL: {32, 64, true},
pkcs11.CKM_SHA384_HMAC: {48, 64, false},
pkcs11.CKM_SHA384_HMAC_GENERAL: {48, 64, true},
pkcs11.CKM_SHA512_HMAC: {64, 128, false},
pkcs11.CKM_SHA512_HMAC_GENERAL: {64, 128, true},
pkcs11.CKM_SHA512_224_HMAC: {28, 128, false},
pkcs11.CKM_SHA512_224_HMAC_GENERAL: {28, 128, true},
pkcs11.CKM_SHA512_256_HMAC: {32, 128, false},
pkcs11.CKM_SHA512_256_HMAC_GENERAL: {32, 128, true},
pkcs11.CKM_RIPEMD160_HMAC: {20, 64, false},
pkcs11.CKM_RIPEMD160_HMAC_GENERAL: {20, 64, true},
}

// NewHMAC returns a new HMAC hash using the given PKCS#11 mechanism
// and key.
// length specifies the output size, for _GENERAL mechanisms.
//
// If the mechanism is not in the built-in list of known mechanisms then the
// Size() function will return whatever length was, even if it is wrong.
// BlockSize() will always return 0 in this case.
//
// The Reset() method is not implemented, and Sum() may only be called once.
// The former limitation may be lifted in future but the latter is fundamental
// and will not change.
func (key *PKCS11SecretKey) NewHMAC(mech int, length int) (h hash.Hash, err error) {
// TODO refactor with newBlockModeCloser
sessionPool := pool.Get(key.Slot)
if sessionPool == nil {
err = fmt.Errorf("crypto11: no session for slot %d", key.Slot)
return
}
ctx, cancel := context.WithTimeout(context.Background(), newSessionTimeout)
defer cancel()
var session pools.Resource
if session, err = sessionPool.Get(ctx); err != nil {
return
}
var hi hmacImplementation
hi.session = session.(*PKCS11Session)
hi.cleanup = func() {
sessionPool.Put(session)
hi.session = nil
}
var params []byte
if info, ok := hmacInfos[mech]; ok {
hi.blockSize = info.blockSize
if info.general {
hi.size = length
params = ulongToBytes(uint(length))
} else {
hi.size = info.size
}
} else {
hi.size = length
}
mechDescription := []*pkcs11.Mechanism{pkcs11.NewMechanism(uint(mech), params)}
if err = hi.session.Ctx.SignInit(hi.session.Handle, mechDescription, key.Handle); err != nil {
hi.cleanup()
return
}
h = &hi
return
}

func (hi *hmacImplementation) Write(p []byte) (n int, err error) {
if err = hi.session.Ctx.SignUpdate(hi.session.Handle, p); err != nil {
return
}
n = len(p)
return
}

func (hi *hmacImplementation) Sum(b []byte) []byte {
var result []byte
var err error
result, err = hi.session.Ctx.SignFinal(hi.session.Handle)
hi.cleanup()
if err != nil {
panic(err)
}
return append(b, result...)
}

func (hi *hmacImplementation) Reset() {
panic("Reset not implemented")
}

func (hi *hmacImplementation) Size() int {
return hi.size
}

func (hi *hmacImplementation) BlockSize() int {
return hi.blockSize
}
105 changes: 105 additions & 0 deletions hmac_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright 2018 Thales e-Security, Inc
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package crypto11

import (
"bytes"
"github.com/miekg/pkcs11"
"hash"
"testing"
)

func TestHmac(t *testing.T) {
ConfigureFromFile("config")
var info pkcs11.Info
var err error
if info, err = libHandle.GetInfo(); err != nil {
t.Errorf("GetInfo: %v", err)
return
}
if info.ManufacturerID == "SoftHSM" {
t.Skipf("HMAC not implemented on SoftHSM")
}
t.Run("HMACSHA1", func(t *testing.T) {
testHmac(t, pkcs11.CKK_SHA_1_HMAC, pkcs11.CKM_SHA_1_HMAC, 0, 20)
})
t.Run("HMACSHA1General", func(t *testing.T) {
testHmac(t, pkcs11.CKK_SHA_1_HMAC, pkcs11.CKM_SHA_1_HMAC_GENERAL, 10, 10)
})
t.Run("HMACSHA256", func(t *testing.T) {
testHmac(t, pkcs11.CKK_SHA256_HMAC, pkcs11.CKM_SHA256_HMAC, 0, 32)
})
Close()
}

func testHmac(t *testing.T, keytype int, mech int, length int, xlength int) {
var err error
var key *PKCS11SecretKey
var id []byte
t.Run("Generate", func(t *testing.T) {
if key, err = GenerateSecretKey(256, Ciphers[keytype]); err != nil {
t.Errorf("crypto11.GenerateSecretKey: %v", err)
return
}
if key == nil {
t.Errorf("crypto11.GenerateSecretKey: returned nil but no error")
return
}
if id, _, err = key.Identify(); err != nil {
t.Errorf("crypto11.PKCS11SecretKey.Identify: %v", err)
return
}
})
if key == nil {
return
}
t.Run("Short", func(t *testing.T) {
input := []byte("a short string")
var h1, h2 hash.Hash
if h1, err = key.NewHMAC(mech, length); err != nil {
t.Errorf("key.NewHMAC: %v", err)
return
}
if n, err := h1.Write(input); err != nil || n != len(input) {
t.Errorf("h1.Write: %v/%d", err, n)
return
}
r1 := h1.Sum([]byte{})
if h2, err = key.NewHMAC(mech, length); err != nil {
t.Errorf("key.NewHMAC: %v", err)
return
}
if n, err := h2.Write(input); err != nil || n != len(input) {
t.Errorf("h2.Write: %v/%d", err, n)
return
}
r2 := h2.Sum([]byte{})
if bytes.Compare(r1, r2) != 0 {
t.Errorf("h1/h2 inconsistent")
return
}
if len(r1) != xlength {
t.Errorf("r1 wrong length (want %v got %v)", xlength, len(r1))
return
}
})
}
Loading

0 comments on commit 102e275

Please sign in to comment.