mirror of
https://github.com/gravitational/teleport.git
synced 2026-09-21 05:55:42 +08:00
Add hsmKeyStore implementation (#7614)
This commit is contained in:
@@ -49,6 +49,7 @@ RUN apt-get update -y --fix-missing && \
|
||||
python-pip \
|
||||
pkg-config \
|
||||
shellcheck \
|
||||
softhsm2 \
|
||||
tar \
|
||||
tree \
|
||||
unzip \
|
||||
@@ -124,5 +125,7 @@ ENV PROTO_INCLUDE "/usr/local/include":"${GOPATH}/src":"${GOPATH}/src/github.com
|
||||
COPY pam/ /opt/pam_teleport/
|
||||
RUN make -C /opt/pam_teleport install
|
||||
|
||||
ENV SOFTHSM2_PATH "/usr/lib/softhsm/libsofthsm2.so"
|
||||
|
||||
VOLUME ["/go/src/github.com/gravitational/teleport"]
|
||||
EXPOSE 6600 2379 2380
|
||||
|
||||
@@ -8,6 +8,7 @@ require (
|
||||
cloud.google.com/go/storage v1.10.0
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.0.1
|
||||
github.com/Microsoft/go-winio v0.4.16
|
||||
github.com/ThalesIgnite/crypto11 v1.2.4
|
||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 // indirect
|
||||
github.com/alecthomas/colour v0.1.0 // indirect
|
||||
github.com/alecthomas/repr v0.0.0-20200325044227-4184120f674c // indirect
|
||||
|
||||
@@ -71,6 +71,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/ThalesIgnite/crypto11 v1.2.4 h1:3MebRK/U0mA2SmSthXAIZAdUA9w8+ZuKem2O6HuR1f8=
|
||||
github.com/ThalesIgnite/crypto11 v1.2.4/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U=
|
||||
@@ -521,6 +523,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
|
||||
github.com/mdp/rsc v0.0.0-20160131164516-90f07065088d h1:j7DAJd/z/JtaXjFtlrLV8NHflBsg1rkrTqAJNLqMWBE=
|
||||
github.com/mdp/rsc v0.0.0-20160131164516-90f07065088d/go.mod h1:fIxvRMy+xQMcJGz9JAV25fJOKMRF1VQY/P8Mrni5XJA=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f h1:eVB9ELsoq5ouItQBr5Tj334bhPJG/MX+m7rTchmzVUQ=
|
||||
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
@@ -698,6 +702,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg=
|
||||
github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
|
||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright 2021 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package keystore provides a generic client and associated helpers for handling
|
||||
// private keys that may be backed by an HSM.
|
||||
//
|
||||
// Notes on testing
|
||||
//
|
||||
// Testing the Keystore package predictably requires an HSM. Testcases are
|
||||
// currently written for the "raw" KeyStore (no HSM), SoftHSMv2, YubiHSM2, and
|
||||
// AWS CloudHSM. Only the "raw" tests run without any setup, but testing for
|
||||
// SoftHSM is enabled by default in the Teleport docker buildbox and will be
|
||||
// run in CI.
|
||||
//
|
||||
// Testing this package with SoftHSMv2
|
||||
//
|
||||
// To test with SoftHSMv2, you must install it (see
|
||||
// https://github.com/opendnssec/SoftHSMv2 or
|
||||
// https://packages.ubuntu.com/search?keywords=softhsm2) and set the
|
||||
// "SOFTHSM2_PATH" environment variable to the location of SoftHSM2's PKCS11
|
||||
// library. Depending how you installed it, this is likely to be
|
||||
// /usr/lib/softhsm/libsofthsm2.so or /usr/local/lib/softhsm/libsofthsm2.so.
|
||||
//
|
||||
// The test will create its own config file and token, and clean up after itself.
|
||||
//
|
||||
// Testing this package with YubiHSM2
|
||||
//
|
||||
// To test with YubiHSM2, you must:
|
||||
//
|
||||
// 1. have a physical YubiHSM plugged in
|
||||
//
|
||||
// 2. install the SDK (https://developers.yubico.com/YubiHSM2/Releases/)
|
||||
//
|
||||
// 3. start the connector "yubihsm-connector -d"
|
||||
//
|
||||
// 4. create a config file
|
||||
// connector = http://127.0.0.1:12345
|
||||
// debug
|
||||
//
|
||||
// 5. set "YUBIHSM_PKCS11_CONF" to the location of your config file
|
||||
//
|
||||
// 6. set "YUBIHSM_PKCS11_PATH" to the location of the PKCS11 library
|
||||
//
|
||||
// The test will use the factory default pin of "0001password" in slot 0.
|
||||
//
|
||||
// Testing this package with AWS CloudHSM
|
||||
//
|
||||
// 1. Create a CloudHSM Cluster and HSM, and activate them https://docs.aws.amazon.com/cloudhsm/latest/userguide/getting-started.html
|
||||
//
|
||||
// 2. Connect an EC2 instance to the cluster https://docs.aws.amazon.com/cloudhsm/latest/userguide/configure-sg-client-instance.html
|
||||
//
|
||||
// 3. Install the CloudHSM client on the EC2 instance https://docs.aws.amazon.com/cloudhsm/latest/userguide/install-and-configure-client-linux.html
|
||||
//
|
||||
// 4. Create a Crypto User (CU) https://docs.aws.amazon.com/cloudhsm/latest/userguide/manage-hsm-users.html
|
||||
//
|
||||
// 5. Set "CLOUDHSM_PIN" to "<username>:<password>" of your crypto user, eg "TestUser:hunter2"
|
||||
//
|
||||
// 6. Run the test on the connected EC2 instance
|
||||
//
|
||||
// Testing Teleport with an HSM-backed CA
|
||||
//
|
||||
// TBD as full support is added.
|
||||
package keystore
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
Copyright 2021 Gravitational, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package keystore
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/json"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/gravitational/teleport"
|
||||
"github.com/gravitational/teleport/api/v7/types"
|
||||
"github.com/gravitational/teleport/lib/sshutils"
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
"github.com/ThalesIgnite/crypto11"
|
||||
"github.com/google/uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var label = []byte("teleport")
|
||||
|
||||
// HSMConfig is used to pass HSM client configuration parameters.
|
||||
type HSMConfig struct {
|
||||
// Path is the path to the PKCS11 module.
|
||||
Path string
|
||||
// SlotNumber is the PKCS11 slot to use.
|
||||
SlotNumber *int
|
||||
// TokenLabel is the label of the PKCS11 token to use.
|
||||
TokenLabel string
|
||||
// Pin is the PKCS11 pin for the given token.
|
||||
Pin string
|
||||
|
||||
// HostUUID is the UUID of the local auth server this HSM is connected to.
|
||||
HostUUID string
|
||||
}
|
||||
|
||||
type hsmKeyStore struct {
|
||||
ctx *crypto11.Context
|
||||
hostUUID string
|
||||
log logrus.FieldLogger
|
||||
}
|
||||
|
||||
func NewHSMKeyStore(config *HSMConfig) (KeyStore, error) {
|
||||
cryptoConfig := &crypto11.Config{
|
||||
Path: config.Path,
|
||||
TokenLabel: config.TokenLabel,
|
||||
SlotNumber: config.SlotNumber,
|
||||
Pin: config.Pin,
|
||||
}
|
||||
ctx, err := crypto11.Configure(cryptoConfig)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
return &hsmKeyStore{
|
||||
ctx: ctx,
|
||||
hostUUID: config.HostUUID,
|
||||
log: logrus.WithFields(logrus.Fields{trace.Component: "HSMKeyStore"}),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *hsmKeyStore) findUnusedID() (uuid.UUID, error) {
|
||||
var id uuid.UUID
|
||||
var err error
|
||||
|
||||
// Some HSMs (like YubiHSM2) will silently truncate the passed ID to as few
|
||||
// as 2 bytes. There's not a great way to detect this and I don't want to
|
||||
// limit the ID to 2 bytes on all systems, so for now we will generate a
|
||||
// few random IDs and hope to avoid a collision. Ideally Teleport should be
|
||||
// the only thing creating keys for this token and there should only be 10
|
||||
// keys per HSM at a given time:
|
||||
// 2(rotation phases) * (4(SSH and TLS for User and Host CA) + 1(JWT CA))
|
||||
maxIterations := 16
|
||||
iterations := 0
|
||||
for ; iterations < maxIterations; iterations++ {
|
||||
id, err = uuid.NewRandom()
|
||||
if err != nil {
|
||||
return id, trace.Wrap(err)
|
||||
}
|
||||
existingSigner, err := c.ctx.FindKeyPair(id[:], label)
|
||||
if err != nil {
|
||||
return id, trace.Wrap(err)
|
||||
}
|
||||
if existingSigner == nil {
|
||||
// failed to find an existing keypair, so this ID is unique
|
||||
break
|
||||
} else {
|
||||
c.log.Warn("Found CKA_ID collision while creating keypair, retrying with new ID")
|
||||
}
|
||||
}
|
||||
if iterations == maxIterations {
|
||||
return id, trace.AlreadyExists("failed to find unused CKA_ID for HSM")
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// GenerateRSA creates a new RSA private key and returns its identifier and a
|
||||
// crypto.Signer. The returned identifier can be passed to GetSigner later to
|
||||
// get the same crypto.Signer.
|
||||
func (c *hsmKeyStore) GenerateRSA() ([]byte, crypto.Signer, error) {
|
||||
id, err := c.findUnusedID()
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
signer, err := c.ctx.GenerateRSAKeyPairWithLabel(id[:], label, teleport.RSAKeySize)
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
key := keyID{
|
||||
HostID: c.hostUUID,
|
||||
KeyID: id.String(),
|
||||
}
|
||||
|
||||
keyID, err := key.marshal()
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
return keyID, signer, nil
|
||||
}
|
||||
|
||||
// GetSigner returns a crypto.Signer for the given key identifier, if it is found.
|
||||
func (c *hsmKeyStore) GetSigner(rawKey []byte) (crypto.Signer, error) {
|
||||
keyType := KeyType(rawKey)
|
||||
switch keyType {
|
||||
case types.PrivateKeyType_PKCS11:
|
||||
keyID, err := parseKeyID(rawKey)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if keyID.HostID != c.hostUUID {
|
||||
return nil, trace.NotFound("given pkcs11 key is for host: %q, but this host is: %q", keyID.HostID, c.hostUUID)
|
||||
}
|
||||
pkcs11ID, err := keyID.pkcs11Key()
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
signer, err := c.ctx.FindKeyPair(pkcs11ID, label)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if signer == nil {
|
||||
return nil, trace.NotFound("failed to find keypair for given id")
|
||||
}
|
||||
return signer, nil
|
||||
case types.PrivateKeyType_RAW:
|
||||
return nil, trace.BadParameter("cannot get raw signer from HSM KeyStore")
|
||||
}
|
||||
return nil, trace.BadParameter("unrecognized key type %s", keyType.String())
|
||||
}
|
||||
|
||||
func (c *hsmKeyStore) selectTLSKeyPair(ca types.CertAuthority) (*types.TLSKeyPair, error) {
|
||||
keyPairs := ca.GetActiveKeys().TLS
|
||||
for _, keyPair := range keyPairs {
|
||||
if keyPair.KeyType == types.PrivateKeyType_PKCS11 {
|
||||
keyID, err := parseKeyID(keyPair.Key)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if keyID.HostID != c.hostUUID {
|
||||
continue
|
||||
}
|
||||
return keyPair, nil
|
||||
}
|
||||
}
|
||||
return nil, trace.NotFound("no local PKCS#11 TLS key pairs found in %s CA for %q", ca.GetType(), ca.GetClusterName())
|
||||
}
|
||||
|
||||
// GetTLSCertAndSigner selects the local TLS keypair and returns the raw TLS cert and crypto.Signer.
|
||||
func (c *hsmKeyStore) GetTLSCertAndSigner(ca types.CertAuthority) ([]byte, crypto.Signer, error) {
|
||||
keyPair, err := c.selectTLSKeyPair(ca)
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
// if there is no key, this CA may only be used for checking
|
||||
if len(keyPair.Key) == 0 {
|
||||
return keyPair.Cert, nil, nil
|
||||
}
|
||||
|
||||
signer, err := c.GetSigner(keyPair.Key)
|
||||
if err != nil {
|
||||
return nil, nil, trace.Wrap(err)
|
||||
}
|
||||
return keyPair.Cert, signer, nil
|
||||
}
|
||||
|
||||
func (c *hsmKeyStore) selectSSHKeyPair(ca types.CertAuthority) (*types.SSHKeyPair, error) {
|
||||
keyPairs := ca.GetActiveKeys().SSH
|
||||
for _, keyPair := range keyPairs {
|
||||
if keyPair.PrivateKeyType == types.PrivateKeyType_PKCS11 {
|
||||
keyID, err := parseKeyID(keyPair.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if keyID.HostID != c.hostUUID {
|
||||
continue
|
||||
}
|
||||
return keyPair, nil
|
||||
}
|
||||
}
|
||||
return nil, trace.NotFound("no local PKCS#11 SSH key pairs found in %s CA for %q", ca.GetType(), ca.GetClusterName())
|
||||
}
|
||||
|
||||
// GetSSHSigner selects the local SSH keypair and returns an ssh.Signer.
|
||||
func (c *hsmKeyStore) GetSSHSigner(ca types.CertAuthority) (sshSigner ssh.Signer, err error) {
|
||||
keyPair, err := c.selectSSHKeyPair(ca)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
|
||||
signer, err := c.GetSigner(keyPair.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
sshSigner, err = ssh.NewSignerFromSigner(signer)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
sshSigner = sshutils.AlgSigner(sshSigner, sshutils.GetSigningAlgName(ca))
|
||||
return sshSigner, nil
|
||||
}
|
||||
|
||||
// GetJWTSigner returns the active jwt signer used to sign tokens.
|
||||
func (c *hsmKeyStore) GetJWTSigner(ca types.CertAuthority) (crypto.Signer, error) {
|
||||
keyPairs := ca.GetActiveKeys().JWT
|
||||
for _, keyPair := range keyPairs {
|
||||
if keyPair.PrivateKeyType == types.PrivateKeyType_PKCS11 {
|
||||
keyID, err := parseKeyID(keyPair.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
if keyID.HostID != c.hostUUID {
|
||||
continue
|
||||
}
|
||||
signer, err := c.GetSigner(keyPair.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return signer, nil
|
||||
}
|
||||
}
|
||||
return nil, trace.NotFound("no local PKCS#11 JWT key pairs found in %s CA for %q", ca.GetType(), ca.GetClusterName())
|
||||
}
|
||||
|
||||
// DeleteKey deletes the given key from the HSM
|
||||
func (c *hsmKeyStore) DeleteKey(rawKey []byte) error {
|
||||
keyID, err := parseKeyID(rawKey)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
if keyID.HostID != c.hostUUID {
|
||||
return trace.NotFound("pkcs11 key is for different host")
|
||||
}
|
||||
pkcs11ID, err := keyID.pkcs11Key()
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
signer, err := c.ctx.FindKeyPair(pkcs11ID, label)
|
||||
if err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
if signer == nil {
|
||||
return trace.NotFound("failed to find keypair for given id")
|
||||
}
|
||||
return trace.Wrap(signer.Delete())
|
||||
}
|
||||
|
||||
type keyID struct {
|
||||
HostID string `json:"host_id"`
|
||||
KeyID string `json:"key_id"`
|
||||
}
|
||||
|
||||
func (k keyID) marshal() ([]byte, error) {
|
||||
buf, err := json.Marshal(k)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
buf = append(append([]byte{}, pkcs11Prefix...), buf...)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (k keyID) pkcs11Key() ([]byte, error) {
|
||||
id, err := uuid.Parse(k.KeyID)
|
||||
if err != nil {
|
||||
return nil, trace.Wrap(err)
|
||||
}
|
||||
return id[:], nil
|
||||
}
|
||||
|
||||
func parseKeyID(key []byte) (keyID, error) {
|
||||
var keyID keyID
|
||||
if KeyType(key) != types.PrivateKeyType_PKCS11 {
|
||||
return keyID, trace.BadParameter("unable to parse invalid pkcs11 key")
|
||||
}
|
||||
// strip pkcs11: prefix
|
||||
key = key[len(pkcs11Prefix):]
|
||||
if err := json.Unmarshal(key, &keyID); err != nil {
|
||||
return keyID, trace.Wrap(err)
|
||||
}
|
||||
return keyID, nil
|
||||
}
|
||||
@@ -22,6 +22,10 @@ import (
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
"github.com/gravitational/teleport/api/v7/types"
|
||||
@@ -29,6 +33,7 @@ import (
|
||||
"github.com/gravitational/teleport/lib/auth/native"
|
||||
"github.com/gravitational/teleport/lib/defaults"
|
||||
"github.com/gravitational/teleport/lib/tlsca"
|
||||
"github.com/gravitational/trace"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/crypto/ssh"
|
||||
@@ -122,9 +127,11 @@ JhuTMEqUaAOZBoQLn+txjl3nu9WwTThJzlY0L4w=
|
||||
)
|
||||
|
||||
func TestKeyStore(t *testing.T) {
|
||||
yubiSlotNumber := 0
|
||||
testcases := []struct {
|
||||
desc string
|
||||
rawConfig *keystore.RawConfig
|
||||
hsmConfig *keystore.HSMConfig
|
||||
shouldSkip func() bool
|
||||
setup func(t *testing.T)
|
||||
}{
|
||||
@@ -135,6 +142,80 @@ func TestKeyStore(t *testing.T) {
|
||||
},
|
||||
shouldSkip: func() bool { return false },
|
||||
},
|
||||
{
|
||||
desc: "softhsm",
|
||||
hsmConfig: &keystore.HSMConfig{
|
||||
Path: os.Getenv("SOFTHSM2_PATH"),
|
||||
TokenLabel: "test",
|
||||
Pin: "password",
|
||||
HostUUID: "server1",
|
||||
},
|
||||
shouldSkip: func() bool {
|
||||
if os.Getenv("SOFTHSM2_PATH") == "" {
|
||||
log.Println("Skipping softhsm test because SOFTHSM2_PATH is not set.")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
setup: func(t *testing.T) {
|
||||
// create tokendir
|
||||
tokenDir, err := os.MkdirTemp("", "softhsm2-tokendir")
|
||||
require.NoError(t, err)
|
||||
|
||||
// create config file
|
||||
configFile, err := os.CreateTemp("", "softhsm2.conf")
|
||||
require.NoError(t, err)
|
||||
os.Setenv("SOFTHSM2_CONF", configFile.Name())
|
||||
|
||||
// write config file
|
||||
_, err = configFile.WriteString(fmt.Sprintf(
|
||||
"directories.tokendir = %s\nobjectstore.backend = file\nlog.level = DEBUG\n",
|
||||
tokenDir))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, configFile.Close())
|
||||
|
||||
// create test token
|
||||
cmd := exec.Command("softhsm2-util", "--init-token", "--slot", "0", "--label", "test", "--so-pin", "password", "--pin", "password")
|
||||
require.NoError(t, cmd.Run())
|
||||
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, os.Remove(configFile.Name()))
|
||||
require.NoError(t, os.RemoveAll(tokenDir))
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "yubihsm",
|
||||
hsmConfig: &keystore.HSMConfig{
|
||||
Path: os.Getenv("YUBIHSM_PKCS11_PATH"),
|
||||
SlotNumber: &yubiSlotNumber,
|
||||
Pin: "0001password",
|
||||
HostUUID: "server1",
|
||||
},
|
||||
shouldSkip: func() bool {
|
||||
if os.Getenv("YUBIHSM_PKCS11_CONF") == "" || os.Getenv("YUBIHSM_PKCS11_PATH") == "" {
|
||||
log.Println("Skipping yubihsm test because YUBIHSM_PKCS11_CONF or YUBIHSM_PKCS11_PATH is not set.")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "cloudhsm",
|
||||
hsmConfig: &keystore.HSMConfig{
|
||||
Path: "/opt/cloudhsm/lib/libcloudhsm_pkcs11.so",
|
||||
TokenLabel: "cavium",
|
||||
Pin: os.Getenv("CLOUDHSM_PIN"),
|
||||
HostUUID: "server1",
|
||||
},
|
||||
shouldSkip: func() bool {
|
||||
if os.Getenv("CLOUDHSM_PIN") == "" {
|
||||
log.Println("Skipping cloudhsm test because CLOUDHSM_PIN is not set.")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
@@ -150,7 +231,14 @@ func TestKeyStore(t *testing.T) {
|
||||
}
|
||||
|
||||
// create the keystore
|
||||
keyStore := keystore.NewRawKeyStore(tc.rawConfig)
|
||||
var keyStore keystore.KeyStore
|
||||
var err error
|
||||
if tc.hsmConfig != nil {
|
||||
keyStore, err = keystore.NewHSMKeyStore(tc.hsmConfig)
|
||||
} else {
|
||||
keyStore = keystore.NewRawKeyStore(tc.rawConfig)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, keyStore)
|
||||
|
||||
// create a key
|
||||
@@ -258,19 +346,31 @@ func TestKeyStore(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// test that keyStore is able to get a signer
|
||||
sshSigner, err = keyStore.GetSSHSigner(ca)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, sshSigner)
|
||||
if tc.hsmConfig != nil {
|
||||
// hsm keyStore should not get any signer from raw keys
|
||||
_, err = keyStore.GetSSHSigner(ca)
|
||||
require.True(t, trace.IsNotFound(err))
|
||||
|
||||
tlsCert, tlsSigner, err = keyStore.GetTLSCertAndSigner(ca)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, tlsCert)
|
||||
require.NotNil(t, tlsSigner)
|
||||
_, _, err = keyStore.GetTLSCertAndSigner(ca)
|
||||
require.True(t, trace.IsNotFound(err))
|
||||
|
||||
jwtSigner, err = keyStore.GetJWTSigner(ca)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, jwtSigner)
|
||||
_, err = keyStore.GetJWTSigner(ca)
|
||||
require.True(t, trace.IsNotFound(err))
|
||||
} else {
|
||||
// raw keyStore should be able to get a signer
|
||||
sshSigner, err = keyStore.GetSSHSigner(ca)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, sshSigner)
|
||||
|
||||
tlsCert, tlsSigner, err = keyStore.GetTLSCertAndSigner(ca)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, tlsCert)
|
||||
require.NotNil(t, tlsSigner)
|
||||
|
||||
jwtSigner, err = keyStore.GetJWTSigner(ca)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, jwtSigner)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
*~
|
||||
crypto11.test
|
||||
demo/*.pem
|
||||
demo/demo
|
||||
/.idea/
|
||||
/vendor/
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
dist: xenial
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.13.x"
|
||||
|
||||
# Xenial comes with v2.0.0 SoftHSM2, which seems to have issues with ECDSA
|
||||
# code points
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- sourceline: 'ppa:pkg-opendnssec/ppa'
|
||||
packages:
|
||||
- softhsm2
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
|
||||
script:
|
||||
- echo directories.tokendir = `pwd`/tokens > softhsm2.conf
|
||||
- echo objecstore.backend = file >> softhsm2.conf
|
||||
- cat softhsm2.conf
|
||||
- mkdir tokens
|
||||
- export SOFTHSM2_CONF=`pwd`/softhsm2.conf
|
||||
- softhsm2-util --init-token --slot 0 --label token1 --so-pin sopassword --pin password
|
||||
- go test -mod readonly -v -bench .
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
MIT License.
|
||||
|
||||
Copyright 2016, 2017 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.
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
Crypto11
|
||||
========
|
||||
|
||||
[](https://godoc.org/github.com/ThalesIgnite/crypto11)
|
||||
[](https://travis-ci.com/ThalesIgnite/crypto11)
|
||||
|
||||
This is an implementation of the standard Golang crypto interfaces that
|
||||
uses [PKCS#11](http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/errata01/os/pkcs11-base-v2.40-errata01-os-complete.html) as a backend. The supported features are:
|
||||
|
||||
* Generation and retrieval of RSA, DSA and ECDSA keys.
|
||||
* Importing and retrieval of x509 certificates
|
||||
* PKCS#1 v1.5 signing.
|
||||
* PKCS#1 PSS signing.
|
||||
* PKCS#1 v1.5 decryption
|
||||
* PKCS#1 OAEP decryption
|
||||
* ECDSA signing.
|
||||
* DSA signing.
|
||||
* Random number generation.
|
||||
* AES and DES3 encryption and decryption.
|
||||
* HMAC support.
|
||||
|
||||
Signing is done through the
|
||||
[crypto.Signer](https://golang.org/pkg/crypto/#Signer) interface and
|
||||
decryption through
|
||||
[crypto.Decrypter](https://golang.org/pkg/crypto/#Decrypter).
|
||||
|
||||
To verify signatures or encrypt messages, retrieve the public key and do it in software.
|
||||
|
||||
See [the documentation](https://godoc.org/github.com/ThalesIgnite/crypto11) for details of various limitations,
|
||||
especially regarding symmetric crypto.
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
Since v1.0.0, crypto11 requires Go v1.11+. Install the library by running:
|
||||
|
||||
```bash
|
||||
go get github.com/ThalesIgnite/crypto11
|
||||
```
|
||||
|
||||
The crypto11 library needs to be configured with information about your PKCS#11 installation. This is either done programmatically
|
||||
(see the `Config` struct in [the documentation](https://godoc.org/github.com/ThalesIgnite/crypto11)) or via a configuration
|
||||
file. The configuration file is a JSON representation of the `Config` struct.
|
||||
|
||||
A minimal configuration file looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"Path" : "/usr/lib/softhsm/libsofthsm2.so",
|
||||
"TokenLabel": "token1",
|
||||
"Pin" : "password"
|
||||
}
|
||||
```
|
||||
|
||||
- `Path` points to the library from your PKCS#11 vendor.
|
||||
- `TokenLabel` is the `CKA_LABEL` of the token you wish to use.
|
||||
- `Pin` is the password for the `CKU_USER` user.
|
||||
|
||||
Testing Guidance
|
||||
================
|
||||
|
||||
Disabling tests
|
||||
---------------
|
||||
|
||||
To disable specific tests, set the environment variable `CRYPTO11_SKIP=<flags>` where `<flags>` is a comma-separated
|
||||
list of the following options:
|
||||
|
||||
* `CERTS` - disables certificate-related tests. Needed for AWS CloudHSM, which doesn't support certificates.
|
||||
* `OAEP_LABEL` - disables RSA OAEP encryption tests that use source data encoding parameter (also known as a 'label'
|
||||
in some crypto libraries). Needed for AWS CloudHSM.
|
||||
* `DSA` - disables DSA tests. Needed for AWS CloudHSM (and any other tokens not supporting DSA).
|
||||
|
||||
Testing with Thales Luna HSM
|
||||
----------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
Testing with AWS CloudHSM
|
||||
-------------------------
|
||||
|
||||
A minimal configuration file for CloudHSM will look like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"Path" : "/opt/cloudhsm/lib/libcloudhsm_pkcs11_standard.so",
|
||||
"TokenLabel": "cavium",
|
||||
"Pin" : "username:password",
|
||||
"UseGCMIVFromHSM" : true,
|
||||
}
|
||||
```
|
||||
|
||||
To run the test suite you must skip unsupported tests:
|
||||
|
||||
```
|
||||
CRYPTO11_SKIP=CERTS,OAEP_LABEL,DSA go test -v
|
||||
```
|
||||
|
||||
Be sure to take note of the supported mechanisms, key types and other idiosyncrasies described at
|
||||
https://docs.aws.amazon.com/cloudhsm/latest/userguide/pkcs11-library.html. Here's a collection of things we
|
||||
noticed when testing with the v2.0.4 PKCS#11 library:
|
||||
|
||||
- 1024-bit RSA keys don't appear to be supported, despite what `C_GetMechanismInfo` tells you.
|
||||
- The `CKM_RSA_PKCS_OAEP` mechanism doesn't support source data. I.e. when constructing a `CK_RSA_PKCS_OAEP_PARAMS`,
|
||||
one must set `pSourceData` to `NULL` and `ulSourceDataLen` to zero.
|
||||
- CloudHSM will generate it's own IV for GCM mode. This is described in their documentation, see footnote 4 on
|
||||
https://docs.aws.amazon.com/cloudhsm/latest/userguide/pkcs11-mechanisms.html.
|
||||
- It appears that `CKA_ID` values must be unique, otherwise you get a `CKR_ATTRIBUTE_VALUE_INVALID` error.
|
||||
- Very rapid session opening can trigger the following error:
|
||||
```
|
||||
C_OpenSession failed with error CKR_ARGUMENTS_BAD : 0x00000007
|
||||
HSM error 8c: HSM Error: Already maximum number of sessions are issued
|
||||
```
|
||||
|
||||
Testing with SoftHSM2
|
||||
---------------------
|
||||
|
||||
To set up a slot:
|
||||
|
||||
$ cat softhsm2.conf
|
||||
directories.tokendir = /home/rjk/go/src/github.com/ThalesIgnite/crypto11/tokens
|
||||
objectstore.backend = file
|
||||
log.level = INFO
|
||||
$ mkdir tokens
|
||||
$ export SOFTHSM2_CONF=`pwd`/softhsm2.conf
|
||||
$ softhsm2-util --init-token --slot 0 --label test
|
||||
=== SO PIN (4-255 characters) ===
|
||||
Please enter SO PIN: ********
|
||||
Please reenter SO PIN: ********
|
||||
=== User PIN (4-255 characters) ===
|
||||
Please enter user PIN: ********
|
||||
Please reenter user PIN: ********
|
||||
The token has been initialized.
|
||||
|
||||
The configuration looks like this:
|
||||
|
||||
$ cat config
|
||||
{
|
||||
"Path" : "/usr/lib/softhsm/libsofthsm2.so",
|
||||
"TokenLabel": "test",
|
||||
"Pin" : "password"
|
||||
}
|
||||
|
||||
(At time of writing) OAEP is only partial and HMAC is unsupported, so expect test skips.
|
||||
|
||||
Testing with nCipher nShield
|
||||
--------------------
|
||||
|
||||
In all cases, it's worth enabling nShield PKCS#11 log output:
|
||||
|
||||
export CKNFAST_DEBUG=2
|
||||
|
||||
To protect keys with a 1/N operator cardset:
|
||||
|
||||
$ cat config
|
||||
{
|
||||
"Path" : "/opt/nfast/toolkits/pkcs11/libcknfast.so",
|
||||
"TokenLabel": "rjk",
|
||||
"Pin" : "password"
|
||||
}
|
||||
|
||||
You can also identify the token by serial number, which in this case
|
||||
means the first 16 hex digits of the operator cardset's token hash:
|
||||
|
||||
$ cat config
|
||||
{
|
||||
"Path" : "/opt/nfast/toolkits/pkcs11/libcknfast.so",
|
||||
"TokenSerial": "1d42780caa22efd5",
|
||||
"Pin" : "password"
|
||||
}
|
||||
|
||||
A card from the cardset must be in the slot when you run `go test`.
|
||||
|
||||
To protect keys with the module only, use the 'accelerator' token:
|
||||
|
||||
$ cat config
|
||||
{
|
||||
"Path" : "/opt/nfast/toolkits/pkcs11/libcknfast.so",
|
||||
"TokenLabel": "accelerator",
|
||||
"Pin" : "password"
|
||||
}
|
||||
|
||||
(At time of writing) GCM is not implemented, so expect test skips.
|
||||
|
||||
Limitations
|
||||
===========
|
||||
|
||||
* The [PKCS1v15DecryptOptions SessionKeyLen](https://golang.org/pkg/crypto/rsa/#PKCS1v15DecryptOptions) field
|
||||
is not implemented and an error is returned if it is nonzero.
|
||||
The reason for this is that it is not possible for crypto11 to guarantee the constant-time behavior in the specification.
|
||||
See [issue #5](https://github.com/ThalesIgnite/crypto11/issues/5) for further discussion.
|
||||
* Symmetric crypto support via [cipher.Block](https://golang.org/pkg/crypto/cipher/#Block) is very slow.
|
||||
You can use the `BlockModeCloser` API
|
||||
(over 400 times as fast on my computer)
|
||||
but you must call the Close()
|
||||
interface (not found in [cipher.BlockMode](https://golang.org/pkg/crypto/cipher/#BlockMode)).
|
||||
See [issue #6](https://github.com/ThalesIgnite/crypto11/issues/6) for further discussion.
|
||||
|
||||
Contributions
|
||||
========
|
||||
|
||||
Contributions are gratefully received. Before beginning work on sizeable changes, please open an issue first to
|
||||
discuss.
|
||||
|
||||
Here are some topics we'd like to cover:
|
||||
|
||||
* Full test instructions for additional PKCS#11 implementations.
|
||||
+189
@@ -0,0 +1,189 @@
|
||||
// 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 (
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
// cipher.AEAD ----------------------------------------------------------
|
||||
|
||||
// A PaddingMode is used by a block cipher (see NewCBC).
|
||||
type PaddingMode int
|
||||
|
||||
const (
|
||||
// PaddingNone represents a block cipher with no padding.
|
||||
PaddingNone PaddingMode = iota
|
||||
|
||||
// PaddingPKCS represents a block cipher used with PKCS#7 padding.
|
||||
PaddingPKCS
|
||||
)
|
||||
|
||||
var errBadGCMNonceSize = errors.New("nonce slice too small to hold IV")
|
||||
|
||||
type genericAead struct {
|
||||
key *SecretKey
|
||||
|
||||
overhead int
|
||||
|
||||
nonceSize int
|
||||
|
||||
// Note - if the GCMParams result is non-nil, the caller must call Free() on the params when
|
||||
// finished.
|
||||
makeMech func(nonce []byte, additionalData []byte, encrypt bool) ([]*pkcs11.Mechanism, *pkcs11.GCMParams, error)
|
||||
}
|
||||
|
||||
// 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 *SecretKey) NewGCM() (cipher.AEAD, error) {
|
||||
if key.Cipher.GCMMech == 0 {
|
||||
return nil, fmt.Errorf("GCM not implemented for key type %#x", key.Cipher.GenParams[0].KeyType)
|
||||
}
|
||||
|
||||
g := genericAead{
|
||||
key: key,
|
||||
overhead: 16,
|
||||
nonceSize: key.context.cfg.GCMIVLength,
|
||||
makeMech: func(nonce []byte, additionalData []byte, encrypt bool) ([]*pkcs11.Mechanism, *pkcs11.GCMParams, error) {
|
||||
var params *pkcs11.GCMParams
|
||||
|
||||
if (encrypt && key.context.cfg.UseGCMIVFromHSM &&
|
||||
!key.context.cfg.GCMIVFromHSMControl.SupplyIvForHSMGCMEncrypt) || (!encrypt &&
|
||||
key.context.cfg.UseGCMIVFromHSM && !key.context.cfg.GCMIVFromHSMControl.SupplyIvForHSMGCMDecrypt) {
|
||||
params = pkcs11.NewGCMParams(nil, additionalData, 16*8 /*bits*/)
|
||||
} else {
|
||||
params = pkcs11.NewGCMParams(nonce, additionalData, 16*8 /*bits*/)
|
||||
}
|
||||
return []*pkcs11.Mechanism{pkcs11.NewMechanism(key.Cipher.GCMMech, params)}, params, nil
|
||||
},
|
||||
}
|
||||
return g, nil
|
||||
}
|
||||
|
||||
// NewCBC returns a given cipher wrapped in CBC mode.
|
||||
//
|
||||
// Despite the cipher.AEAD return type, there is no support for additional data and no authentication.
|
||||
// This method exists to provide a convenient way to do bulk (possibly padded) CBC encryption.
|
||||
// Think carefully before passing the cipher.AEAD to any consumer that expects authentication.
|
||||
func (key *SecretKey) NewCBC(paddingMode PaddingMode) (cipher.AEAD, error) {
|
||||
|
||||
var pkcsMech uint
|
||||
|
||||
switch paddingMode {
|
||||
case PaddingNone:
|
||||
pkcsMech = key.Cipher.CBCMech
|
||||
case PaddingPKCS:
|
||||
pkcsMech = key.Cipher.CBCPKCSMech
|
||||
default:
|
||||
return nil, errors.New("unrecognized padding mode")
|
||||
}
|
||||
|
||||
g := genericAead{
|
||||
key: key,
|
||||
overhead: 0,
|
||||
nonceSize: key.BlockSize(),
|
||||
makeMech: func(nonce []byte, additionalData []byte, encrypt bool) ([]*pkcs11.Mechanism, *pkcs11.GCMParams, error) {
|
||||
if len(additionalData) > 0 {
|
||||
return nil, nil, errors.New("additional data not supported for CBC mode")
|
||||
}
|
||||
|
||||
return []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcsMech, nonce)}, nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func (g genericAead) NonceSize() int {
|
||||
return g.nonceSize
|
||||
}
|
||||
|
||||
func (g genericAead) Overhead() int {
|
||||
return g.overhead
|
||||
}
|
||||
|
||||
func (g genericAead) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||
|
||||
var result []byte
|
||||
if err := g.key.context.withSession(func(session *pkcs11Session) (err error) {
|
||||
mech, params, err := g.makeMech(nonce, additionalData, true)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer params.Free()
|
||||
|
||||
if err = session.ctx.EncryptInit(session.handle, mech, g.key.handle); err != nil {
|
||||
err = fmt.Errorf("C_EncryptInit: %v", err)
|
||||
return
|
||||
}
|
||||
if result, err = session.ctx.Encrypt(session.handle, plaintext); err != nil {
|
||||
err = fmt.Errorf("C_Encrypt: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if g.key.context.cfg.UseGCMIVFromHSM && g.key.context.cfg.GCMIVFromHSMControl.SupplyIvForHSMGCMEncrypt {
|
||||
if len(nonce) != len(params.IV()) {
|
||||
return errBadGCMNonceSize
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
dst = append(dst, result...)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func (g genericAead) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
var result []byte
|
||||
if err := g.key.context.withSession(func(session *pkcs11Session) (err error) {
|
||||
mech, params, err := g.makeMech(nonce, additionalData, false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer params.Free()
|
||||
|
||||
if err = session.ctx.DecryptInit(session.handle, mech, g.key.handle); err != nil {
|
||||
err = fmt.Errorf("C_DecryptInit: %v", err)
|
||||
return
|
||||
}
|
||||
if result, err = session.ctx.Decrypt(session.handle, ciphertext); err != nil {
|
||||
err = fmt.Errorf("C_Decrypt: %v", err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dst = append(dst, result...)
|
||||
return dst, nil
|
||||
}
|
||||
+510
@@ -0,0 +1,510 @@
|
||||
package crypto11
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
// AttributeType represents a PKCS#11 CK_ATTRIBUTE value.
|
||||
type AttributeType = uint
|
||||
|
||||
// Attribute represents a PKCS#11 CK_ATTRIBUTE type.
|
||||
type Attribute = pkcs11.Attribute
|
||||
|
||||
//noinspection GoUnusedConst,GoDeprecation
|
||||
const (
|
||||
CkaClass = AttributeType(0x00000000)
|
||||
CkaToken = AttributeType(0x00000001)
|
||||
CkaPrivate = AttributeType(0x00000002)
|
||||
CkaLabel = AttributeType(0x00000003)
|
||||
CkaApplication = AttributeType(0x00000010)
|
||||
CkaValue = AttributeType(0x00000011)
|
||||
CkaObjectId = AttributeType(0x00000012)
|
||||
CkaCertificateType = AttributeType(0x00000080)
|
||||
CkaIssuer = AttributeType(0x00000081)
|
||||
CkaSerialNumber = AttributeType(0x00000082)
|
||||
CkaAcIssuer = AttributeType(0x00000083)
|
||||
CkaOwner = AttributeType(0x00000084)
|
||||
CkaAttrTypes = AttributeType(0x00000085)
|
||||
CkaTrusted = AttributeType(0x00000086)
|
||||
CkaCertificateCategory = AttributeType(0x00000087)
|
||||
CkaJavaMIDPSecurityDomain = AttributeType(0x00000088)
|
||||
CkaUrl = AttributeType(0x00000089)
|
||||
CkaHashOfSubjectPublicKey = AttributeType(0x0000008A)
|
||||
CkaHashOfIssuerPublicKey = AttributeType(0x0000008B)
|
||||
CkaNameHashAlgorithm = AttributeType(0x0000008C)
|
||||
CkaCheckValue = AttributeType(0x00000090)
|
||||
|
||||
CkaKeyType = AttributeType(0x00000100)
|
||||
CkaSubject = AttributeType(0x00000101)
|
||||
CkaId = AttributeType(0x00000102)
|
||||
CkaSensitive = AttributeType(0x00000103)
|
||||
CkaEncrypt = AttributeType(0x00000104)
|
||||
CkaDecrypt = AttributeType(0x00000105)
|
||||
CkaWrap = AttributeType(0x00000106)
|
||||
CkaUnwrap = AttributeType(0x00000107)
|
||||
CkaSign = AttributeType(0x00000108)
|
||||
CkaSignRecover = AttributeType(0x00000109)
|
||||
CkaVerify = AttributeType(0x0000010A)
|
||||
CkaVerifyRecover = AttributeType(0x0000010B)
|
||||
CkaDerive = AttributeType(0x0000010C)
|
||||
CkaStartDate = AttributeType(0x00000110)
|
||||
CkaEndDate = AttributeType(0x00000111)
|
||||
CkaModulus = AttributeType(0x00000120)
|
||||
CkaModulusBits = AttributeType(0x00000121)
|
||||
CkaPublicExponent = AttributeType(0x00000122)
|
||||
CkaPrivateExponent = AttributeType(0x00000123)
|
||||
CkaPrime1 = AttributeType(0x00000124)
|
||||
CkaPrime2 = AttributeType(0x00000125)
|
||||
CkaExponent1 = AttributeType(0x00000126)
|
||||
CkaExponent2 = AttributeType(0x00000127)
|
||||
CkaCoefficient = AttributeType(0x00000128)
|
||||
CkaPublicKeyInfo = AttributeType(0x00000129)
|
||||
CkaPrime = AttributeType(0x00000130)
|
||||
CkaSubprime = AttributeType(0x00000131)
|
||||
CkaBase = AttributeType(0x00000132)
|
||||
|
||||
CkaPrimeBits = AttributeType(0x00000133)
|
||||
CkaSubprimeBits = AttributeType(0x00000134)
|
||||
/* (To retain backwards-compatibility) */
|
||||
CkaSubPrimeBits = CkaSubprimeBits
|
||||
|
||||
CkaValueBits = AttributeType(0x00000160)
|
||||
CkaValueLen = AttributeType(0x00000161)
|
||||
CkaExtractable = AttributeType(0x00000162)
|
||||
CkaLocal = AttributeType(0x00000163)
|
||||
CkaNeverExtractable = AttributeType(0x00000164)
|
||||
CkaAlwaysSensitive = AttributeType(0x00000165)
|
||||
CkaKeyGenMechanism = AttributeType(0x00000166)
|
||||
|
||||
CkaModifiable = AttributeType(0x00000170)
|
||||
CkaCopyable = AttributeType(0x00000171)
|
||||
|
||||
/* new for v2.40 */
|
||||
CkaDestroyable = AttributeType(0x00000172)
|
||||
|
||||
/* CKA_ECDSA_PARAMS is deprecated in v2.11,
|
||||
* CKA_EC_PARAMS is preferred. */
|
||||
CkaEcdsaParams = AttributeType(0x00000180)
|
||||
CkaEcParams = AttributeType(0x00000180)
|
||||
|
||||
CkaEcPoint = AttributeType(0x00000181)
|
||||
|
||||
/* CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS,
|
||||
* are new for v2.10. Deprecated in v2.11 and onwards. */
|
||||
CkaSecondaryAuth = AttributeType(0x00000200) /* Deprecated */
|
||||
CkaAuthPinFlags = AttributeType(0x00000201) /* Deprecated */
|
||||
|
||||
CkaAlwaysAuthenticate = AttributeType(0x00000202)
|
||||
|
||||
CkaWrapWithTrusted = AttributeType(0x00000210)
|
||||
|
||||
ckfArrayAttribute = AttributeType(0x40000000)
|
||||
|
||||
CkaWrapTemplate = ckfArrayAttribute | AttributeType(0x00000211)
|
||||
CkaUnwrapTemplate = ckfArrayAttribute | AttributeType(0x00000212)
|
||||
|
||||
CkaOtpFormat = AttributeType(0x00000220)
|
||||
CkaOtpLength = AttributeType(0x00000221)
|
||||
CkaOtpTimeInterval = AttributeType(0x00000222)
|
||||
CkaOtpUserFriendlyMode = AttributeType(0x00000223)
|
||||
CkaOtpChallengeRequirement = AttributeType(0x00000224)
|
||||
CkaOtpTimeRequirement = AttributeType(0x00000225)
|
||||
CkaOtpCounterRequirement = AttributeType(0x00000226)
|
||||
CkaOtpPinRequirement = AttributeType(0x00000227)
|
||||
CkaOtpCounter = AttributeType(0x0000022E)
|
||||
CkaOtpTime = AttributeType(0x0000022F)
|
||||
CkaOtpUserIdentifier = AttributeType(0x0000022A)
|
||||
CkaOtpServiceIdentifier = AttributeType(0x0000022B)
|
||||
CkaOtpServiceLogo = AttributeType(0x0000022C)
|
||||
CkaOtpServiceLogoType = AttributeType(0x0000022D)
|
||||
|
||||
CkaGOSTR3410Params = AttributeType(0x00000250)
|
||||
CkaGOSTR3411Params = AttributeType(0x00000251)
|
||||
CkaGOST28147Params = AttributeType(0x00000252)
|
||||
|
||||
CkaHwFeatureType = AttributeType(0x00000300)
|
||||
CkaResetOnInit = AttributeType(0x00000301)
|
||||
CkaHasReset = AttributeType(0x00000302)
|
||||
|
||||
CkaPixelX = AttributeType(0x00000400)
|
||||
CkaPixelY = AttributeType(0x00000401)
|
||||
CkaResolution = AttributeType(0x00000402)
|
||||
CkaCharRows = AttributeType(0x00000403)
|
||||
CkaCharColumns = AttributeType(0x00000404)
|
||||
CkaColor = AttributeType(0x00000405)
|
||||
CkaBitsPerPixel = AttributeType(0x00000406)
|
||||
CkaCharSets = AttributeType(0x00000480)
|
||||
CkaEncodingMethods = AttributeType(0x00000481)
|
||||
CkaMimeTypes = AttributeType(0x00000482)
|
||||
CkaMechanismType = AttributeType(0x00000500)
|
||||
CkaRequiredCmsAttributes = AttributeType(0x00000501)
|
||||
CkaDefaultCmsAttributes = AttributeType(0x00000502)
|
||||
CkaSupportedCmsAttributes = AttributeType(0x00000503)
|
||||
CkaAllowedMechanisms = ckfArrayAttribute | AttributeType(0x00000600)
|
||||
)
|
||||
|
||||
// NewAttribute is a helper function that populates a new Attribute for common data types. This function will
|
||||
// return an error if value is not of type bool, int, uint, string, []byte or time.Time (or is nil).
|
||||
func NewAttribute(attributeType AttributeType, value interface{}) (a *Attribute, err error) {
|
||||
// catch any panics from the pkcs11.NewAttribute() call to handle the error cleanly
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New(fmt.Sprintf("failed creating Attribute: %v", r))
|
||||
}
|
||||
}()
|
||||
|
||||
pAttr := pkcs11.NewAttribute(attributeType, value)
|
||||
return pAttr, nil
|
||||
}
|
||||
|
||||
// CopyAttribute returns a deep copy of the given Attribute.
|
||||
func CopyAttribute(a *Attribute) *Attribute {
|
||||
var value []byte
|
||||
if a.Value != nil && len(a.Value) > 0 {
|
||||
value = append([]byte(nil), a.Value...)
|
||||
}
|
||||
return &pkcs11.Attribute{
|
||||
Type: a.Type,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
// An AttributeSet groups together operations that are common for a collection of Attributes.
|
||||
type AttributeSet map[AttributeType]*Attribute
|
||||
|
||||
// NewAttributeSet creates an empty AttributeSet.
|
||||
func NewAttributeSet() AttributeSet {
|
||||
return make(AttributeSet)
|
||||
}
|
||||
|
||||
// Set stores a new attribute in the AttributeSet. Any existing value will be overwritten. This function will return an
|
||||
// error if value is not of type bool, int, uint, string, []byte or time.Time (or is nil).
|
||||
func (a AttributeSet) Set(attributeType AttributeType, value interface{}) error {
|
||||
attr, err := NewAttribute(attributeType, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a[attributeType] = attr
|
||||
return nil
|
||||
}
|
||||
|
||||
// cloneFrom make this AttributeSet a clone of the supplied set. Values are deep copied.
|
||||
func (a AttributeSet) cloneFrom(set AttributeSet) {
|
||||
for key := range a {
|
||||
delete(a, key)
|
||||
}
|
||||
|
||||
// Use Copy to do the deep cloning for us
|
||||
c := set.Copy()
|
||||
for k, v := range c {
|
||||
a[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// AddIfNotPresent adds the attributes if the Attribute Type is not already present in the AttributeSet.
|
||||
func (a AttributeSet) AddIfNotPresent(additional []*Attribute) {
|
||||
for _, additionalAttr := range additional {
|
||||
// Only add the attribute if it is not already present in the Attribute map
|
||||
if _, ok := a[additionalAttr.Type]; !ok {
|
||||
a[additionalAttr.Type] = additionalAttr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ToSlice returns a deep copy of Attributes contained in the AttributeSet.
|
||||
func (a AttributeSet) ToSlice() []*Attribute {
|
||||
var attributes []*Attribute
|
||||
for _, v := range a {
|
||||
duplicateAttr := CopyAttribute(v)
|
||||
attributes = append(attributes, duplicateAttr)
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
// Copy returns a deep copy of the AttributeSet. This function will return an error if value is not of type
|
||||
// bool, int, uint, string, []byte or time.Time (or is nil).
|
||||
func (a AttributeSet) Copy() AttributeSet {
|
||||
b := NewAttributeSet()
|
||||
for _, v := range a {
|
||||
b[v.Type] = CopyAttribute(v)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Unset removes an attribute from the attributes set. If the set does not contain the attribute, this
|
||||
// is a no-op.
|
||||
func (a AttributeSet) Unset(attributeType AttributeType) {
|
||||
delete(a, attributeType)
|
||||
}
|
||||
|
||||
func (a AttributeSet) String() string {
|
||||
result := new(strings.Builder)
|
||||
for attr, value := range a {
|
||||
_, _ = fmt.Fprintf(result, "%s: %x\n", attributeTypeString(attr), value.Value)
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// NewAttributeSetWithID is a helper function that populates a new slice of Attributes with the provided ID.
|
||||
// This function returns an error if the ID is an empty slice.
|
||||
func NewAttributeSetWithID(id []byte) (AttributeSet, error) {
|
||||
if err := notNilBytes(id, "id"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a := NewAttributeSet()
|
||||
_ = a.Set(CkaId, id) // error not possible for []byte
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// NewAttributeSetWithIDAndLabel is a helper function that populates a new slice of Attributes with the
|
||||
// provided ID and Label. This function returns an error if either the ID or the Label is an empty slice.
|
||||
func NewAttributeSetWithIDAndLabel(id, label []byte) (a AttributeSet, err error) {
|
||||
if a, err = NewAttributeSetWithID(id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := notNilBytes(label, "label"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_ = a.Set(CkaLabel, label) // error not possible with []byte
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func attributeTypeString(a AttributeType) string {
|
||||
//noinspection GoDeprecation
|
||||
switch a {
|
||||
case CkaClass:
|
||||
return "CkaClass"
|
||||
case CkaToken:
|
||||
return "CkaToken"
|
||||
case CkaPrivate:
|
||||
return "CkaPrivate"
|
||||
case CkaLabel:
|
||||
return "CkaLabel"
|
||||
case CkaApplication:
|
||||
return "CkaApplication"
|
||||
case CkaValue:
|
||||
return "CkaValue"
|
||||
case CkaObjectId:
|
||||
return "CkaObjectId"
|
||||
case CkaCertificateType:
|
||||
return "CkaCertificateType"
|
||||
case CkaIssuer:
|
||||
return "CkaIssuer"
|
||||
case CkaSerialNumber:
|
||||
return "CkaSerialNumber"
|
||||
case CkaAcIssuer:
|
||||
return "CkaAcIssuer"
|
||||
case CkaOwner:
|
||||
return "CkaOwner"
|
||||
case CkaAttrTypes:
|
||||
return "CkaAttrTypes"
|
||||
case CkaTrusted:
|
||||
return "CkaTrusted"
|
||||
case CkaCertificateCategory:
|
||||
return "CkaCertificateCategory"
|
||||
case CkaJavaMIDPSecurityDomain:
|
||||
return "CkaJavaMIDPSecurityDomain"
|
||||
case CkaUrl:
|
||||
return "CkaUrl"
|
||||
case CkaHashOfSubjectPublicKey:
|
||||
return "CkaHashOfSubjectPublicKey"
|
||||
case CkaHashOfIssuerPublicKey:
|
||||
return "CkaHashOfIssuerPublicKey"
|
||||
case CkaNameHashAlgorithm:
|
||||
return "CkaNameHashAlgorithm"
|
||||
case CkaCheckValue:
|
||||
return "CkaCheckValue"
|
||||
|
||||
case CkaKeyType:
|
||||
return "CkaKeyType"
|
||||
case CkaSubject:
|
||||
return "CkaSubject"
|
||||
case CkaId:
|
||||
return "CkaId"
|
||||
case CkaSensitive:
|
||||
return "CkaSensitive"
|
||||
case CkaEncrypt:
|
||||
return "CkaEncrypt"
|
||||
case CkaDecrypt:
|
||||
return "CkaDecrypt"
|
||||
case CkaWrap:
|
||||
return "CkaWrap"
|
||||
case CkaUnwrap:
|
||||
return "CkaUnwrap"
|
||||
case CkaSign:
|
||||
return "CkaSign"
|
||||
case CkaSignRecover:
|
||||
return "CkaSignRecover"
|
||||
case CkaVerify:
|
||||
return "CkaVerify"
|
||||
case CkaVerifyRecover:
|
||||
return "CkaVerifyRecover"
|
||||
case CkaDerive:
|
||||
return "CkaDerive"
|
||||
case CkaStartDate:
|
||||
return "CkaStartDate"
|
||||
case CkaEndDate:
|
||||
return "CkaEndDate"
|
||||
case CkaModulus:
|
||||
return "CkaModulus"
|
||||
case CkaModulusBits:
|
||||
return "CkaModulusBits"
|
||||
case CkaPublicExponent:
|
||||
return "CkaPublicExponent"
|
||||
case CkaPrivateExponent:
|
||||
return "CkaPrivateExponent"
|
||||
case CkaPrime1:
|
||||
return "CkaPrime1"
|
||||
case CkaPrime2:
|
||||
return "CkaPrime2"
|
||||
case CkaExponent1:
|
||||
return "CkaExponent1"
|
||||
case CkaExponent2:
|
||||
return "CkaExponent2"
|
||||
case CkaCoefficient:
|
||||
return "CkaCoefficient"
|
||||
case CkaPublicKeyInfo:
|
||||
return "CkaPublicKeyInfo"
|
||||
case CkaPrime:
|
||||
return "CkaPrime"
|
||||
case CkaSubprime:
|
||||
return "CkaSubprime"
|
||||
case CkaBase:
|
||||
return "CkaBase"
|
||||
|
||||
case CkaPrimeBits:
|
||||
return "CkaPrimeBits"
|
||||
case CkaSubprimeBits:
|
||||
return "CkaSubprimeBits"
|
||||
|
||||
case CkaValueBits:
|
||||
return "CkaValueBits"
|
||||
case CkaValueLen:
|
||||
return "CkaValueLen"
|
||||
case CkaExtractable:
|
||||
return "CkaExtractable"
|
||||
case CkaLocal:
|
||||
return "CkaLocal"
|
||||
case CkaNeverExtractable:
|
||||
return "CkaNeverExtractable"
|
||||
case CkaAlwaysSensitive:
|
||||
return "CkaAlwaysSensitive"
|
||||
case CkaKeyGenMechanism:
|
||||
return "CkaKeyGenMechanism"
|
||||
|
||||
case CkaModifiable:
|
||||
return "CkaModifiable"
|
||||
case CkaCopyable:
|
||||
return "CkaCopyable"
|
||||
|
||||
case CkaDestroyable:
|
||||
return "CkaDestroyable"
|
||||
|
||||
case CkaEcParams:
|
||||
return "CkaEcParams"
|
||||
|
||||
case CkaEcPoint:
|
||||
return "CkaEcPoint"
|
||||
|
||||
case CkaSecondaryAuth:
|
||||
return "CkaSecondaryAuth"
|
||||
case CkaAuthPinFlags:
|
||||
return "CkaAuthPinFlags"
|
||||
|
||||
case CkaAlwaysAuthenticate:
|
||||
return "CkaAlwaysAuthenticate"
|
||||
|
||||
case CkaWrapWithTrusted:
|
||||
return "CkaWrapWithTrusted"
|
||||
|
||||
case ckfArrayAttribute:
|
||||
return "ckfArrayAttribute"
|
||||
|
||||
case CkaWrapTemplate:
|
||||
return "CkaWrapTemplate"
|
||||
case CkaUnwrapTemplate:
|
||||
return "CkaUnwrapTemplate"
|
||||
|
||||
case CkaOtpFormat:
|
||||
return "CkaOtpFormat"
|
||||
case CkaOtpLength:
|
||||
return "CkaOtpLength"
|
||||
case CkaOtpTimeInterval:
|
||||
return "CkaOtpTimeInterval"
|
||||
case CkaOtpUserFriendlyMode:
|
||||
return "CkaOtpUserFriendlyMode"
|
||||
case CkaOtpChallengeRequirement:
|
||||
return "CkaOtpChallengeRequirement"
|
||||
case CkaOtpTimeRequirement:
|
||||
return "CkaOtpTimeRequirement"
|
||||
case CkaOtpCounterRequirement:
|
||||
return "CkaOtpCounterRequirement"
|
||||
case CkaOtpPinRequirement:
|
||||
return "CkaOtpPinRequirement"
|
||||
case CkaOtpCounter:
|
||||
return "CkaOtpCounter"
|
||||
case CkaOtpTime:
|
||||
return "CkaOtpTime"
|
||||
case CkaOtpUserIdentifier:
|
||||
return "CkaOtpUserIdentifier"
|
||||
case CkaOtpServiceIdentifier:
|
||||
return "CkaOtpServiceIdentifier"
|
||||
case CkaOtpServiceLogo:
|
||||
return "CkaOtpServiceLogo"
|
||||
case CkaOtpServiceLogoType:
|
||||
return "CkaOtpServiceLogoType"
|
||||
|
||||
case CkaGOSTR3410Params:
|
||||
return "CkaGOSTR3410Params"
|
||||
case CkaGOSTR3411Params:
|
||||
return "CkaGOSTR3411Params"
|
||||
case CkaGOST28147Params:
|
||||
return "CkaGOST28147Params"
|
||||
|
||||
case CkaHwFeatureType:
|
||||
return "CkaHwFeatureType"
|
||||
case CkaResetOnInit:
|
||||
return "CkaResetOnInit"
|
||||
case CkaHasReset:
|
||||
return "CkaHasReset"
|
||||
|
||||
case CkaPixelX:
|
||||
return "CkaPixelX"
|
||||
case CkaPixelY:
|
||||
return "CkaPixelY"
|
||||
case CkaResolution:
|
||||
return "CkaResolution"
|
||||
case CkaCharRows:
|
||||
return "CkaCharRows"
|
||||
case CkaCharColumns:
|
||||
return "CkaCharColumns"
|
||||
case CkaColor:
|
||||
return "CkaColor"
|
||||
case CkaBitsPerPixel:
|
||||
return "CkaBitsPerPixel"
|
||||
case CkaCharSets:
|
||||
return "CkaCharSets"
|
||||
case CkaEncodingMethods:
|
||||
return "CkaEncodingMethods"
|
||||
case CkaMimeTypes:
|
||||
return "CkaMimeTypes"
|
||||
case CkaMechanismType:
|
||||
return "CkaMechanismType"
|
||||
case CkaRequiredCmsAttributes:
|
||||
return "CkaRequiredCmsAttributes"
|
||||
case CkaDefaultCmsAttributes:
|
||||
return "CkaDefaultCmsAttributes"
|
||||
case CkaSupportedCmsAttributes:
|
||||
return "CkaSupportedCmsAttributes"
|
||||
case CkaAllowedMechanisms:
|
||||
return "CkaAllowedMechanisms"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
// cipher.Block ---------------------------------------------------------
|
||||
|
||||
// BlockSize returns the cipher's block size in bytes.
|
||||
func (key *SecretKey) BlockSize() int {
|
||||
return key.Cipher.BlockSize
|
||||
}
|
||||
|
||||
// Decrypt decrypts the first block in src into dst.
|
||||
// Dst and src must overlap entirely or not at all.
|
||||
//
|
||||
// Using this method for bulk operation is very inefficient, as it makes a round trip to the HSM
|
||||
// (which may be network-connected) for each block.
|
||||
// For more efficient operation, see NewCBCDecrypterCloser, NewCBCDecrypter or NewCBC.
|
||||
func (key *SecretKey) Decrypt(dst, src []byte) {
|
||||
var result []byte
|
||||
if err := key.context.withSession(func(session *pkcs11Session) (err error) {
|
||||
mech := []*pkcs11.Mechanism{pkcs11.NewMechanism(key.Cipher.ECBMech, nil)}
|
||||
if err = session.ctx.DecryptInit(session.handle, mech, key.handle); err != nil {
|
||||
return
|
||||
}
|
||||
if result, err = session.ctx.Decrypt(session.handle, src[:key.Cipher.BlockSize]); err != nil {
|
||||
return
|
||||
}
|
||||
if len(result) != key.Cipher.BlockSize {
|
||||
err = fmt.Errorf("C_Decrypt: returned %v bytes, wanted %v", len(result), key.Cipher.BlockSize)
|
||||
return
|
||||
}
|
||||
return
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
copy(dst[:key.Cipher.BlockSize], result)
|
||||
}
|
||||
}
|
||||
|
||||
// Encrypt encrypts the first block in src into dst.
|
||||
// Dst and src must overlap entirely or not at all.
|
||||
//
|
||||
// Using this method for bulk operation is very inefficient, as it makes a round trip to the HSM
|
||||
// (which may be network-connected) for each block.
|
||||
// For more efficient operation, see NewCBCEncrypterCloser, NewCBCEncrypter or NewCBC.
|
||||
func (key *SecretKey) Encrypt(dst, src []byte) {
|
||||
var result []byte
|
||||
if err := key.context.withSession(func(session *pkcs11Session) (err error) {
|
||||
mech := []*pkcs11.Mechanism{pkcs11.NewMechanism(key.Cipher.ECBMech, nil)}
|
||||
if err = session.ctx.EncryptInit(session.handle, mech, key.handle); err != nil {
|
||||
return
|
||||
}
|
||||
if result, err = session.ctx.Encrypt(session.handle, src[:key.Cipher.BlockSize]); err != nil {
|
||||
return
|
||||
}
|
||||
if len(result) != key.Cipher.BlockSize {
|
||||
err = fmt.Errorf("C_Encrypt: unexpectedly returned %v bytes, wanted %v", len(result), key.Cipher.BlockSize)
|
||||
return
|
||||
}
|
||||
return
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
copy(dst[:key.Cipher.BlockSize], result)
|
||||
}
|
||||
}
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
// 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 (
|
||||
"crypto/cipher"
|
||||
"runtime"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
// cipher.BlockMode -----------------------------------------------------
|
||||
|
||||
// BlockModeCloser represents a block cipher running in a block-based mode (e.g. CBC).
|
||||
//
|
||||
// BlockModeCloser embeds cipher.BlockMode, and can be used as such.
|
||||
// However, in this case
|
||||
// (or if the Close() method is not explicitly called for any other reason),
|
||||
// resources allocated to it may remain live indefinitely.
|
||||
type BlockModeCloser interface {
|
||||
cipher.BlockMode
|
||||
|
||||
// Close() releases resources associated with the block mode.
|
||||
Close()
|
||||
}
|
||||
|
||||
const (
|
||||
modeEncrypt = iota // blockModeCloser is in encrypt mode
|
||||
modeDecrypt // blockModeCloser is in decrypt mode
|
||||
)
|
||||
|
||||
// NewCBCEncrypter returns a cipher.BlockMode which encrypts in cipher block chaining mode, using the given key.
|
||||
// The length of iv must be the same as the key's block size.
|
||||
//
|
||||
// The new BlockMode acquires persistent resources which are released (eventually) by a finalizer.
|
||||
// If this is a problem for your application then use NewCBCEncrypterCloser instead.
|
||||
//
|
||||
// If that is not possible then adding calls to runtime.GC() may help.
|
||||
func (key *SecretKey) NewCBCEncrypter(iv []byte) (cipher.BlockMode, error) {
|
||||
return key.newBlockModeCloser(key.Cipher.CBCMech, modeEncrypt, iv, true)
|
||||
}
|
||||
|
||||
// NewCBCDecrypter returns a cipher.BlockMode which decrypts in cipher block chaining mode, using the given key.
|
||||
// The length of iv must be the same as the key's block size and must match the iv used to encrypt the data.
|
||||
//
|
||||
// The new BlockMode acquires persistent resources which are released (eventually) by a finalizer.
|
||||
// If this is a problem for your application then use NewCBCDecrypterCloser instead.
|
||||
//
|
||||
// If that is not possible then adding calls to runtime.GC() may help.
|
||||
func (key *SecretKey) NewCBCDecrypter(iv []byte) (cipher.BlockMode, error) {
|
||||
return key.newBlockModeCloser(key.Cipher.CBCMech, modeDecrypt, iv, true)
|
||||
}
|
||||
|
||||
// NewCBCEncrypterCloser returns a BlockModeCloser which encrypts in cipher block chaining mode, using the given key.
|
||||
// The length of iv must be the same as the key's block size.
|
||||
//
|
||||
// Use of NewCBCEncrypterCloser rather than NewCBCEncrypter represents a commitment to call the Close() method
|
||||
// of the returned BlockModeCloser.
|
||||
func (key *SecretKey) NewCBCEncrypterCloser(iv []byte) (BlockModeCloser, error) {
|
||||
return key.newBlockModeCloser(key.Cipher.CBCMech, modeEncrypt, iv, false)
|
||||
}
|
||||
|
||||
// NewCBCDecrypterCloser returns a BlockModeCloser which decrypts in cipher block chaining mode, using the given key.
|
||||
// The length of iv must be the same as the key's block size and must match the iv used to encrypt the data.
|
||||
//
|
||||
// Use of NewCBCDecrypterCloser rather than NewCBCEncrypter represents a commitment to call the Close() method
|
||||
// of the returned BlockModeCloser.
|
||||
func (key *SecretKey) NewCBCDecrypterCloser(iv []byte) (BlockModeCloser, error) {
|
||||
return key.newBlockModeCloser(key.Cipher.CBCMech, modeDecrypt, iv, false)
|
||||
}
|
||||
|
||||
// blockModeCloser is a concrete implementation of BlockModeCloser supporting CBC.
|
||||
type blockModeCloser struct {
|
||||
// PKCS#11 session to use
|
||||
session *pkcs11Session
|
||||
|
||||
// Cipher block size
|
||||
blockSize int
|
||||
|
||||
// modeDecrypt or modeEncrypt
|
||||
mode int
|
||||
|
||||
// Cleanup function
|
||||
cleanup func()
|
||||
}
|
||||
|
||||
// newBlockModeCloser creates a new blockModeCloser for the chosen mechanism and mode.
|
||||
func (key *SecretKey) newBlockModeCloser(mech uint, mode int, iv []byte, setFinalizer bool) (*blockModeCloser, error) {
|
||||
|
||||
session, err := key.context.getSession()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bmc := &blockModeCloser{
|
||||
session: session,
|
||||
blockSize: key.Cipher.BlockSize,
|
||||
mode: mode,
|
||||
cleanup: func() {
|
||||
key.context.pool.Put(session)
|
||||
},
|
||||
}
|
||||
mechDescription := []*pkcs11.Mechanism{pkcs11.NewMechanism(mech, iv)}
|
||||
|
||||
switch mode {
|
||||
case modeDecrypt:
|
||||
err = session.ctx.DecryptInit(session.handle, mechDescription, key.handle)
|
||||
case modeEncrypt:
|
||||
err = session.ctx.EncryptInit(bmc.session.handle, mechDescription, key.handle)
|
||||
default:
|
||||
panic("unexpected mode")
|
||||
}
|
||||
if err != nil {
|
||||
bmc.cleanup()
|
||||
return nil, err
|
||||
}
|
||||
if setFinalizer {
|
||||
runtime.SetFinalizer(bmc, finalizeBlockModeCloser)
|
||||
}
|
||||
|
||||
return bmc, nil
|
||||
}
|
||||
|
||||
func finalizeBlockModeCloser(obj interface{}) {
|
||||
obj.(*blockModeCloser).Close()
|
||||
}
|
||||
|
||||
func (bmc *blockModeCloser) BlockSize() int {
|
||||
return bmc.blockSize
|
||||
}
|
||||
|
||||
func (bmc *blockModeCloser) CryptBlocks(dst, src []byte) {
|
||||
if len(dst) < len(src) {
|
||||
panic("destination buffer too small")
|
||||
}
|
||||
if len(src)%bmc.blockSize != 0 {
|
||||
panic("input is not a whole number of blocks")
|
||||
}
|
||||
var result []byte
|
||||
var err error
|
||||
switch bmc.mode {
|
||||
case modeDecrypt:
|
||||
result, err = bmc.session.ctx.DecryptUpdate(bmc.session.handle, src)
|
||||
case modeEncrypt:
|
||||
result, err = bmc.session.ctx.EncryptUpdate(bmc.session.handle, src)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// PKCS#11 2.40 s5.2 says that the operation must produce as much output
|
||||
// as possible, so we should never have less than we submitted for CBC.
|
||||
// This could be different for other modes but we don't implement any yet.
|
||||
if len(result) != len(src) {
|
||||
panic("nontrivial result from *Final operation")
|
||||
}
|
||||
copy(dst[:len(result)], result)
|
||||
runtime.KeepAlive(bmc)
|
||||
}
|
||||
|
||||
func (bmc *blockModeCloser) Close() {
|
||||
if bmc.session == nil {
|
||||
return
|
||||
}
|
||||
var result []byte
|
||||
var err error
|
||||
switch bmc.mode {
|
||||
case modeDecrypt:
|
||||
result, err = bmc.session.ctx.DecryptFinal(bmc.session.handle)
|
||||
case modeEncrypt:
|
||||
result, err = bmc.session.ctx.EncryptFinal(bmc.session.handle)
|
||||
}
|
||||
bmc.session = nil
|
||||
bmc.cleanup()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// PKCS#11 2.40 s5.2 says that the operation must produce as much output
|
||||
// as possible, so we should never have any left over for CBC.
|
||||
// This could be different for other modes but we don't implement any yet.
|
||||
if len(result) > 0 {
|
||||
panic("nontrivial result from *Final operation")
|
||||
}
|
||||
}
|
||||
+299
@@ -0,0 +1,299 @@
|
||||
// Copyright 2019 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 (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"math/big"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// FindCertificate retrieves a previously imported certificate. Any combination of id, label
|
||||
// and serial can be provided. An error is return if all are nil.
|
||||
func findCertificate(session *pkcs11Session, id []byte, label []byte, serial *big.Int) (cert *x509.Certificate, err error) {
|
||||
|
||||
rawCertificate, err := findRawCertificate(session, id, label, serial)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rawCertificate != nil {
|
||||
cert, err = x509.ParseCertificate(rawCertificate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return cert, err
|
||||
}
|
||||
|
||||
func findRawCertificate(session *pkcs11Session, id []byte, label []byte, serial *big.Int) (rawCertificate []byte, err error) {
|
||||
if id == nil && label == nil && serial == nil {
|
||||
return nil, errors.New("id, label and serial cannot all be nil")
|
||||
}
|
||||
|
||||
var template []*pkcs11.Attribute
|
||||
|
||||
if id != nil {
|
||||
template = append(template, pkcs11.NewAttribute(pkcs11.CKA_ID, id))
|
||||
}
|
||||
if label != nil {
|
||||
template = append(template, pkcs11.NewAttribute(pkcs11.CKA_LABEL, label))
|
||||
}
|
||||
if serial != nil {
|
||||
derSerial, err := asn1.Marshal(serial)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "failed to encode serial")
|
||||
}
|
||||
|
||||
template = append(template, pkcs11.NewAttribute(pkcs11.CKA_SERIAL_NUMBER, derSerial))
|
||||
}
|
||||
|
||||
template = append(template, pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_CERTIFICATE))
|
||||
|
||||
if err = session.ctx.FindObjectsInit(session.handle, template); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
finalErr := session.ctx.FindObjectsFinal(session.handle)
|
||||
if err == nil {
|
||||
err = finalErr
|
||||
}
|
||||
}()
|
||||
|
||||
handles, _, err := session.ctx.FindObjects(session.handle, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(handles) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
attributes := []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_VALUE, 0),
|
||||
}
|
||||
|
||||
if attributes, err = session.ctx.GetAttributeValue(session.handle, handles[0], attributes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawCertificate = attributes[0].Value
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FindCertificate retrieves a previously imported certificate. Any combination of id, label
|
||||
// and serial can be provided. An error is return if all are nil.
|
||||
func (c *Context) FindCertificate(id []byte, label []byte, serial *big.Int) (*x509.Certificate, error) {
|
||||
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
var cert *x509.Certificate
|
||||
err := c.withSession(func(session *pkcs11Session) (err error) {
|
||||
cert, err = findCertificate(session, id, label, serial)
|
||||
return err
|
||||
})
|
||||
|
||||
return cert, err
|
||||
}
|
||||
|
||||
func (c *Context) FindAllPairedCertificates() (certificates []tls.Certificate, err error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
err = c.withSession(func(session *pkcs11Session) error {
|
||||
// Add the private key class to the template to find the private half
|
||||
privAttributes := AttributeSet{}
|
||||
err = privAttributes.Set(CkaClass, pkcs11.CKO_PRIVATE_KEY)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privHandles, err := findKeysWithAttributes(session, privAttributes.ToSlice())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, privHandle := range privHandles {
|
||||
|
||||
privateKey, certificate, err := c.makeKeyPair(session, &privHandle)
|
||||
|
||||
if err == errNoCkaId || err == errNoPublicHalf {
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if certificate == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
tlsCert := tls.Certificate{
|
||||
Leaf: certificate,
|
||||
PrivateKey: privateKey,
|
||||
}
|
||||
|
||||
tlsCert.Certificate = append(tlsCert.Certificate, certificate.Raw)
|
||||
certificates = append(certificates, tlsCert)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ImportCertificate imports a certificate onto the token. The id parameter is used to
|
||||
// set CKA_ID and must be non-nil.
|
||||
func (c *Context) ImportCertificate(id []byte, certificate *x509.Certificate) error {
|
||||
if c.closed.Get() {
|
||||
return errClosed
|
||||
}
|
||||
|
||||
if err := notNilBytes(id, "id"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template, err := NewAttributeSetWithID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.ImportCertificateWithAttributes(template, certificate)
|
||||
}
|
||||
|
||||
// ImportCertificateWithLabel imports a certificate onto the token. The id and label parameters are used to
|
||||
// set CKA_ID and CKA_LABEL respectively and must be non-nil.
|
||||
func (c *Context) ImportCertificateWithLabel(id []byte, label []byte, certificate *x509.Certificate) error {
|
||||
if c.closed.Get() {
|
||||
return errClosed
|
||||
}
|
||||
|
||||
if err := notNilBytes(id, "id"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := notNilBytes(label, "label"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template, err := NewAttributeSetWithIDAndLabel(id, label)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.ImportCertificateWithAttributes(template, certificate)
|
||||
}
|
||||
|
||||
// ImportCertificateWithAttributes imports a certificate onto the token. After this function returns, template
|
||||
// will contain the attributes applied to the certificate. If required attributes are missing, they will be set to a
|
||||
// default value.
|
||||
func (c *Context) ImportCertificateWithAttributes(template AttributeSet, certificate *x509.Certificate) error {
|
||||
if c.closed.Get() {
|
||||
return errClosed
|
||||
}
|
||||
|
||||
if certificate == nil {
|
||||
return errors.New("certificate cannot be nil")
|
||||
}
|
||||
|
||||
serial, err := asn1.Marshal(certificate.SerialNumber)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template.AddIfNotPresent([]*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_CERTIFICATE),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_CERTIFICATE_TYPE, pkcs11.CKC_X_509),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PRIVATE, false),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SUBJECT, certificate.RawSubject),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_ISSUER, certificate.RawIssuer),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SERIAL_NUMBER, serial),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_VALUE, certificate.Raw),
|
||||
})
|
||||
|
||||
err = c.withSession(func(session *pkcs11Session) error {
|
||||
_, err = session.ctx.CreateObject(session.handle, template.ToSlice())
|
||||
return err
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteCertificate destroys a previously imported certificate. it will return
|
||||
// nil if succeeds or if the certificate does not exist. Any combination of id,
|
||||
// label and serial can be provided. An error is return if all are nil.
|
||||
func (c *Context) DeleteCertificate(id []byte, label []byte, serial *big.Int) error {
|
||||
if id == nil && label == nil && serial == nil {
|
||||
return errors.New("id, label and serial cannot all be nil")
|
||||
}
|
||||
|
||||
template := []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_CERTIFICATE),
|
||||
}
|
||||
|
||||
if id != nil {
|
||||
template = append(template, pkcs11.NewAttribute(pkcs11.CKA_ID, id))
|
||||
}
|
||||
if label != nil {
|
||||
template = append(template, pkcs11.NewAttribute(pkcs11.CKA_LABEL, label))
|
||||
}
|
||||
if serial != nil {
|
||||
asn1Serial, err := asn1.Marshal(serial)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
template = append(template, pkcs11.NewAttribute(pkcs11.CKA_SERIAL_NUMBER, asn1Serial))
|
||||
}
|
||||
|
||||
err := c.withSession(func(session *pkcs11Session) error {
|
||||
err := session.ctx.FindObjectsInit(session.handle, template)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
handles, _, err := session.ctx.FindObjects(session.handle, 1)
|
||||
finalErr := session.ctx.FindObjectsFinal(session.handle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if finalErr != nil {
|
||||
return finalErr
|
||||
}
|
||||
if len(handles) == 0 {
|
||||
return nil
|
||||
}
|
||||
return session.ctx.DestroyObject(session.handle, handles[0])
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
// Copyright 2017 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 (
|
||||
"C"
|
||||
"encoding/asn1"
|
||||
"math/big"
|
||||
"unsafe"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func ulongToBytes(n uint) []byte {
|
||||
return C.GoBytes(unsafe.Pointer(&n), C.sizeof_ulong) // ugh!
|
||||
}
|
||||
|
||||
func bytesToUlong(bs []byte) (n uint) {
|
||||
sliceSize := len(bs)
|
||||
if sliceSize == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
value := *(*uint)(unsafe.Pointer(&bs[0]))
|
||||
if sliceSize > C.sizeof_ulong {
|
||||
return value
|
||||
}
|
||||
|
||||
// truncate the value to the # of bits present in the byte slice since
|
||||
// the unsafe pointer will always grab/convert ULONG # of bytes
|
||||
var mask uint
|
||||
for i := 0; i < sliceSize; i++ {
|
||||
mask |= 0xff << uint(i * 8)
|
||||
}
|
||||
return value & mask
|
||||
}
|
||||
|
||||
func concat(slices ...[]byte) []byte {
|
||||
n := 0
|
||||
for _, slice := range slices {
|
||||
n += len(slice)
|
||||
}
|
||||
r := make([]byte, n)
|
||||
n = 0
|
||||
for _, slice := range slices {
|
||||
n += copy(r[n:], slice)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Representation of a *DSA signature
|
||||
type dsaSignature struct {
|
||||
R, S *big.Int
|
||||
}
|
||||
|
||||
// Populate a dsaSignature from a raw byte sequence
|
||||
func (sig *dsaSignature) unmarshalBytes(sigBytes []byte) error {
|
||||
if len(sigBytes) == 0 || len(sigBytes)%2 != 0 {
|
||||
return errors.New("DSA signature length is invalid from token")
|
||||
}
|
||||
n := len(sigBytes) / 2
|
||||
sig.R, sig.S = new(big.Int), new(big.Int)
|
||||
sig.R.SetBytes(sigBytes[:n])
|
||||
sig.S.SetBytes(sigBytes[n:])
|
||||
return nil
|
||||
}
|
||||
|
||||
// Populate a dsaSignature from DER encoding
|
||||
func (sig *dsaSignature) unmarshalDER(sigDER []byte) error {
|
||||
if rest, err := asn1.Unmarshal(sigDER, sig); err != nil {
|
||||
return errors.WithMessage(err, "DSA signature contains invalid ASN.1 data")
|
||||
} else if len(rest) > 0 {
|
||||
return errors.New("unexpected data found after DSA signature")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Return the DER encoding of a dsaSignature
|
||||
func (sig *dsaSignature) marshalDER() ([]byte, error) {
|
||||
return asn1.Marshal(*sig)
|
||||
}
|
||||
|
||||
// Compute *DSA signature and marshal the result in DER form
|
||||
func (c *Context) dsaGeneric(key pkcs11.ObjectHandle, mechanism uint, digest []byte) ([]byte, error) {
|
||||
var err error
|
||||
var sigBytes []byte
|
||||
var sig dsaSignature
|
||||
mech := []*pkcs11.Mechanism{pkcs11.NewMechanism(mechanism, nil)}
|
||||
err = c.withSession(func(session *pkcs11Session) error {
|
||||
if err = c.ctx.SignInit(session.handle, mech, key); err != nil {
|
||||
return err
|
||||
}
|
||||
sigBytes, err = c.ctx.Sign(session.handle, digest)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = sig.unmarshalBytes(sigBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sig.marshalDER()
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Path" : "/usr/lib/softhsm/libsofthsm2.so",
|
||||
"TokenLabel": "token1",
|
||||
"Pin" : "password"
|
||||
}
|
||||
+470
@@ -0,0 +1,470 @@
|
||||
// Copyright 2016 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 enables access to cryptographic keys from PKCS#11 using Go crypto API.
|
||||
//
|
||||
// Configuration
|
||||
//
|
||||
// PKCS#11 tokens are accessed via Context objects. Each Context connects to one token.
|
||||
//
|
||||
// Context objects are created by calling Configure or ConfigureFromFile.
|
||||
// In the latter case, the file should contain a JSON representation of
|
||||
// a Config.
|
||||
//
|
||||
// Key Generation and Usage
|
||||
//
|
||||
// There is support for generating DSA, RSA and ECDSA keys. These keys
|
||||
// can be found later using FindKeyPair. All three key types implement
|
||||
// the crypto.Signer interface and the RSA keys also implement crypto.Decrypter.
|
||||
//
|
||||
// RSA keys obtained through FindKeyPair will need a type assertion to be
|
||||
// used for decryption. Assert either crypto.Decrypter or SignerDecrypter, as you
|
||||
// prefer.
|
||||
//
|
||||
// Symmetric keys can also be generated. These are found later using FindKey.
|
||||
// See the documentation for SecretKey for further information.
|
||||
//
|
||||
// Sessions and concurrency
|
||||
//
|
||||
// Note that PKCS#11 session handles must not be used concurrently
|
||||
// from multiple threads. Consumers of the Signer interface know
|
||||
// nothing of this and expect to be able to sign from multiple threads
|
||||
// without constraint. We address this as follows.
|
||||
//
|
||||
// 1. When a Context is created, a session is created and the user is
|
||||
// logged in. This session remains open until the Context is closed,
|
||||
// to ensure all object handles remain valid and to avoid repeatedly
|
||||
// calling C_Login.
|
||||
//
|
||||
// 2. The Context also maintains a pool of read-write sessions. The pool expands
|
||||
// dynamically as needed, but never beyond the maximum number of r/w sessions
|
||||
// supported by the token (as reported by C_GetInfo). If other applications
|
||||
// are using the token, a lower limit should be set in the Config.
|
||||
//
|
||||
// 3. Each operation transiently takes a session from the pool. They
|
||||
// have exclusive use of the session, meeting PKCS#11's concurrency
|
||||
// requirements. Sessions are returned to the pool afterwards and may
|
||||
// be re-used.
|
||||
//
|
||||
// Behaviour of the pool can be tweaked via Config fields:
|
||||
//
|
||||
// - PoolWaitTimeout controls how long an operation can block waiting on a
|
||||
// session from the pool. A zero value means there is no limit. Timeouts
|
||||
// occur if the pool is fully used and additional operations are requested.
|
||||
//
|
||||
// - MaxSessions sets an upper bound on the number of sessions. If this value is zero,
|
||||
// a default maximum is used (see DefaultMaxSessions). In every case the maximum
|
||||
// supported sessions as reported by the token is obeyed.
|
||||
//
|
||||
// Limitations
|
||||
//
|
||||
// The PKCS1v15DecryptOptions SessionKeyLen field is not implemented
|
||||
// and an error is returned if it is nonzero.
|
||||
// The reason for this is that it is not possible for crypto11 to guarantee the constant-time behavior in the specification.
|
||||
// See https://github.com/thalesignite/crypto11/issues/5 for further discussion.
|
||||
//
|
||||
// Symmetric crypto support via cipher.Block is very slow.
|
||||
// You can use the BlockModeCloser API
|
||||
// but you must call the Close() interface (not found in cipher.BlockMode).
|
||||
// See https://github.com/ThalesIgnite/crypto11/issues/6 for further discussion.
|
||||
package crypto11
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/thales-e-security/pool"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultMaxSessions controls the maximum number of concurrent sessions to
|
||||
// open, unless otherwise specified in the Config object.
|
||||
DefaultMaxSessions = 1024
|
||||
|
||||
// DefaultGCMIVLength controls the expected length of IVs generated by the token
|
||||
DefaultGCMIVLength = 16
|
||||
)
|
||||
|
||||
// errTokenNotFound represents the failure to find the requested PKCS#11 token
|
||||
var errTokenNotFound = errors.New("could not find PKCS#11 token")
|
||||
|
||||
// errClosed is returned if a Context is used after a call to Close.
|
||||
var errClosed = errors.New("cannot used closed Context")
|
||||
|
||||
// pkcs11Object contains a reference to a loaded PKCS#11 object.
|
||||
type pkcs11Object struct {
|
||||
// The PKCS#11 object handle.
|
||||
handle pkcs11.ObjectHandle
|
||||
|
||||
// The PKCS#11 context. This is used to find a session handle that can
|
||||
// access this object.
|
||||
context *Context
|
||||
}
|
||||
|
||||
func (o *pkcs11Object) Delete() error {
|
||||
return o.context.withSession(func(session *pkcs11Session) error {
|
||||
err := session.ctx.DestroyObject(session.handle, o.handle)
|
||||
return errors.WithMessage(err, "failed to destroy key")
|
||||
})
|
||||
}
|
||||
|
||||
// pkcs11PrivateKey contains a reference to a loaded PKCS#11 private key object.
|
||||
type pkcs11PrivateKey struct {
|
||||
pkcs11Object
|
||||
|
||||
// pubKeyHandle is a handle to the public key.
|
||||
pubKeyHandle pkcs11.ObjectHandle
|
||||
|
||||
// pubKey is an exported copy of the public key. We pre-export the key material because crypto.Signer.Public
|
||||
// doesn't allow us to return errors.
|
||||
pubKey crypto.PublicKey
|
||||
}
|
||||
|
||||
// Delete implements Signer.Delete.
|
||||
func (k *pkcs11PrivateKey) Delete() error {
|
||||
err := k.pkcs11Object.Delete()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return k.context.withSession(func(session *pkcs11Session) error {
|
||||
err := session.ctx.DestroyObject(session.handle, k.pubKeyHandle)
|
||||
return errors.WithMessage(err, "failed to destroy public key")
|
||||
})
|
||||
}
|
||||
|
||||
// A Context stores the connection state to a PKCS#11 token. Use Configure or ConfigureFromFile to create a new
|
||||
// Context. Call Close when finished with the token, to free up resources.
|
||||
//
|
||||
// All functions, except Close, are safe to call from multiple goroutines.
|
||||
type Context struct {
|
||||
// Atomic fields must be at top (according to the package owners)
|
||||
closed pool.AtomicBool
|
||||
|
||||
ctx *pkcs11.Ctx
|
||||
cfg *Config
|
||||
|
||||
token *pkcs11.TokenInfo
|
||||
slot uint
|
||||
pool *pool.ResourcePool
|
||||
|
||||
// persistentSession is a session held open so we can be confident handles and login status
|
||||
// persist for the duration of this context
|
||||
persistentSession pkcs11.SessionHandle
|
||||
}
|
||||
|
||||
// Signer is a PKCS#11 key that implements crypto.Signer.
|
||||
type Signer interface {
|
||||
crypto.Signer
|
||||
|
||||
// Delete deletes the key pair from the token.
|
||||
Delete() error
|
||||
}
|
||||
|
||||
// SignerDecrypter is a PKCS#11 key implements crypto.Signer and crypto.Decrypter.
|
||||
type SignerDecrypter interface {
|
||||
Signer
|
||||
|
||||
// Decrypt implements crypto.Decrypter.
|
||||
Decrypt(rand io.Reader, msg []byte, opts crypto.DecrypterOpts) (plaintext []byte, err error)
|
||||
}
|
||||
|
||||
// findToken finds a token given exactly one of serial, label or slotNumber
|
||||
func (c *Context) findToken(slots []uint, serial, label string, slotNumber *int) (uint, *pkcs11.TokenInfo, error) {
|
||||
for _, slot := range slots {
|
||||
|
||||
tokenInfo, err := c.ctx.GetTokenInfo(slot)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
if (slotNumber != nil && uint(*slotNumber) == slot) ||
|
||||
(tokenInfo.SerialNumber != "" && tokenInfo.SerialNumber == serial) ||
|
||||
(tokenInfo.Label != "" && tokenInfo.Label == label) {
|
||||
|
||||
return slot, &tokenInfo, nil
|
||||
}
|
||||
|
||||
}
|
||||
return 0, nil, errTokenNotFound
|
||||
}
|
||||
|
||||
// Config holds PKCS#11 configuration information.
|
||||
//
|
||||
// A token may be selected by label, serial number or slot number. It is an error to specify
|
||||
// more than one way to select the token.
|
||||
//
|
||||
// Supply this to Configure(), or alternatively use ConfigureFromFile().
|
||||
type Config struct {
|
||||
// Full path to PKCS#11 library.
|
||||
Path string
|
||||
|
||||
// Token serial number.
|
||||
TokenSerial string
|
||||
|
||||
// Token label.
|
||||
TokenLabel string
|
||||
|
||||
// SlotNumber identifies a token to use by the slot containing it.
|
||||
SlotNumber *int
|
||||
|
||||
// User PIN (password).
|
||||
Pin string
|
||||
|
||||
// Maximum number of concurrent sessions to open. If zero, DefaultMaxSessions is used.
|
||||
// Otherwise, the value specified must be at least 2.
|
||||
MaxSessions int
|
||||
|
||||
// Maximum time to wait for a session from the sessions pool. Zero means wait indefinitely.
|
||||
PoolWaitTimeout time.Duration
|
||||
|
||||
// LoginNotSupported should be set to true for tokens that do not support logging in.
|
||||
LoginNotSupported bool
|
||||
|
||||
// UseGCMIVFromHSM should be set to true for tokens such as CloudHSM, which ignore the supplied IV for
|
||||
// GCM mode and generate their own. In this case, the token will write the IV used into the CK_GCM_PARAMS.
|
||||
// If UseGCMIVFromHSM is true, we will copy this IV and overwrite the 'nonce' slice passed to Seal and Open. It
|
||||
// is therefore necessary that the nonce is the correct length (12 bytes for CloudHSM).
|
||||
UseGCMIVFromHSM bool
|
||||
|
||||
// GCMIVLength is the length of IVs to use in GCM mode. Refer to NIST SP800-38 for guidance on the length of
|
||||
// RBG-based IVs in GCM mode. When the UseGCMIVFromHSM parameter is true
|
||||
GCMIVLength int
|
||||
|
||||
GCMIVFromHSMControl GCMIVFromHSMConfig
|
||||
}
|
||||
|
||||
type GCMIVFromHSMConfig struct {
|
||||
|
||||
// SupplyIvForHSMGCM_encrypt controls the supply of a non-nil IV for GCM use during C_EncryptInit
|
||||
SupplyIvForHSMGCMEncrypt bool
|
||||
|
||||
// SupplyIvForHSMGCM_decrypt controls the supply of a non-nil IV for GCM use during C_DecryptInit
|
||||
SupplyIvForHSMGCMDecrypt bool
|
||||
}
|
||||
|
||||
// refCount counts the number of contexts using a particular P11 library. It must not be read or modified
|
||||
// without holding refCountMutex.
|
||||
var refCount = map[string]int{}
|
||||
var refCountMutex = sync.Mutex{}
|
||||
|
||||
// Configure creates a new Context based on the supplied PKCS#11 configuration.
|
||||
func Configure(config *Config) (*Context, error) {
|
||||
// Have we been given exactly one way to select a token?
|
||||
var fields []string
|
||||
if config.SlotNumber != nil {
|
||||
fields = append(fields, "slot number")
|
||||
}
|
||||
if config.TokenLabel != "" {
|
||||
fields = append(fields, "token label")
|
||||
}
|
||||
if config.TokenSerial != "" {
|
||||
fields = append(fields, "token serial number")
|
||||
}
|
||||
if len(fields) == 0 {
|
||||
return nil, fmt.Errorf("config must specify exactly one way to select a token: none given")
|
||||
} else if len(fields) > 1 {
|
||||
return nil, fmt.Errorf("config must specify exactly one way to select a token: %v given", strings.Join(fields, ", "))
|
||||
}
|
||||
|
||||
if config.MaxSessions == 0 {
|
||||
config.MaxSessions = DefaultMaxSessions
|
||||
}
|
||||
if config.MaxSessions == 1 {
|
||||
return nil, errors.New("MaxSessions must be larger than 1")
|
||||
}
|
||||
|
||||
if config.GCMIVLength == 0 {
|
||||
config.GCMIVLength = DefaultGCMIVLength
|
||||
}
|
||||
|
||||
instance := &Context{
|
||||
cfg: config,
|
||||
ctx: pkcs11.New(config.Path),
|
||||
}
|
||||
|
||||
if instance.ctx == nil {
|
||||
return nil, errors.New("could not open PKCS#11")
|
||||
}
|
||||
|
||||
// Check how many contexts are currently using this library
|
||||
refCountMutex.Lock()
|
||||
defer refCountMutex.Unlock()
|
||||
numExistingContexts := refCount[config.Path]
|
||||
|
||||
// Only Initialize if we are the first Context using the library
|
||||
if numExistingContexts == 0 {
|
||||
if err := instance.ctx.Initialize(); err != nil {
|
||||
instance.ctx.Destroy()
|
||||
return nil, errors.WithMessage(err, "failed to initialize PKCS#11 library")
|
||||
}
|
||||
}
|
||||
slots, err := instance.ctx.GetSlotList(true)
|
||||
if err != nil {
|
||||
_ = instance.ctx.Finalize()
|
||||
instance.ctx.Destroy()
|
||||
return nil, errors.WithMessage(err, "failed to list PKCS#11 slots")
|
||||
}
|
||||
|
||||
instance.slot, instance.token, err = instance.findToken(slots, config.TokenSerial, config.TokenLabel, config.SlotNumber)
|
||||
if err != nil {
|
||||
_ = instance.ctx.Finalize()
|
||||
instance.ctx.Destroy()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the session pool.
|
||||
maxSessions := instance.cfg.MaxSessions
|
||||
tokenMaxSessions := instance.token.MaxRwSessionCount
|
||||
if tokenMaxSessions != pkcs11.CK_EFFECTIVELY_INFINITE && tokenMaxSessions != pkcs11.CK_UNAVAILABLE_INFORMATION {
|
||||
maxSessions = min(maxSessions, castDown(tokenMaxSessions))
|
||||
}
|
||||
|
||||
// We will use one session to keep state alive, so the pool gets maxSessions - 1
|
||||
instance.pool = pool.NewResourcePool(instance.resourcePoolFactoryFunc, maxSessions-1, maxSessions-1, 0, 0)
|
||||
|
||||
// Create a long-term session and log it in (if supported). This session won't be used by callers, instead it is
|
||||
// used to keep a connection alive to the token to ensure object handles and the log in status remain accessible.
|
||||
instance.persistentSession, err = instance.ctx.OpenSession(instance.slot, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
|
||||
if err != nil {
|
||||
_ = instance.ctx.Finalize()
|
||||
instance.ctx.Destroy()
|
||||
return nil, errors.WithMessagef(err, "failed to create long term session")
|
||||
}
|
||||
|
||||
if !config.LoginNotSupported {
|
||||
// Try to log in our persistent session. This may fail with CKR_USER_ALREADY_LOGGED_IN if another instance
|
||||
// already exists.
|
||||
err = instance.ctx.Login(instance.persistentSession, pkcs11.CKU_USER, instance.cfg.Pin)
|
||||
if err != nil {
|
||||
|
||||
pErr, isP11Error := err.(pkcs11.Error)
|
||||
|
||||
if !isP11Error || pErr != pkcs11.CKR_USER_ALREADY_LOGGED_IN {
|
||||
_ = instance.ctx.Finalize()
|
||||
instance.ctx.Destroy()
|
||||
return nil, errors.WithMessagef(err, "failed to log into long term session")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Increment the reference count
|
||||
refCount[config.Path] = numExistingContexts + 1
|
||||
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if b < a {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// castDown returns orig as a signed integer. If an overflow would have occurred,
|
||||
// the maximum possible value is returned.
|
||||
func castDown(orig uint) int {
|
||||
// From https://stackoverflow.com/a/6878625/474189
|
||||
const maxUint = ^uint(0)
|
||||
const maxInt = int(maxUint >> 1)
|
||||
|
||||
if orig > uint(maxInt) {
|
||||
return maxInt
|
||||
}
|
||||
|
||||
return int(orig)
|
||||
}
|
||||
|
||||
// ConfigureFromFile is a convenience method, which parses the configuration file
|
||||
// and calls Configure. The configuration file should be a JSON representation
|
||||
// of a Config object.
|
||||
func ConfigureFromFile(configLocation string) (*Context, error) {
|
||||
config, err := loadConfigFromFile(configLocation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return Configure(config)
|
||||
}
|
||||
|
||||
// loadConfigFromFile reads a Config struct from a file.
|
||||
func loadConfigFromFile(configLocation string) (*Config, error) {
|
||||
file, err := os.Open(configLocation)
|
||||
if err != nil {
|
||||
return nil, errors.WithMessagef(err, "could not open config file: %s", configLocation)
|
||||
}
|
||||
defer func() {
|
||||
closeErr := file.Close()
|
||||
if err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
}()
|
||||
|
||||
configDecoder := json.NewDecoder(file)
|
||||
config := &Config{}
|
||||
err = configDecoder.Decode(config)
|
||||
return config, errors.WithMessage(err, "could decode config file:")
|
||||
}
|
||||
|
||||
// Close releases resources used by the Context and unloads the PKCS #11 library if there are no other
|
||||
// Contexts using it. Close blocks until existing operations have finished. A closed Context cannot be reused.
|
||||
func (c *Context) Close() error {
|
||||
|
||||
// Take lock on the reference count
|
||||
refCountMutex.Lock()
|
||||
defer refCountMutex.Unlock()
|
||||
|
||||
c.closed.Set(true)
|
||||
|
||||
// Block until all resources returned to pool
|
||||
c.pool.Close()
|
||||
|
||||
// Close our long-term session. We ignore any returned error,
|
||||
// since we plan to kill our collection to the library anyway.
|
||||
_ = c.ctx.CloseSession(c.persistentSession)
|
||||
|
||||
count, found := refCount[c.cfg.Path]
|
||||
if !found || count == 0 {
|
||||
// We have somehow lost track of reference counts, this is very bad
|
||||
panic("invalid reference count for PKCS#11 library")
|
||||
}
|
||||
|
||||
refCount[c.cfg.Path] = count - 1
|
||||
|
||||
// If we were the last Context, finalize the library
|
||||
if count == 1 {
|
||||
err := c.ctx.Finalize()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.ctx.Destroy()
|
||||
return nil
|
||||
}
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
// Copyright 2016, 2017 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 (
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
pkcs11 "github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
// pkcs11PrivateKeyDSA contains a reference to a loaded PKCS#11 DSA private key object.
|
||||
type pkcs11PrivateKeyDSA struct {
|
||||
pkcs11PrivateKey
|
||||
}
|
||||
|
||||
// Export the public key corresponding to a private DSA key.
|
||||
func exportDSAPublicKey(session *pkcs11Session, pubHandle pkcs11.ObjectHandle) (crypto.PublicKey, error) {
|
||||
template := []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PRIME, nil),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SUBPRIME, nil),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_BASE, nil),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_VALUE, nil),
|
||||
}
|
||||
exported, err := session.ctx.GetAttributeValue(session.handle, pubHandle, template)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var p, q, g, y big.Int
|
||||
p.SetBytes(exported[0].Value)
|
||||
q.SetBytes(exported[1].Value)
|
||||
g.SetBytes(exported[2].Value)
|
||||
y.SetBytes(exported[3].Value)
|
||||
result := dsa.PublicKey{
|
||||
Parameters: dsa.Parameters{
|
||||
P: &p,
|
||||
Q: &q,
|
||||
G: &g,
|
||||
},
|
||||
Y: &y,
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func notNilBytes(obj []byte, name string) error {
|
||||
if obj == nil {
|
||||
return errors.Errorf("%s cannot be nil", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateDSAKeyPair creates a DSA key pair on the token. The id parameter is used to
|
||||
// set CKA_ID and must be non-nil.
|
||||
func (c *Context) GenerateDSAKeyPair(id []byte, params *dsa.Parameters) (Signer, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
public, err := NewAttributeSetWithID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Copy the AttributeSet to allow modifications.
|
||||
private := public.Copy()
|
||||
|
||||
return c.GenerateDSAKeyPairWithAttributes(public, private, params)
|
||||
}
|
||||
|
||||
// GenerateDSAKeyPairWithLabel creates a DSA key pair on the token. The id and label parameters are used to
|
||||
// set CKA_ID and CKA_LABEL respectively and must be non-nil.
|
||||
func (c *Context) GenerateDSAKeyPairWithLabel(id, label []byte, params *dsa.Parameters) (Signer, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
public, err := NewAttributeSetWithIDAndLabel(id, label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Copy the AttributeSet to allow modifications.
|
||||
private := public.Copy()
|
||||
|
||||
return c.GenerateDSAKeyPairWithAttributes(public, private, params)
|
||||
}
|
||||
|
||||
// GenerateDSAKeyPairWithAttributes creates a DSA key pair on the token. After this function returns, public and private
|
||||
// will contain the attributes applied to the key pair. If required attributes are missing, they will be set to a
|
||||
// default value.
|
||||
func (c *Context) GenerateDSAKeyPairWithAttributes(public, private AttributeSet, params *dsa.Parameters) (Signer, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
var k Signer
|
||||
err := c.withSession(func(session *pkcs11Session) error {
|
||||
p := params.P.Bytes()
|
||||
q := params.Q.Bytes()
|
||||
g := params.G.Bytes()
|
||||
|
||||
public.AddIfNotPresent([]*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, pkcs11.CKK_DSA),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_VERIFY, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PRIME, p),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SUBPRIME, q),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_BASE, g),
|
||||
})
|
||||
private.AddIfNotPresent([]*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SIGN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SENSITIVE, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, false),
|
||||
})
|
||||
|
||||
mech := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_DSA_KEY_PAIR_GEN, nil)}
|
||||
pubHandle, privHandle, err := session.ctx.GenerateKeyPair(session.handle,
|
||||
mech,
|
||||
public.ToSlice(),
|
||||
private.ToSlice())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pub, err := exportDSAPublicKey(session, pubHandle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k = &pkcs11PrivateKeyDSA{
|
||||
pkcs11PrivateKey: pkcs11PrivateKey{
|
||||
pkcs11Object: pkcs11Object{
|
||||
handle: privHandle,
|
||||
context: c,
|
||||
},
|
||||
pubKeyHandle: pubHandle,
|
||||
pubKey: pub,
|
||||
}}
|
||||
return nil
|
||||
|
||||
})
|
||||
return k, err
|
||||
}
|
||||
|
||||
// Sign signs a message using a DSA key.
|
||||
//
|
||||
// This completes the implemention of crypto.Signer for pkcs11PrivateKeyDSA.
|
||||
//
|
||||
// PKCS#11 expects to pick its own random data for signatures, so the rand argument is ignored.
|
||||
//
|
||||
// The return value is a DER-encoded byteblock.
|
||||
func (signer *pkcs11PrivateKeyDSA) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||
return signer.context.dsaGeneric(signer.handle, pkcs11.CKM_DSA, digest)
|
||||
}
|
||||
+302
@@ -0,0 +1,302 @@
|
||||
// Copyright 2016, 2017 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"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"encoding/asn1"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// errUnsupportedEllipticCurve is returned when an elliptic curve
|
||||
// unsupported by crypto11 is specified. Note that the error behavior
|
||||
// for an elliptic curve unsupported by the underlying PKCS#11
|
||||
// implementation will be different.
|
||||
var errUnsupportedEllipticCurve = errors.New("unsupported elliptic curve")
|
||||
|
||||
// pkcs11PrivateKeyECDSA contains a reference to a loaded PKCS#11 ECDSA private key object.
|
||||
type pkcs11PrivateKeyECDSA struct {
|
||||
pkcs11PrivateKey
|
||||
}
|
||||
|
||||
// Information about an Elliptic Curve
|
||||
type curveInfo struct {
|
||||
// ASN.1 marshaled OID
|
||||
oid []byte
|
||||
|
||||
// Curve definition in Go form
|
||||
curve elliptic.Curve
|
||||
}
|
||||
|
||||
// ASN.1 marshal some value and panic on error
|
||||
func mustMarshal(val interface{}) []byte {
|
||||
if b, err := asn1.Marshal(val); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return b
|
||||
}
|
||||
}
|
||||
|
||||
// Note: some of these are outside what crypto/elliptic currently
|
||||
// knows about. So I'm making a (reasonable) assumption about what
|
||||
// they will be called if they are either added or if someone
|
||||
// specifies them explicitly.
|
||||
//
|
||||
// For public key export, the curve has to be a known one, otherwise
|
||||
// you're stuffed. This is probably better fixed by adding well-known
|
||||
// curves to crypto/elliptic rather than having a private copy here.
|
||||
var wellKnownCurves = map[string]curveInfo{
|
||||
"P-192": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 1}),
|
||||
nil,
|
||||
},
|
||||
"P-224": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 3, 132, 0, 33}),
|
||||
elliptic.P224(),
|
||||
},
|
||||
"P-256": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7}),
|
||||
elliptic.P256(),
|
||||
},
|
||||
"P-384": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 3, 132, 0, 34}),
|
||||
elliptic.P384(),
|
||||
},
|
||||
"P-521": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 3, 132, 0, 35}),
|
||||
elliptic.P521(),
|
||||
},
|
||||
|
||||
"K-163": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 3, 132, 0, 1}),
|
||||
nil,
|
||||
},
|
||||
"K-233": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 3, 132, 0, 26}),
|
||||
nil,
|
||||
},
|
||||
"K-283": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 3, 132, 0, 16}),
|
||||
nil,
|
||||
},
|
||||
"K-409": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 3, 132, 0, 36}),
|
||||
nil,
|
||||
},
|
||||
"K-571": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 3, 132, 0, 38}),
|
||||
nil,
|
||||
},
|
||||
|
||||
"B-163": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 3, 132, 0, 15}),
|
||||
nil,
|
||||
},
|
||||
"B-233": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 3, 132, 0, 27}),
|
||||
nil,
|
||||
},
|
||||
"B-283": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 3, 132, 0, 17}),
|
||||
nil,
|
||||
},
|
||||
"B-409": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 3, 132, 0, 37}),
|
||||
nil,
|
||||
},
|
||||
"B-571": {
|
||||
mustMarshal(asn1.ObjectIdentifier{1, 3, 132, 0, 39}),
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
func marshalEcParams(c elliptic.Curve) ([]byte, error) {
|
||||
if ci, ok := wellKnownCurves[c.Params().Name]; ok {
|
||||
return ci.oid, nil
|
||||
}
|
||||
// TODO use ANSI X9.62 ECParameters representation instead
|
||||
return nil, errUnsupportedEllipticCurve
|
||||
}
|
||||
|
||||
func unmarshalEcParams(b []byte) (elliptic.Curve, error) {
|
||||
// See if it's a well-known curve
|
||||
for _, ci := range wellKnownCurves {
|
||||
if bytes.Equal(b, ci.oid) {
|
||||
if ci.curve != nil {
|
||||
return ci.curve, nil
|
||||
}
|
||||
return nil, errUnsupportedEllipticCurve
|
||||
}
|
||||
}
|
||||
// TODO try ANSI X9.62 ECParameters representation
|
||||
return nil, errUnsupportedEllipticCurve
|
||||
}
|
||||
|
||||
func unmarshalEcPoint(b []byte, c elliptic.Curve) (*big.Int, *big.Int, error) {
|
||||
var pointBytes []byte
|
||||
extra, err := asn1.Unmarshal(b, &pointBytes)
|
||||
if err != nil {
|
||||
return nil, nil, errors.WithMessage(err, "elliptic curve point is invalid ASN.1")
|
||||
}
|
||||
|
||||
if len(extra) > 0 {
|
||||
// We weren't expecting extra data
|
||||
return nil, nil, errors.New("unexpected data found when parsing elliptic curve point")
|
||||
}
|
||||
|
||||
x, y := elliptic.Unmarshal(c, pointBytes)
|
||||
if x == nil || y == nil {
|
||||
return nil, nil, errors.New("failed to parse elliptic curve point")
|
||||
}
|
||||
return x, y, nil
|
||||
}
|
||||
|
||||
// Export the public key corresponding to a private ECDSA key.
|
||||
func exportECDSAPublicKey(session *pkcs11Session, pubHandle pkcs11.ObjectHandle) (crypto.PublicKey, error) {
|
||||
var err error
|
||||
var attributes []*pkcs11.Attribute
|
||||
var pub ecdsa.PublicKey
|
||||
template := []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_ECDSA_PARAMS, nil),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EC_POINT, nil),
|
||||
}
|
||||
if attributes, err = session.ctx.GetAttributeValue(session.handle, pubHandle, template); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pub.Curve, err = unmarshalEcParams(attributes[0].Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pub.X, pub.Y, err = unmarshalEcPoint(attributes[1].Value, pub.Curve); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pub, nil
|
||||
}
|
||||
|
||||
// GenerateECDSAKeyPair creates a ECDSA key pair on the token using curve c. The id parameter is used to
|
||||
// set CKA_ID and must be non-nil. Only a limited set of named elliptic curves are supported. The
|
||||
// underlying PKCS#11 implementation may impose further restrictions.
|
||||
func (c *Context) GenerateECDSAKeyPair(id []byte, curve elliptic.Curve) (Signer, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
public, err := NewAttributeSetWithID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Copy the AttributeSet to allow modifications.
|
||||
private := public.Copy()
|
||||
|
||||
return c.GenerateECDSAKeyPairWithAttributes(public, private, curve)
|
||||
}
|
||||
|
||||
// GenerateECDSAKeyPairWithLabel creates a ECDSA key pair on the token using curve c. The id and label parameters are used to
|
||||
// set CKA_ID and CKA_LABEL respectively and must be non-nil. Only a limited set of named elliptic curves are supported. The
|
||||
// underlying PKCS#11 implementation may impose further restrictions.
|
||||
func (c *Context) GenerateECDSAKeyPairWithLabel(id, label []byte, curve elliptic.Curve) (Signer, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
public, err := NewAttributeSetWithIDAndLabel(id, label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Copy the AttributeSet to allow modifications.
|
||||
private := public.Copy()
|
||||
|
||||
return c.GenerateECDSAKeyPairWithAttributes(public, private, curve)
|
||||
}
|
||||
|
||||
// GenerateECDSAKeyPairWithAttributes generates an ECDSA key pair on the token. After this function returns, public and
|
||||
// private will contain the attributes applied to the key pair. If required attributes are missing, they will be set to
|
||||
// a default value.
|
||||
func (c *Context) GenerateECDSAKeyPairWithAttributes(public, private AttributeSet, curve elliptic.Curve) (Signer, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
var k Signer
|
||||
err := c.withSession(func(session *pkcs11Session) error {
|
||||
|
||||
parameters, err := marshalEcParams(curve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
public.AddIfNotPresent([]*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, pkcs11.CKK_ECDSA),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_VERIFY, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_ECDSA_PARAMS, parameters),
|
||||
})
|
||||
private.AddIfNotPresent([]*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SIGN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SENSITIVE, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, false),
|
||||
})
|
||||
|
||||
mech := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_ECDSA_KEY_PAIR_GEN, nil)}
|
||||
pubHandle, privHandle, err := session.ctx.GenerateKeyPair(session.handle,
|
||||
mech,
|
||||
public.ToSlice(),
|
||||
private.ToSlice())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pub, err := exportECDSAPublicKey(session, pubHandle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k = &pkcs11PrivateKeyECDSA{
|
||||
pkcs11PrivateKey: pkcs11PrivateKey{
|
||||
pkcs11Object: pkcs11Object{
|
||||
handle: privHandle,
|
||||
context: c,
|
||||
},
|
||||
pubKeyHandle: pubHandle,
|
||||
pubKey: pub,
|
||||
}}
|
||||
return nil
|
||||
})
|
||||
return k, err
|
||||
}
|
||||
|
||||
// Sign signs a message using an ECDSA key.
|
||||
//
|
||||
// This completes the implemention of crypto.Signer for pkcs11PrivateKeyECDSA.
|
||||
//
|
||||
// PKCS#11 expects to pick its own random data where necessary for signatures, so the rand argument is ignored.
|
||||
//
|
||||
// The return value is a DER-encoded byteblock.
|
||||
func (signer *pkcs11PrivateKeyECDSA) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
|
||||
return signer.context.dsaGeneric(signer.handle, pkcs11.CKM_ECDSA, digest)
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
module github.com/ThalesIgnite/crypto11
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/thales-e-security/pool v0.0.2
|
||||
)
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f h1:eVB9ELsoq5ouItQBr5Tj334bhPJG/MX+m7rTchmzVUQ=
|
||||
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg=
|
||||
github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
"hash"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
const (
|
||||
// NFCK_VENDOR_NCIPHER distinguishes nShield vendor-specific mechanisms.
|
||||
NFCK_VENDOR_NCIPHER = 0xde436972
|
||||
|
||||
// CKM_NCIPHER is the base for nShield vendor-specific mechanisms.
|
||||
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
|
||||
|
||||
// Signing key
|
||||
key *SecretKey
|
||||
|
||||
// Hash size
|
||||
size int
|
||||
|
||||
// Block size
|
||||
blockSize int
|
||||
|
||||
// PKCS#11 mechanism information
|
||||
mechDescription []*pkcs11.Mechanism
|
||||
|
||||
// Cleanup function
|
||||
cleanup func()
|
||||
|
||||
// Count of updates
|
||||
updates uint64
|
||||
|
||||
// Result, or nil if we don't have the answer yet
|
||||
result []byte
|
||||
}
|
||||
|
||||
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},
|
||||
}
|
||||
|
||||
// errHmacClosed is called if an HMAC is updated after it has finished.
|
||||
var errHmacClosed = errors.New("already called Sum()")
|
||||
|
||||
// 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.
|
||||
// After Sum() is called no new data may be added.
|
||||
func (key *SecretKey) NewHMAC(mech int, length int) (hash.Hash, error) {
|
||||
hi := hmacImplementation{
|
||||
key: key,
|
||||
}
|
||||
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
|
||||
}
|
||||
hi.mechDescription = []*pkcs11.Mechanism{pkcs11.NewMechanism(uint(mech), params)}
|
||||
if err := hi.initialize(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &hi, nil
|
||||
}
|
||||
|
||||
func (hi *hmacImplementation) initialize() (err error) {
|
||||
session, err := hi.key.context.getSession()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hi.session = session
|
||||
hi.cleanup = func() {
|
||||
hi.key.context.pool.Put(session)
|
||||
hi.session = nil
|
||||
}
|
||||
if err = hi.session.ctx.SignInit(hi.session.handle, hi.mechDescription, hi.key.handle); err != nil {
|
||||
hi.cleanup()
|
||||
return
|
||||
}
|
||||
hi.updates = 0
|
||||
hi.result = nil
|
||||
return
|
||||
}
|
||||
|
||||
func (hi *hmacImplementation) Write(p []byte) (n int, err error) {
|
||||
if hi.result != nil {
|
||||
if len(p) > 0 {
|
||||
err = errHmacClosed
|
||||
}
|
||||
return
|
||||
}
|
||||
if err = hi.session.ctx.SignUpdate(hi.session.handle, p); err != nil {
|
||||
return
|
||||
}
|
||||
hi.updates++
|
||||
n = len(p)
|
||||
return
|
||||
}
|
||||
|
||||
func (hi *hmacImplementation) Sum(b []byte) []byte {
|
||||
if hi.result == nil {
|
||||
var err error
|
||||
if hi.updates == 0 {
|
||||
// http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/os/pkcs11-base-v2.40-os.html#_Toc322855304
|
||||
// We must ensure that C_SignUpdate is called _at least once_.
|
||||
if err = hi.session.ctx.SignUpdate(hi.session.handle, []byte{}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
hi.result, err = hi.session.ctx.SignFinal(hi.session.handle)
|
||||
hi.cleanup()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return append(b, hi.result...)
|
||||
}
|
||||
|
||||
func (hi *hmacImplementation) Reset() {
|
||||
hi.Sum(nil) // Clean up
|
||||
|
||||
// Assign the error to "_" to indicate we are knowingly ignoring this. It may have been
|
||||
// sensible to panic at this stage, but we cannot add a panic without breaking backwards
|
||||
// compatibility.
|
||||
_ = hi.initialize()
|
||||
}
|
||||
|
||||
func (hi *hmacImplementation) Size() int {
|
||||
return hi.size
|
||||
}
|
||||
|
||||
func (hi *hmacImplementation) BlockSize() int {
|
||||
return hi.blockSize
|
||||
}
|
||||
+613
@@ -0,0 +1,613 @@
|
||||
// Copyright 2016, 2017 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 (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"github.com/miekg/pkcs11"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const maxHandlePerFind = 20
|
||||
|
||||
// errNoCkaId is returned if a private key is found which has no CKA_ID attribute
|
||||
var errNoCkaId = errors.New("private key has no CKA_ID")
|
||||
|
||||
// errNoPublicHalf is returned if a public half cannot be found to match a given private key
|
||||
var errNoPublicHalf = errors.New("could not find public key to match private key")
|
||||
|
||||
func findKeysWithAttributes(session *pkcs11Session, template []*pkcs11.Attribute) (handles []pkcs11.ObjectHandle, err error) {
|
||||
if err = session.ctx.FindObjectsInit(session.handle, template); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
finalErr := session.ctx.FindObjectsFinal(session.handle)
|
||||
if err == nil {
|
||||
err = finalErr
|
||||
}
|
||||
}()
|
||||
|
||||
newhandles, _, err := session.ctx.FindObjects(session.handle, maxHandlePerFind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for len(newhandles) > 0 {
|
||||
handles = append(handles, newhandles...)
|
||||
|
||||
newhandles, _, err = session.ctx.FindObjects(session.handle, maxHandlePerFind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return handles, nil
|
||||
}
|
||||
|
||||
// Find key objects. For asymmetric keys this only finds one half so
|
||||
// callers will call it twice. Returns nil if the key does not exist on the token.
|
||||
func findKeys(session *pkcs11Session, id []byte, label []byte, keyclass *uint, keytype *uint) (handles []pkcs11.ObjectHandle, err error) {
|
||||
var template []*pkcs11.Attribute
|
||||
|
||||
if keyclass != nil {
|
||||
template = append(template, pkcs11.NewAttribute(pkcs11.CKA_CLASS, *keyclass))
|
||||
}
|
||||
if keytype != nil {
|
||||
template = append(template, pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, *keytype))
|
||||
}
|
||||
if id != nil {
|
||||
template = append(template, pkcs11.NewAttribute(pkcs11.CKA_ID, id))
|
||||
}
|
||||
if label != nil {
|
||||
template = append(template, pkcs11.NewAttribute(pkcs11.CKA_LABEL, label))
|
||||
}
|
||||
|
||||
if handles, err = findKeysWithAttributes(session, template); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return handles, nil
|
||||
}
|
||||
|
||||
// Find a key object. For asymmetric keys this only finds one half so
|
||||
// callers will call it twice. Returns nil if the key does not exist on the token.
|
||||
func findKey(session *pkcs11Session, id []byte, label []byte, keyclass *uint, keytype *uint) (obj *pkcs11.ObjectHandle, err error) {
|
||||
handles, err := findKeys(session, id, label, keyclass, keytype)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(handles) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return &handles[0], nil
|
||||
}
|
||||
|
||||
// Takes a handles to the private half of a keypair, locates the public half with the matching CKA_ID and CKA_LABEL
|
||||
// values and constructs a keypair object from them both.
|
||||
func (c *Context) makeKeyPair(session *pkcs11Session, privHandle *pkcs11.ObjectHandle) (signer Signer, certificate *x509.Certificate, err error) {
|
||||
attributes := []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_ID, nil),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_LABEL, nil),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, 0),
|
||||
}
|
||||
if attributes, err = session.ctx.GetAttributeValue(session.handle, *privHandle, attributes); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
id := attributes[0].Value
|
||||
label := attributes[1].Value
|
||||
keyType := bytesToUlong(attributes[2].Value)
|
||||
|
||||
// Ensure the private key actually has a non-empty CKA_ID to match on
|
||||
if id == nil || len(id) == 0 {
|
||||
return nil, nil, errNoCkaId
|
||||
}
|
||||
|
||||
var pubHandle *pkcs11.ObjectHandle
|
||||
|
||||
// Find the public half which has a matching CKA_ID
|
||||
pubHandle, err = findKey(session, id, label, uintPtr(pkcs11.CKO_PUBLIC_KEY), &keyType)
|
||||
if err != nil {
|
||||
p11Err, ok := err.(pkcs11.Error)
|
||||
|
||||
if len(label) == 0 && ok && p11Err == pkcs11.CKR_TEMPLATE_INCONSISTENT {
|
||||
// This probably means we are using a token that doesn't like us passing empty attributes in a template.
|
||||
// For instance CloudHSM cannot search for a key with CKA_LABEL="". So if the private key doesn't have a
|
||||
// label, we need to pass nil into findKeys, then match against the first key without a label.
|
||||
|
||||
pubHandles, err := findKeys(session, id, nil, uintPtr(pkcs11.CKO_PUBLIC_KEY), &keyType)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for _, handle := range pubHandles {
|
||||
template := []*pkcs11.Attribute{pkcs11.NewAttribute(pkcs11.CKA_LABEL, nil)}
|
||||
template, err = session.ctx.GetAttributeValue(session.handle, handle, template)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(template[0].Value) == 0 {
|
||||
pubHandle = &handle
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if pubHandle == nil {
|
||||
// Try harder to find a matching public key, based on CKA_ID alone
|
||||
pubHandle, err = findKey(session, id, nil, uintPtr(pkcs11.CKO_PUBLIC_KEY), &keyType)
|
||||
}
|
||||
|
||||
resultPkcs11PrivateKey := pkcs11PrivateKey{
|
||||
pkcs11Object: pkcs11Object{
|
||||
handle: *privHandle,
|
||||
context: c,
|
||||
},
|
||||
}
|
||||
|
||||
var pub crypto.PublicKey
|
||||
certificate, _ = findCertificate(session, id, nil, nil)
|
||||
if certificate != nil && pubHandle == nil {
|
||||
pub = certificate.PublicKey
|
||||
}
|
||||
|
||||
if pub == nil && pubHandle == nil {
|
||||
// We can't return a Signer if we don't have private and public key. Treat it as an error.
|
||||
return nil, nil, errNoPublicHalf
|
||||
}
|
||||
|
||||
switch keyType {
|
||||
case pkcs11.CKK_DSA:
|
||||
result := &pkcs11PrivateKeyDSA{pkcs11PrivateKey: resultPkcs11PrivateKey}
|
||||
if pubHandle != nil {
|
||||
if pub, err = exportDSAPublicKey(session, *pubHandle); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
result.pkcs11PrivateKey.pubKeyHandle = *pubHandle
|
||||
}
|
||||
|
||||
result.pkcs11PrivateKey.pubKey = pub
|
||||
return result, certificate, nil
|
||||
|
||||
case pkcs11.CKK_RSA:
|
||||
result := &pkcs11PrivateKeyRSA{pkcs11PrivateKey: resultPkcs11PrivateKey}
|
||||
if pubHandle != nil {
|
||||
if pub, err = exportRSAPublicKey(session, *pubHandle); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
result.pkcs11PrivateKey.pubKeyHandle = *pubHandle
|
||||
}
|
||||
|
||||
result.pkcs11PrivateKey.pubKey = pub
|
||||
return result, certificate, nil
|
||||
|
||||
case pkcs11.CKK_ECDSA:
|
||||
result := &pkcs11PrivateKeyECDSA{pkcs11PrivateKey: resultPkcs11PrivateKey}
|
||||
if pubHandle != nil {
|
||||
if pub, err = exportECDSAPublicKey(session, *pubHandle); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
result.pkcs11PrivateKey.pubKeyHandle = *pubHandle
|
||||
}
|
||||
|
||||
result.pkcs11PrivateKey.pubKey = pub
|
||||
return result, certificate, nil
|
||||
|
||||
default:
|
||||
return nil, nil, errors.Errorf("unsupported key type: %X", keyType)
|
||||
}
|
||||
}
|
||||
|
||||
// FindKeyPair retrieves a previously created asymmetric key pair, or nil if it cannot be found.
|
||||
//
|
||||
// At least one of id and label must be specified.
|
||||
// Only private keys that have a non-empty CKA_ID will be found, as this is required to locate the matching public key.
|
||||
// If the private key is found, but the public key with a corresponding CKA_ID is not, the key is not returned
|
||||
// because we cannot implement crypto.Signer without the public key.
|
||||
func (c *Context) FindKeyPair(id []byte, label []byte) (Signer, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
result, err := c.FindKeyPairs(id, label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(result) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return result[0], nil
|
||||
}
|
||||
|
||||
// FindKeyPairs retrieves all matching asymmetric key pairs, or a nil slice if none can be found.
|
||||
//
|
||||
// At least one of id and label must be specified.
|
||||
// Only private keys that have a non-empty CKA_ID will be found, as this is required to locate the matching public key.
|
||||
// If the private key is found, but the public key with a corresponding CKA_ID is not, the key is not returned
|
||||
// because we cannot implement crypto.Signer without the public key.
|
||||
func (c *Context) FindKeyPairs(id []byte, label []byte) (signer []Signer, err error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
if id == nil && label == nil {
|
||||
return nil, errors.New("id and label cannot both be nil")
|
||||
}
|
||||
|
||||
attributes := NewAttributeSet()
|
||||
|
||||
if id != nil {
|
||||
err = attributes.Set(CkaId, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if label != nil {
|
||||
err = attributes.Set(CkaLabel, label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return c.FindKeyPairsWithAttributes(attributes)
|
||||
}
|
||||
|
||||
// FindKeyPairWithAttributes retrieves a previously created asymmetric key pair, or nil if it cannot be found.
|
||||
// The given attributes are matched against the private half only. Then the public half with a matching CKA_ID
|
||||
// and CKA_LABEL values is found.
|
||||
//
|
||||
// Only private keys that have a non-empty CKA_ID will be found, as this is required to locate the matching public key.
|
||||
// If the private key is found, but the public key with a corresponding CKA_ID is not, the key is not returned
|
||||
// because we cannot implement crypto.Signer without the public key.
|
||||
func (c *Context) FindKeyPairWithAttributes(attributes AttributeSet) (Signer, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
result, err := c.FindKeyPairsWithAttributes(attributes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(result) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return result[0], nil
|
||||
}
|
||||
|
||||
// FindKeyPairsWithAttributes retrieves previously created asymmetric key pairs, or nil if none can be found.
|
||||
// The given attributes are matched against the private half only. Then the public half with a matching CKA_ID
|
||||
// and CKA_LABEL values is found.
|
||||
//
|
||||
// Only private keys that have a non-empty CKA_ID will be found, as this is required to locate the matching public key.
|
||||
// If the private key is found, but the public key with a corresponding CKA_ID is not, the key is not returned
|
||||
// because we cannot implement crypto.Signer without the public key.
|
||||
func (c *Context) FindKeyPairsWithAttributes(attributes AttributeSet) (signer []Signer, err error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
var keys []Signer
|
||||
|
||||
if _, ok := attributes[CkaClass]; ok {
|
||||
return nil, errors.Errorf("keypair attribute set must not contain CkaClass")
|
||||
}
|
||||
|
||||
err = c.withSession(func(session *pkcs11Session) error {
|
||||
// Add the private key class to the template to find the private half
|
||||
privAttributes := attributes.Copy()
|
||||
err = privAttributes.Set(CkaClass, pkcs11.CKO_PRIVATE_KEY)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privHandles, err := findKeysWithAttributes(session, privAttributes.ToSlice())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, privHandle := range privHandles {
|
||||
k, _, err := c.makeKeyPair(session, &privHandle)
|
||||
|
||||
if err == errNoCkaId || err == errNoPublicHalf {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// FindAllKeyPairs retrieves all existing asymmetric key pairs, or a nil slice if none can be found.
|
||||
//
|
||||
// If a private key is found, but the corresponding public key is not, the key is not returned because we cannot
|
||||
// implement crypto.Signer without the public key.
|
||||
func (c *Context) FindAllKeyPairs() ([]Signer, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
return c.FindKeyPairsWithAttributes(NewAttributeSet())
|
||||
}
|
||||
|
||||
// Public returns the public half of a private key.
|
||||
//
|
||||
// This partially implements the go.crypto.Signer and go.crypto.Decrypter interfaces for
|
||||
// pkcs11PrivateKey. (The remains of the implementation is in the
|
||||
// key-specific types.)
|
||||
func (k pkcs11PrivateKey) Public() crypto.PublicKey {
|
||||
return k.pubKey
|
||||
}
|
||||
|
||||
// FindKey retrieves a previously created symmetric key, or nil if it cannot be found.
|
||||
//
|
||||
// Either (but not both) of id and label may be nil, in which case they are ignored.
|
||||
func (c *Context) FindKey(id []byte, label []byte) (*SecretKey, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
result, err := c.FindKeys(id, label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(result) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return result[0], nil
|
||||
}
|
||||
|
||||
// FindKeys retrieves all matching symmetric keys, or a nil slice if none can be found.
|
||||
//
|
||||
// At least one of id and label must be specified.
|
||||
func (c *Context) FindKeys(id []byte, label []byte) (key []*SecretKey, err error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
if id == nil && label == nil {
|
||||
return nil, errors.New("id and label cannot both be nil")
|
||||
}
|
||||
|
||||
attributes := NewAttributeSet()
|
||||
|
||||
if id != nil {
|
||||
err = attributes.Set(CkaId, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if label != nil {
|
||||
err = attributes.Set(CkaLabel, label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return c.FindKeysWithAttributes(attributes)
|
||||
}
|
||||
|
||||
// FindKeyWithAttributes retrieves a previously created symmetric key, or nil if it cannot be found.
|
||||
func (c *Context) FindKeyWithAttributes(attributes AttributeSet) (*SecretKey, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
result, err := c.FindKeysWithAttributes(attributes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(result) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return result[0], nil
|
||||
}
|
||||
|
||||
// FindKeysWithAttributes retrieves previously created symmetric keys, or a nil slice if none can be found.
|
||||
func (c *Context) FindKeysWithAttributes(attributes AttributeSet) ([]*SecretKey, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
var keys []*SecretKey
|
||||
|
||||
if _, ok := attributes[CkaClass]; ok {
|
||||
return nil, errors.Errorf("key attribute set must not contain CkaClass")
|
||||
}
|
||||
|
||||
err := c.withSession(func(session *pkcs11Session) error {
|
||||
// Add the private key class to the template to find the private half
|
||||
privAttributes := attributes.Copy()
|
||||
err := privAttributes.Set(CkaClass, pkcs11.CKO_SECRET_KEY)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privHandles, err := findKeysWithAttributes(session, privAttributes.ToSlice())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, privHandle := range privHandles {
|
||||
attributes := []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, 0),
|
||||
}
|
||||
if attributes, err = session.ctx.GetAttributeValue(session.handle, privHandle, attributes); err != nil {
|
||||
return err
|
||||
}
|
||||
keyType := bytesToUlong(attributes[0].Value)
|
||||
|
||||
if cipher, ok := Ciphers[int(keyType)]; ok {
|
||||
k := &SecretKey{pkcs11Object{privHandle, c}, cipher}
|
||||
keys = append(keys, k)
|
||||
} else {
|
||||
return errors.Errorf("unsupported key type: %X", keyType)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
// FindAllKeyPairs retrieves all existing symmetric keys, or a nil slice if none can be found.
|
||||
func (c *Context) FindAllKeys() ([]*SecretKey, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
return c.FindKeysWithAttributes(NewAttributeSet())
|
||||
}
|
||||
|
||||
func uintPtr(i uint) *uint { return &i }
|
||||
|
||||
func (c *Context) getAttributes(handle pkcs11.ObjectHandle, attributes []AttributeType) (a AttributeSet, err error) {
|
||||
values := NewAttributeSet()
|
||||
|
||||
err = c.withSession(func(session *pkcs11Session) error {
|
||||
var attrs []*pkcs11.Attribute
|
||||
for _, a := range attributes {
|
||||
attrs = append(attrs, pkcs11.NewAttribute(a, nil))
|
||||
}
|
||||
|
||||
p11values, err := session.ctx.GetAttributeValue(session.handle, handle, attrs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
values.AddIfNotPresent(p11values)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return values, err
|
||||
}
|
||||
|
||||
// GetAttributes gets the values of the specified attributes on the given key or keypair.
|
||||
// If the key is asymmetric, then the attributes are retrieved from the private half.
|
||||
//
|
||||
// If the object is not a crypto11 key or keypair then an error is returned.
|
||||
func (c *Context) GetAttributes(key interface{}, attributes []AttributeType) (a AttributeSet, err error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
var handle pkcs11.ObjectHandle
|
||||
|
||||
switch k := (key).(type) {
|
||||
case *pkcs11PrivateKeyDSA:
|
||||
handle = k.handle
|
||||
case *pkcs11PrivateKeyRSA:
|
||||
handle = k.handle
|
||||
case *pkcs11PrivateKeyECDSA:
|
||||
handle = k.handle
|
||||
case *SecretKey:
|
||||
handle = k.handle
|
||||
default:
|
||||
return nil, errors.Errorf("not a PKCS#11 key")
|
||||
}
|
||||
|
||||
return c.getAttributes(handle, attributes)
|
||||
}
|
||||
|
||||
// GetAttribute gets the value of the specified attribute on the given key or keypair.
|
||||
// If the key is asymmetric, then the attribute is retrieved from the private half.
|
||||
//
|
||||
// If the object is not a crypto11 key or keypair then an error is returned.
|
||||
func (c *Context) GetAttribute(key interface{}, attribute AttributeType) (a *Attribute, err error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
set, err := c.GetAttributes(key, []AttributeType{attribute})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return set[attribute], nil
|
||||
}
|
||||
|
||||
// GetPubAttributes gets the values of the specified attributes on the public half of the given keypair.
|
||||
//
|
||||
// If the object is not a crypto11 keypair then an error is returned.
|
||||
func (c *Context) GetPubAttributes(key interface{}, attributes []AttributeType) (a AttributeSet, err error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
var handle pkcs11.ObjectHandle
|
||||
|
||||
switch k := (key).(type) {
|
||||
case *pkcs11PrivateKeyDSA:
|
||||
handle = k.pubKeyHandle
|
||||
case *pkcs11PrivateKeyRSA:
|
||||
handle = k.pubKeyHandle
|
||||
case *pkcs11PrivateKeyECDSA:
|
||||
handle = k.pubKeyHandle
|
||||
default:
|
||||
return nil, errors.Errorf("not an asymmetric PKCS#11 key")
|
||||
}
|
||||
|
||||
return c.getAttributes(handle, attributes)
|
||||
}
|
||||
|
||||
// GetPubAttribute gets the value of the specified attribute on the public half of the given key.
|
||||
//
|
||||
// If the object is not a crypto11 keypair then an error is returned.
|
||||
func (c *Context) GetPubAttribute(key interface{}, attribute AttributeType) (a *Attribute, err error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
set, err := c.GetPubAttributes(key, []AttributeType{attribute})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return set[attribute], nil
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// Copyright 2016, 2017 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 (
|
||||
"io"
|
||||
)
|
||||
|
||||
// NewRandomReader returns a reader for the random number generator on the token.
|
||||
func (c *Context) NewRandomReader() (io.Reader, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
return pkcs11RandReader{c}, nil
|
||||
}
|
||||
|
||||
// pkcs11RandReader is a random number reader that uses PKCS#11.
|
||||
type pkcs11RandReader struct {
|
||||
context *Context
|
||||
}
|
||||
|
||||
// This implements the Reader interface for pkcs11RandReader.
|
||||
func (r pkcs11RandReader) Read(data []byte) (n int, err error) {
|
||||
var result []byte
|
||||
|
||||
if err = r.context.withSession(func(session *pkcs11Session) error {
|
||||
result, err = r.context.ctx.GenerateRandom(session.handle, len(data))
|
||||
return err
|
||||
}); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(data, result)
|
||||
return len(result), err
|
||||
}
|
||||
+325
@@ -0,0 +1,325 @@
|
||||
// Copyright 2016, 2017 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 (
|
||||
"crypto"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
// errMalformedRSAPublicKey is returned when an RSA public key is not in a suitable form.
|
||||
//
|
||||
// Currently this means that the public exponent is either bigger than
|
||||
// 32 bits, or less than 2.
|
||||
var errMalformedRSAPublicKey = errors.New("malformed RSA public key")
|
||||
|
||||
// errUnsupportedRSAOptions is returned when an unsupported RSA option is requested.
|
||||
//
|
||||
// Currently this means a nontrivial SessionKeyLen when decrypting; or
|
||||
// an unsupported hash function; or crypto.rsa.PSSSaltLengthAuto was
|
||||
// requested.
|
||||
var errUnsupportedRSAOptions = errors.New("unsupported RSA option value")
|
||||
|
||||
// pkcs11PrivateKeyRSA contains a reference to a loaded PKCS#11 RSA private key object.
|
||||
type pkcs11PrivateKeyRSA struct {
|
||||
pkcs11PrivateKey
|
||||
}
|
||||
|
||||
// Export the public key corresponding to a private RSA key.
|
||||
func exportRSAPublicKey(session *pkcs11Session, pubHandle pkcs11.ObjectHandle) (crypto.PublicKey, error) {
|
||||
template := []*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_MODULUS, nil),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, nil),
|
||||
}
|
||||
exported, err := session.ctx.GetAttributeValue(session.handle, pubHandle, template)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var modulus = new(big.Int)
|
||||
modulus.SetBytes(exported[0].Value)
|
||||
var bigExponent = new(big.Int)
|
||||
bigExponent.SetBytes(exported[1].Value)
|
||||
if bigExponent.BitLen() > 32 {
|
||||
return nil, errMalformedRSAPublicKey
|
||||
}
|
||||
if bigExponent.Sign() < 1 {
|
||||
return nil, errMalformedRSAPublicKey
|
||||
}
|
||||
exponent := int(bigExponent.Uint64())
|
||||
result := rsa.PublicKey{
|
||||
N: modulus,
|
||||
E: exponent,
|
||||
}
|
||||
if result.E < 2 {
|
||||
return nil, errMalformedRSAPublicKey
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// GenerateRSAKeyPair creates an RSA key pair on the token. The id parameter is used to
|
||||
// set CKA_ID and must be non-nil. RSA private keys are generated with both sign and decrypt
|
||||
// permissions, and a public exponent of 65537.
|
||||
func (c *Context) GenerateRSAKeyPair(id []byte, bits int) (SignerDecrypter, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
public, err := NewAttributeSetWithID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Copy the AttributeSet to allow modifications.
|
||||
private := public.Copy()
|
||||
|
||||
return c.GenerateRSAKeyPairWithAttributes(public, private, bits)
|
||||
}
|
||||
|
||||
// GenerateRSAKeyPairWithLabel creates an RSA key pair on the token. The id and label parameters are used to
|
||||
// set CKA_ID and CKA_LABEL respectively and must be non-nil. RSA private keys are generated with both sign and decrypt
|
||||
// permissions, and a public exponent of 65537.
|
||||
func (c *Context) GenerateRSAKeyPairWithLabel(id, label []byte, bits int) (SignerDecrypter, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
public, err := NewAttributeSetWithIDAndLabel(id, label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Copy the AttributeSet to allow modifications.
|
||||
private := public.Copy()
|
||||
|
||||
return c.GenerateRSAKeyPairWithAttributes(public, private, bits)
|
||||
}
|
||||
|
||||
// GenerateRSAKeyPairWithAttributes generates an RSA key pair on the token. After this function returns, public and
|
||||
// private will contain the attributes applied to the key pair. If required attributes are missing, they will be set to
|
||||
// a default value.
|
||||
func (c *Context) GenerateRSAKeyPairWithAttributes(public, private AttributeSet, bits int) (SignerDecrypter, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
var k SignerDecrypter
|
||||
|
||||
err := c.withSession(func(session *pkcs11Session) error {
|
||||
|
||||
public.AddIfNotPresent([]*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_PUBLIC_KEY),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_KEY_TYPE, pkcs11.CKK_RSA),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_VERIFY, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_ENCRYPT, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_PUBLIC_EXPONENT, []byte{1, 0, 1}),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_MODULUS_BITS, bits),
|
||||
})
|
||||
private.AddIfNotPresent([]*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SIGN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_DECRYPT, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SENSITIVE, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, false),
|
||||
})
|
||||
|
||||
mech := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_KEY_PAIR_GEN, nil)}
|
||||
pubHandle, privHandle, err := session.ctx.GenerateKeyPair(session.handle,
|
||||
mech,
|
||||
public.ToSlice(),
|
||||
private.ToSlice())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pub, err := exportRSAPublicKey(session, pubHandle)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k = &pkcs11PrivateKeyRSA{
|
||||
pkcs11PrivateKey: pkcs11PrivateKey{
|
||||
pkcs11Object: pkcs11Object{
|
||||
handle: privHandle,
|
||||
context: c,
|
||||
},
|
||||
pubKeyHandle: pubHandle,
|
||||
pubKey: pub,
|
||||
}}
|
||||
return nil
|
||||
})
|
||||
return k, err
|
||||
}
|
||||
|
||||
// Decrypt decrypts a message using a RSA key.
|
||||
//
|
||||
// This completes the implemention of crypto.Decrypter for pkcs11PrivateKeyRSA.
|
||||
//
|
||||
// Note that the SessionKeyLen option (for PKCS#1v1.5 decryption) is not supported.
|
||||
//
|
||||
// The underlying PKCS#11 implementation may impose further restrictions.
|
||||
func (priv *pkcs11PrivateKeyRSA) Decrypt(rand io.Reader, ciphertext []byte, options crypto.DecrypterOpts) (plaintext []byte, err error) {
|
||||
err = priv.context.withSession(func(session *pkcs11Session) error {
|
||||
if options == nil {
|
||||
plaintext, err = decryptPKCS1v15(session, priv, ciphertext, 0)
|
||||
} else {
|
||||
switch o := options.(type) {
|
||||
case *rsa.PKCS1v15DecryptOptions:
|
||||
plaintext, err = decryptPKCS1v15(session, priv, ciphertext, o.SessionKeyLen)
|
||||
case *rsa.OAEPOptions:
|
||||
plaintext, err = decryptOAEP(session, priv, ciphertext, o.Hash, o.Label)
|
||||
default:
|
||||
err = errUnsupportedRSAOptions
|
||||
}
|
||||
}
|
||||
return err
|
||||
})
|
||||
return plaintext, err
|
||||
}
|
||||
|
||||
func decryptPKCS1v15(session *pkcs11Session, key *pkcs11PrivateKeyRSA, ciphertext []byte, sessionKeyLen int) ([]byte, error) {
|
||||
if sessionKeyLen != 0 {
|
||||
return nil, errUnsupportedRSAOptions
|
||||
}
|
||||
mech := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS, nil)}
|
||||
if err := session.ctx.DecryptInit(session.handle, mech, key.handle); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return session.ctx.Decrypt(session.handle, ciphertext)
|
||||
}
|
||||
|
||||
func decryptOAEP(session *pkcs11Session, key *pkcs11PrivateKeyRSA, ciphertext []byte, hashFunction crypto.Hash,
|
||||
label []byte) ([]byte, error) {
|
||||
|
||||
hashAlg, mgfAlg, _, err := hashToPKCS11(hashFunction)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mech := pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_OAEP,
|
||||
pkcs11.NewOAEPParams(hashAlg, mgfAlg, pkcs11.CKZ_DATA_SPECIFIED, label))
|
||||
|
||||
err = session.ctx.DecryptInit(session.handle, []*pkcs11.Mechanism{mech}, key.handle)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return session.ctx.Decrypt(session.handle, ciphertext)
|
||||
}
|
||||
|
||||
func hashToPKCS11(hashFunction crypto.Hash) (hashAlg uint, mgfAlg uint, hashLen uint, err error) {
|
||||
switch hashFunction {
|
||||
case crypto.SHA1:
|
||||
return pkcs11.CKM_SHA_1, pkcs11.CKG_MGF1_SHA1, 20, nil
|
||||
case crypto.SHA224:
|
||||
return pkcs11.CKM_SHA224, pkcs11.CKG_MGF1_SHA224, 28, nil
|
||||
case crypto.SHA256:
|
||||
return pkcs11.CKM_SHA256, pkcs11.CKG_MGF1_SHA256, 32, nil
|
||||
case crypto.SHA384:
|
||||
return pkcs11.CKM_SHA384, pkcs11.CKG_MGF1_SHA384, 48, nil
|
||||
case crypto.SHA512:
|
||||
return pkcs11.CKM_SHA512, pkcs11.CKG_MGF1_SHA512, 64, nil
|
||||
default:
|
||||
return 0, 0, 0, errUnsupportedRSAOptions
|
||||
}
|
||||
}
|
||||
|
||||
func signPSS(session *pkcs11Session, key *pkcs11PrivateKeyRSA, digest []byte, opts *rsa.PSSOptions) ([]byte, error) {
|
||||
var hMech, mgf, hLen, sLen uint
|
||||
var err error
|
||||
if hMech, mgf, hLen, err = hashToPKCS11(opts.Hash); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch opts.SaltLength {
|
||||
case rsa.PSSSaltLengthAuto: // parseltongue constant
|
||||
// TODO we could (in principle) work out the biggest
|
||||
// possible size from the key, but until someone has
|
||||
// the effort to do that...
|
||||
return nil, errUnsupportedRSAOptions
|
||||
case rsa.PSSSaltLengthEqualsHash:
|
||||
sLen = hLen
|
||||
default:
|
||||
sLen = uint(opts.SaltLength)
|
||||
}
|
||||
// TODO this is pretty horrible, maybe the PKCS#11 wrapper
|
||||
// could be improved to help us out here
|
||||
parameters := concat(ulongToBytes(hMech),
|
||||
ulongToBytes(mgf),
|
||||
ulongToBytes(sLen))
|
||||
mech := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS_PSS, parameters)}
|
||||
if err = session.ctx.SignInit(session.handle, mech, key.handle); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return session.ctx.Sign(session.handle, digest)
|
||||
}
|
||||
|
||||
var pkcs1Prefix = map[crypto.Hash][]byte{
|
||||
crypto.SHA1: {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
|
||||
crypto.SHA224: {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c},
|
||||
crypto.SHA256: {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
|
||||
crypto.SHA384: {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
|
||||
crypto.SHA512: {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
|
||||
}
|
||||
|
||||
func signPKCS1v15(session *pkcs11Session, key *pkcs11PrivateKeyRSA, digest []byte, hash crypto.Hash) (signature []byte, err error) {
|
||||
/* Calculate T for EMSA-PKCS1-v1_5. */
|
||||
oid := pkcs1Prefix[hash]
|
||||
T := make([]byte, len(oid)+len(digest))
|
||||
copy(T[0:len(oid)], oid)
|
||||
copy(T[len(oid):], digest)
|
||||
mech := []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_RSA_PKCS, nil)}
|
||||
err = session.ctx.SignInit(session.handle, mech, key.handle)
|
||||
if err == nil {
|
||||
signature, err = session.ctx.Sign(session.handle, T)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Sign signs a message using a RSA key.
|
||||
//
|
||||
// This completes the implemention of crypto.Signer for pkcs11PrivateKeyRSA.
|
||||
//
|
||||
// PKCS#11 expects to pick its own random data where necessary for signatures, so the rand argument is ignored.
|
||||
//
|
||||
// Note that (at present) the crypto.rsa.PSSSaltLengthAuto option is
|
||||
// not supported. The caller must either use
|
||||
// crypto.rsa.PSSSaltLengthEqualsHash (recommended) or pass an
|
||||
// explicit salt length. Moreover the underlying PKCS#11
|
||||
// implementation may impose further restrictions.
|
||||
func (priv *pkcs11PrivateKeyRSA) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
||||
err = priv.context.withSession(func(session *pkcs11Session) error {
|
||||
switch opts.(type) {
|
||||
case *rsa.PSSOptions:
|
||||
signature, err = signPSS(session, priv, digest, opts.(*rsa.PSSOptions))
|
||||
default: /* PKCS1-v1_5 */
|
||||
signature, err = signPKCS1v15(session, priv, digest, opts.HashFunc())
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return signature, err
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
// Copyright 2016, 2017 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"
|
||||
"errors"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
"github.com/thales-e-security/pool"
|
||||
)
|
||||
|
||||
// pkcs11Session wraps a PKCS#11 session handle so we can use it in a resource pool.
|
||||
type pkcs11Session struct {
|
||||
ctx *pkcs11.Ctx
|
||||
handle pkcs11.SessionHandle
|
||||
}
|
||||
|
||||
// Close is required to satisfy the pools.Resource interface. It closes the session, but swallows any
|
||||
// errors that occur.
|
||||
func (s pkcs11Session) Close() {
|
||||
// We cannot return an error, so we swallow it
|
||||
_ = s.ctx.CloseSession(s.handle)
|
||||
}
|
||||
|
||||
// withSession executes a function with a session.
|
||||
func (c *Context) withSession(f func(session *pkcs11Session) error) error {
|
||||
session, err := c.getSession()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.pool.Put(session)
|
||||
|
||||
return f(session)
|
||||
}
|
||||
|
||||
// getSession retrieves a session from the pool, respecting the timeout defined in the Context config.
|
||||
// Callers are responsible for putting this session back in the pool.
|
||||
func (c *Context) getSession() (*pkcs11Session, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
if c.cfg.PoolWaitTimeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(context.Background(), c.cfg.PoolWaitTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
resource, err := c.pool.Get(ctx)
|
||||
if err == pool.ErrClosed {
|
||||
// Our Context must have been closed, return a nicer error.
|
||||
// We don't use errClosed to ensure our tests identify functions that aren't checking for closure
|
||||
// correctly.
|
||||
return nil, errors.New("context is closed")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resource.(*pkcs11Session), nil
|
||||
}
|
||||
|
||||
// resourcePoolFactoryFunc is called by the resource pool when a new session is needed.
|
||||
func (c *Context) resourcePoolFactoryFunc() (pool.Resource, error) {
|
||||
session, err := c.ctx.OpenSession(c.slot, pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pkcs11Session{c.ctx, session}, nil
|
||||
}
|
||||
+366
@@ -0,0 +1,366 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
// SymmetricGenParams holds a consistent (key type, mechanism) key generation pair.
|
||||
type SymmetricGenParams struct {
|
||||
// Key type (CKK_...)
|
||||
KeyType uint
|
||||
|
||||
// Key generation mechanism (CKM_..._KEY_GEN)
|
||||
GenMech uint
|
||||
}
|
||||
|
||||
// SymmetricCipher represents information about a symmetric cipher.
|
||||
type SymmetricCipher struct {
|
||||
// Possible key generation parameters
|
||||
// (For HMAC this varies between PKCS#11 implementations.)
|
||||
GenParams []SymmetricGenParams
|
||||
|
||||
// Block size in bytes
|
||||
BlockSize int
|
||||
|
||||
// True if encryption supported
|
||||
Encrypt bool
|
||||
|
||||
// True if MAC supported
|
||||
MAC bool
|
||||
|
||||
// ECB mechanism (CKM_..._ECB)
|
||||
ECBMech uint
|
||||
|
||||
// CBC mechanism (CKM_..._CBC)
|
||||
CBCMech uint
|
||||
|
||||
// CBC mechanism with PKCS#7 padding (CKM_..._CBC)
|
||||
CBCPKCSMech uint
|
||||
|
||||
// GCM mechanism (CKM_..._GCM)
|
||||
GCMMech uint
|
||||
}
|
||||
|
||||
// CipherAES describes the AES cipher. Use this with the
|
||||
// GenerateSecretKey... functions.
|
||||
var CipherAES = &SymmetricCipher{
|
||||
GenParams: []SymmetricGenParams{
|
||||
{
|
||||
KeyType: pkcs11.CKK_AES,
|
||||
GenMech: pkcs11.CKM_AES_KEY_GEN,
|
||||
},
|
||||
},
|
||||
BlockSize: 16,
|
||||
Encrypt: true,
|
||||
MAC: false,
|
||||
ECBMech: pkcs11.CKM_AES_ECB,
|
||||
CBCMech: pkcs11.CKM_AES_CBC,
|
||||
CBCPKCSMech: pkcs11.CKM_AES_CBC_PAD,
|
||||
GCMMech: pkcs11.CKM_AES_GCM,
|
||||
}
|
||||
|
||||
// CipherDES3 describes the three-key triple-DES cipher. Use this with the
|
||||
// GenerateSecretKey... functions.
|
||||
var CipherDES3 = &SymmetricCipher{
|
||||
GenParams: []SymmetricGenParams{
|
||||
{
|
||||
KeyType: pkcs11.CKK_DES3,
|
||||
GenMech: pkcs11.CKM_DES3_KEY_GEN,
|
||||
},
|
||||
},
|
||||
BlockSize: 8,
|
||||
Encrypt: true,
|
||||
MAC: false,
|
||||
ECBMech: pkcs11.CKM_DES3_ECB,
|
||||
CBCMech: pkcs11.CKM_DES3_CBC,
|
||||
CBCPKCSMech: pkcs11.CKM_DES3_CBC_PAD,
|
||||
GCMMech: 0,
|
||||
}
|
||||
|
||||
// CipherGeneric describes the CKK_GENERIC_SECRET key type. Use this with the
|
||||
// GenerateSecretKey... functions.
|
||||
//
|
||||
// The spec promises that this mechanism can be used to perform HMAC
|
||||
// operations, although implementations vary;
|
||||
// CipherHMACSHA1 and so on may give better results.
|
||||
var CipherGeneric = &SymmetricCipher{
|
||||
GenParams: []SymmetricGenParams{
|
||||
{
|
||||
KeyType: pkcs11.CKK_GENERIC_SECRET,
|
||||
GenMech: pkcs11.CKM_GENERIC_SECRET_KEY_GEN,
|
||||
},
|
||||
},
|
||||
BlockSize: 64,
|
||||
Encrypt: false,
|
||||
MAC: true,
|
||||
ECBMech: 0,
|
||||
CBCMech: 0,
|
||||
GCMMech: 0,
|
||||
}
|
||||
|
||||
// CipherHMACSHA1 describes the CKK_SHA_1_HMAC key type. Use this with the
|
||||
// GenerateSecretKey... functions.
|
||||
var CipherHMACSHA1 = &SymmetricCipher{
|
||||
GenParams: []SymmetricGenParams{
|
||||
{
|
||||
KeyType: pkcs11.CKK_SHA_1_HMAC,
|
||||
GenMech: CKM_NC_SHA_1_HMAC_KEY_GEN,
|
||||
},
|
||||
{
|
||||
KeyType: pkcs11.CKK_GENERIC_SECRET,
|
||||
GenMech: pkcs11.CKM_GENERIC_SECRET_KEY_GEN,
|
||||
},
|
||||
},
|
||||
BlockSize: 64,
|
||||
Encrypt: false,
|
||||
MAC: true,
|
||||
ECBMech: 0,
|
||||
CBCMech: 0,
|
||||
GCMMech: 0,
|
||||
}
|
||||
|
||||
// CipherHMACSHA224 describes the CKK_SHA224_HMAC key type. Use this with the
|
||||
// GenerateSecretKey... functions.
|
||||
var CipherHMACSHA224 = &SymmetricCipher{
|
||||
GenParams: []SymmetricGenParams{
|
||||
{
|
||||
KeyType: pkcs11.CKK_SHA224_HMAC,
|
||||
GenMech: CKM_NC_SHA224_HMAC_KEY_GEN,
|
||||
},
|
||||
{
|
||||
KeyType: pkcs11.CKK_GENERIC_SECRET,
|
||||
GenMech: pkcs11.CKM_GENERIC_SECRET_KEY_GEN,
|
||||
},
|
||||
},
|
||||
BlockSize: 64,
|
||||
Encrypt: false,
|
||||
MAC: true,
|
||||
ECBMech: 0,
|
||||
CBCMech: 0,
|
||||
GCMMech: 0,
|
||||
}
|
||||
|
||||
// CipherHMACSHA256 describes the CKK_SHA256_HMAC key type. Use this with the
|
||||
// GenerateSecretKey... functions.
|
||||
var CipherHMACSHA256 = &SymmetricCipher{
|
||||
GenParams: []SymmetricGenParams{
|
||||
{
|
||||
KeyType: pkcs11.CKK_SHA256_HMAC,
|
||||
GenMech: CKM_NC_SHA256_HMAC_KEY_GEN,
|
||||
},
|
||||
{
|
||||
KeyType: pkcs11.CKK_GENERIC_SECRET,
|
||||
GenMech: pkcs11.CKM_GENERIC_SECRET_KEY_GEN,
|
||||
},
|
||||
},
|
||||
BlockSize: 64,
|
||||
Encrypt: false,
|
||||
MAC: true,
|
||||
ECBMech: 0,
|
||||
CBCMech: 0,
|
||||
GCMMech: 0,
|
||||
}
|
||||
|
||||
// CipherHMACSHA384 describes the CKK_SHA384_HMAC key type. Use this with the
|
||||
// GenerateSecretKey... functions.
|
||||
var CipherHMACSHA384 = &SymmetricCipher{
|
||||
GenParams: []SymmetricGenParams{
|
||||
{
|
||||
KeyType: pkcs11.CKK_SHA384_HMAC,
|
||||
GenMech: CKM_NC_SHA384_HMAC_KEY_GEN,
|
||||
},
|
||||
{
|
||||
KeyType: pkcs11.CKK_GENERIC_SECRET,
|
||||
GenMech: pkcs11.CKM_GENERIC_SECRET_KEY_GEN,
|
||||
},
|
||||
},
|
||||
BlockSize: 64,
|
||||
Encrypt: false,
|
||||
MAC: true,
|
||||
ECBMech: 0,
|
||||
CBCMech: 0,
|
||||
GCMMech: 0,
|
||||
}
|
||||
|
||||
// CipherHMACSHA512 describes the CKK_SHA512_HMAC key type. Use this with the
|
||||
// GenerateSecretKey... functions.
|
||||
var CipherHMACSHA512 = &SymmetricCipher{
|
||||
GenParams: []SymmetricGenParams{
|
||||
{
|
||||
KeyType: pkcs11.CKK_SHA512_HMAC,
|
||||
GenMech: CKM_NC_SHA512_HMAC_KEY_GEN,
|
||||
},
|
||||
{
|
||||
KeyType: pkcs11.CKK_GENERIC_SECRET,
|
||||
GenMech: pkcs11.CKM_GENERIC_SECRET_KEY_GEN,
|
||||
},
|
||||
},
|
||||
BlockSize: 128,
|
||||
Encrypt: false,
|
||||
MAC: true,
|
||||
ECBMech: 0,
|
||||
CBCMech: 0,
|
||||
GCMMech: 0,
|
||||
}
|
||||
|
||||
// Ciphers is a map of PKCS#11 key types (CKK_...) to symmetric cipher information.
|
||||
var Ciphers = map[int]*SymmetricCipher{
|
||||
pkcs11.CKK_AES: CipherAES,
|
||||
pkcs11.CKK_DES3: CipherDES3,
|
||||
pkcs11.CKK_GENERIC_SECRET: CipherGeneric,
|
||||
pkcs11.CKK_SHA_1_HMAC: CipherHMACSHA1,
|
||||
pkcs11.CKK_SHA224_HMAC: CipherHMACSHA224,
|
||||
pkcs11.CKK_SHA256_HMAC: CipherHMACSHA256,
|
||||
pkcs11.CKK_SHA384_HMAC: CipherHMACSHA384,
|
||||
pkcs11.CKK_SHA512_HMAC: CipherHMACSHA512,
|
||||
}
|
||||
|
||||
// SecretKey contains a reference to a loaded PKCS#11 symmetric key object.
|
||||
//
|
||||
// A *SecretKey implements the cipher.Block interface, allowing it be used
|
||||
// as the argument to cipher.NewCBCEncrypter and similar methods.
|
||||
// For bulk operation this is very inefficient;
|
||||
// using NewCBCEncrypterCloser, NewCBCEncrypter or NewCBC from this package is
|
||||
// much faster.
|
||||
type SecretKey struct {
|
||||
pkcs11Object
|
||||
|
||||
// Symmetric cipher information
|
||||
Cipher *SymmetricCipher
|
||||
}
|
||||
|
||||
// GenerateSecretKey creates an secret key of given length and type. The id parameter is used to
|
||||
// set CKA_ID and must be non-nil.
|
||||
func (c *Context) GenerateSecretKey(id []byte, bits int, cipher *SymmetricCipher) (*SecretKey, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
template, err := NewAttributeSetWithID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.GenerateSecretKeyWithAttributes(template, bits, cipher)
|
||||
}
|
||||
|
||||
// GenerateSecretKey creates an secret key of given length and type. The id and label parameters are used to
|
||||
// set CKA_ID and CKA_LABEL respectively and must be non-nil.
|
||||
func (c *Context) GenerateSecretKeyWithLabel(id, label []byte, bits int, cipher *SymmetricCipher) (*SecretKey, error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
template, err := NewAttributeSetWithIDAndLabel(id, label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.GenerateSecretKeyWithAttributes(template, bits, cipher)
|
||||
|
||||
}
|
||||
|
||||
// GenerateSecretKeyWithAttributes creates an secret key of given length and type. After this function returns, template
|
||||
// will contain the attributes applied to the key. If required attributes are missing, they will be set to a default
|
||||
// value.
|
||||
func (c *Context) GenerateSecretKeyWithAttributes(template AttributeSet, bits int, cipher *SymmetricCipher) (k *SecretKey, err error) {
|
||||
if c.closed.Get() {
|
||||
return nil, errClosed
|
||||
}
|
||||
|
||||
err = c.withSession(func(session *pkcs11Session) error {
|
||||
|
||||
// CKK_*_HMAC exists but there is no specific corresponding CKM_*_KEY_GEN
|
||||
// mechanism. Therefore we attempt both CKM_GENERIC_SECRET_KEY_GEN and
|
||||
// vendor-specific mechanisms.
|
||||
|
||||
template.AddIfNotPresent([]*pkcs11.Attribute{
|
||||
pkcs11.NewAttribute(pkcs11.CKA_CLASS, pkcs11.CKO_SECRET_KEY),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_TOKEN, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SIGN, cipher.MAC),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_VERIFY, cipher.MAC),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_ENCRYPT, cipher.Encrypt), // Not supported on CloudHSM
|
||||
pkcs11.NewAttribute(pkcs11.CKA_DECRYPT, cipher.Encrypt), // Not supported on CloudHSM
|
||||
pkcs11.NewAttribute(pkcs11.CKA_SENSITIVE, true),
|
||||
pkcs11.NewAttribute(pkcs11.CKA_EXTRACTABLE, false),
|
||||
})
|
||||
if bits > 0 {
|
||||
_ = template.Set(pkcs11.CKA_VALUE_LEN, bits/8) // safe for an int
|
||||
}
|
||||
|
||||
for n, genMech := range cipher.GenParams {
|
||||
|
||||
_ = template.Set(CkaKeyType, genMech.KeyType)
|
||||
|
||||
mech := []*pkcs11.Mechanism{pkcs11.NewMechanism(genMech.GenMech, nil)}
|
||||
|
||||
privHandle, err := session.ctx.GenerateKey(session.handle, mech, template.ToSlice())
|
||||
if err == nil {
|
||||
k = &SecretKey{pkcs11Object{privHandle, c}, cipher}
|
||||
return nil
|
||||
}
|
||||
|
||||
// As a special case, AWS CloudHSM does not accept CKA_ENCRYPT and CKA_DECRYPT on a
|
||||
// Generic Secret key. If we are in that special case, try again without those attributes.
|
||||
if e, ok := err.(pkcs11.Error); ok && e == pkcs11.CKR_ARGUMENTS_BAD && genMech.GenMech == pkcs11.CKM_GENERIC_SECRET_KEY_GEN {
|
||||
adjustedTemplate := template.Copy()
|
||||
adjustedTemplate.Unset(CkaEncrypt)
|
||||
adjustedTemplate.Unset(CkaDecrypt)
|
||||
|
||||
privHandle, err = session.ctx.GenerateKey(session.handle, mech, adjustedTemplate.ToSlice())
|
||||
if err == nil {
|
||||
// Store the actual attributes
|
||||
template.cloneFrom(adjustedTemplate)
|
||||
|
||||
k = &SecretKey{pkcs11Object{privHandle, c}, cipher}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if n == len(cipher.GenParams)-1 {
|
||||
// If we have tried all available gen params, we should return a sensible error. So we skip the
|
||||
// retry logic below and return directly.
|
||||
return err
|
||||
}
|
||||
|
||||
// nShield returns CKR_TEMPLATE_INCONSISTENT if if doesn't like the CKK/CKM combination.
|
||||
// AWS CloudHSM returns CKR_ATTRIBUTE_VALUE_INVALID in the same circumstances.
|
||||
if e, ok := err.(pkcs11.Error); ok &&
|
||||
e == pkcs11.CKR_TEMPLATE_INCONSISTENT || e == pkcs11.CKR_ATTRIBUTE_VALUE_INVALID {
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// We can only get here if there were no GenParams
|
||||
return errors.New("cipher must have GenParams")
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Delete deletes the secret key from the token.
|
||||
func (key *SecretKey) Delete() error {
|
||||
return key.pkcs11Object.Delete()
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
tags
|
||||
test_db/*/generation
|
||||
test_db/*/*.lock
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
language: go
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
go:
|
||||
- 1.9
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go test -v ./...
|
||||
|
||||
before_script:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get -y install libsofthsm
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2013 Miek Gieben. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Miek Gieben nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
# Makefile for releasing.
|
||||
#
|
||||
# The release is controlled from version.go. The version found there is
|
||||
# used to tag the git repo, we're not building any artifects so there is nothing
|
||||
# to upload to github.
|
||||
#
|
||||
# * Up the version in version.go
|
||||
# * Run: make -f Makefile.release release
|
||||
# * will *commit* your change with 'Release $VERSION'
|
||||
# * push to github
|
||||
#
|
||||
|
||||
define GO
|
||||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/miekg/pkcs11"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(pkcs11.Release.String())
|
||||
}
|
||||
endef
|
||||
|
||||
$(file > version_release.go,$(GO))
|
||||
VERSION:=$(shell go run -tags release version_release.go)
|
||||
TAG="v$(VERSION)"
|
||||
|
||||
all:
|
||||
rm -f version_release.go
|
||||
@echo Use the \'release\' target to start a release $(VERSION)
|
||||
|
||||
.PHONY: run
|
||||
run:
|
||||
rm -f version_release.go
|
||||
@echo $(VERSION)
|
||||
|
||||
.PHONY: release
|
||||
release: commit push
|
||||
@echo Released $(VERSION)
|
||||
|
||||
.PHONY: commit
|
||||
commit:
|
||||
rm -f version_release.go
|
||||
@echo Committing release $(VERSION)
|
||||
git commit -am"Release $(VERSION)"
|
||||
git tag $(TAG)
|
||||
|
||||
.PHONY: push
|
||||
push:
|
||||
@echo Pushing release $(VERSION) to master
|
||||
git push --tags
|
||||
git push
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
# PKCS#11 [](https://travis-ci.org/miekg/pkcs11) [](http://godoc.org/github.com/miekg/pkcs11)
|
||||
|
||||
This is a Go implementation of the PKCS#11 API. It wraps the library closely, but uses Go idiom were
|
||||
it makes sense. It has been tested with SoftHSM.
|
||||
|
||||
## SoftHSM
|
||||
|
||||
* Make it use a custom configuration file `export SOFTHSM_CONF=$PWD/softhsm.conf`
|
||||
|
||||
* Then use `softhsm` to init it
|
||||
|
||||
~~~
|
||||
softhsm --init-token --slot 0 --label test --pin 1234
|
||||
~~~
|
||||
|
||||
* Then use `libsofthsm.so` as the pkcs11 module:
|
||||
|
||||
~~~ go
|
||||
p := pkcs11.New("/usr/lib/softhsm/libsofthsm.so")
|
||||
~~~
|
||||
|
||||
## Examples
|
||||
|
||||
A skeleton program would look somewhat like this (yes, pkcs#11 is verbose):
|
||||
|
||||
~~~ go
|
||||
p := pkcs11.New("/usr/lib/softhsm/libsofthsm.so")
|
||||
err := p.Initialize()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer p.Destroy()
|
||||
defer p.Finalize()
|
||||
|
||||
slots, err := p.GetSlotList(true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
session, err := p.OpenSession(slots[0], pkcs11.CKF_SERIAL_SESSION|pkcs11.CKF_RW_SESSION)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer p.CloseSession(session)
|
||||
|
||||
err = p.Login(session, pkcs11.CKU_USER, "1234")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer p.Logout(session)
|
||||
|
||||
p.DigestInit(session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_SHA_1, nil)})
|
||||
hash, err := p.Digest(session, []byte("this is a string"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, d := range hash {
|
||||
fmt.Printf("%x", d)
|
||||
}
|
||||
fmt.Println()
|
||||
~~~
|
||||
|
||||
Further examples are included in the tests.
|
||||
|
||||
To expose PKCS#11 keys using the [crypto.Signer interface](https://golang.org/pkg/crypto/#Signer),
|
||||
please see [github.com/thalesignite/crypto11](https://github.com/thalesignite/crypto11).
|
||||
+736
@@ -0,0 +1,736 @@
|
||||
// Copyright 2013 Miek Gieben. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pkcs11
|
||||
|
||||
const (
|
||||
CKU_SO uint = 0
|
||||
CKU_USER uint = 1
|
||||
CKU_CONTEXT_SPECIFIC uint = 2
|
||||
)
|
||||
|
||||
const (
|
||||
CKO_DATA uint = 0x00000000
|
||||
CKO_CERTIFICATE uint = 0x00000001
|
||||
CKO_PUBLIC_KEY uint = 0x00000002
|
||||
CKO_PRIVATE_KEY uint = 0x00000003
|
||||
CKO_SECRET_KEY uint = 0x00000004
|
||||
CKO_HW_FEATURE uint = 0x00000005
|
||||
CKO_DOMAIN_PARAMETERS uint = 0x00000006
|
||||
CKO_MECHANISM uint = 0x00000007
|
||||
CKO_OTP_KEY uint = 0x00000008
|
||||
CKO_VENDOR_DEFINED uint = 0x80000000
|
||||
)
|
||||
|
||||
const (
|
||||
CKG_MGF1_SHA1 uint = 0x00000001
|
||||
CKG_MGF1_SHA224 uint = 0x00000005
|
||||
CKG_MGF1_SHA256 uint = 0x00000002
|
||||
CKG_MGF1_SHA384 uint = 0x00000003
|
||||
CKG_MGF1_SHA512 uint = 0x00000004
|
||||
CKG_MGF1_SHA3_224 uint = 0x00000006
|
||||
CKG_MGF1_SHA3_256 uint = 0x00000007
|
||||
CKG_MGF1_SHA3_384 uint = 0x00000008
|
||||
CKG_MGF1_SHA3_512 uint = 0x00000009
|
||||
)
|
||||
|
||||
const (
|
||||
CKZ_DATA_SPECIFIED uint = 0x00000001
|
||||
)
|
||||
|
||||
// Generated with: awk '/#define CK[AFKMRC]/{ print $2 " = " $3 }' pkcs11t.h | sed -e 's/UL$//g' -e 's/UL)$/)/g'
|
||||
|
||||
// All the flag (CKF_), attribute (CKA_), error code (CKR_), key type (CKK_), certificate type (CKC_) and
|
||||
// mechanism (CKM_) constants as defined in PKCS#11.
|
||||
const (
|
||||
CKF_TOKEN_PRESENT = 0x00000001
|
||||
CKF_REMOVABLE_DEVICE = 0x00000002
|
||||
CKF_HW_SLOT = 0x00000004
|
||||
CKF_RNG = 0x00000001
|
||||
CKF_WRITE_PROTECTED = 0x00000002
|
||||
CKF_LOGIN_REQUIRED = 0x00000004
|
||||
CKF_USER_PIN_INITIALIZED = 0x00000008
|
||||
CKF_RESTORE_KEY_NOT_NEEDED = 0x00000020
|
||||
CKF_CLOCK_ON_TOKEN = 0x00000040
|
||||
CKF_PROTECTED_AUTHENTICATION_PATH = 0x00000100
|
||||
CKF_DUAL_CRYPTO_OPERATIONS = 0x00000200
|
||||
CKF_TOKEN_INITIALIZED = 0x00000400
|
||||
CKF_SECONDARY_AUTHENTICATION = 0x00000800
|
||||
CKF_USER_PIN_COUNT_LOW = 0x00010000
|
||||
CKF_USER_PIN_FINAL_TRY = 0x00020000
|
||||
CKF_USER_PIN_LOCKED = 0x00040000
|
||||
CKF_USER_PIN_TO_BE_CHANGED = 0x00080000
|
||||
CKF_SO_PIN_COUNT_LOW = 0x00100000
|
||||
CKF_SO_PIN_FINAL_TRY = 0x00200000
|
||||
CKF_SO_PIN_LOCKED = 0x00400000
|
||||
CKF_SO_PIN_TO_BE_CHANGED = 0x00800000
|
||||
CKF_ERROR_STATE = 0x01000000
|
||||
CKF_RW_SESSION = 0x00000002
|
||||
CKF_SERIAL_SESSION = 0x00000004
|
||||
CKK_RSA = 0x00000000
|
||||
CKK_DSA = 0x00000001
|
||||
CKK_DH = 0x00000002
|
||||
CKK_ECDSA = 0x00000003
|
||||
CKK_EC = 0x00000003
|
||||
CKK_X9_42_DH = 0x00000004
|
||||
CKK_KEA = 0x00000005
|
||||
CKK_GENERIC_SECRET = 0x00000010
|
||||
CKK_RC2 = 0x00000011
|
||||
CKK_RC4 = 0x00000012
|
||||
CKK_DES = 0x00000013
|
||||
CKK_DES2 = 0x00000014
|
||||
CKK_DES3 = 0x00000015
|
||||
CKK_CAST = 0x00000016
|
||||
CKK_CAST3 = 0x00000017
|
||||
CKK_CAST5 = 0x00000018
|
||||
CKK_CAST128 = 0x00000018
|
||||
CKK_RC5 = 0x00000019
|
||||
CKK_IDEA = 0x0000001A
|
||||
CKK_SKIPJACK = 0x0000001B
|
||||
CKK_BATON = 0x0000001C
|
||||
CKK_JUNIPER = 0x0000001D
|
||||
CKK_CDMF = 0x0000001E
|
||||
CKK_AES = 0x0000001F
|
||||
CKK_BLOWFISH = 0x00000020
|
||||
CKK_TWOFISH = 0x00000021
|
||||
CKK_SECURID = 0x00000022
|
||||
CKK_HOTP = 0x00000023
|
||||
CKK_ACTI = 0x00000024
|
||||
CKK_CAMELLIA = 0x00000025
|
||||
CKK_ARIA = 0x00000026
|
||||
CKK_SHA512_224_HMAC = 0x00000027
|
||||
CKK_SHA512_256_HMAC = 0x00000028
|
||||
CKK_SHA512_T_HMAC = 0x00000029
|
||||
CKK_SHA_1_HMAC = 0x00000028
|
||||
CKK_SHA224_HMAC = 0x0000002E
|
||||
CKK_SHA256_HMAC = 0x0000002B
|
||||
CKK_SHA384_HMAC = 0x0000002C
|
||||
CKK_SHA512_HMAC = 0x0000002D
|
||||
CKK_SEED = 0x0000002F
|
||||
CKK_GOSTR3410 = 0x00000030
|
||||
CKK_GOSTR3411 = 0x00000031
|
||||
CKK_GOST28147 = 0x00000032
|
||||
CKK_SHA3_224_HMAC = 0x00000033
|
||||
CKK_SHA3_256_HMAC = 0x00000034
|
||||
CKK_SHA3_384_HMAC = 0x00000035
|
||||
CKK_SHA3_512_HMAC = 0x00000036
|
||||
CKK_VENDOR_DEFINED = 0x80000000
|
||||
CKC_X_509 = 0x00000000
|
||||
CKC_X_509_ATTR_CERT = 0x00000001
|
||||
CKC_WTLS = 0x00000002
|
||||
CKC_VENDOR_DEFINED = 0x80000000
|
||||
CKF_ARRAY_ATTRIBUTE = 0x40000000
|
||||
CKA_CLASS = 0x00000000
|
||||
CKA_TOKEN = 0x00000001
|
||||
CKA_PRIVATE = 0x00000002
|
||||
CKA_LABEL = 0x00000003
|
||||
CKA_APPLICATION = 0x00000010
|
||||
CKA_VALUE = 0x00000011
|
||||
CKA_OBJECT_ID = 0x00000012
|
||||
CKA_CERTIFICATE_TYPE = 0x00000080
|
||||
CKA_ISSUER = 0x00000081
|
||||
CKA_SERIAL_NUMBER = 0x00000082
|
||||
CKA_AC_ISSUER = 0x00000083
|
||||
CKA_OWNER = 0x00000084
|
||||
CKA_ATTR_TYPES = 0x00000085
|
||||
CKA_TRUSTED = 0x00000086
|
||||
CKA_CERTIFICATE_CATEGORY = 0x00000087
|
||||
CKA_JAVA_MIDP_SECURITY_DOMAIN = 0x00000088
|
||||
CKA_URL = 0x00000089
|
||||
CKA_HASH_OF_SUBJECT_PUBLIC_KEY = 0x0000008A
|
||||
CKA_HASH_OF_ISSUER_PUBLIC_KEY = 0x0000008B
|
||||
CKA_NAME_HASH_ALGORITHM = 0x0000008C
|
||||
CKA_CHECK_VALUE = 0x00000090
|
||||
CKA_KEY_TYPE = 0x00000100
|
||||
CKA_SUBJECT = 0x00000101
|
||||
CKA_ID = 0x00000102
|
||||
CKA_SENSITIVE = 0x00000103
|
||||
CKA_ENCRYPT = 0x00000104
|
||||
CKA_DECRYPT = 0x00000105
|
||||
CKA_WRAP = 0x00000106
|
||||
CKA_UNWRAP = 0x00000107
|
||||
CKA_SIGN = 0x00000108
|
||||
CKA_SIGN_RECOVER = 0x00000109
|
||||
CKA_VERIFY = 0x0000010A
|
||||
CKA_VERIFY_RECOVER = 0x0000010B
|
||||
CKA_DERIVE = 0x0000010C
|
||||
CKA_START_DATE = 0x00000110
|
||||
CKA_END_DATE = 0x00000111
|
||||
CKA_MODULUS = 0x00000120
|
||||
CKA_MODULUS_BITS = 0x00000121
|
||||
CKA_PUBLIC_EXPONENT = 0x00000122
|
||||
CKA_PRIVATE_EXPONENT = 0x00000123
|
||||
CKA_PRIME_1 = 0x00000124
|
||||
CKA_PRIME_2 = 0x00000125
|
||||
CKA_EXPONENT_1 = 0x00000126
|
||||
CKA_EXPONENT_2 = 0x00000127
|
||||
CKA_COEFFICIENT = 0x00000128
|
||||
CKA_PUBLIC_KEY_INFO = 0x00000129
|
||||
CKA_PRIME = 0x00000130
|
||||
CKA_SUBPRIME = 0x00000131
|
||||
CKA_BASE = 0x00000132
|
||||
CKA_PRIME_BITS = 0x00000133
|
||||
CKA_SUBPRIME_BITS = 0x00000134
|
||||
CKA_SUB_PRIME_BITS = CKA_SUBPRIME_BITS
|
||||
CKA_VALUE_BITS = 0x00000160
|
||||
CKA_VALUE_LEN = 0x00000161
|
||||
CKA_EXTRACTABLE = 0x00000162
|
||||
CKA_LOCAL = 0x00000163
|
||||
CKA_NEVER_EXTRACTABLE = 0x00000164
|
||||
CKA_ALWAYS_SENSITIVE = 0x00000165
|
||||
CKA_KEY_GEN_MECHANISM = 0x00000166
|
||||
CKA_MODIFIABLE = 0x00000170
|
||||
CKA_COPYABLE = 0x00000171
|
||||
CKA_DESTROYABLE = 0x00000172
|
||||
CKA_ECDSA_PARAMS = 0x00000180
|
||||
CKA_EC_PARAMS = 0x00000180
|
||||
CKA_EC_POINT = 0x00000181
|
||||
CKA_SECONDARY_AUTH = 0x00000200
|
||||
CKA_AUTH_PIN_FLAGS = 0x00000201
|
||||
CKA_ALWAYS_AUTHENTICATE = 0x00000202
|
||||
CKA_WRAP_WITH_TRUSTED = 0x00000210
|
||||
CKA_WRAP_TEMPLATE = CKF_ARRAY_ATTRIBUTE | 0x00000211
|
||||
CKA_UNWRAP_TEMPLATE = CKF_ARRAY_ATTRIBUTE | 0x00000212
|
||||
CKA_OTP_FORMAT = 0x00000220
|
||||
CKA_OTP_LENGTH = 0x00000221
|
||||
CKA_OTP_TIME_INTERVAL = 0x00000222
|
||||
CKA_OTP_USER_FRIENDLY_MODE = 0x00000223
|
||||
CKA_OTP_CHALLENGE_REQUIREMENT = 0x00000224
|
||||
CKA_OTP_TIME_REQUIREMENT = 0x00000225
|
||||
CKA_OTP_COUNTER_REQUIREMENT = 0x00000226
|
||||
CKA_OTP_PIN_REQUIREMENT = 0x00000227
|
||||
CKA_OTP_COUNTER = 0x0000022E
|
||||
CKA_OTP_TIME = 0x0000022F
|
||||
CKA_OTP_USER_IDENTIFIER = 0x0000022A
|
||||
CKA_OTP_SERVICE_IDENTIFIER = 0x0000022B
|
||||
CKA_OTP_SERVICE_LOGO = 0x0000022C
|
||||
CKA_OTP_SERVICE_LOGO_TYPE = 0x0000022D
|
||||
CKA_GOSTR3410_PARAMS = 0x00000250
|
||||
CKA_GOSTR3411_PARAMS = 0x00000251
|
||||
CKA_GOST28147_PARAMS = 0x00000252
|
||||
CKA_HW_FEATURE_TYPE = 0x00000300
|
||||
CKA_RESET_ON_INIT = 0x00000301
|
||||
CKA_HAS_RESET = 0x00000302
|
||||
CKA_PIXEL_X = 0x00000400
|
||||
CKA_PIXEL_Y = 0x00000401
|
||||
CKA_RESOLUTION = 0x00000402
|
||||
CKA_CHAR_ROWS = 0x00000403
|
||||
CKA_CHAR_COLUMNS = 0x00000404
|
||||
CKA_COLOR = 0x00000405
|
||||
CKA_BITS_PER_PIXEL = 0x00000406
|
||||
CKA_CHAR_SETS = 0x00000480
|
||||
CKA_ENCODING_METHODS = 0x00000481
|
||||
CKA_MIME_TYPES = 0x00000482
|
||||
CKA_MECHANISM_TYPE = 0x00000500
|
||||
CKA_REQUIRED_CMS_ATTRIBUTES = 0x00000501
|
||||
CKA_DEFAULT_CMS_ATTRIBUTES = 0x00000502
|
||||
CKA_SUPPORTED_CMS_ATTRIBUTES = 0x00000503
|
||||
CKA_ALLOWED_MECHANISMS = CKF_ARRAY_ATTRIBUTE | 0x00000600
|
||||
CKA_VENDOR_DEFINED = 0x80000000
|
||||
CKM_RSA_PKCS_KEY_PAIR_GEN = 0x00000000
|
||||
CKM_RSA_PKCS = 0x00000001
|
||||
CKM_RSA_9796 = 0x00000002
|
||||
CKM_RSA_X_509 = 0x00000003
|
||||
CKM_MD2_RSA_PKCS = 0x00000004
|
||||
CKM_MD5_RSA_PKCS = 0x00000005
|
||||
CKM_SHA1_RSA_PKCS = 0x00000006
|
||||
CKM_RIPEMD128_RSA_PKCS = 0x00000007
|
||||
CKM_RIPEMD160_RSA_PKCS = 0x00000008
|
||||
CKM_RSA_PKCS_OAEP = 0x00000009
|
||||
CKM_RSA_X9_31_KEY_PAIR_GEN = 0x0000000A
|
||||
CKM_RSA_X9_31 = 0x0000000B
|
||||
CKM_SHA1_RSA_X9_31 = 0x0000000C
|
||||
CKM_RSA_PKCS_PSS = 0x0000000D
|
||||
CKM_SHA1_RSA_PKCS_PSS = 0x0000000E
|
||||
CKM_DSA_KEY_PAIR_GEN = 0x00000010
|
||||
CKM_DSA = 0x00000011
|
||||
CKM_DSA_SHA1 = 0x00000012
|
||||
CKM_DSA_FIPS_G_GEN = 0x00000013
|
||||
CKM_DSA_SHA224 = 0x00000014
|
||||
CKM_DSA_SHA256 = 0x00000015
|
||||
CKM_DSA_SHA384 = 0x00000016
|
||||
CKM_DSA_SHA512 = 0x00000017
|
||||
CKM_DSA_SHA3_224 = 0x00000018
|
||||
CKM_DSA_SHA3_256 = 0x00000019
|
||||
CKM_DSA_SHA3_384 = 0x0000001A
|
||||
CKM_DSA_SHA3_512 = 0x0000001B
|
||||
CKM_DH_PKCS_KEY_PAIR_GEN = 0x00000020
|
||||
CKM_DH_PKCS_DERIVE = 0x00000021
|
||||
CKM_X9_42_DH_KEY_PAIR_GEN = 0x00000030
|
||||
CKM_X9_42_DH_DERIVE = 0x00000031
|
||||
CKM_X9_42_DH_HYBRID_DERIVE = 0x00000032
|
||||
CKM_X9_42_MQV_DERIVE = 0x00000033
|
||||
CKM_SHA256_RSA_PKCS = 0x00000040
|
||||
CKM_SHA384_RSA_PKCS = 0x00000041
|
||||
CKM_SHA512_RSA_PKCS = 0x00000042
|
||||
CKM_SHA256_RSA_PKCS_PSS = 0x00000043
|
||||
CKM_SHA384_RSA_PKCS_PSS = 0x00000044
|
||||
CKM_SHA512_RSA_PKCS_PSS = 0x00000045
|
||||
CKM_SHA224_RSA_PKCS = 0x00000046
|
||||
CKM_SHA224_RSA_PKCS_PSS = 0x00000047
|
||||
CKM_SHA512_224 = 0x00000048
|
||||
CKM_SHA512_224_HMAC = 0x00000049
|
||||
CKM_SHA512_224_HMAC_GENERAL = 0x0000004A
|
||||
CKM_SHA512_224_KEY_DERIVATION = 0x0000004B
|
||||
CKM_SHA512_256 = 0x0000004C
|
||||
CKM_SHA512_256_HMAC = 0x0000004D
|
||||
CKM_SHA512_256_HMAC_GENERAL = 0x0000004E
|
||||
CKM_SHA512_256_KEY_DERIVATION = 0x0000004F
|
||||
CKM_SHA512_T = 0x00000050
|
||||
CKM_SHA512_T_HMAC = 0x00000051
|
||||
CKM_SHA512_T_HMAC_GENERAL = 0x00000052
|
||||
CKM_SHA512_T_KEY_DERIVATION = 0x00000053
|
||||
CKM_SHA3_256_RSA_PKCS = 0x00000060
|
||||
CKM_SHA3_384_RSA_PKCS = 0x00000061
|
||||
CKM_SHA3_512_RSA_PKCS = 0x00000062
|
||||
CKM_SHA3_256_RSA_PKCS_PSS = 0x00000063
|
||||
CKM_SHA3_384_RSA_PKCS_PSS = 0x00000064
|
||||
CKM_SHA3_512_RSA_PKCS_PSS = 0x00000065
|
||||
CKM_SHA3_224_RSA_PKCS = 0x00000066
|
||||
CKM_SHA3_224_RSA_PKCS_PSS = 0x00000067
|
||||
CKM_RC2_KEY_GEN = 0x00000100
|
||||
CKM_RC2_ECB = 0x00000101
|
||||
CKM_RC2_CBC = 0x00000102
|
||||
CKM_RC2_MAC = 0x00000103
|
||||
CKM_RC2_MAC_GENERAL = 0x00000104
|
||||
CKM_RC2_CBC_PAD = 0x00000105
|
||||
CKM_RC4_KEY_GEN = 0x00000110
|
||||
CKM_RC4 = 0x00000111
|
||||
CKM_DES_KEY_GEN = 0x00000120
|
||||
CKM_DES_ECB = 0x00000121
|
||||
CKM_DES_CBC = 0x00000122
|
||||
CKM_DES_MAC = 0x00000123
|
||||
CKM_DES_MAC_GENERAL = 0x00000124
|
||||
CKM_DES_CBC_PAD = 0x00000125
|
||||
CKM_DES2_KEY_GEN = 0x00000130
|
||||
CKM_DES3_KEY_GEN = 0x00000131
|
||||
CKM_DES3_ECB = 0x00000132
|
||||
CKM_DES3_CBC = 0x00000133
|
||||
CKM_DES3_MAC = 0x00000134
|
||||
CKM_DES3_MAC_GENERAL = 0x00000135
|
||||
CKM_DES3_CBC_PAD = 0x00000136
|
||||
CKM_DES3_CMAC_GENERAL = 0x00000137
|
||||
CKM_DES3_CMAC = 0x00000138
|
||||
CKM_CDMF_KEY_GEN = 0x00000140
|
||||
CKM_CDMF_ECB = 0x00000141
|
||||
CKM_CDMF_CBC = 0x00000142
|
||||
CKM_CDMF_MAC = 0x00000143
|
||||
CKM_CDMF_MAC_GENERAL = 0x00000144
|
||||
CKM_CDMF_CBC_PAD = 0x00000145
|
||||
CKM_DES_OFB64 = 0x00000150
|
||||
CKM_DES_OFB8 = 0x00000151
|
||||
CKM_DES_CFB64 = 0x00000152
|
||||
CKM_DES_CFB8 = 0x00000153
|
||||
CKM_MD2 = 0x00000200
|
||||
CKM_MD2_HMAC = 0x00000201
|
||||
CKM_MD2_HMAC_GENERAL = 0x00000202
|
||||
CKM_MD5 = 0x00000210
|
||||
CKM_MD5_HMAC = 0x00000211
|
||||
CKM_MD5_HMAC_GENERAL = 0x00000212
|
||||
CKM_SHA_1 = 0x00000220
|
||||
CKM_SHA_1_HMAC = 0x00000221
|
||||
CKM_SHA_1_HMAC_GENERAL = 0x00000222
|
||||
CKM_RIPEMD128 = 0x00000230
|
||||
CKM_RIPEMD128_HMAC = 0x00000231
|
||||
CKM_RIPEMD128_HMAC_GENERAL = 0x00000232
|
||||
CKM_RIPEMD160 = 0x00000240
|
||||
CKM_RIPEMD160_HMAC = 0x00000241
|
||||
CKM_RIPEMD160_HMAC_GENERAL = 0x00000242
|
||||
CKM_SHA256 = 0x00000250
|
||||
CKM_SHA256_HMAC = 0x00000251
|
||||
CKM_SHA256_HMAC_GENERAL = 0x00000252
|
||||
CKM_SHA224 = 0x00000255
|
||||
CKM_SHA224_HMAC = 0x00000256
|
||||
CKM_SHA224_HMAC_GENERAL = 0x00000257
|
||||
CKM_SHA384 = 0x00000260
|
||||
CKM_SHA384_HMAC = 0x00000261
|
||||
CKM_SHA384_HMAC_GENERAL = 0x00000262
|
||||
CKM_SHA512 = 0x00000270
|
||||
CKM_SHA512_HMAC = 0x00000271
|
||||
CKM_SHA512_HMAC_GENERAL = 0x00000272
|
||||
CKM_SECURID_KEY_GEN = 0x00000280
|
||||
CKM_SECURID = 0x00000282
|
||||
CKM_HOTP_KEY_GEN = 0x00000290
|
||||
CKM_HOTP = 0x00000291
|
||||
CKM_ACTI = 0x000002A0
|
||||
CKM_ACTI_KEY_GEN = 0x000002A1
|
||||
CKM_SHA3_256 = 0x000002B0
|
||||
CKM_SHA3_256_HMAC = 0x000002B1
|
||||
CKM_SHA3_256_HMAC_GENERAL = 0x000002B2
|
||||
CKM_SHA3_256_KEY_GEN = 0x000002B3
|
||||
CKM_SHA3_224 = 0x000002B5
|
||||
CKM_SHA3_224_HMAC = 0x000002B6
|
||||
CKM_SHA3_224_HMAC_GENERAL = 0x000002B7
|
||||
CKM_SHA3_224_KEY_GEN = 0x000002B8
|
||||
CKM_SHA3_384 = 0x000002C0
|
||||
CKM_SHA3_384_HMAC = 0x000002C1
|
||||
CKM_SHA3_384_HMAC_GENERAL = 0x000002C2
|
||||
CKM_SHA3_384_KEY_GEN = 0x000002C3
|
||||
CKM_SHA3_512 = 0x000002D0
|
||||
CKM_SHA3_512_HMAC = 0x000002D1
|
||||
CKM_SHA3_512_HMAC_GENERAL = 0x000002D2
|
||||
CKM_SHA3_512_KEY_GEN = 0x000002D3
|
||||
CKM_CAST_KEY_GEN = 0x00000300
|
||||
CKM_CAST_ECB = 0x00000301
|
||||
CKM_CAST_CBC = 0x00000302
|
||||
CKM_CAST_MAC = 0x00000303
|
||||
CKM_CAST_MAC_GENERAL = 0x00000304
|
||||
CKM_CAST_CBC_PAD = 0x00000305
|
||||
CKM_CAST3_KEY_GEN = 0x00000310
|
||||
CKM_CAST3_ECB = 0x00000311
|
||||
CKM_CAST3_CBC = 0x00000312
|
||||
CKM_CAST3_MAC = 0x00000313
|
||||
CKM_CAST3_MAC_GENERAL = 0x00000314
|
||||
CKM_CAST3_CBC_PAD = 0x00000315
|
||||
CKM_CAST5_KEY_GEN = 0x00000320
|
||||
CKM_CAST128_KEY_GEN = 0x00000320
|
||||
CKM_CAST5_ECB = 0x00000321
|
||||
CKM_CAST128_ECB = 0x00000321
|
||||
CKM_CAST5_CBC = 0x00000322
|
||||
CKM_CAST128_CBC = 0x00000322
|
||||
CKM_CAST5_MAC = 0x00000323
|
||||
CKM_CAST128_MAC = 0x00000323
|
||||
CKM_CAST5_MAC_GENERAL = 0x00000324
|
||||
CKM_CAST128_MAC_GENERAL = 0x00000324
|
||||
CKM_CAST5_CBC_PAD = 0x00000325
|
||||
CKM_CAST128_CBC_PAD = 0x00000325
|
||||
CKM_RC5_KEY_GEN = 0x00000330
|
||||
CKM_RC5_ECB = 0x00000331
|
||||
CKM_RC5_CBC = 0x00000332
|
||||
CKM_RC5_MAC = 0x00000333
|
||||
CKM_RC5_MAC_GENERAL = 0x00000334
|
||||
CKM_RC5_CBC_PAD = 0x00000335
|
||||
CKM_IDEA_KEY_GEN = 0x00000340
|
||||
CKM_IDEA_ECB = 0x00000341
|
||||
CKM_IDEA_CBC = 0x00000342
|
||||
CKM_IDEA_MAC = 0x00000343
|
||||
CKM_IDEA_MAC_GENERAL = 0x00000344
|
||||
CKM_IDEA_CBC_PAD = 0x00000345
|
||||
CKM_GENERIC_SECRET_KEY_GEN = 0x00000350
|
||||
CKM_CONCATENATE_BASE_AND_KEY = 0x00000360
|
||||
CKM_CONCATENATE_BASE_AND_DATA = 0x00000362
|
||||
CKM_CONCATENATE_DATA_AND_BASE = 0x00000363
|
||||
CKM_XOR_BASE_AND_DATA = 0x00000364
|
||||
CKM_EXTRACT_KEY_FROM_KEY = 0x00000365
|
||||
CKM_SSL3_PRE_MASTER_KEY_GEN = 0x00000370
|
||||
CKM_SSL3_MASTER_KEY_DERIVE = 0x00000371
|
||||
CKM_SSL3_KEY_AND_MAC_DERIVE = 0x00000372
|
||||
CKM_SSL3_MASTER_KEY_DERIVE_DH = 0x00000373
|
||||
CKM_TLS_PRE_MASTER_KEY_GEN = 0x00000374
|
||||
CKM_TLS_MASTER_KEY_DERIVE = 0x00000375
|
||||
CKM_TLS_KEY_AND_MAC_DERIVE = 0x00000376
|
||||
CKM_TLS_MASTER_KEY_DERIVE_DH = 0x00000377
|
||||
CKM_TLS_PRF = 0x00000378
|
||||
CKM_SSL3_MD5_MAC = 0x00000380
|
||||
CKM_SSL3_SHA1_MAC = 0x00000381
|
||||
CKM_MD5_KEY_DERIVATION = 0x00000390
|
||||
CKM_MD2_KEY_DERIVATION = 0x00000391
|
||||
CKM_SHA1_KEY_DERIVATION = 0x00000392
|
||||
CKM_SHA256_KEY_DERIVATION = 0x00000393
|
||||
CKM_SHA384_KEY_DERIVATION = 0x00000394
|
||||
CKM_SHA512_KEY_DERIVATION = 0x00000395
|
||||
CKM_SHA224_KEY_DERIVATION = 0x00000396
|
||||
CKM_SHA3_256_KEY_DERIVE = 0x00000397
|
||||
CKM_SHA3_224_KEY_DERIVE = 0x00000398
|
||||
CKM_SHA3_384_KEY_DERIVE = 0x00000399
|
||||
CKM_SHA3_512_KEY_DERIVE = 0x0000039A
|
||||
CKM_SHAKE_128_KEY_DERIVE = 0x0000039B
|
||||
CKM_SHAKE_256_KEY_DERIVE = 0x0000039C
|
||||
CKM_PBE_MD2_DES_CBC = 0x000003A0
|
||||
CKM_PBE_MD5_DES_CBC = 0x000003A1
|
||||
CKM_PBE_MD5_CAST_CBC = 0x000003A2
|
||||
CKM_PBE_MD5_CAST3_CBC = 0x000003A3
|
||||
CKM_PBE_MD5_CAST5_CBC = 0x000003A4
|
||||
CKM_PBE_MD5_CAST128_CBC = 0x000003A4
|
||||
CKM_PBE_SHA1_CAST5_CBC = 0x000003A5
|
||||
CKM_PBE_SHA1_CAST128_CBC = 0x000003A5
|
||||
CKM_PBE_SHA1_RC4_128 = 0x000003A6
|
||||
CKM_PBE_SHA1_RC4_40 = 0x000003A7
|
||||
CKM_PBE_SHA1_DES3_EDE_CBC = 0x000003A8
|
||||
CKM_PBE_SHA1_DES2_EDE_CBC = 0x000003A9
|
||||
CKM_PBE_SHA1_RC2_128_CBC = 0x000003AA
|
||||
CKM_PBE_SHA1_RC2_40_CBC = 0x000003AB
|
||||
CKM_PKCS5_PBKD2 = 0x000003B0
|
||||
CKM_PBA_SHA1_WITH_SHA1_HMAC = 0x000003C0
|
||||
CKM_WTLS_PRE_MASTER_KEY_GEN = 0x000003D0
|
||||
CKM_WTLS_MASTER_KEY_DERIVE = 0x000003D1
|
||||
CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC = 0x000003D2
|
||||
CKM_WTLS_PRF = 0x000003D3
|
||||
CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE = 0x000003D4
|
||||
CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE = 0x000003D5
|
||||
CKM_TLS10_MAC_SERVER = 0x000003D6
|
||||
CKM_TLS10_MAC_CLIENT = 0x000003D7
|
||||
CKM_TLS12_MAC = 0x000003D8
|
||||
CKM_TLS12_KDF = 0x000003D9
|
||||
CKM_TLS12_MASTER_KEY_DERIVE = 0x000003E0
|
||||
CKM_TLS12_KEY_AND_MAC_DERIVE = 0x000003E1
|
||||
CKM_TLS12_MASTER_KEY_DERIVE_DH = 0x000003E2
|
||||
CKM_TLS12_KEY_SAFE_DERIVE = 0x000003E3
|
||||
CKM_TLS_MAC = 0x000003E4
|
||||
CKM_TLS_KDF = 0x000003E5
|
||||
CKM_KEY_WRAP_LYNKS = 0x00000400
|
||||
CKM_KEY_WRAP_SET_OAEP = 0x00000401
|
||||
CKM_CMS_SIG = 0x00000500
|
||||
CKM_KIP_DERIVE = 0x00000510
|
||||
CKM_KIP_WRAP = 0x00000511
|
||||
CKM_KIP_MAC = 0x00000512
|
||||
CKM_CAMELLIA_KEY_GEN = 0x00000550
|
||||
CKM_CAMELLIA_ECB = 0x00000551
|
||||
CKM_CAMELLIA_CBC = 0x00000552
|
||||
CKM_CAMELLIA_MAC = 0x00000553
|
||||
CKM_CAMELLIA_MAC_GENERAL = 0x00000554
|
||||
CKM_CAMELLIA_CBC_PAD = 0x00000555
|
||||
CKM_CAMELLIA_ECB_ENCRYPT_DATA = 0x00000556
|
||||
CKM_CAMELLIA_CBC_ENCRYPT_DATA = 0x00000557
|
||||
CKM_CAMELLIA_CTR = 0x00000558
|
||||
CKM_ARIA_KEY_GEN = 0x00000560
|
||||
CKM_ARIA_ECB = 0x00000561
|
||||
CKM_ARIA_CBC = 0x00000562
|
||||
CKM_ARIA_MAC = 0x00000563
|
||||
CKM_ARIA_MAC_GENERAL = 0x00000564
|
||||
CKM_ARIA_CBC_PAD = 0x00000565
|
||||
CKM_ARIA_ECB_ENCRYPT_DATA = 0x00000566
|
||||
CKM_ARIA_CBC_ENCRYPT_DATA = 0x00000567
|
||||
CKM_SEED_KEY_GEN = 0x00000650
|
||||
CKM_SEED_ECB = 0x00000651
|
||||
CKM_SEED_CBC = 0x00000652
|
||||
CKM_SEED_MAC = 0x00000653
|
||||
CKM_SEED_MAC_GENERAL = 0x00000654
|
||||
CKM_SEED_CBC_PAD = 0x00000655
|
||||
CKM_SEED_ECB_ENCRYPT_DATA = 0x00000656
|
||||
CKM_SEED_CBC_ENCRYPT_DATA = 0x00000657
|
||||
CKM_SKIPJACK_KEY_GEN = 0x00001000
|
||||
CKM_SKIPJACK_ECB64 = 0x00001001
|
||||
CKM_SKIPJACK_CBC64 = 0x00001002
|
||||
CKM_SKIPJACK_OFB64 = 0x00001003
|
||||
CKM_SKIPJACK_CFB64 = 0x00001004
|
||||
CKM_SKIPJACK_CFB32 = 0x00001005
|
||||
CKM_SKIPJACK_CFB16 = 0x00001006
|
||||
CKM_SKIPJACK_CFB8 = 0x00001007
|
||||
CKM_SKIPJACK_WRAP = 0x00001008
|
||||
CKM_SKIPJACK_PRIVATE_WRAP = 0x00001009
|
||||
CKM_SKIPJACK_RELAYX = 0x0000100a
|
||||
CKM_KEA_KEY_PAIR_GEN = 0x00001010
|
||||
CKM_KEA_KEY_DERIVE = 0x00001011
|
||||
CKM_KEA_DERIVE = 0x00001012
|
||||
CKM_FORTEZZA_TIMESTAMP = 0x00001020
|
||||
CKM_BATON_KEY_GEN = 0x00001030
|
||||
CKM_BATON_ECB128 = 0x00001031
|
||||
CKM_BATON_ECB96 = 0x00001032
|
||||
CKM_BATON_CBC128 = 0x00001033
|
||||
CKM_BATON_COUNTER = 0x00001034
|
||||
CKM_BATON_SHUFFLE = 0x00001035
|
||||
CKM_BATON_WRAP = 0x00001036
|
||||
CKM_ECDSA_KEY_PAIR_GEN = 0x00001040
|
||||
CKM_EC_KEY_PAIR_GEN = 0x00001040
|
||||
CKM_ECDSA = 0x00001041
|
||||
CKM_ECDSA_SHA1 = 0x00001042
|
||||
CKM_ECDSA_SHA224 = 0x00001043
|
||||
CKM_ECDSA_SHA256 = 0x00001044
|
||||
CKM_ECDSA_SHA384 = 0x00001045
|
||||
CKM_ECDSA_SHA512 = 0x00001046
|
||||
CKM_ECDH1_DERIVE = 0x00001050
|
||||
CKM_ECDH1_COFACTOR_DERIVE = 0x00001051
|
||||
CKM_ECMQV_DERIVE = 0x00001052
|
||||
CKM_ECDH_AES_KEY_WRAP = 0x00001053
|
||||
CKM_RSA_AES_KEY_WRAP = 0x00001054
|
||||
CKM_JUNIPER_KEY_GEN = 0x00001060
|
||||
CKM_JUNIPER_ECB128 = 0x00001061
|
||||
CKM_JUNIPER_CBC128 = 0x00001062
|
||||
CKM_JUNIPER_COUNTER = 0x00001063
|
||||
CKM_JUNIPER_SHUFFLE = 0x00001064
|
||||
CKM_JUNIPER_WRAP = 0x00001065
|
||||
CKM_FASTHASH = 0x00001070
|
||||
CKM_AES_KEY_GEN = 0x00001080
|
||||
CKM_AES_ECB = 0x00001081
|
||||
CKM_AES_CBC = 0x00001082
|
||||
CKM_AES_MAC = 0x00001083
|
||||
CKM_AES_MAC_GENERAL = 0x00001084
|
||||
CKM_AES_CBC_PAD = 0x00001085
|
||||
CKM_AES_CTR = 0x00001086
|
||||
CKM_AES_GCM = 0x00001087
|
||||
CKM_AES_CCM = 0x00001088
|
||||
CKM_AES_CMAC_GENERAL = 0x00001089
|
||||
CKM_AES_CMAC = 0x0000108A
|
||||
CKM_AES_CTS = 0x0000108B
|
||||
CKM_AES_XCBC_MAC = 0x0000108C
|
||||
CKM_AES_XCBC_MAC_96 = 0x0000108D
|
||||
CKM_AES_GMAC = 0x0000108E
|
||||
CKM_BLOWFISH_KEY_GEN = 0x00001090
|
||||
CKM_BLOWFISH_CBC = 0x00001091
|
||||
CKM_TWOFISH_KEY_GEN = 0x00001092
|
||||
CKM_TWOFISH_CBC = 0x00001093
|
||||
CKM_BLOWFISH_CBC_PAD = 0x00001094
|
||||
CKM_TWOFISH_CBC_PAD = 0x00001095
|
||||
CKM_DES_ECB_ENCRYPT_DATA = 0x00001100
|
||||
CKM_DES_CBC_ENCRYPT_DATA = 0x00001101
|
||||
CKM_DES3_ECB_ENCRYPT_DATA = 0x00001102
|
||||
CKM_DES3_CBC_ENCRYPT_DATA = 0x00001103
|
||||
CKM_AES_ECB_ENCRYPT_DATA = 0x00001104
|
||||
CKM_AES_CBC_ENCRYPT_DATA = 0x00001105
|
||||
CKM_GOSTR3410_KEY_PAIR_GEN = 0x00001200
|
||||
CKM_GOSTR3410 = 0x00001201
|
||||
CKM_GOSTR3410_WITH_GOSTR3411 = 0x00001202
|
||||
CKM_GOSTR3410_KEY_WRAP = 0x00001203
|
||||
CKM_GOSTR3410_DERIVE = 0x00001204
|
||||
CKM_GOSTR3411 = 0x00001210
|
||||
CKM_GOSTR3411_HMAC = 0x00001211
|
||||
CKM_GOST28147_KEY_GEN = 0x00001220
|
||||
CKM_GOST28147_ECB = 0x00001221
|
||||
CKM_GOST28147 = 0x00001222
|
||||
CKM_GOST28147_MAC = 0x00001223
|
||||
CKM_GOST28147_KEY_WRAP = 0x00001224
|
||||
CKM_DSA_PARAMETER_GEN = 0x00002000
|
||||
CKM_DH_PKCS_PARAMETER_GEN = 0x00002001
|
||||
CKM_X9_42_DH_PARAMETER_GEN = 0x00002002
|
||||
CKM_DSA_PROBABLISTIC_PARAMETER_GEN = 0x00002003
|
||||
CKM_DSA_SHAWE_TAYLOR_PARAMETER_GEN = 0x00002004
|
||||
CKM_AES_OFB = 0x00002104
|
||||
CKM_AES_CFB64 = 0x00002105
|
||||
CKM_AES_CFB8 = 0x00002106
|
||||
CKM_AES_CFB128 = 0x00002107
|
||||
CKM_AES_CFB1 = 0x00002108
|
||||
CKM_AES_KEY_WRAP = 0x00002109
|
||||
CKM_AES_KEY_WRAP_PAD = 0x0000210A
|
||||
CKM_RSA_PKCS_TPM_1_1 = 0x00004001
|
||||
CKM_RSA_PKCS_OAEP_TPM_1_1 = 0x00004002
|
||||
CKM_VENDOR_DEFINED = 0x80000000
|
||||
CKF_HW = 0x00000001
|
||||
CKF_ENCRYPT = 0x00000100
|
||||
CKF_DECRYPT = 0x00000200
|
||||
CKF_DIGEST = 0x00000400
|
||||
CKF_SIGN = 0x00000800
|
||||
CKF_SIGN_RECOVER = 0x00001000
|
||||
CKF_VERIFY = 0x00002000
|
||||
CKF_VERIFY_RECOVER = 0x00004000
|
||||
CKF_GENERATE = 0x00008000
|
||||
CKF_GENERATE_KEY_PAIR = 0x00010000
|
||||
CKF_WRAP = 0x00020000
|
||||
CKF_UNWRAP = 0x00040000
|
||||
CKF_DERIVE = 0x00080000
|
||||
CKF_EC_F_P = 0x00100000
|
||||
CKF_EC_F_2M = 0x00200000
|
||||
CKF_EC_ECPARAMETERS = 0x00400000
|
||||
CKF_EC_NAMEDCURVE = 0x00800000
|
||||
CKF_EC_UNCOMPRESS = 0x01000000
|
||||
CKF_EC_COMPRESS = 0x02000000
|
||||
CKF_EXTENSION = 0x80000000
|
||||
CKR_OK = 0x00000000
|
||||
CKR_CANCEL = 0x00000001
|
||||
CKR_HOST_MEMORY = 0x00000002
|
||||
CKR_SLOT_ID_INVALID = 0x00000003
|
||||
CKR_GENERAL_ERROR = 0x00000005
|
||||
CKR_FUNCTION_FAILED = 0x00000006
|
||||
CKR_ARGUMENTS_BAD = 0x00000007
|
||||
CKR_NO_EVENT = 0x00000008
|
||||
CKR_NEED_TO_CREATE_THREADS = 0x00000009
|
||||
CKR_CANT_LOCK = 0x0000000A
|
||||
CKR_ATTRIBUTE_READ_ONLY = 0x00000010
|
||||
CKR_ATTRIBUTE_SENSITIVE = 0x00000011
|
||||
CKR_ATTRIBUTE_TYPE_INVALID = 0x00000012
|
||||
CKR_ATTRIBUTE_VALUE_INVALID = 0x00000013
|
||||
CKR_ACTION_PROHIBITED = 0x0000001B
|
||||
CKR_DATA_INVALID = 0x00000020
|
||||
CKR_DATA_LEN_RANGE = 0x00000021
|
||||
CKR_DEVICE_ERROR = 0x00000030
|
||||
CKR_DEVICE_MEMORY = 0x00000031
|
||||
CKR_DEVICE_REMOVED = 0x00000032
|
||||
CKR_ENCRYPTED_DATA_INVALID = 0x00000040
|
||||
CKR_ENCRYPTED_DATA_LEN_RANGE = 0x00000041
|
||||
CKR_FUNCTION_CANCELED = 0x00000050
|
||||
CKR_FUNCTION_NOT_PARALLEL = 0x00000051
|
||||
CKR_FUNCTION_NOT_SUPPORTED = 0x00000054
|
||||
CKR_KEY_HANDLE_INVALID = 0x00000060
|
||||
CKR_KEY_SIZE_RANGE = 0x00000062
|
||||
CKR_KEY_TYPE_INCONSISTENT = 0x00000063
|
||||
CKR_KEY_NOT_NEEDED = 0x00000064
|
||||
CKR_KEY_CHANGED = 0x00000065
|
||||
CKR_KEY_NEEDED = 0x00000066
|
||||
CKR_KEY_INDIGESTIBLE = 0x00000067
|
||||
CKR_KEY_FUNCTION_NOT_PERMITTED = 0x00000068
|
||||
CKR_KEY_NOT_WRAPPABLE = 0x00000069
|
||||
CKR_KEY_UNEXTRACTABLE = 0x0000006A
|
||||
CKR_MECHANISM_INVALID = 0x00000070
|
||||
CKR_MECHANISM_PARAM_INVALID = 0x00000071
|
||||
CKR_OBJECT_HANDLE_INVALID = 0x00000082
|
||||
CKR_OPERATION_ACTIVE = 0x00000090
|
||||
CKR_OPERATION_NOT_INITIALIZED = 0x00000091
|
||||
CKR_PIN_INCORRECT = 0x000000A0
|
||||
CKR_PIN_INVALID = 0x000000A1
|
||||
CKR_PIN_LEN_RANGE = 0x000000A2
|
||||
CKR_PIN_EXPIRED = 0x000000A3
|
||||
CKR_PIN_LOCKED = 0x000000A4
|
||||
CKR_SESSION_CLOSED = 0x000000B0
|
||||
CKR_SESSION_COUNT = 0x000000B1
|
||||
CKR_SESSION_HANDLE_INVALID = 0x000000B3
|
||||
CKR_SESSION_PARALLEL_NOT_SUPPORTED = 0x000000B4
|
||||
CKR_SESSION_READ_ONLY = 0x000000B5
|
||||
CKR_SESSION_EXISTS = 0x000000B6
|
||||
CKR_SESSION_READ_ONLY_EXISTS = 0x000000B7
|
||||
CKR_SESSION_READ_WRITE_SO_EXISTS = 0x000000B8
|
||||
CKR_SIGNATURE_INVALID = 0x000000C0
|
||||
CKR_SIGNATURE_LEN_RANGE = 0x000000C1
|
||||
CKR_TEMPLATE_INCOMPLETE = 0x000000D0
|
||||
CKR_TEMPLATE_INCONSISTENT = 0x000000D1
|
||||
CKR_TOKEN_NOT_PRESENT = 0x000000E0
|
||||
CKR_TOKEN_NOT_RECOGNIZED = 0x000000E1
|
||||
CKR_TOKEN_WRITE_PROTECTED = 0x000000E2
|
||||
CKR_UNWRAPPING_KEY_HANDLE_INVALID = 0x000000F0
|
||||
CKR_UNWRAPPING_KEY_SIZE_RANGE = 0x000000F1
|
||||
CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT = 0x000000F2
|
||||
CKR_USER_ALREADY_LOGGED_IN = 0x00000100
|
||||
CKR_USER_NOT_LOGGED_IN = 0x00000101
|
||||
CKR_USER_PIN_NOT_INITIALIZED = 0x00000102
|
||||
CKR_USER_TYPE_INVALID = 0x00000103
|
||||
CKR_USER_ANOTHER_ALREADY_LOGGED_IN = 0x00000104
|
||||
CKR_USER_TOO_MANY_TYPES = 0x00000105
|
||||
CKR_WRAPPED_KEY_INVALID = 0x00000110
|
||||
CKR_WRAPPED_KEY_LEN_RANGE = 0x00000112
|
||||
CKR_WRAPPING_KEY_HANDLE_INVALID = 0x00000113
|
||||
CKR_WRAPPING_KEY_SIZE_RANGE = 0x00000114
|
||||
CKR_WRAPPING_KEY_TYPE_INCONSISTENT = 0x00000115
|
||||
CKR_RANDOM_SEED_NOT_SUPPORTED = 0x00000120
|
||||
CKR_RANDOM_NO_RNG = 0x00000121
|
||||
CKR_DOMAIN_PARAMS_INVALID = 0x00000130
|
||||
CKR_CURVE_NOT_SUPPORTED = 0x00000140
|
||||
CKR_BUFFER_TOO_SMALL = 0x00000150
|
||||
CKR_SAVED_STATE_INVALID = 0x00000160
|
||||
CKR_INFORMATION_SENSITIVE = 0x00000170
|
||||
CKR_STATE_UNSAVEABLE = 0x00000180
|
||||
CKR_CRYPTOKI_NOT_INITIALIZED = 0x00000190
|
||||
CKR_CRYPTOKI_ALREADY_INITIALIZED = 0x00000191
|
||||
CKR_MUTEX_BAD = 0x000001A0
|
||||
CKR_MUTEX_NOT_LOCKED = 0x000001A1
|
||||
CKR_NEW_PIN_MODE = 0x000001B0
|
||||
CKR_NEXT_OTP = 0x000001B1
|
||||
CKR_EXCEEDED_MAX_ITERATIONS = 0x000001C0
|
||||
CKR_FIPS_SELF_TEST_FAILED = 0x000001C1
|
||||
CKR_LIBRARY_LOAD_FAILED = 0x000001C2
|
||||
CKR_PIN_TOO_WEAK = 0x000001C3
|
||||
CKR_PUBLIC_KEY_INVALID = 0x000001C4
|
||||
CKR_FUNCTION_REJECTED = 0x00000200
|
||||
CKR_VENDOR_DEFINED = 0x80000000
|
||||
CKF_LIBRARY_CANT_CREATE_OS_THREADS = 0x00000001
|
||||
CKF_OS_LOCKING_OK = 0x00000002
|
||||
CKF_DONT_BLOCK = 1
|
||||
CKF_NEXT_OTP = 0x00000001
|
||||
CKF_EXCLUDE_TIME = 0x00000002
|
||||
CKF_EXCLUDE_COUNTER = 0x00000004
|
||||
CKF_EXCLUDE_CHALLENGE = 0x00000008
|
||||
CKF_EXCLUDE_PIN = 0x00000010
|
||||
CKF_USER_FRIENDLY_OTP = 0x00000020
|
||||
CKD_NULL = 0x00000001
|
||||
CKD_SHA1_KDF = 0x00000002
|
||||
)
|
||||
|
||||
// Special return values defined in PKCS#11 v2.40 section 3.2.
|
||||
const (
|
||||
// CK_EFFECTIVELY_INFINITE may be returned in the CK_TOKEN_INFO fields ulMaxSessionCount and ulMaxRwSessionCount.
|
||||
// It indicates there is no practical limit on the number of sessions.
|
||||
CK_EFFECTIVELY_INFINITE = 0
|
||||
|
||||
// CK_UNAVAILABLE_INFORMATION may be returned for several fields within CK_TOKEN_INFO. It indicates
|
||||
// the token is unable or unwilling to provide the requested information.
|
||||
CK_UNAVAILABLE_INFORMATION = ^uint(0)
|
||||
)
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
// Copyright 2013 Miek Gieben. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pkcs11
|
||||
|
||||
// awk '/#define CKR_/{ print $3":\""$2"\"," }' pkcs11t.h
|
||||
|
||||
var strerror = map[uint]string{
|
||||
0x00000000: "CKR_OK",
|
||||
0x00000001: "CKR_CANCEL",
|
||||
0x00000002: "CKR_HOST_MEMORY",
|
||||
0x00000003: "CKR_SLOT_ID_INVALID",
|
||||
0x00000005: "CKR_GENERAL_ERROR",
|
||||
0x00000006: "CKR_FUNCTION_FAILED",
|
||||
0x00000007: "CKR_ARGUMENTS_BAD",
|
||||
0x00000008: "CKR_NO_EVENT",
|
||||
0x00000009: "CKR_NEED_TO_CREATE_THREADS",
|
||||
0x0000000A: "CKR_CANT_LOCK",
|
||||
0x00000010: "CKR_ATTRIBUTE_READ_ONLY",
|
||||
0x00000011: "CKR_ATTRIBUTE_SENSITIVE",
|
||||
0x00000012: "CKR_ATTRIBUTE_TYPE_INVALID",
|
||||
0x00000013: "CKR_ATTRIBUTE_VALUE_INVALID",
|
||||
0x00000020: "CKR_DATA_INVALID",
|
||||
0x00000021: "CKR_DATA_LEN_RANGE",
|
||||
0x00000030: "CKR_DEVICE_ERROR",
|
||||
0x00000031: "CKR_DEVICE_MEMORY",
|
||||
0x00000032: "CKR_DEVICE_REMOVED",
|
||||
0x00000040: "CKR_ENCRYPTED_DATA_INVALID",
|
||||
0x00000041: "CKR_ENCRYPTED_DATA_LEN_RANGE",
|
||||
0x00000050: "CKR_FUNCTION_CANCELED",
|
||||
0x00000051: "CKR_FUNCTION_NOT_PARALLEL",
|
||||
0x00000054: "CKR_FUNCTION_NOT_SUPPORTED",
|
||||
0x00000060: "CKR_KEY_HANDLE_INVALID",
|
||||
0x00000062: "CKR_KEY_SIZE_RANGE",
|
||||
0x00000063: "CKR_KEY_TYPE_INCONSISTENT",
|
||||
0x00000064: "CKR_KEY_NOT_NEEDED",
|
||||
0x00000065: "CKR_KEY_CHANGED",
|
||||
0x00000066: "CKR_KEY_NEEDED",
|
||||
0x00000067: "CKR_KEY_INDIGESTIBLE",
|
||||
0x00000068: "CKR_KEY_FUNCTION_NOT_PERMITTED",
|
||||
0x00000069: "CKR_KEY_NOT_WRAPPABLE",
|
||||
0x0000006A: "CKR_KEY_UNEXTRACTABLE",
|
||||
0x00000070: "CKR_MECHANISM_INVALID",
|
||||
0x00000071: "CKR_MECHANISM_PARAM_INVALID",
|
||||
0x00000082: "CKR_OBJECT_HANDLE_INVALID",
|
||||
0x00000090: "CKR_OPERATION_ACTIVE",
|
||||
0x00000091: "CKR_OPERATION_NOT_INITIALIZED",
|
||||
0x000000A0: "CKR_PIN_INCORRECT",
|
||||
0x000000A1: "CKR_PIN_INVALID",
|
||||
0x000000A2: "CKR_PIN_LEN_RANGE",
|
||||
0x000000A3: "CKR_PIN_EXPIRED",
|
||||
0x000000A4: "CKR_PIN_LOCKED",
|
||||
0x000000B0: "CKR_SESSION_CLOSED",
|
||||
0x000000B1: "CKR_SESSION_COUNT",
|
||||
0x000000B3: "CKR_SESSION_HANDLE_INVALID",
|
||||
0x000000B4: "CKR_SESSION_PARALLEL_NOT_SUPPORTED",
|
||||
0x000000B5: "CKR_SESSION_READ_ONLY",
|
||||
0x000000B6: "CKR_SESSION_EXISTS",
|
||||
0x000000B7: "CKR_SESSION_READ_ONLY_EXISTS",
|
||||
0x000000B8: "CKR_SESSION_READ_WRITE_SO_EXISTS",
|
||||
0x000000C0: "CKR_SIGNATURE_INVALID",
|
||||
0x000000C1: "CKR_SIGNATURE_LEN_RANGE",
|
||||
0x000000D0: "CKR_TEMPLATE_INCOMPLETE",
|
||||
0x000000D1: "CKR_TEMPLATE_INCONSISTENT",
|
||||
0x000000E0: "CKR_TOKEN_NOT_PRESENT",
|
||||
0x000000E1: "CKR_TOKEN_NOT_RECOGNIZED",
|
||||
0x000000E2: "CKR_TOKEN_WRITE_PROTECTED",
|
||||
0x000000F0: "CKR_UNWRAPPING_KEY_HANDLE_INVALID",
|
||||
0x000000F1: "CKR_UNWRAPPING_KEY_SIZE_RANGE",
|
||||
0x000000F2: "CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT",
|
||||
0x00000100: "CKR_USER_ALREADY_LOGGED_IN",
|
||||
0x00000101: "CKR_USER_NOT_LOGGED_IN",
|
||||
0x00000102: "CKR_USER_PIN_NOT_INITIALIZED",
|
||||
0x00000103: "CKR_USER_TYPE_INVALID",
|
||||
0x00000104: "CKR_USER_ANOTHER_ALREADY_LOGGED_IN",
|
||||
0x00000105: "CKR_USER_TOO_MANY_TYPES",
|
||||
0x00000110: "CKR_WRAPPED_KEY_INVALID",
|
||||
0x00000112: "CKR_WRAPPED_KEY_LEN_RANGE",
|
||||
0x00000113: "CKR_WRAPPING_KEY_HANDLE_INVALID",
|
||||
0x00000114: "CKR_WRAPPING_KEY_SIZE_RANGE",
|
||||
0x00000115: "CKR_WRAPPING_KEY_TYPE_INCONSISTENT",
|
||||
0x00000120: "CKR_RANDOM_SEED_NOT_SUPPORTED",
|
||||
0x00000121: "CKR_RANDOM_NO_RNG",
|
||||
0x00000130: "CKR_DOMAIN_PARAMS_INVALID",
|
||||
0x00000150: "CKR_BUFFER_TOO_SMALL",
|
||||
0x00000160: "CKR_SAVED_STATE_INVALID",
|
||||
0x00000170: "CKR_INFORMATION_SENSITIVE",
|
||||
0x00000180: "CKR_STATE_UNSAVEABLE",
|
||||
0x00000190: "CKR_CRYPTOKI_NOT_INITIALIZED",
|
||||
0x00000191: "CKR_CRYPTOKI_ALREADY_INITIALIZED",
|
||||
0x000001A0: "CKR_MUTEX_BAD",
|
||||
0x000001A1: "CKR_MUTEX_NOT_LOCKED",
|
||||
0x000001B0: "CKR_NEW_PIN_MODE",
|
||||
0x000001B1: "CKR_NEXT_OTP",
|
||||
0x00000200: "CKR_FUNCTION_REJECTED",
|
||||
0x80000000: "CKR_VENDOR_DEFINED",
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
module github.com/miekg/pkcs11
|
||||
|
||||
go 1.12
|
||||
BIN
Binary file not shown.
+190
@@ -0,0 +1,190 @@
|
||||
// Copyright 2013 Miek Gieben. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pkcs11
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "pkcs11go.h"
|
||||
|
||||
static inline void putOAEPParams(CK_RSA_PKCS_OAEP_PARAMS_PTR params, CK_VOID_PTR pSourceData, CK_ULONG ulSourceDataLen)
|
||||
{
|
||||
params->pSourceData = pSourceData;
|
||||
params->ulSourceDataLen = ulSourceDataLen;
|
||||
}
|
||||
|
||||
static inline void putECDH1SharedParams(CK_ECDH1_DERIVE_PARAMS_PTR params, CK_VOID_PTR pSharedData, CK_ULONG ulSharedDataLen)
|
||||
{
|
||||
params->pSharedData = pSharedData;
|
||||
params->ulSharedDataLen = ulSharedDataLen;
|
||||
}
|
||||
|
||||
static inline void putECDH1PublicParams(CK_ECDH1_DERIVE_PARAMS_PTR params, CK_VOID_PTR pPublicData, CK_ULONG ulPublicDataLen)
|
||||
{
|
||||
params->pPublicData = pPublicData;
|
||||
params->ulPublicDataLen = ulPublicDataLen;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
// GCMParams represents the parameters for the AES-GCM mechanism.
|
||||
type GCMParams struct {
|
||||
arena
|
||||
params *C.CK_GCM_PARAMS
|
||||
iv []byte
|
||||
aad []byte
|
||||
tagSize int
|
||||
}
|
||||
|
||||
// NewGCMParams returns a pointer to AES-GCM parameters that can be used with the CKM_AES_GCM mechanism.
|
||||
// The Free() method must be called after the operation is complete.
|
||||
//
|
||||
// Note that some HSMs, like CloudHSM, will ignore the IV you pass in and write their
|
||||
// own. As a result, to support all libraries, memory is not freed
|
||||
// automatically, so that after the EncryptInit/Encrypt operation the HSM's IV
|
||||
// can be read back out. It is up to the caller to ensure that Free() is called
|
||||
// on the GCMParams object at an appropriate time, which is after
|
||||
//
|
||||
// Encrypt/Decrypt. As an example:
|
||||
//
|
||||
// gcmParams := pkcs11.NewGCMParams(make([]byte, 12), nil, 128)
|
||||
// p.ctx.EncryptInit(session, []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_AES_GCM, gcmParams)},
|
||||
// aesObjHandle)
|
||||
// ct, _ := p.ctx.Encrypt(session, pt)
|
||||
// iv := gcmParams.IV()
|
||||
// gcmParams.Free()
|
||||
//
|
||||
func NewGCMParams(iv, aad []byte, tagSize int) *GCMParams {
|
||||
return &GCMParams{
|
||||
iv: iv,
|
||||
aad: aad,
|
||||
tagSize: tagSize,
|
||||
}
|
||||
}
|
||||
|
||||
func cGCMParams(p *GCMParams) []byte {
|
||||
params := C.CK_GCM_PARAMS{
|
||||
ulTagBits: C.CK_ULONG(p.tagSize),
|
||||
}
|
||||
var arena arena
|
||||
if len(p.iv) > 0 {
|
||||
iv, ivLen := arena.Allocate(p.iv)
|
||||
params.pIv = C.CK_BYTE_PTR(iv)
|
||||
params.ulIvLen = ivLen
|
||||
params.ulIvBits = ivLen * 8
|
||||
}
|
||||
if len(p.aad) > 0 {
|
||||
aad, aadLen := arena.Allocate(p.aad)
|
||||
params.pAAD = C.CK_BYTE_PTR(aad)
|
||||
params.ulAADLen = aadLen
|
||||
}
|
||||
p.Free()
|
||||
p.arena = arena
|
||||
p.params = ¶ms
|
||||
return C.GoBytes(unsafe.Pointer(¶ms), C.int(unsafe.Sizeof(params)))
|
||||
}
|
||||
|
||||
// IV returns a copy of the actual IV used for the operation.
|
||||
//
|
||||
// Some HSMs may ignore the user-specified IV and write their own at the end of
|
||||
// the encryption operation; this method allows you to retrieve it.
|
||||
func (p *GCMParams) IV() []byte {
|
||||
if p == nil || p.params == nil {
|
||||
return nil
|
||||
}
|
||||
newIv := C.GoBytes(unsafe.Pointer(p.params.pIv), C.int(p.params.ulIvLen))
|
||||
iv := make([]byte, len(newIv))
|
||||
copy(iv, newIv)
|
||||
return iv
|
||||
}
|
||||
|
||||
// Free deallocates the memory reserved for the HSM to write back the actual IV.
|
||||
//
|
||||
// This must be called after the entire operation is complete, i.e. after
|
||||
// Encrypt or EncryptFinal. It is safe to call Free multiple times.
|
||||
func (p *GCMParams) Free() {
|
||||
if p == nil || p.arena == nil {
|
||||
return
|
||||
}
|
||||
p.arena.Free()
|
||||
p.params = nil
|
||||
p.arena = nil
|
||||
}
|
||||
|
||||
// NewPSSParams creates a CK_RSA_PKCS_PSS_PARAMS structure and returns it as a byte array for use with the CKM_RSA_PKCS_PSS mechanism.
|
||||
func NewPSSParams(hashAlg, mgf, saltLength uint) []byte {
|
||||
p := C.CK_RSA_PKCS_PSS_PARAMS{
|
||||
hashAlg: C.CK_MECHANISM_TYPE(hashAlg),
|
||||
mgf: C.CK_RSA_PKCS_MGF_TYPE(mgf),
|
||||
sLen: C.CK_ULONG(saltLength),
|
||||
}
|
||||
return C.GoBytes(unsafe.Pointer(&p), C.int(unsafe.Sizeof(p)))
|
||||
}
|
||||
|
||||
// OAEPParams can be passed to NewMechanism to implement CKM_RSA_PKCS_OAEP.
|
||||
type OAEPParams struct {
|
||||
HashAlg uint
|
||||
MGF uint
|
||||
SourceType uint
|
||||
SourceData []byte
|
||||
}
|
||||
|
||||
// NewOAEPParams creates a CK_RSA_PKCS_OAEP_PARAMS structure suitable for use with the CKM_RSA_PKCS_OAEP mechanism.
|
||||
func NewOAEPParams(hashAlg, mgf, sourceType uint, sourceData []byte) *OAEPParams {
|
||||
return &OAEPParams{
|
||||
HashAlg: hashAlg,
|
||||
MGF: mgf,
|
||||
SourceType: sourceType,
|
||||
SourceData: sourceData,
|
||||
}
|
||||
}
|
||||
|
||||
func cOAEPParams(p *OAEPParams, arena arena) ([]byte, arena) {
|
||||
params := C.CK_RSA_PKCS_OAEP_PARAMS{
|
||||
hashAlg: C.CK_MECHANISM_TYPE(p.HashAlg),
|
||||
mgf: C.CK_RSA_PKCS_MGF_TYPE(p.MGF),
|
||||
source: C.CK_RSA_PKCS_OAEP_SOURCE_TYPE(p.SourceType),
|
||||
}
|
||||
if len(p.SourceData) != 0 {
|
||||
buf, len := arena.Allocate(p.SourceData)
|
||||
// field is unaligned on windows so this has to call into C
|
||||
C.putOAEPParams(¶ms, buf, len)
|
||||
}
|
||||
return C.GoBytes(unsafe.Pointer(¶ms), C.int(unsafe.Sizeof(params))), arena
|
||||
}
|
||||
|
||||
// ECDH1DeriveParams can be passed to NewMechanism to implement CK_ECDH1_DERIVE_PARAMS.
|
||||
type ECDH1DeriveParams struct {
|
||||
KDF uint
|
||||
SharedData []byte
|
||||
PublicKeyData []byte
|
||||
}
|
||||
|
||||
// NewECDH1DeriveParams creates a CK_ECDH1_DERIVE_PARAMS structure suitable for use with the CKM_ECDH1_DERIVE mechanism.
|
||||
func NewECDH1DeriveParams(kdf uint, sharedData []byte, publicKeyData []byte) *ECDH1DeriveParams {
|
||||
return &ECDH1DeriveParams{
|
||||
KDF: kdf,
|
||||
SharedData: sharedData,
|
||||
PublicKeyData: publicKeyData,
|
||||
}
|
||||
}
|
||||
|
||||
func cECDH1DeriveParams(p *ECDH1DeriveParams, arena arena) ([]byte, arena) {
|
||||
params := C.CK_ECDH1_DERIVE_PARAMS{
|
||||
kdf: C.CK_EC_KDF_TYPE(p.KDF),
|
||||
}
|
||||
|
||||
// SharedData MUST be null if key derivation function (KDF) is CKD_NULL
|
||||
if len(p.SharedData) != 0 {
|
||||
sharedData, sharedDataLen := arena.Allocate(p.SharedData)
|
||||
C.putECDH1SharedParams(¶ms, sharedData, sharedDataLen)
|
||||
}
|
||||
|
||||
publicKeyData, publicKeyDataLen := arena.Allocate(p.PublicKeyData)
|
||||
C.putECDH1PublicParams(¶ms, publicKeyData, publicKeyDataLen)
|
||||
|
||||
return C.GoBytes(unsafe.Pointer(¶ms), C.int(unsafe.Sizeof(params))), arena
|
||||
}
|
||||
+1606
File diff suppressed because it is too large
Load Diff
+265
@@ -0,0 +1,265 @@
|
||||
/* Copyright (c) OASIS Open 2016. All Rights Reserved./
|
||||
* /Distributed under the terms of the OASIS IPR Policy,
|
||||
* [http://www.oasis-open.org/policies-guidelines/ipr], AS-IS, WITHOUT ANY
|
||||
* IMPLIED OR EXPRESS WARRANTY; there is no warranty of MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE or NONINFRINGEMENT of the rights of others.
|
||||
*/
|
||||
|
||||
/* Latest version of the specification:
|
||||
* http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
|
||||
*/
|
||||
|
||||
#ifndef _PKCS11_H_
|
||||
#define _PKCS11_H_ 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Before including this file (pkcs11.h) (or pkcs11t.h by
|
||||
* itself), 5 platform-specific macros must be defined. These
|
||||
* macros are described below, and typical definitions for them
|
||||
* are also given. Be advised that these definitions can depend
|
||||
* on both the platform and the compiler used (and possibly also
|
||||
* on whether a Cryptoki library is linked statically or
|
||||
* dynamically).
|
||||
*
|
||||
* In addition to defining these 5 macros, the packing convention
|
||||
* for Cryptoki structures should be set. The Cryptoki
|
||||
* convention on packing is that structures should be 1-byte
|
||||
* aligned.
|
||||
*
|
||||
* If you're using Microsoft Developer Studio 5.0 to produce
|
||||
* Win32 stuff, this might be done by using the following
|
||||
* preprocessor directive before including pkcs11.h or pkcs11t.h:
|
||||
*
|
||||
* #pragma pack(push, cryptoki, 1)
|
||||
*
|
||||
* and using the following preprocessor directive after including
|
||||
* pkcs11.h or pkcs11t.h:
|
||||
*
|
||||
* #pragma pack(pop, cryptoki)
|
||||
*
|
||||
* If you're using an earlier version of Microsoft Developer
|
||||
* Studio to produce Win16 stuff, this might be done by using
|
||||
* the following preprocessor directive before including
|
||||
* pkcs11.h or pkcs11t.h:
|
||||
*
|
||||
* #pragma pack(1)
|
||||
*
|
||||
* In a UNIX environment, you're on your own for this. You might
|
||||
* not need to do (or be able to do!) anything.
|
||||
*
|
||||
*
|
||||
* Now for the macros:
|
||||
*
|
||||
*
|
||||
* 1. CK_PTR: The indirection string for making a pointer to an
|
||||
* object. It can be used like this:
|
||||
*
|
||||
* typedef CK_BYTE CK_PTR CK_BYTE_PTR;
|
||||
*
|
||||
* If you're using Microsoft Developer Studio 5.0 to produce
|
||||
* Win32 stuff, it might be defined by:
|
||||
*
|
||||
* #define CK_PTR *
|
||||
*
|
||||
* If you're using an earlier version of Microsoft Developer
|
||||
* Studio to produce Win16 stuff, it might be defined by:
|
||||
*
|
||||
* #define CK_PTR far *
|
||||
*
|
||||
* In a typical UNIX environment, it might be defined by:
|
||||
*
|
||||
* #define CK_PTR *
|
||||
*
|
||||
*
|
||||
* 2. CK_DECLARE_FUNCTION(returnType, name): A macro which makes
|
||||
* an importable Cryptoki library function declaration out of a
|
||||
* return type and a function name. It should be used in the
|
||||
* following fashion:
|
||||
*
|
||||
* extern CK_DECLARE_FUNCTION(CK_RV, C_Initialize)(
|
||||
* CK_VOID_PTR pReserved
|
||||
* );
|
||||
*
|
||||
* If you're using Microsoft Developer Studio 5.0 to declare a
|
||||
* function in a Win32 Cryptoki .dll, it might be defined by:
|
||||
*
|
||||
* #define CK_DECLARE_FUNCTION(returnType, name) \
|
||||
* returnType __declspec(dllimport) name
|
||||
*
|
||||
* If you're using an earlier version of Microsoft Developer
|
||||
* Studio to declare a function in a Win16 Cryptoki .dll, it
|
||||
* might be defined by:
|
||||
*
|
||||
* #define CK_DECLARE_FUNCTION(returnType, name) \
|
||||
* returnType __export _far _pascal name
|
||||
*
|
||||
* In a UNIX environment, it might be defined by:
|
||||
*
|
||||
* #define CK_DECLARE_FUNCTION(returnType, name) \
|
||||
* returnType name
|
||||
*
|
||||
*
|
||||
* 3. CK_DECLARE_FUNCTION_POINTER(returnType, name): A macro
|
||||
* which makes a Cryptoki API function pointer declaration or
|
||||
* function pointer type declaration out of a return type and a
|
||||
* function name. It should be used in the following fashion:
|
||||
*
|
||||
* // Define funcPtr to be a pointer to a Cryptoki API function
|
||||
* // taking arguments args and returning CK_RV.
|
||||
* CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtr)(args);
|
||||
*
|
||||
* or
|
||||
*
|
||||
* // Define funcPtrType to be the type of a pointer to a
|
||||
* // Cryptoki API function taking arguments args and returning
|
||||
* // CK_RV, and then define funcPtr to be a variable of type
|
||||
* // funcPtrType.
|
||||
* typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtrType)(args);
|
||||
* funcPtrType funcPtr;
|
||||
*
|
||||
* If you're using Microsoft Developer Studio 5.0 to access
|
||||
* functions in a Win32 Cryptoki .dll, in might be defined by:
|
||||
*
|
||||
* #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
|
||||
* returnType __declspec(dllimport) (* name)
|
||||
*
|
||||
* If you're using an earlier version of Microsoft Developer
|
||||
* Studio to access functions in a Win16 Cryptoki .dll, it might
|
||||
* be defined by:
|
||||
*
|
||||
* #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
|
||||
* returnType __export _far _pascal (* name)
|
||||
*
|
||||
* In a UNIX environment, it might be defined by:
|
||||
*
|
||||
* #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
|
||||
* returnType (* name)
|
||||
*
|
||||
*
|
||||
* 4. CK_CALLBACK_FUNCTION(returnType, name): A macro which makes
|
||||
* a function pointer type for an application callback out of
|
||||
* a return type for the callback and a name for the callback.
|
||||
* It should be used in the following fashion:
|
||||
*
|
||||
* CK_CALLBACK_FUNCTION(CK_RV, myCallback)(args);
|
||||
*
|
||||
* to declare a function pointer, myCallback, to a callback
|
||||
* which takes arguments args and returns a CK_RV. It can also
|
||||
* be used like this:
|
||||
*
|
||||
* typedef CK_CALLBACK_FUNCTION(CK_RV, myCallbackType)(args);
|
||||
* myCallbackType myCallback;
|
||||
*
|
||||
* If you're using Microsoft Developer Studio 5.0 to do Win32
|
||||
* Cryptoki development, it might be defined by:
|
||||
*
|
||||
* #define CK_CALLBACK_FUNCTION(returnType, name) \
|
||||
* returnType (* name)
|
||||
*
|
||||
* If you're using an earlier version of Microsoft Developer
|
||||
* Studio to do Win16 development, it might be defined by:
|
||||
*
|
||||
* #define CK_CALLBACK_FUNCTION(returnType, name) \
|
||||
* returnType _far _pascal (* name)
|
||||
*
|
||||
* In a UNIX environment, it might be defined by:
|
||||
*
|
||||
* #define CK_CALLBACK_FUNCTION(returnType, name) \
|
||||
* returnType (* name)
|
||||
*
|
||||
*
|
||||
* 5. NULL_PTR: This macro is the value of a NULL pointer.
|
||||
*
|
||||
* In any ANSI/ISO C environment (and in many others as well),
|
||||
* this should best be defined by
|
||||
*
|
||||
* #ifndef NULL_PTR
|
||||
* #define NULL_PTR 0
|
||||
* #endif
|
||||
*/
|
||||
|
||||
|
||||
/* All the various Cryptoki types and #define'd values are in the
|
||||
* file pkcs11t.h.
|
||||
*/
|
||||
#include "pkcs11t.h"
|
||||
|
||||
#define __PASTE(x,y) x##y
|
||||
|
||||
|
||||
/* ==============================================================
|
||||
* Define the "extern" form of all the entry points.
|
||||
* ==============================================================
|
||||
*/
|
||||
|
||||
#define CK_NEED_ARG_LIST 1
|
||||
#define CK_PKCS11_FUNCTION_INFO(name) \
|
||||
extern CK_DECLARE_FUNCTION(CK_RV, name)
|
||||
|
||||
/* pkcs11f.h has all the information about the Cryptoki
|
||||
* function prototypes.
|
||||
*/
|
||||
#include "pkcs11f.h"
|
||||
|
||||
#undef CK_NEED_ARG_LIST
|
||||
#undef CK_PKCS11_FUNCTION_INFO
|
||||
|
||||
|
||||
/* ==============================================================
|
||||
* Define the typedef form of all the entry points. That is, for
|
||||
* each Cryptoki function C_XXX, define a type CK_C_XXX which is
|
||||
* a pointer to that kind of function.
|
||||
* ==============================================================
|
||||
*/
|
||||
|
||||
#define CK_NEED_ARG_LIST 1
|
||||
#define CK_PKCS11_FUNCTION_INFO(name) \
|
||||
typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, __PASTE(CK_,name))
|
||||
|
||||
/* pkcs11f.h has all the information about the Cryptoki
|
||||
* function prototypes.
|
||||
*/
|
||||
#include "pkcs11f.h"
|
||||
|
||||
#undef CK_NEED_ARG_LIST
|
||||
#undef CK_PKCS11_FUNCTION_INFO
|
||||
|
||||
|
||||
/* ==============================================================
|
||||
* Define structed vector of entry points. A CK_FUNCTION_LIST
|
||||
* contains a CK_VERSION indicating a library's Cryptoki version
|
||||
* and then a whole slew of function pointers to the routines in
|
||||
* the library. This type was declared, but not defined, in
|
||||
* pkcs11t.h.
|
||||
* ==============================================================
|
||||
*/
|
||||
|
||||
#define CK_PKCS11_FUNCTION_INFO(name) \
|
||||
__PASTE(CK_,name) name;
|
||||
|
||||
struct CK_FUNCTION_LIST {
|
||||
|
||||
CK_VERSION version; /* Cryptoki version */
|
||||
|
||||
/* Pile all the function pointers into the CK_FUNCTION_LIST. */
|
||||
/* pkcs11f.h has all the information about the Cryptoki
|
||||
* function prototypes.
|
||||
*/
|
||||
#include "pkcs11f.h"
|
||||
|
||||
};
|
||||
|
||||
#undef CK_PKCS11_FUNCTION_INFO
|
||||
|
||||
|
||||
#undef __PASTE
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _PKCS11_H_ */
|
||||
|
||||
+939
@@ -0,0 +1,939 @@
|
||||
/* Copyright (c) OASIS Open 2016. All Rights Reserved./
|
||||
* /Distributed under the terms of the OASIS IPR Policy,
|
||||
* [http://www.oasis-open.org/policies-guidelines/ipr], AS-IS, WITHOUT ANY
|
||||
* IMPLIED OR EXPRESS WARRANTY; there is no warranty of MERCHANTABILITY, FITNESS FOR A
|
||||
* PARTICULAR PURPOSE or NONINFRINGEMENT of the rights of others.
|
||||
*/
|
||||
|
||||
/* Latest version of the specification:
|
||||
* http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html
|
||||
*/
|
||||
|
||||
/* This header file contains pretty much everything about all the
|
||||
* Cryptoki function prototypes. Because this information is
|
||||
* used for more than just declaring function prototypes, the
|
||||
* order of the functions appearing herein is important, and
|
||||
* should not be altered.
|
||||
*/
|
||||
|
||||
/* General-purpose */
|
||||
|
||||
/* C_Initialize initializes the Cryptoki library. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_Initialize)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_VOID_PTR pInitArgs /* if this is not NULL_PTR, it gets
|
||||
* cast to CK_C_INITIALIZE_ARGS_PTR
|
||||
* and dereferenced
|
||||
*/
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_Finalize indicates that an application is done with the
|
||||
* Cryptoki library.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_Finalize)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_VOID_PTR pReserved /* reserved. Should be NULL_PTR */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_GetInfo returns general information about Cryptoki. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_GetInfo)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_INFO_PTR pInfo /* location that receives information */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_GetFunctionList returns the function list. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_GetFunctionList)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_FUNCTION_LIST_PTR_PTR ppFunctionList /* receives pointer to
|
||||
* function list
|
||||
*/
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Slot and token management */
|
||||
|
||||
/* C_GetSlotList obtains a list of slots in the system. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_GetSlotList)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_BBOOL tokenPresent, /* only slots with tokens */
|
||||
CK_SLOT_ID_PTR pSlotList, /* receives array of slot IDs */
|
||||
CK_ULONG_PTR pulCount /* receives number of slots */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_GetSlotInfo obtains information about a particular slot in
|
||||
* the system.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_GetSlotInfo)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SLOT_ID slotID, /* the ID of the slot */
|
||||
CK_SLOT_INFO_PTR pInfo /* receives the slot information */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_GetTokenInfo obtains information about a particular token
|
||||
* in the system.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_GetTokenInfo)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SLOT_ID slotID, /* ID of the token's slot */
|
||||
CK_TOKEN_INFO_PTR pInfo /* receives the token information */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_GetMechanismList obtains a list of mechanism types
|
||||
* supported by a token.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_GetMechanismList)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SLOT_ID slotID, /* ID of token's slot */
|
||||
CK_MECHANISM_TYPE_PTR pMechanismList, /* gets mech. array */
|
||||
CK_ULONG_PTR pulCount /* gets # of mechs. */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_GetMechanismInfo obtains information about a particular
|
||||
* mechanism possibly supported by a token.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_GetMechanismInfo)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SLOT_ID slotID, /* ID of the token's slot */
|
||||
CK_MECHANISM_TYPE type, /* type of mechanism */
|
||||
CK_MECHANISM_INFO_PTR pInfo /* receives mechanism info */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_InitToken initializes a token. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_InitToken)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SLOT_ID slotID, /* ID of the token's slot */
|
||||
CK_UTF8CHAR_PTR pPin, /* the SO's initial PIN */
|
||||
CK_ULONG ulPinLen, /* length in bytes of the PIN */
|
||||
CK_UTF8CHAR_PTR pLabel /* 32-byte token label (blank padded) */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_InitPIN initializes the normal user's PIN. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_InitPIN)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_UTF8CHAR_PTR pPin, /* the normal user's PIN */
|
||||
CK_ULONG ulPinLen /* length in bytes of the PIN */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_SetPIN modifies the PIN of the user who is logged in. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_SetPIN)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_UTF8CHAR_PTR pOldPin, /* the old PIN */
|
||||
CK_ULONG ulOldLen, /* length of the old PIN */
|
||||
CK_UTF8CHAR_PTR pNewPin, /* the new PIN */
|
||||
CK_ULONG ulNewLen /* length of the new PIN */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Session management */
|
||||
|
||||
/* C_OpenSession opens a session between an application and a
|
||||
* token.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_OpenSession)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SLOT_ID slotID, /* the slot's ID */
|
||||
CK_FLAGS flags, /* from CK_SESSION_INFO */
|
||||
CK_VOID_PTR pApplication, /* passed to callback */
|
||||
CK_NOTIFY Notify, /* callback function */
|
||||
CK_SESSION_HANDLE_PTR phSession /* gets session handle */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_CloseSession closes a session between an application and a
|
||||
* token.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_CloseSession)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession /* the session's handle */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_CloseAllSessions closes all sessions with a token. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_CloseAllSessions)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SLOT_ID slotID /* the token's slot */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_GetSessionInfo obtains information about the session. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_GetSessionInfo)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_SESSION_INFO_PTR pInfo /* receives session info */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_GetOperationState obtains the state of the cryptographic operation
|
||||
* in a session.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_GetOperationState)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session's handle */
|
||||
CK_BYTE_PTR pOperationState, /* gets state */
|
||||
CK_ULONG_PTR pulOperationStateLen /* gets state length */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_SetOperationState restores the state of the cryptographic
|
||||
* operation in a session.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_SetOperationState)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session's handle */
|
||||
CK_BYTE_PTR pOperationState, /* holds state */
|
||||
CK_ULONG ulOperationStateLen, /* holds state length */
|
||||
CK_OBJECT_HANDLE hEncryptionKey, /* en/decryption key */
|
||||
CK_OBJECT_HANDLE hAuthenticationKey /* sign/verify key */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_Login logs a user into a token. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_Login)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_USER_TYPE userType, /* the user type */
|
||||
CK_UTF8CHAR_PTR pPin, /* the user's PIN */
|
||||
CK_ULONG ulPinLen /* the length of the PIN */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_Logout logs a user out from a token. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_Logout)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession /* the session's handle */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Object management */
|
||||
|
||||
/* C_CreateObject creates a new object. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_CreateObject)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_ATTRIBUTE_PTR pTemplate, /* the object's template */
|
||||
CK_ULONG ulCount, /* attributes in template */
|
||||
CK_OBJECT_HANDLE_PTR phObject /* gets new object's handle. */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_CopyObject copies an object, creating a new object for the
|
||||
* copy.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_CopyObject)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_OBJECT_HANDLE hObject, /* the object's handle */
|
||||
CK_ATTRIBUTE_PTR pTemplate, /* template for new object */
|
||||
CK_ULONG ulCount, /* attributes in template */
|
||||
CK_OBJECT_HANDLE_PTR phNewObject /* receives handle of copy */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_DestroyObject destroys an object. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_DestroyObject)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_OBJECT_HANDLE hObject /* the object's handle */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_GetObjectSize gets the size of an object in bytes. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_GetObjectSize)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_OBJECT_HANDLE hObject, /* the object's handle */
|
||||
CK_ULONG_PTR pulSize /* receives size of object */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_GetAttributeValue obtains the value of one or more object
|
||||
* attributes.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_GetAttributeValue)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_OBJECT_HANDLE hObject, /* the object's handle */
|
||||
CK_ATTRIBUTE_PTR pTemplate, /* specifies attrs; gets vals */
|
||||
CK_ULONG ulCount /* attributes in template */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_SetAttributeValue modifies the value of one or more object
|
||||
* attributes.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_SetAttributeValue)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_OBJECT_HANDLE hObject, /* the object's handle */
|
||||
CK_ATTRIBUTE_PTR pTemplate, /* specifies attrs and values */
|
||||
CK_ULONG ulCount /* attributes in template */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_FindObjectsInit initializes a search for token and session
|
||||
* objects that match a template.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_FindObjectsInit)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_ATTRIBUTE_PTR pTemplate, /* attribute values to match */
|
||||
CK_ULONG ulCount /* attrs in search template */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_FindObjects continues a search for token and session
|
||||
* objects that match a template, obtaining additional object
|
||||
* handles.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_FindObjects)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session's handle */
|
||||
CK_OBJECT_HANDLE_PTR phObject, /* gets obj. handles */
|
||||
CK_ULONG ulMaxObjectCount, /* max handles to get */
|
||||
CK_ULONG_PTR pulObjectCount /* actual # returned */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_FindObjectsFinal finishes a search for token and session
|
||||
* objects.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_FindObjectsFinal)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession /* the session's handle */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Encryption and decryption */
|
||||
|
||||
/* C_EncryptInit initializes an encryption operation. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_EncryptInit)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_MECHANISM_PTR pMechanism, /* the encryption mechanism */
|
||||
CK_OBJECT_HANDLE hKey /* handle of encryption key */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_Encrypt encrypts single-part data. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_Encrypt)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session's handle */
|
||||
CK_BYTE_PTR pData, /* the plaintext data */
|
||||
CK_ULONG ulDataLen, /* bytes of plaintext */
|
||||
CK_BYTE_PTR pEncryptedData, /* gets ciphertext */
|
||||
CK_ULONG_PTR pulEncryptedDataLen /* gets c-text size */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_EncryptUpdate continues a multiple-part encryption
|
||||
* operation.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_EncryptUpdate)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session's handle */
|
||||
CK_BYTE_PTR pPart, /* the plaintext data */
|
||||
CK_ULONG ulPartLen, /* plaintext data len */
|
||||
CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
|
||||
CK_ULONG_PTR pulEncryptedPartLen /* gets c-text size */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_EncryptFinal finishes a multiple-part encryption
|
||||
* operation.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_EncryptFinal)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session handle */
|
||||
CK_BYTE_PTR pLastEncryptedPart, /* last c-text */
|
||||
CK_ULONG_PTR pulLastEncryptedPartLen /* gets last size */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_DecryptInit initializes a decryption operation. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_DecryptInit)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_MECHANISM_PTR pMechanism, /* the decryption mechanism */
|
||||
CK_OBJECT_HANDLE hKey /* handle of decryption key */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_Decrypt decrypts encrypted data in a single part. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_Decrypt)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session's handle */
|
||||
CK_BYTE_PTR pEncryptedData, /* ciphertext */
|
||||
CK_ULONG ulEncryptedDataLen, /* ciphertext length */
|
||||
CK_BYTE_PTR pData, /* gets plaintext */
|
||||
CK_ULONG_PTR pulDataLen /* gets p-text size */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_DecryptUpdate continues a multiple-part decryption
|
||||
* operation.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_DecryptUpdate)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session's handle */
|
||||
CK_BYTE_PTR pEncryptedPart, /* encrypted data */
|
||||
CK_ULONG ulEncryptedPartLen, /* input length */
|
||||
CK_BYTE_PTR pPart, /* gets plaintext */
|
||||
CK_ULONG_PTR pulPartLen /* p-text size */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_DecryptFinal finishes a multiple-part decryption
|
||||
* operation.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_DecryptFinal)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_BYTE_PTR pLastPart, /* gets plaintext */
|
||||
CK_ULONG_PTR pulLastPartLen /* p-text size */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Message digesting */
|
||||
|
||||
/* C_DigestInit initializes a message-digesting operation. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_DigestInit)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_MECHANISM_PTR pMechanism /* the digesting mechanism */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_Digest digests data in a single part. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_Digest)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_BYTE_PTR pData, /* data to be digested */
|
||||
CK_ULONG ulDataLen, /* bytes of data to digest */
|
||||
CK_BYTE_PTR pDigest, /* gets the message digest */
|
||||
CK_ULONG_PTR pulDigestLen /* gets digest length */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_DigestUpdate continues a multiple-part message-digesting
|
||||
* operation.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_DigestUpdate)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_BYTE_PTR pPart, /* data to be digested */
|
||||
CK_ULONG ulPartLen /* bytes of data to be digested */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_DigestKey continues a multi-part message-digesting
|
||||
* operation, by digesting the value of a secret key as part of
|
||||
* the data already digested.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_DigestKey)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_OBJECT_HANDLE hKey /* secret key to digest */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_DigestFinal finishes a multiple-part message-digesting
|
||||
* operation.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_DigestFinal)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_BYTE_PTR pDigest, /* gets the message digest */
|
||||
CK_ULONG_PTR pulDigestLen /* gets byte count of digest */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Signing and MACing */
|
||||
|
||||
/* C_SignInit initializes a signature (private key encryption)
|
||||
* operation, where the signature is (will be) an appendix to
|
||||
* the data, and plaintext cannot be recovered from the
|
||||
* signature.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_SignInit)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_MECHANISM_PTR pMechanism, /* the signature mechanism */
|
||||
CK_OBJECT_HANDLE hKey /* handle of signature key */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_Sign signs (encrypts with private key) data in a single
|
||||
* part, where the signature is (will be) an appendix to the
|
||||
* data, and plaintext cannot be recovered from the signature.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_Sign)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_BYTE_PTR pData, /* the data to sign */
|
||||
CK_ULONG ulDataLen, /* count of bytes to sign */
|
||||
CK_BYTE_PTR pSignature, /* gets the signature */
|
||||
CK_ULONG_PTR pulSignatureLen /* gets signature length */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_SignUpdate continues a multiple-part signature operation,
|
||||
* where the signature is (will be) an appendix to the data,
|
||||
* and plaintext cannot be recovered from the signature.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_SignUpdate)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_BYTE_PTR pPart, /* the data to sign */
|
||||
CK_ULONG ulPartLen /* count of bytes to sign */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_SignFinal finishes a multiple-part signature operation,
|
||||
* returning the signature.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_SignFinal)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_BYTE_PTR pSignature, /* gets the signature */
|
||||
CK_ULONG_PTR pulSignatureLen /* gets signature length */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_SignRecoverInit initializes a signature operation, where
|
||||
* the data can be recovered from the signature.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_SignRecoverInit)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_MECHANISM_PTR pMechanism, /* the signature mechanism */
|
||||
CK_OBJECT_HANDLE hKey /* handle of the signature key */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_SignRecover signs data in a single operation, where the
|
||||
* data can be recovered from the signature.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_SignRecover)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_BYTE_PTR pData, /* the data to sign */
|
||||
CK_ULONG ulDataLen, /* count of bytes to sign */
|
||||
CK_BYTE_PTR pSignature, /* gets the signature */
|
||||
CK_ULONG_PTR pulSignatureLen /* gets signature length */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Verifying signatures and MACs */
|
||||
|
||||
/* C_VerifyInit initializes a verification operation, where the
|
||||
* signature is an appendix to the data, and plaintext cannot
|
||||
* cannot be recovered from the signature (e.g. DSA).
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_VerifyInit)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_MECHANISM_PTR pMechanism, /* the verification mechanism */
|
||||
CK_OBJECT_HANDLE hKey /* verification key */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_Verify verifies a signature in a single-part operation,
|
||||
* where the signature is an appendix to the data, and plaintext
|
||||
* cannot be recovered from the signature.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_Verify)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_BYTE_PTR pData, /* signed data */
|
||||
CK_ULONG ulDataLen, /* length of signed data */
|
||||
CK_BYTE_PTR pSignature, /* signature */
|
||||
CK_ULONG ulSignatureLen /* signature length*/
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_VerifyUpdate continues a multiple-part verification
|
||||
* operation, where the signature is an appendix to the data,
|
||||
* and plaintext cannot be recovered from the signature.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_VerifyUpdate)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_BYTE_PTR pPart, /* signed data */
|
||||
CK_ULONG ulPartLen /* length of signed data */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_VerifyFinal finishes a multiple-part verification
|
||||
* operation, checking the signature.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_VerifyFinal)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_BYTE_PTR pSignature, /* signature to verify */
|
||||
CK_ULONG ulSignatureLen /* signature length */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_VerifyRecoverInit initializes a signature verification
|
||||
* operation, where the data is recovered from the signature.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_VerifyRecoverInit)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_MECHANISM_PTR pMechanism, /* the verification mechanism */
|
||||
CK_OBJECT_HANDLE hKey /* verification key */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_VerifyRecover verifies a signature in a single-part
|
||||
* operation, where the data is recovered from the signature.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_VerifyRecover)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_BYTE_PTR pSignature, /* signature to verify */
|
||||
CK_ULONG ulSignatureLen, /* signature length */
|
||||
CK_BYTE_PTR pData, /* gets signed data */
|
||||
CK_ULONG_PTR pulDataLen /* gets signed data len */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Dual-function cryptographic operations */
|
||||
|
||||
/* C_DigestEncryptUpdate continues a multiple-part digesting
|
||||
* and encryption operation.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_DigestEncryptUpdate)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session's handle */
|
||||
CK_BYTE_PTR pPart, /* the plaintext data */
|
||||
CK_ULONG ulPartLen, /* plaintext length */
|
||||
CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
|
||||
CK_ULONG_PTR pulEncryptedPartLen /* gets c-text length */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_DecryptDigestUpdate continues a multiple-part decryption and
|
||||
* digesting operation.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_DecryptDigestUpdate)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session's handle */
|
||||
CK_BYTE_PTR pEncryptedPart, /* ciphertext */
|
||||
CK_ULONG ulEncryptedPartLen, /* ciphertext length */
|
||||
CK_BYTE_PTR pPart, /* gets plaintext */
|
||||
CK_ULONG_PTR pulPartLen /* gets plaintext len */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_SignEncryptUpdate continues a multiple-part signing and
|
||||
* encryption operation.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_SignEncryptUpdate)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session's handle */
|
||||
CK_BYTE_PTR pPart, /* the plaintext data */
|
||||
CK_ULONG ulPartLen, /* plaintext length */
|
||||
CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
|
||||
CK_ULONG_PTR pulEncryptedPartLen /* gets c-text length */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_DecryptVerifyUpdate continues a multiple-part decryption and
|
||||
* verify operation.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_DecryptVerifyUpdate)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session's handle */
|
||||
CK_BYTE_PTR pEncryptedPart, /* ciphertext */
|
||||
CK_ULONG ulEncryptedPartLen, /* ciphertext length */
|
||||
CK_BYTE_PTR pPart, /* gets plaintext */
|
||||
CK_ULONG_PTR pulPartLen /* gets p-text length */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Key management */
|
||||
|
||||
/* C_GenerateKey generates a secret key, creating a new key
|
||||
* object.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_GenerateKey)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_MECHANISM_PTR pMechanism, /* key generation mech. */
|
||||
CK_ATTRIBUTE_PTR pTemplate, /* template for new key */
|
||||
CK_ULONG ulCount, /* # of attrs in template */
|
||||
CK_OBJECT_HANDLE_PTR phKey /* gets handle of new key */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_GenerateKeyPair generates a public-key/private-key pair,
|
||||
* creating new key objects.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_GenerateKeyPair)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session handle */
|
||||
CK_MECHANISM_PTR pMechanism, /* key-gen mech. */
|
||||
CK_ATTRIBUTE_PTR pPublicKeyTemplate, /* template for pub. key */
|
||||
CK_ULONG ulPublicKeyAttributeCount, /* # pub. attrs. */
|
||||
CK_ATTRIBUTE_PTR pPrivateKeyTemplate, /* template for priv. key */
|
||||
CK_ULONG ulPrivateKeyAttributeCount, /* # priv. attrs. */
|
||||
CK_OBJECT_HANDLE_PTR phPublicKey, /* gets pub. key handle */
|
||||
CK_OBJECT_HANDLE_PTR phPrivateKey /* gets priv. key handle */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_WrapKey wraps (i.e., encrypts) a key. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_WrapKey)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_MECHANISM_PTR pMechanism, /* the wrapping mechanism */
|
||||
CK_OBJECT_HANDLE hWrappingKey, /* wrapping key */
|
||||
CK_OBJECT_HANDLE hKey, /* key to be wrapped */
|
||||
CK_BYTE_PTR pWrappedKey, /* gets wrapped key */
|
||||
CK_ULONG_PTR pulWrappedKeyLen /* gets wrapped key size */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_UnwrapKey unwraps (decrypts) a wrapped key, creating a new
|
||||
* key object.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_UnwrapKey)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session's handle */
|
||||
CK_MECHANISM_PTR pMechanism, /* unwrapping mech. */
|
||||
CK_OBJECT_HANDLE hUnwrappingKey, /* unwrapping key */
|
||||
CK_BYTE_PTR pWrappedKey, /* the wrapped key */
|
||||
CK_ULONG ulWrappedKeyLen, /* wrapped key len */
|
||||
CK_ATTRIBUTE_PTR pTemplate, /* new key template */
|
||||
CK_ULONG ulAttributeCount, /* template length */
|
||||
CK_OBJECT_HANDLE_PTR phKey /* gets new handle */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_DeriveKey derives a key from a base key, creating a new key
|
||||
* object.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_DeriveKey)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* session's handle */
|
||||
CK_MECHANISM_PTR pMechanism, /* key deriv. mech. */
|
||||
CK_OBJECT_HANDLE hBaseKey, /* base key */
|
||||
CK_ATTRIBUTE_PTR pTemplate, /* new key template */
|
||||
CK_ULONG ulAttributeCount, /* template length */
|
||||
CK_OBJECT_HANDLE_PTR phKey /* gets new handle */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Random number generation */
|
||||
|
||||
/* C_SeedRandom mixes additional seed material into the token's
|
||||
* random number generator.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_SeedRandom)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_BYTE_PTR pSeed, /* the seed material */
|
||||
CK_ULONG ulSeedLen /* length of seed material */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_GenerateRandom generates random data. */
|
||||
CK_PKCS11_FUNCTION_INFO(C_GenerateRandom)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession, /* the session's handle */
|
||||
CK_BYTE_PTR RandomData, /* receives the random data */
|
||||
CK_ULONG ulRandomLen /* # of bytes to generate */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Parallel function management */
|
||||
|
||||
/* C_GetFunctionStatus is a legacy function; it obtains an
|
||||
* updated status of a function running in parallel with an
|
||||
* application.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_GetFunctionStatus)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession /* the session's handle */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_CancelFunction is a legacy function; it cancels a function
|
||||
* running in parallel.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_CancelFunction)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_SESSION_HANDLE hSession /* the session's handle */
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* C_WaitForSlotEvent waits for a slot event (token insertion,
|
||||
* removal, etc.) to occur.
|
||||
*/
|
||||
CK_PKCS11_FUNCTION_INFO(C_WaitForSlotEvent)
|
||||
#ifdef CK_NEED_ARG_LIST
|
||||
(
|
||||
CK_FLAGS flags, /* blocking/nonblocking flag */
|
||||
CK_SLOT_ID_PTR pSlot, /* location that receives the slot ID */
|
||||
CK_VOID_PTR pRserved /* reserved. Should be NULL_PTR */
|
||||
);
|
||||
#endif
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
|
||||
#define CK_PTR *
|
||||
#ifndef NULL_PTR
|
||||
#define NULL_PTR 0
|
||||
#endif
|
||||
#define CK_DEFINE_FUNCTION(returnType, name) returnType name
|
||||
#define CK_DECLARE_FUNCTION(returnType, name) returnType name
|
||||
#define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (* name)
|
||||
#define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name)
|
||||
|
||||
#include <unistd.h>
|
||||
#ifdef PACKED_STRUCTURES
|
||||
# pragma pack(push, 1)
|
||||
# include "pkcs11.h"
|
||||
# pragma pack(pop)
|
||||
#else
|
||||
# include "pkcs11.h"
|
||||
#endif
|
||||
|
||||
// Copy of CK_INFO but with default alignment (not packed). Go hides unaligned
|
||||
// struct fields so copying to an aligned struct is necessary to read CK_INFO
|
||||
// from Go on Windows where packing is required.
|
||||
typedef struct ckInfo {
|
||||
CK_VERSION cryptokiVersion;
|
||||
CK_UTF8CHAR manufacturerID[32];
|
||||
CK_FLAGS flags;
|
||||
CK_UTF8CHAR libraryDescription[32];
|
||||
CK_VERSION libraryVersion;
|
||||
} ckInfo, *ckInfoPtr;
|
||||
+2047
File diff suppressed because it is too large
Load Diff
+17
@@ -0,0 +1,17 @@
|
||||
// +build release
|
||||
|
||||
package pkcs11
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Release is current version of the pkcs11 library.
|
||||
var Release = R{1, 0, 2}
|
||||
|
||||
// R holds the version of this library.
|
||||
type R struct {
|
||||
Major, Minor, Patch int
|
||||
}
|
||||
|
||||
func (r R) String() string {
|
||||
return fmt.Sprintf("%d.%d.%d", r.Major, r.Minor, r.Patch)
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
0:hsm.db
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
log.level = INFO
|
||||
objectstore.backend = file
|
||||
directories.tokendir = test_data
|
||||
slots.removable = false
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
// Copyright 2013 Miek Gieben. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pkcs11
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "pkcs11go.h"
|
||||
|
||||
CK_ULONG Index(CK_ULONG_PTR array, CK_ULONG i)
|
||||
{
|
||||
return array[i];
|
||||
}
|
||||
|
||||
static inline void putAttributePval(CK_ATTRIBUTE_PTR a, CK_VOID_PTR pValue)
|
||||
{
|
||||
a->pValue = pValue;
|
||||
}
|
||||
|
||||
static inline void putMechanismParam(CK_MECHANISM_PTR m, CK_VOID_PTR pParameter)
|
||||
{
|
||||
m->pParameter = pParameter;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type arena []unsafe.Pointer
|
||||
|
||||
func (a *arena) Allocate(obj []byte) (C.CK_VOID_PTR, C.CK_ULONG) {
|
||||
cobj := C.calloc(C.size_t(len(obj)), 1)
|
||||
*a = append(*a, cobj)
|
||||
C.memmove(cobj, unsafe.Pointer(&obj[0]), C.size_t(len(obj)))
|
||||
return C.CK_VOID_PTR(cobj), C.CK_ULONG(len(obj))
|
||||
}
|
||||
|
||||
func (a arena) Free() {
|
||||
for _, p := range a {
|
||||
C.free(p)
|
||||
}
|
||||
}
|
||||
|
||||
// toList converts from a C style array to a []uint.
|
||||
func toList(clist C.CK_ULONG_PTR, size C.CK_ULONG) []uint {
|
||||
l := make([]uint, int(size))
|
||||
for i := 0; i < len(l); i++ {
|
||||
l[i] = uint(C.Index(clist, C.CK_ULONG(i)))
|
||||
}
|
||||
defer C.free(unsafe.Pointer(clist))
|
||||
return l
|
||||
}
|
||||
|
||||
// cBBool converts a bool to a CK_BBOOL.
|
||||
func cBBool(x bool) C.CK_BBOOL {
|
||||
if x {
|
||||
return C.CK_BBOOL(C.CK_TRUE)
|
||||
}
|
||||
return C.CK_BBOOL(C.CK_FALSE)
|
||||
}
|
||||
|
||||
func uintToBytes(x uint64) []byte {
|
||||
ul := C.CK_ULONG(x)
|
||||
return C.GoBytes(unsafe.Pointer(&ul), C.int(unsafe.Sizeof(ul)))
|
||||
}
|
||||
|
||||
// Error represents an PKCS#11 error.
|
||||
type Error uint
|
||||
|
||||
func (e Error) Error() string {
|
||||
return fmt.Sprintf("pkcs11: 0x%X: %s", uint(e), strerror[uint(e)])
|
||||
}
|
||||
|
||||
func toError(e C.CK_RV) error {
|
||||
if e == C.CKR_OK {
|
||||
return nil
|
||||
}
|
||||
return Error(e)
|
||||
}
|
||||
|
||||
// SessionHandle is a Cryptoki-assigned value that identifies a session.
|
||||
type SessionHandle uint
|
||||
|
||||
// ObjectHandle is a token-specific identifier for an object.
|
||||
type ObjectHandle uint
|
||||
|
||||
// Version represents any version information from the library.
|
||||
type Version struct {
|
||||
Major byte
|
||||
Minor byte
|
||||
}
|
||||
|
||||
func toVersion(version C.CK_VERSION) Version {
|
||||
return Version{byte(version.major), byte(version.minor)}
|
||||
}
|
||||
|
||||
// SlotEvent holds the SlotID which for which an slot event (token insertion,
|
||||
// removal, etc.) occurred.
|
||||
type SlotEvent struct {
|
||||
SlotID uint
|
||||
}
|
||||
|
||||
// Info provides information about the library and hardware used.
|
||||
type Info struct {
|
||||
CryptokiVersion Version
|
||||
ManufacturerID string
|
||||
Flags uint
|
||||
LibraryDescription string
|
||||
LibraryVersion Version
|
||||
}
|
||||
|
||||
// SlotInfo provides information about a slot.
|
||||
type SlotInfo struct {
|
||||
SlotDescription string // 64 bytes.
|
||||
ManufacturerID string // 32 bytes.
|
||||
Flags uint
|
||||
HardwareVersion Version
|
||||
FirmwareVersion Version
|
||||
}
|
||||
|
||||
// TokenInfo provides information about a token.
|
||||
type TokenInfo struct {
|
||||
Label string
|
||||
ManufacturerID string
|
||||
Model string
|
||||
SerialNumber string
|
||||
Flags uint
|
||||
MaxSessionCount uint
|
||||
SessionCount uint
|
||||
MaxRwSessionCount uint
|
||||
RwSessionCount uint
|
||||
MaxPinLen uint
|
||||
MinPinLen uint
|
||||
TotalPublicMemory uint
|
||||
FreePublicMemory uint
|
||||
TotalPrivateMemory uint
|
||||
FreePrivateMemory uint
|
||||
HardwareVersion Version
|
||||
FirmwareVersion Version
|
||||
UTCTime string
|
||||
}
|
||||
|
||||
// SessionInfo provides information about a session.
|
||||
type SessionInfo struct {
|
||||
SlotID uint
|
||||
State uint
|
||||
Flags uint
|
||||
DeviceError uint
|
||||
}
|
||||
|
||||
// Attribute holds an attribute type/value combination.
|
||||
type Attribute struct {
|
||||
Type uint
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// NewAttribute allocates a Attribute and returns a pointer to it.
|
||||
// Note that this is merely a convenience function, as values returned
|
||||
// from the HSM are not converted back to Go values, those are just raw
|
||||
// byte slices.
|
||||
func NewAttribute(typ uint, x interface{}) *Attribute {
|
||||
// This function nicely transforms *to* an attribute, but there is
|
||||
// no corresponding function that transform back *from* an attribute,
|
||||
// which in PKCS#11 is just an byte array.
|
||||
a := new(Attribute)
|
||||
a.Type = typ
|
||||
if x == nil {
|
||||
return a
|
||||
}
|
||||
switch v := x.(type) {
|
||||
case bool:
|
||||
if v {
|
||||
a.Value = []byte{1}
|
||||
} else {
|
||||
a.Value = []byte{0}
|
||||
}
|
||||
case int:
|
||||
a.Value = uintToBytes(uint64(v))
|
||||
case uint:
|
||||
a.Value = uintToBytes(uint64(v))
|
||||
case string:
|
||||
a.Value = []byte(v)
|
||||
case []byte:
|
||||
a.Value = v
|
||||
case time.Time: // for CKA_DATE
|
||||
a.Value = cDate(v)
|
||||
default:
|
||||
panic("pkcs11: unhandled attribute type")
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// cAttribute returns the start address and the length of an attribute list.
|
||||
func cAttributeList(a []*Attribute) (arena, C.CK_ATTRIBUTE_PTR, C.CK_ULONG) {
|
||||
var arena arena
|
||||
if len(a) == 0 {
|
||||
return nil, nil, 0
|
||||
}
|
||||
pa := make([]C.CK_ATTRIBUTE, len(a))
|
||||
for i, attr := range a {
|
||||
pa[i]._type = C.CK_ATTRIBUTE_TYPE(attr.Type)
|
||||
if len(attr.Value) != 0 {
|
||||
buf, len := arena.Allocate(attr.Value)
|
||||
// field is unaligned on windows so this has to call into C
|
||||
C.putAttributePval(&pa[i], buf)
|
||||
pa[i].ulValueLen = len
|
||||
}
|
||||
}
|
||||
return arena, &pa[0], C.CK_ULONG(len(a))
|
||||
}
|
||||
|
||||
func cDate(t time.Time) []byte {
|
||||
b := make([]byte, 8)
|
||||
year, month, day := t.Date()
|
||||
y := fmt.Sprintf("%4d", year)
|
||||
m := fmt.Sprintf("%02d", month)
|
||||
d1 := fmt.Sprintf("%02d", day)
|
||||
b[0], b[1], b[2], b[3] = y[0], y[1], y[2], y[3]
|
||||
b[4], b[5] = m[0], m[1]
|
||||
b[6], b[7] = d1[0], d1[1]
|
||||
return b
|
||||
}
|
||||
|
||||
// Mechanism holds an mechanism type/value combination.
|
||||
type Mechanism struct {
|
||||
Mechanism uint
|
||||
Parameter []byte
|
||||
generator interface{}
|
||||
}
|
||||
|
||||
// NewMechanism returns a pointer to an initialized Mechanism.
|
||||
func NewMechanism(mech uint, x interface{}) *Mechanism {
|
||||
m := new(Mechanism)
|
||||
m.Mechanism = mech
|
||||
if x == nil {
|
||||
return m
|
||||
}
|
||||
|
||||
switch p := x.(type) {
|
||||
case *GCMParams, *OAEPParams, *ECDH1DeriveParams:
|
||||
// contains pointers; defer serialization until cMechanism
|
||||
m.generator = p
|
||||
case []byte:
|
||||
m.Parameter = p
|
||||
default:
|
||||
panic("parameter must be one of type: []byte, *GCMParams, *OAEPParams, *ECDH1DeriveParams")
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func cMechanism(mechList []*Mechanism) (arena, *C.CK_MECHANISM) {
|
||||
if len(mechList) != 1 {
|
||||
panic("expected exactly one mechanism")
|
||||
}
|
||||
mech := mechList[0]
|
||||
cmech := &C.CK_MECHANISM{mechanism: C.CK_MECHANISM_TYPE(mech.Mechanism)}
|
||||
// params that contain pointers are allocated here
|
||||
param := mech.Parameter
|
||||
var arena arena
|
||||
switch p := mech.generator.(type) {
|
||||
case *GCMParams:
|
||||
// uses its own arena because it has to outlive this function call (yuck)
|
||||
param = cGCMParams(p)
|
||||
case *OAEPParams:
|
||||
param, arena = cOAEPParams(p, arena)
|
||||
case *ECDH1DeriveParams:
|
||||
param, arena = cECDH1DeriveParams(p, arena)
|
||||
}
|
||||
if len(param) != 0 {
|
||||
buf, len := arena.Allocate(param)
|
||||
// field is unaligned on windows so this has to call into C
|
||||
C.putMechanismParam(cmech, buf)
|
||||
cmech.ulParameterLen = len
|
||||
}
|
||||
return arena, cmech
|
||||
}
|
||||
|
||||
// MechanismInfo provides information about a particular mechanism.
|
||||
type MechanismInfo struct {
|
||||
MinKeySize uint
|
||||
MaxKeySize uint
|
||||
Flags uint
|
||||
}
|
||||
|
||||
// stubData is a persistent nonempty byte array used by cMessage.
|
||||
var stubData = []byte{0}
|
||||
|
||||
// cMessage returns the pointer/length pair corresponding to data.
|
||||
func cMessage(data []byte) (dataPtr C.CK_BYTE_PTR) {
|
||||
l := len(data)
|
||||
if l == 0 {
|
||||
// &data[0] is forbidden in this case, so use a nontrivial array instead.
|
||||
data = stubData
|
||||
}
|
||||
return C.CK_BYTE_PTR(unsafe.Pointer(&data[0]))
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package pkcs11
|
||||
|
||||
// Vendor specific range for Ncipher network HSM.
|
||||
const (
|
||||
NFCK_VENDOR_NCIPHER = 0xde436972
|
||||
CKA_NCIPHER = NFCK_VENDOR_NCIPHER
|
||||
CKM_NCIPHER = NFCK_VENDOR_NCIPHER
|
||||
CKK_NCIPHER = NFCK_VENDOR_NCIPHER
|
||||
)
|
||||
|
||||
// Vendor specific mechanisms for HMAC on Ncipher HSMs where Ncipher does not allow use of generic_secret keys.
|
||||
const (
|
||||
CKM_NC_SHA_1_HMAC_KEY_GEN = CKM_NCIPHER + 0x3 /* no params */
|
||||
CKM_NC_MD5_HMAC_KEY_GEN = CKM_NCIPHER + 0x6 /* no params */
|
||||
CKM_NC_SHA224_HMAC_KEY_GEN = CKM_NCIPHER + 0x24 /* no params */
|
||||
CKM_NC_SHA256_HMAC_KEY_GEN = CKM_NCIPHER + 0x25 /* no params */
|
||||
CKM_NC_SHA384_HMAC_KEY_GEN = CKM_NCIPHER + 0x26 /* no params */
|
||||
CKM_NC_SHA512_HMAC_KEY_GEN = CKM_NCIPHER + 0x27 /* no params */
|
||||
)
|
||||
|
||||
// Vendor specific range for Mozilla NSS.
|
||||
const (
|
||||
NSSCK_VENDOR_NSS = 0x4E534350
|
||||
CKO_NSS = CKO_VENDOR_DEFINED | NSSCK_VENDOR_NSS
|
||||
CKK_NSS = CKK_VENDOR_DEFINED | NSSCK_VENDOR_NSS
|
||||
CKC_NSS = CKC_VENDOR_DEFINED | NSSCK_VENDOR_NSS
|
||||
CKA_NSS = CKA_VENDOR_DEFINED | NSSCK_VENDOR_NSS
|
||||
CKA_TRUST = CKA_NSS + 0x2000
|
||||
CKM_NSS = CKM_VENDOR_DEFINED | NSSCK_VENDOR_NSS
|
||||
CKR_NSS = CKM_VENDOR_DEFINED | NSSCK_VENDOR_NSS
|
||||
CKT_VENDOR_DEFINED = 0x80000000
|
||||
CKT_NSS = CKT_VENDOR_DEFINED | NSSCK_VENDOR_NSS
|
||||
)
|
||||
|
||||
// Vendor specific values for Mozilla NSS.
|
||||
const (
|
||||
CKO_NSS_CRL = CKO_NSS + 1
|
||||
CKO_NSS_SMIME = CKO_NSS + 2
|
||||
CKO_NSS_TRUST = CKO_NSS + 3
|
||||
CKO_NSS_BUILTIN_ROOT_LIST = CKO_NSS + 4
|
||||
CKO_NSS_NEWSLOT = CKO_NSS + 5
|
||||
CKO_NSS_DELSLOT = CKO_NSS + 6
|
||||
CKK_NSS_PKCS8 = CKK_NSS + 1
|
||||
CKK_NSS_JPAKE_ROUND1 = CKK_NSS + 2
|
||||
CKK_NSS_JPAKE_ROUND2 = CKK_NSS + 3
|
||||
CKK_NSS_CHACHA20 = CKK_NSS + 4
|
||||
CKA_NSS_URL = CKA_NSS + 1
|
||||
CKA_NSS_EMAIL = CKA_NSS + 2
|
||||
CKA_NSS_SMIME_INFO = CKA_NSS + 3
|
||||
CKA_NSS_SMIME_TIMESTAMP = CKA_NSS + 4
|
||||
CKA_NSS_PKCS8_SALT = CKA_NSS + 5
|
||||
CKA_NSS_PASSWORD_CHECK = CKA_NSS + 6
|
||||
CKA_NSS_EXPIRES = CKA_NSS + 7
|
||||
CKA_NSS_KRL = CKA_NSS + 8
|
||||
CKA_NSS_PQG_COUNTER = CKA_NSS + 20
|
||||
CKA_NSS_PQG_SEED = CKA_NSS + 21
|
||||
CKA_NSS_PQG_H = CKA_NSS + 22
|
||||
CKA_NSS_PQG_SEED_BITS = CKA_NSS + 23
|
||||
CKA_NSS_MODULE_SPEC = CKA_NSS + 24
|
||||
CKA_NSS_OVERRIDE_EXTENSIONS = CKA_NSS + 25
|
||||
CKA_NSS_JPAKE_SIGNERID = CKA_NSS + 26
|
||||
CKA_NSS_JPAKE_PEERID = CKA_NSS + 27
|
||||
CKA_NSS_JPAKE_GX1 = CKA_NSS + 28
|
||||
CKA_NSS_JPAKE_GX2 = CKA_NSS + 29
|
||||
CKA_NSS_JPAKE_GX3 = CKA_NSS + 30
|
||||
CKA_NSS_JPAKE_GX4 = CKA_NSS + 31
|
||||
CKA_NSS_JPAKE_X2 = CKA_NSS + 32
|
||||
CKA_NSS_JPAKE_X2S = CKA_NSS + 33
|
||||
CKA_NSS_MOZILLA_CA_POLICY = CKA_NSS + 34
|
||||
CKA_TRUST_DIGITAL_SIGNATURE = CKA_TRUST + 1
|
||||
CKA_TRUST_NON_REPUDIATION = CKA_TRUST + 2
|
||||
CKA_TRUST_KEY_ENCIPHERMENT = CKA_TRUST + 3
|
||||
CKA_TRUST_DATA_ENCIPHERMENT = CKA_TRUST + 4
|
||||
CKA_TRUST_KEY_AGREEMENT = CKA_TRUST + 5
|
||||
CKA_TRUST_KEY_CERT_SIGN = CKA_TRUST + 6
|
||||
CKA_TRUST_CRL_SIGN = CKA_TRUST + 7
|
||||
CKA_TRUST_SERVER_AUTH = CKA_TRUST + 8
|
||||
CKA_TRUST_CLIENT_AUTH = CKA_TRUST + 9
|
||||
CKA_TRUST_CODE_SIGNING = CKA_TRUST + 10
|
||||
CKA_TRUST_EMAIL_PROTECTION = CKA_TRUST + 11
|
||||
CKA_TRUST_IPSEC_END_SYSTEM = CKA_TRUST + 12
|
||||
CKA_TRUST_IPSEC_TUNNEL = CKA_TRUST + 13
|
||||
CKA_TRUST_IPSEC_USER = CKA_TRUST + 14
|
||||
CKA_TRUST_TIME_STAMPING = CKA_TRUST + 15
|
||||
CKA_TRUST_STEP_UP_APPROVED = CKA_TRUST + 16
|
||||
CKA_CERT_SHA1_HASH = CKA_TRUST + 100
|
||||
CKA_CERT_MD5_HASH = CKA_TRUST + 101
|
||||
CKM_NSS_AES_KEY_WRAP = CKM_NSS + 1
|
||||
CKM_NSS_AES_KEY_WRAP_PAD = CKM_NSS + 2
|
||||
CKM_NSS_HKDF_SHA1 = CKM_NSS + 3
|
||||
CKM_NSS_HKDF_SHA256 = CKM_NSS + 4
|
||||
CKM_NSS_HKDF_SHA384 = CKM_NSS + 5
|
||||
CKM_NSS_HKDF_SHA512 = CKM_NSS + 6
|
||||
CKM_NSS_JPAKE_ROUND1_SHA1 = CKM_NSS + 7
|
||||
CKM_NSS_JPAKE_ROUND1_SHA256 = CKM_NSS + 8
|
||||
CKM_NSS_JPAKE_ROUND1_SHA384 = CKM_NSS + 9
|
||||
CKM_NSS_JPAKE_ROUND1_SHA512 = CKM_NSS + 10
|
||||
CKM_NSS_JPAKE_ROUND2_SHA1 = CKM_NSS + 11
|
||||
CKM_NSS_JPAKE_ROUND2_SHA256 = CKM_NSS + 12
|
||||
CKM_NSS_JPAKE_ROUND2_SHA384 = CKM_NSS + 13
|
||||
CKM_NSS_JPAKE_ROUND2_SHA512 = CKM_NSS + 14
|
||||
CKM_NSS_JPAKE_FINAL_SHA1 = CKM_NSS + 15
|
||||
CKM_NSS_JPAKE_FINAL_SHA256 = CKM_NSS + 16
|
||||
CKM_NSS_JPAKE_FINAL_SHA384 = CKM_NSS + 17
|
||||
CKM_NSS_JPAKE_FINAL_SHA512 = CKM_NSS + 18
|
||||
CKM_NSS_HMAC_CONSTANT_TIME = CKM_NSS + 19
|
||||
CKM_NSS_SSL3_MAC_CONSTANT_TIME = CKM_NSS + 20
|
||||
CKM_NSS_TLS_PRF_GENERAL_SHA256 = CKM_NSS + 21
|
||||
CKM_NSS_TLS_MASTER_KEY_DERIVE_SHA256 = CKM_NSS + 22
|
||||
CKM_NSS_TLS_KEY_AND_MAC_DERIVE_SHA256 = CKM_NSS + 23
|
||||
CKM_NSS_TLS_MASTER_KEY_DERIVE_DH_SHA256 = CKM_NSS + 24
|
||||
CKM_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE = CKM_NSS + 25
|
||||
CKM_NSS_TLS_EXTENDED_MASTER_KEY_DERIVE_DH = CKM_NSS + 26
|
||||
CKM_NSS_CHACHA20_KEY_GEN = CKM_NSS + 27
|
||||
CKM_NSS_CHACHA20_POLY1305 = CKM_NSS + 28
|
||||
CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN = CKM_NSS + 29
|
||||
CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN = CKM_NSS + 30
|
||||
CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN = CKM_NSS + 31
|
||||
CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN = CKM_NSS + 32
|
||||
CKR_NSS_CERTDB_FAILED = CKR_NSS + 1
|
||||
CKR_NSS_KEYDB_FAILED = CKR_NSS + 2
|
||||
CKT_NSS_TRUSTED = CKT_NSS + 1
|
||||
CKT_NSS_TRUSTED_DELEGATOR = CKT_NSS + 2
|
||||
CKT_NSS_MUST_VERIFY_TRUST = CKT_NSS + 3
|
||||
CKT_NSS_NOT_TRUSTED = CKT_NSS + 10
|
||||
CKT_NSS_TRUST_UNKNOWN = CKT_NSS + 5
|
||||
)
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
pool is a copy of a few packages from https://github.com/vitessio/vitess.
|
||||
|
||||
Vitess has some useful Go packages, however they are not versioned with Go modules,
|
||||
which causes issues (e.g. https://github.com/ThalesIgnite/crypto11/issues/56). They
|
||||
are also buried inside a large project, which forms a heavyweight dependency.
|
||||
|
||||
This package exposes the resource pool implementation and some of the atomic types.
|
||||
+186
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
Copyright 2017 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package pool
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AtomicInt32 is a wrapper with a simpler interface around atomic.(Add|Store|Load|CompareAndSwap)Int32 functions.
|
||||
type AtomicInt32 struct {
|
||||
int32
|
||||
}
|
||||
|
||||
// NewAtomicInt32 initializes a new AtomicInt32 with a given value.
|
||||
func NewAtomicInt32(n int32) AtomicInt32 {
|
||||
return AtomicInt32{n}
|
||||
}
|
||||
|
||||
// Add atomically adds n to the value.
|
||||
func (i *AtomicInt32) Add(n int32) int32 {
|
||||
return atomic.AddInt32(&i.int32, n)
|
||||
}
|
||||
|
||||
// Set atomically sets n as new value.
|
||||
func (i *AtomicInt32) Set(n int32) {
|
||||
atomic.StoreInt32(&i.int32, n)
|
||||
}
|
||||
|
||||
// Get atomically returns the current value.
|
||||
func (i *AtomicInt32) Get() int32 {
|
||||
return atomic.LoadInt32(&i.int32)
|
||||
}
|
||||
|
||||
// CompareAndSwap atomatically swaps the old with the new value.
|
||||
func (i *AtomicInt32) CompareAndSwap(oldval, newval int32) (swapped bool) {
|
||||
return atomic.CompareAndSwapInt32(&i.int32, oldval, newval)
|
||||
}
|
||||
|
||||
// AtomicInt64 is a wrapper with a simpler interface around atomic.(Add|Store|Load|CompareAndSwap)Int64 functions.
|
||||
type AtomicInt64 struct {
|
||||
int64
|
||||
}
|
||||
|
||||
// NewAtomicInt64 initializes a new AtomicInt64 with a given value.
|
||||
func NewAtomicInt64(n int64) AtomicInt64 {
|
||||
return AtomicInt64{n}
|
||||
}
|
||||
|
||||
// Add atomically adds n to the value.
|
||||
func (i *AtomicInt64) Add(n int64) int64 {
|
||||
return atomic.AddInt64(&i.int64, n)
|
||||
}
|
||||
|
||||
// Set atomically sets n as new value.
|
||||
func (i *AtomicInt64) Set(n int64) {
|
||||
atomic.StoreInt64(&i.int64, n)
|
||||
}
|
||||
|
||||
// Get atomically returns the current value.
|
||||
func (i *AtomicInt64) Get() int64 {
|
||||
return atomic.LoadInt64(&i.int64)
|
||||
}
|
||||
|
||||
// CompareAndSwap atomatically swaps the old with the new value.
|
||||
func (i *AtomicInt64) CompareAndSwap(oldval, newval int64) (swapped bool) {
|
||||
return atomic.CompareAndSwapInt64(&i.int64, oldval, newval)
|
||||
}
|
||||
|
||||
// AtomicDuration is a wrapper with a simpler interface around atomic.(Add|Store|Load|CompareAndSwap)Int64 functions.
|
||||
type AtomicDuration struct {
|
||||
int64
|
||||
}
|
||||
|
||||
// NewAtomicDuration initializes a new AtomicDuration with a given value.
|
||||
func NewAtomicDuration(duration time.Duration) AtomicDuration {
|
||||
return AtomicDuration{int64(duration)}
|
||||
}
|
||||
|
||||
// Add atomically adds duration to the value.
|
||||
func (d *AtomicDuration) Add(duration time.Duration) time.Duration {
|
||||
return time.Duration(atomic.AddInt64(&d.int64, int64(duration)))
|
||||
}
|
||||
|
||||
// Set atomically sets duration as new value.
|
||||
func (d *AtomicDuration) Set(duration time.Duration) {
|
||||
atomic.StoreInt64(&d.int64, int64(duration))
|
||||
}
|
||||
|
||||
// Get atomically returns the current value.
|
||||
func (d *AtomicDuration) Get() time.Duration {
|
||||
return time.Duration(atomic.LoadInt64(&d.int64))
|
||||
}
|
||||
|
||||
// CompareAndSwap atomatically swaps the old with the new value.
|
||||
func (d *AtomicDuration) CompareAndSwap(oldval, newval time.Duration) (swapped bool) {
|
||||
return atomic.CompareAndSwapInt64(&d.int64, int64(oldval), int64(newval))
|
||||
}
|
||||
|
||||
// AtomicBool gives an atomic boolean variable.
|
||||
type AtomicBool struct {
|
||||
int32
|
||||
}
|
||||
|
||||
// NewAtomicBool initializes a new AtomicBool with a given value.
|
||||
func NewAtomicBool(n bool) AtomicBool {
|
||||
if n {
|
||||
return AtomicBool{1}
|
||||
}
|
||||
return AtomicBool{0}
|
||||
}
|
||||
|
||||
// Set atomically sets n as new value.
|
||||
func (i *AtomicBool) Set(n bool) {
|
||||
if n {
|
||||
atomic.StoreInt32(&i.int32, 1)
|
||||
} else {
|
||||
atomic.StoreInt32(&i.int32, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Get atomically returns the current value.
|
||||
func (i *AtomicBool) Get() bool {
|
||||
return atomic.LoadInt32(&i.int32) != 0
|
||||
}
|
||||
|
||||
// CompareAndSwap atomatically swaps the old with the new value.
|
||||
func (i *AtomicBool) CompareAndSwap(o, n bool) bool {
|
||||
var old, new int32
|
||||
if o {
|
||||
old = 1
|
||||
}
|
||||
if n {
|
||||
new = 1
|
||||
}
|
||||
return atomic.CompareAndSwapInt32(&i.int32, old, new)
|
||||
}
|
||||
|
||||
// AtomicString gives you atomic-style APIs for string, but
|
||||
// it's only a convenience wrapper that uses a mutex. So, it's
|
||||
// not as efficient as the rest of the atomic types.
|
||||
type AtomicString struct {
|
||||
mu sync.Mutex
|
||||
str string
|
||||
}
|
||||
|
||||
// Set atomically sets str as new value.
|
||||
func (s *AtomicString) Set(str string) {
|
||||
s.mu.Lock()
|
||||
s.str = str
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
// Get atomically returns the current value.
|
||||
func (s *AtomicString) Get() string {
|
||||
s.mu.Lock()
|
||||
str := s.str
|
||||
s.mu.Unlock()
|
||||
return str
|
||||
}
|
||||
|
||||
// CompareAndSwap atomatically swaps the old with the new value.
|
||||
func (s *AtomicString) CompareAndSwap(oldval, newval string) (swqpped bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if s.str == oldval {
|
||||
s.str = newval
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
module github.com/thales-e-security/pool
|
||||
|
||||
go 1.12
|
||||
+383
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
Copyright 2017 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package pools provides functionality to manage and reuse resources
|
||||
// like connections.
|
||||
//
|
||||
// Modified by Duncan Jones to reduce the number of external dependencies.
|
||||
package pool
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrClosed is returned if ResourcePool is used when it's closed.
|
||||
ErrClosed = errors.New("resource pool is closed")
|
||||
|
||||
// ErrTimeout is returned if a resource get times out.
|
||||
ErrTimeout = errors.New("resource pool timed out")
|
||||
|
||||
prefillTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
// Factory is a function that can be used to create a resource.
|
||||
type Factory func() (Resource, error)
|
||||
|
||||
// Resource defines the interface that every resource must provide.
|
||||
// Thread synchronization between Close() and IsClosed()
|
||||
// is the responsibility of the caller.
|
||||
type Resource interface {
|
||||
Close()
|
||||
}
|
||||
|
||||
// ResourcePool allows you to use a pool of resources.
|
||||
type ResourcePool struct {
|
||||
// stats. Atomic fields must remain at the top in order to prevent panics on certain architectures.
|
||||
available AtomicInt64
|
||||
active AtomicInt64
|
||||
inUse AtomicInt64
|
||||
waitCount AtomicInt64
|
||||
waitTime AtomicDuration
|
||||
idleClosed AtomicInt64
|
||||
|
||||
capacity AtomicInt64
|
||||
idleTimeout AtomicDuration
|
||||
|
||||
resources chan resourceWrapper
|
||||
factory Factory
|
||||
idleTimer *Timer
|
||||
}
|
||||
|
||||
type resourceWrapper struct {
|
||||
resource Resource
|
||||
timeUsed time.Time
|
||||
}
|
||||
|
||||
// NewResourcePool creates a new ResourcePool pool.
|
||||
// capacity is the number of possible resources in the pool:
|
||||
// there can be up to 'capacity' of these at a given time.
|
||||
// maxCap specifies the extent to which the pool can be resized
|
||||
// in the future through the SetCapacity function.
|
||||
// You cannot resize the pool beyond maxCap.
|
||||
// If a resource is unused beyond idleTimeout, it's replaced
|
||||
// with a new one.
|
||||
// An idleTimeout of 0 means that there is no timeout.
|
||||
// A non-zero value of prefillParallelism causes the pool to be pre-filled.
|
||||
// The value specifies how many resources can be opened in parallel.
|
||||
func NewResourcePool(factory Factory, capacity, maxCap int, idleTimeout time.Duration, prefillParallelism int) *ResourcePool {
|
||||
if capacity <= 0 || maxCap <= 0 || capacity > maxCap {
|
||||
panic(errors.New("invalid/out of range capacity"))
|
||||
}
|
||||
rp := &ResourcePool{
|
||||
resources: make(chan resourceWrapper, maxCap),
|
||||
factory: factory,
|
||||
available: NewAtomicInt64(int64(capacity)),
|
||||
capacity: NewAtomicInt64(int64(capacity)),
|
||||
idleTimeout: NewAtomicDuration(idleTimeout),
|
||||
}
|
||||
for i := 0; i < capacity; i++ {
|
||||
rp.resources <- resourceWrapper{}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), prefillTimeout)
|
||||
defer cancel()
|
||||
if prefillParallelism != 0 {
|
||||
sem := NewSemaphore(prefillParallelism, 0 /* timeout */)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < capacity; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
_ = sem.Acquire()
|
||||
defer sem.Release()
|
||||
|
||||
// If context has expired, give up.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
r, err := rp.Get(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rp.Put(r)
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
if idleTimeout != 0 {
|
||||
rp.idleTimer = NewTimer(idleTimeout / 10)
|
||||
rp.idleTimer.Start(rp.closeIdleResources)
|
||||
}
|
||||
return rp
|
||||
}
|
||||
|
||||
// Close empties the pool calling Close on all its resources.
|
||||
// You can call Close while there are outstanding resources.
|
||||
// It waits for all resources to be returned (Put).
|
||||
// After a Close, Get is not allowed.
|
||||
func (rp *ResourcePool) Close() {
|
||||
if rp.idleTimer != nil {
|
||||
rp.idleTimer.Stop()
|
||||
}
|
||||
_ = rp.SetCapacity(0)
|
||||
}
|
||||
|
||||
// IsClosed returns true if the resource pool is closed.
|
||||
func (rp *ResourcePool) IsClosed() (closed bool) {
|
||||
return rp.capacity.Get() == 0
|
||||
}
|
||||
|
||||
// closeIdleResources scans the pool for idle resources
|
||||
func (rp *ResourcePool) closeIdleResources() {
|
||||
available := int(rp.Available())
|
||||
idleTimeout := rp.IdleTimeout()
|
||||
|
||||
for i := 0; i < available; i++ {
|
||||
var wrapper resourceWrapper
|
||||
select {
|
||||
case wrapper = <-rp.resources:
|
||||
default:
|
||||
// stop early if we don't get anything new from the pool
|
||||
return
|
||||
}
|
||||
|
||||
func() {
|
||||
defer func() { rp.resources <- wrapper }()
|
||||
|
||||
if wrapper.resource != nil && idleTimeout > 0 && time.Until(wrapper.timeUsed.Add(idleTimeout)) < 0 {
|
||||
wrapper.resource.Close()
|
||||
rp.idleClosed.Add(1)
|
||||
rp.reopenResource(&wrapper)
|
||||
}
|
||||
}()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Get will return the next available resource. If capacity
|
||||
// has not been reached, it will create a new one using the factory. Otherwise,
|
||||
// it will wait till the next resource becomes available or a timeout.
|
||||
// A timeout of 0 is an indefinite wait.
|
||||
func (rp *ResourcePool) Get(ctx context.Context) (resource Resource, err error) {
|
||||
return rp.get(ctx)
|
||||
}
|
||||
|
||||
func (rp *ResourcePool) get(ctx context.Context) (resource Resource, err error) {
|
||||
// If ctx has already expired, avoid racing with rp's resource channel.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ErrTimeout
|
||||
default:
|
||||
}
|
||||
|
||||
// Fetch
|
||||
var wrapper resourceWrapper
|
||||
var ok bool
|
||||
select {
|
||||
case wrapper, ok = <-rp.resources:
|
||||
default:
|
||||
startTime := time.Now()
|
||||
select {
|
||||
case wrapper, ok = <-rp.resources:
|
||||
case <-ctx.Done():
|
||||
return nil, ErrTimeout
|
||||
}
|
||||
rp.recordWait(startTime)
|
||||
}
|
||||
if !ok {
|
||||
return nil, ErrClosed
|
||||
}
|
||||
|
||||
// Unwrap
|
||||
if wrapper.resource == nil {
|
||||
wrapper.resource, err = rp.factory()
|
||||
if err != nil {
|
||||
rp.resources <- resourceWrapper{}
|
||||
return nil, err
|
||||
}
|
||||
rp.active.Add(1)
|
||||
}
|
||||
rp.available.Add(-1)
|
||||
rp.inUse.Add(1)
|
||||
return wrapper.resource, err
|
||||
}
|
||||
|
||||
// Put will return a resource to the pool. For every successful Get,
|
||||
// a corresponding Put is required. If you no longer need a resource,
|
||||
// you will need to call Put(nil) instead of returning the closed resource.
|
||||
// This will cause a new resource to be created in its place.
|
||||
func (rp *ResourcePool) Put(resource Resource) {
|
||||
var wrapper resourceWrapper
|
||||
if resource != nil {
|
||||
wrapper = resourceWrapper{
|
||||
resource: resource,
|
||||
timeUsed: time.Now(),
|
||||
}
|
||||
} else {
|
||||
rp.reopenResource(&wrapper)
|
||||
}
|
||||
select {
|
||||
case rp.resources <- wrapper:
|
||||
default:
|
||||
panic(errors.New("attempt to Put into a full ResourcePool"))
|
||||
}
|
||||
rp.inUse.Add(-1)
|
||||
rp.available.Add(1)
|
||||
}
|
||||
|
||||
func (rp *ResourcePool) reopenResource(wrapper *resourceWrapper) {
|
||||
if r, err := rp.factory(); err == nil {
|
||||
wrapper.resource = r
|
||||
wrapper.timeUsed = time.Now()
|
||||
} else {
|
||||
wrapper.resource = nil
|
||||
rp.active.Add(-1)
|
||||
}
|
||||
}
|
||||
|
||||
// SetCapacity changes the capacity of the pool.
|
||||
// You can use it to shrink or expand, but not beyond
|
||||
// the max capacity. If the change requires the pool
|
||||
// to be shrunk, SetCapacity waits till the necessary
|
||||
// number of resources are returned to the pool.
|
||||
// A SetCapacity of 0 is equivalent to closing the ResourcePool.
|
||||
func (rp *ResourcePool) SetCapacity(capacity int) error {
|
||||
if capacity < 0 || capacity > cap(rp.resources) {
|
||||
return fmt.Errorf("capacity %d is out of range", capacity)
|
||||
}
|
||||
|
||||
// Atomically swap new capacity with old, but only
|
||||
// if old capacity is non-zero.
|
||||
var oldcap int
|
||||
for {
|
||||
oldcap = int(rp.capacity.Get())
|
||||
if oldcap == 0 {
|
||||
return ErrClosed
|
||||
}
|
||||
if oldcap == capacity {
|
||||
return nil
|
||||
}
|
||||
if rp.capacity.CompareAndSwap(int64(oldcap), int64(capacity)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if capacity < oldcap {
|
||||
for i := 0; i < oldcap-capacity; i++ {
|
||||
wrapper := <-rp.resources
|
||||
if wrapper.resource != nil {
|
||||
wrapper.resource.Close()
|
||||
rp.active.Add(-1)
|
||||
}
|
||||
rp.available.Add(-1)
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < capacity-oldcap; i++ {
|
||||
rp.resources <- resourceWrapper{}
|
||||
rp.available.Add(1)
|
||||
}
|
||||
}
|
||||
if capacity == 0 {
|
||||
close(rp.resources)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rp *ResourcePool) recordWait(start time.Time) {
|
||||
rp.waitCount.Add(1)
|
||||
rp.waitTime.Add(time.Since(start))
|
||||
}
|
||||
|
||||
// SetIdleTimeout sets the idle timeout. It can only be used if there was an
|
||||
// idle timeout set when the pool was created.
|
||||
func (rp *ResourcePool) SetIdleTimeout(idleTimeout time.Duration) {
|
||||
if rp.idleTimer == nil {
|
||||
panic("SetIdleTimeout called when timer not initialized")
|
||||
}
|
||||
|
||||
rp.idleTimeout.Set(idleTimeout)
|
||||
rp.idleTimer.SetInterval(idleTimeout / 10)
|
||||
}
|
||||
|
||||
// StatsJSON returns the stats in JSON format.
|
||||
func (rp *ResourcePool) StatsJSON() string {
|
||||
return fmt.Sprintf(`{"Capacity": %v, "Available": %v, "Active": %v, "InUse": %v, "MaxCapacity": %v, "WaitCount": %v, "WaitTime": %v, "IdleTimeout": %v, "IdleClosed": %v}`,
|
||||
rp.Capacity(),
|
||||
rp.Available(),
|
||||
rp.Active(),
|
||||
rp.InUse(),
|
||||
rp.MaxCap(),
|
||||
rp.WaitCount(),
|
||||
rp.WaitTime().Nanoseconds(),
|
||||
rp.IdleTimeout().Nanoseconds(),
|
||||
rp.IdleClosed(),
|
||||
)
|
||||
}
|
||||
|
||||
// Capacity returns the capacity.
|
||||
func (rp *ResourcePool) Capacity() int64 {
|
||||
return rp.capacity.Get()
|
||||
}
|
||||
|
||||
// Available returns the number of currently unused and available resources.
|
||||
func (rp *ResourcePool) Available() int64 {
|
||||
return rp.available.Get()
|
||||
}
|
||||
|
||||
// Active returns the number of active (i.e. non-nil) resources either in the
|
||||
// pool or claimed for use
|
||||
func (rp *ResourcePool) Active() int64 {
|
||||
return rp.active.Get()
|
||||
}
|
||||
|
||||
// InUse returns the number of claimed resources from the pool
|
||||
func (rp *ResourcePool) InUse() int64 {
|
||||
return rp.inUse.Get()
|
||||
}
|
||||
|
||||
// MaxCap returns the max capacity.
|
||||
func (rp *ResourcePool) MaxCap() int64 {
|
||||
return int64(cap(rp.resources))
|
||||
}
|
||||
|
||||
// WaitCount returns the total number of waits.
|
||||
func (rp *ResourcePool) WaitCount() int64 {
|
||||
return rp.waitCount.Get()
|
||||
}
|
||||
|
||||
// WaitTime returns the total wait time.
|
||||
func (rp *ResourcePool) WaitTime() time.Duration {
|
||||
return rp.waitTime.Get()
|
||||
}
|
||||
|
||||
// IdleTimeout returns the idle timeout.
|
||||
func (rp *ResourcePool) IdleTimeout() time.Duration {
|
||||
return rp.idleTimeout.Get()
|
||||
}
|
||||
|
||||
// IdleClosed returns the count of resources closed due to idle timeout.
|
||||
func (rp *ResourcePool) IdleClosed() int64 {
|
||||
return rp.idleClosed.Get()
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright 2017 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package pool
|
||||
|
||||
// What's in a name? Channels have all you need to emulate a counting
|
||||
// semaphore with a boatload of extra functionality. However, in some
|
||||
// cases, you just want a familiar API.
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Semaphore is a counting semaphore with the option to
|
||||
// specify a timeout.
|
||||
type Semaphore struct {
|
||||
slots chan struct{}
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// NewSemaphore creates a Semaphore. The count parameter must be a positive
|
||||
// number. A timeout of zero means that there is no timeout.
|
||||
func NewSemaphore(count int, timeout time.Duration) *Semaphore {
|
||||
sem := &Semaphore{
|
||||
slots: make(chan struct{}, count),
|
||||
timeout: timeout,
|
||||
}
|
||||
for i := 0; i < count; i++ {
|
||||
sem.slots <- struct{}{}
|
||||
}
|
||||
return sem
|
||||
}
|
||||
|
||||
// Acquire returns true on successful acquisition, and
|
||||
// false on a timeout.
|
||||
func (sem *Semaphore) Acquire() bool {
|
||||
if sem.timeout == 0 {
|
||||
<-sem.slots
|
||||
return true
|
||||
}
|
||||
tm := time.NewTimer(sem.timeout)
|
||||
defer tm.Stop()
|
||||
select {
|
||||
case <-sem.slots:
|
||||
return true
|
||||
case <-tm.C:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// TryAcquire acquires a semaphore if it's immediately available.
|
||||
// It returns false otherwise.
|
||||
func (sem *Semaphore) TryAcquire() bool {
|
||||
select {
|
||||
case <-sem.slots:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Release releases the acquired semaphore. You must
|
||||
// not release more than the number of semaphores you've
|
||||
// acquired.
|
||||
func (sem *Semaphore) Release() {
|
||||
sem.slots <- struct{}{}
|
||||
}
|
||||
|
||||
// Size returns the current number of available slots.
|
||||
func (sem *Semaphore) Size() int {
|
||||
return len(sem.slots)
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
Copyright 2017 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Modified by Duncan Jones to remove the external dependency.
|
||||
*/
|
||||
|
||||
// Package timer provides various enhanced timer functions.
|
||||
package pool
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Out-of-band messages
|
||||
type typeAction int
|
||||
|
||||
const (
|
||||
timerStop typeAction = iota
|
||||
timerReset
|
||||
timerTrigger
|
||||
)
|
||||
|
||||
/*
|
||||
Timer provides timer functionality that can be controlled
|
||||
by the user. You start the timer by providing it a callback function,
|
||||
which it will call at the specified interval.
|
||||
|
||||
var t = timer.NewTimer(1e9)
|
||||
t.Start(KeepHouse)
|
||||
|
||||
func KeepHouse() {
|
||||
// do house keeping work
|
||||
}
|
||||
|
||||
You can stop the timer by calling t.Stop, which is guaranteed to
|
||||
wait if KeepHouse is being executed.
|
||||
|
||||
You can create an untimely trigger by calling t.Trigger. You can also
|
||||
schedule an untimely trigger by calling t.TriggerAfter.
|
||||
|
||||
The timer interval can be changed on the fly by calling t.SetInterval.
|
||||
A zero value interval will cause the timer to wait indefinitely, and it
|
||||
will react only to an explicit Trigger or Stop.
|
||||
*/
|
||||
type Timer struct {
|
||||
interval AtomicDuration
|
||||
|
||||
// state management
|
||||
mu sync.Mutex
|
||||
running bool
|
||||
|
||||
// msg is used for out-of-band messages
|
||||
msg chan typeAction
|
||||
}
|
||||
|
||||
// NewTimer creates a new Timer object
|
||||
func NewTimer(interval time.Duration) *Timer {
|
||||
tm := &Timer{
|
||||
msg: make(chan typeAction),
|
||||
}
|
||||
tm.interval.Set(interval)
|
||||
return tm
|
||||
}
|
||||
|
||||
// Start starts the timer.
|
||||
func (tm *Timer) Start(keephouse func()) {
|
||||
tm.mu.Lock()
|
||||
defer tm.mu.Unlock()
|
||||
if tm.running {
|
||||
return
|
||||
}
|
||||
tm.running = true
|
||||
go tm.run(keephouse)
|
||||
}
|
||||
|
||||
func (tm *Timer) run(keephouse func()) {
|
||||
var timer *time.Timer
|
||||
for {
|
||||
var ch <-chan time.Time
|
||||
interval := tm.interval.Get()
|
||||
if interval > 0 {
|
||||
timer = time.NewTimer(interval)
|
||||
ch = timer.C
|
||||
}
|
||||
select {
|
||||
case action := <-tm.msg:
|
||||
if timer != nil {
|
||||
timer.Stop()
|
||||
timer = nil
|
||||
}
|
||||
switch action {
|
||||
case timerStop:
|
||||
return
|
||||
case timerReset:
|
||||
continue
|
||||
}
|
||||
case <-ch:
|
||||
}
|
||||
keephouse()
|
||||
}
|
||||
}
|
||||
|
||||
// SetInterval changes the wait interval.
|
||||
// It will cause the timer to restart the wait.
|
||||
func (tm *Timer) SetInterval(ns time.Duration) {
|
||||
tm.interval.Set(ns)
|
||||
tm.mu.Lock()
|
||||
defer tm.mu.Unlock()
|
||||
if tm.running {
|
||||
tm.msg <- timerReset
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger will cause the timer to immediately execute the keephouse function.
|
||||
// It will then cause the timer to restart the wait.
|
||||
func (tm *Timer) Trigger() {
|
||||
tm.mu.Lock()
|
||||
defer tm.mu.Unlock()
|
||||
if tm.running {
|
||||
tm.msg <- timerTrigger
|
||||
}
|
||||
}
|
||||
|
||||
// TriggerAfter waits for the specified duration and triggers the next event.
|
||||
func (tm *Timer) TriggerAfter(duration time.Duration) {
|
||||
go func() {
|
||||
time.Sleep(duration)
|
||||
tm.Trigger()
|
||||
}()
|
||||
}
|
||||
|
||||
// Stop will stop the timer. It guarantees that the timer will not execute
|
||||
// any more calls to keephouse once it has returned.
|
||||
func (tm *Timer) Stop() {
|
||||
tm.mu.Lock()
|
||||
defer tm.mu.Unlock()
|
||||
if tm.running {
|
||||
tm.msg <- timerStop
|
||||
tm.running = false
|
||||
}
|
||||
}
|
||||
|
||||
// Interval returns the current interval.
|
||||
func (tm *Timer) Interval() time.Duration {
|
||||
return tm.interval.Get()
|
||||
}
|
||||
Vendored
+7
@@ -43,6 +43,9 @@ github.com/HdrHistogram/hdrhistogram-go
|
||||
## explicit
|
||||
github.com/Microsoft/go-winio
|
||||
github.com/Microsoft/go-winio/pkg/guid
|
||||
# github.com/ThalesIgnite/crypto11 v1.2.4
|
||||
## explicit
|
||||
github.com/ThalesIgnite/crypto11
|
||||
# github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38
|
||||
## explicit
|
||||
# github.com/alecthomas/colour v0.1.0
|
||||
@@ -407,6 +410,8 @@ github.com/matttproud/golang_protobuf_extensions/pbutil
|
||||
github.com/mdp/rsc/gf256
|
||||
github.com/mdp/rsc/qr
|
||||
github.com/mdp/rsc/qr/coding
|
||||
# github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f
|
||||
github.com/miekg/pkcs11
|
||||
# github.com/mitchellh/go-wordwrap v1.0.1
|
||||
## explicit
|
||||
github.com/mitchellh/go-wordwrap
|
||||
@@ -500,6 +505,8 @@ github.com/spf13/pflag
|
||||
## explicit
|
||||
github.com/stretchr/testify/assert
|
||||
github.com/stretchr/testify/require
|
||||
# github.com/thales-e-security/pool v0.0.2
|
||||
github.com/thales-e-security/pool
|
||||
# github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5
|
||||
## explicit
|
||||
# github.com/tstranex/u2f v0.0.0-20160508205855-eb799ce68da4
|
||||
|
||||
Reference in New Issue
Block a user