pax_global_header00006660000000000000000000000064140067352070014516gustar00rootroot0000000000000052 comment=2e05147a33ca754e5bd6aed0f2db7966a06d1efd crypto11-1.2.4/000077500000000000000000000000001400673520700132045ustar00rootroot00000000000000crypto11-1.2.4/.gitignore000066400000000000000000000000671400673520700151770ustar00rootroot00000000000000*~ crypto11.test demo/*.pem demo/demo /.idea/ /vendor/ crypto11-1.2.4/.travis.yml000066400000000000000000000011021400673520700153070ustar00rootroot00000000000000dist: 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 . crypto11-1.2.4/LICENCE.txt000066400000000000000000000020721400673520700150100ustar00rootroot00000000000000MIT 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. crypto11-1.2.4/README.md000066400000000000000000000156401400673520700144710ustar00rootroot00000000000000Crypto11 ======== [![GoDoc](https://godoc.org/github.com/ThalesIgnite/crypto11?status.svg)](https://godoc.org/github.com/ThalesIgnite/crypto11) [![Build Status](https://travis-ci.com/ThalesIgnite/crypto11.svg?branch=master)](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=` where `` 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. crypto11-1.2.4/aead.go000066400000000000000000000133431400673520700144310ustar00rootroot00000000000000// 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 } crypto11-1.2.4/attributes.go000066400000000000000000000361551400673520700157330ustar00rootroot00000000000000package 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" } } crypto11-1.2.4/attributes_test.go000066400000000000000000000005661400673520700167670ustar00rootroot00000000000000package crypto11 import ( "testing" "github.com/stretchr/testify/assert" ) func TestSetNoPanicOnWrongType(t *testing.T) { a := NewAttributeSet() err := a.Set(CkaId, []string{"this is not allowed"}) assert.Error(t, err) } func TestNewAttributeNoPanicOnWrongType(t *testing.T) { _, err := NewAttribute(CkaId, []string{"this is not allowed"}) assert.Error(t, err) } crypto11-1.2.4/block.go000066400000000000000000000065771400673520700146440ustar00rootroot00000000000000// 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) } } crypto11-1.2.4/blockmode.go000066400000000000000000000155071400673520700155020ustar00rootroot00000000000000// 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") } } crypto11-1.2.4/certificates.go000066400000000000000000000205611400673520700162040ustar00rootroot00000000000000// 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 } crypto11-1.2.4/certificates_test.go000066400000000000000000000143321400673520700172420ustar00rootroot00000000000000// 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/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/asn1" "math/big" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestCertificate(t *testing.T) { skipTest(t, skipTestCert) ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() id := randomBytes() label := randomBytes() cert := generateRandomCert(t) err = ctx.ImportCertificateWithLabel(id, label, cert) require.NoError(t, err) cert2, err := ctx.FindCertificate(nil, label, nil) require.NoError(t, err) require.NotNil(t, cert2) assert.Equal(t, cert.Signature, cert2.Signature) cert2, err = ctx.FindCertificate(nil, []byte("test2"), nil) require.NoError(t, err) assert.Nil(t, cert2) cert2, err = ctx.FindCertificate(nil, nil, cert.SerialNumber) require.NoError(t, err) require.NotNil(t, cert2) assert.Equal(t, cert.Signature, cert2.Signature) } // Test that provided attributes override default values func TestCertificateAttributes(t *testing.T) { skipTest(t, skipTestCert) ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() cert := generateRandomCert(t) // We import this with a different serial number, to test this is obeyed ourSerial := new(big.Int) ourSerial.Add(cert.SerialNumber, big.NewInt(1)) derSerial, err := asn1.Marshal(ourSerial) require.NoError(t, err) template := NewAttributeSet() err = template.Set(CkaSerialNumber, derSerial) require.NoError(t, err) err = ctx.ImportCertificateWithAttributes(template, cert) require.NoError(t, err) // Try to find with old serial c, err := ctx.FindCertificate(nil, nil, cert.SerialNumber) assert.Nil(t, c) // Find with new serial c, err = ctx.FindCertificate(nil, nil, ourSerial) assert.NotNil(t, c) } func TestCertificateRequiredArgs(t *testing.T) { skipTest(t, skipTestCert) ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() cert := generateRandomCert(t) val := randomBytes() err = ctx.ImportCertificateWithLabel(nil, val, cert) require.Error(t, err) err = ctx.ImportCertificateWithLabel(val, nil, cert) require.Error(t, err) err = ctx.ImportCertificateWithLabel(val, val, nil) require.Error(t, err) } func TestDeleteCertificate(t *testing.T) { skipTest(t, skipTestCert) ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() randomCert := func() ([]byte, []byte, *x509.Certificate) { id := randomBytes() label := randomBytes() cert := generateRandomCert(t) return id, label, cert } importCertificate := func() ([]byte, []byte, *big.Int) { id, label, cert := randomCert() err = ctx.ImportCertificateWithLabel(id, label, cert) require.NoError(t, err) cert2, err := ctx.FindCertificate(id, label, cert.SerialNumber) require.NoError(t, err) require.NotNil(t, cert2) assert.Equal(t, cert.Signature, cert2.Signature) return id, label, cert.SerialNumber } err = ctx.DeleteCertificate(nil, nil, nil) require.Error(t, err) id, label, cert := randomCert() err = ctx.DeleteCertificate(id, label, cert.SerialNumber) require.NoError(t, err) id, label, serial := importCertificate() err = ctx.DeleteCertificate(id, label, serial) require.NoError(t, err) cert, err = ctx.FindCertificate(id, label, serial) require.NoError(t, err) require.Nil(t, cert) id, label, serial = importCertificate() err = ctx.DeleteCertificate(id, label, nil) require.NoError(t, err) cert, err = ctx.FindCertificate(id, label, serial) require.NoError(t, err) require.Nil(t, cert) id, label, serial = importCertificate() err = ctx.DeleteCertificate(id, nil, nil) require.NoError(t, err) cert, err = ctx.FindCertificate(id, label, serial) require.NoError(t, err) require.Nil(t, cert) id, label, serial = importCertificate() err = ctx.DeleteCertificate(nil, label, nil) require.NoError(t, err) cert, err = ctx.FindCertificate(id, label, serial) require.NoError(t, err) require.Nil(t, cert) id, label, serial = importCertificate() err = ctx.DeleteCertificate(nil, nil, serial) require.NoError(t, err) cert, err = ctx.FindCertificate(id, label, serial) require.NoError(t, err) require.Nil(t, cert) } func generateRandomCert(t *testing.T) *x509.Certificate { serial, err := rand.Int(rand.Reader, big.NewInt(20000)) require.NoError(t, err) ca := &x509.Certificate{ Subject: pkix.Name{ CommonName: "Foo", }, SerialNumber: serial, NotAfter: time.Now().Add(365 * 24 * time.Hour), IsCA: true, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, BasicConstraintsValid: true, } key, err := rsa.GenerateKey(rand.Reader, 4096) require.NoError(t, err) csr := &key.PublicKey certBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, csr, key) require.NoError(t, err) cert, err := x509.ParseCertificate(certBytes) require.NoError(t, err) return cert } crypto11-1.2.4/close_test.go000066400000000000000000000065331400673520700157060ustar00rootroot00000000000000// 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/dsa" "crypto/elliptic" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestErrorAfterClosed(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) err = ctx.Close() require.NoError(t, err) bytes := randomBytes() _, err = ctx.FindKey(bytes, nil) assert.Equal(t, errClosed, err) _, err = ctx.FindKeys(bytes, nil) assert.Equal(t, errClosed, err) _, err = ctx.FindKeysWithAttributes(NewAttributeSet()) assert.Equal(t, errClosed, err) _, err = ctx.FindKeyPair(bytes, nil) assert.Equal(t, errClosed, err) _, err = ctx.FindKeyPairs(bytes, nil) assert.Equal(t, errClosed, err) _, err = ctx.FindKeyPairsWithAttributes(NewAttributeSet()) assert.Equal(t, errClosed, err) _, err = ctx.GenerateSecretKey(bytes, 256, CipherAES) assert.Equal(t, errClosed, err) _, err = ctx.GenerateSecretKeyWithLabel(bytes, bytes, 256, CipherAES) assert.Equal(t, errClosed, err) _, err = ctx.GenerateRSAKeyPair(bytes, 2048) assert.Equal(t, errClosed, err) _, err = ctx.GenerateRSAKeyPairWithLabel(bytes, bytes, 2048) assert.Equal(t, errClosed, err) _, err = ctx.GenerateDSAKeyPair(bytes, dsaSizes[dsa.L1024N160]) assert.Equal(t, errClosed, err) _, err = ctx.GenerateDSAKeyPairWithLabel(bytes, bytes, dsaSizes[dsa.L1024N160]) assert.Equal(t, errClosed, err) _, err = ctx.GenerateECDSAKeyPair(bytes, elliptic.P224()) assert.Equal(t, errClosed, err) _, err = ctx.GenerateECDSAKeyPairWithLabel(bytes, bytes, elliptic.P224()) assert.Equal(t, errClosed, err) _, err = ctx.NewRandomReader() assert.Equal(t, errClosed, err) cert := generateRandomCert(t) err = ctx.ImportCertificate(bytes, cert) assert.Equal(t, errClosed, err) err = ctx.ImportCertificateWithLabel(bytes, bytes, cert) assert.Equal(t, errClosed, err) err = ctx.ImportCertificateWithAttributes(NewAttributeSet(), cert) assert.Equal(t, errClosed, err) _, err = ctx.GetAttribute(nil, CkaLabel) assert.Equal(t, errClosed, err) _, err = ctx.GetAttributes(nil, []AttributeType{CkaLabel}) assert.Equal(t, errClosed, err) _, err = ctx.GetPubAttribute(nil, CkaLabel) assert.Equal(t, errClosed, err) _, err = ctx.GetPubAttributes(nil, []AttributeType{CkaLabel}) assert.Equal(t, errClosed, err) } crypto11-1.2.4/common.go000066400000000000000000000067631400673520700150370ustar00rootroot00000000000000// 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() } crypto11-1.2.4/common_test.go000066400000000000000000000015071400673520700160650ustar00rootroot00000000000000package crypto11 import ( "testing" ) func TestULongMasking(t *testing.T) { ulongData := uint(0x33221100ddccbbaa) ulongSlice := ulongToBytes(ulongData) // Build an slice that is longer than the size of a ulong extraLongSlice := append(ulongSlice, ulongSlice...) tests := []struct { slice []uint8 expected uint }{ {ulongSlice[0:0], 0}, {ulongSlice[0:1], 0xaa}, {ulongSlice[0:2], 0xbbaa}, {ulongSlice[0:3], 0xccbbaa}, {ulongSlice[0:4], 0xddccbbaa}, {ulongSlice[0:5], 0x00ddccbbaa}, {ulongSlice[0:6], 0x1100ddccbbaa}, {ulongSlice[0:7], 0x221100ddccbbaa}, {ulongSlice[0:8], 0x33221100ddccbbaa}, {extraLongSlice, 0x33221100ddccbbaa}, } for _, test := range tests { got := bytesToUlong(test.slice) if test.expected != got { t.Errorf("conversion failed: 0x%X != 0x%X", test.expected, got) } } } crypto11-1.2.4/config000066400000000000000000000001411400673520700143700ustar00rootroot00000000000000{ "Path" : "/usr/lib/softhsm/libsofthsm2.so", "TokenLabel": "token1", "Pin" : "password" } crypto11-1.2.4/crypto11.go000066400000000000000000000370011400673520700152160ustar00rootroot00000000000000// 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 } crypto11-1.2.4/crypto11_test.go000066400000000000000000000162341400673520700162620ustar00rootroot00000000000000// 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 ( "encoding/json" "fmt" "log" "math/rand" "os" "testing" "time" "github.com/miekg/pkcs11" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestKeysPersistAcrossContexts(t *testing.T) { // Verify that close and re-open works. ctx, err := ConfigureFromFile("config") require.NoError(t, err) id := randomBytes() _, err = ctx.GenerateRSAKeyPair(id, rsaSize) if err != nil { _ = ctx.Close() t.Fatal(err) } require.NoError(t, ctx.Close()) ctx, err = ConfigureFromFile("config") require.NoError(t, err) key2, err := ctx.FindKeyPair(id, nil) require.NoError(t, err) testRsaSigning(t, key2, false) _ = key2.Delete() require.NoError(t, ctx.Close()) } func configureWithPin(t *testing.T) (*Context, error) { cfg, err := getConfig("config") if err != nil { t.Fatal(err) } if cfg.Pin == "" { t.Fatal("invalid configuration. configuration must have PIN non empty.") } ctx, err := Configure(cfg) if err != nil { t.Fatal("failed to configure service:", err) } return ctx, nil } func getConfig(configLocation string) (ctx *Config, err error) { file, err := os.Open(configLocation) if err != nil { log.Printf("Could not open config file: %s", configLocation) return nil, err } defer func() { err = file.Close() }() configDecoder := json.NewDecoder(file) config := &Config{} err = configDecoder.Decode(config) if err != nil { log.Printf("Could decode config file: %s", err.Error()) return nil, err } return config, nil } func TestKeyPairDelete(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() id := randomBytes() key, err := ctx.GenerateRSAKeyPair(id, 2048) require.NoError(t, err) // Check we can find it _, err = ctx.FindKeyPair(id, nil) require.NoError(t, err) err = key.Delete() require.NoError(t, err) k, err := ctx.FindKeyPair(id, nil) require.NoError(t, err) require.Nil(t, k) } func TestKeyDelete(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() id := randomBytes() key, err := ctx.GenerateSecretKey(id, 128, CipherAES) require.NoError(t, err) // Check we can find it _, err = ctx.FindKey(id, nil) require.NoError(t, err) err = key.Delete() require.NoError(t, err) k, err := ctx.FindKey(id, nil) require.NoError(t, err) require.Nil(t, k) } func TestAmbiguousTokenConfig(t *testing.T) { slotNum := 1 tests := []struct { config *Config err string }{ { config: &Config{TokenSerial: "serial", TokenLabel: "label"}, err: "config must specify exactly one way to select a token: token label, token serial number given", }, { config: &Config{TokenSerial: "serial", SlotNumber: &slotNum}, err: "config must specify exactly one way to select a token: slot number, token serial number given", }, { config: &Config{SlotNumber: &slotNum, TokenLabel: "label"}, err: "config must specify exactly one way to select a token: slot number, token label given", }, { config: &Config{}, err: "config must specify exactly one way to select a token: none given", }, } for i, test := range tests { t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) { _, err := Configure(test.config) if assert.Error(t, err) { assert.Equal(t, test.err, err.Error()) } }) } } func TestSelectBySlot(t *testing.T) { config, err := loadConfigFromFile("config") require.NoError(t, err) // Look up slot number for label ctx, err := Configure(config) require.NoError(t, err) slotNumber := int(ctx.slot) t.Logf("Using slot %d", slotNumber) err = ctx.Close() require.NoError(t, err) slotConfig := &Config{ SlotNumber: &slotNumber, Pin: config.Pin, Path: config.Path, } ctx, err = Configure(slotConfig) require.NoError(t, err) slotNumber2 := int(ctx.slot) err = ctx.Close() require.NoError(t, err) assert.Equal(t, slotNumber, slotNumber2) } func TestSelectByNonExistingSlot(t *testing.T) { config, err := loadConfigFromFile("config") require.NoError(t, err) rand.Seed(time.Now().UnixNano()) randomSlot := int(rand.Uint32()) config.TokenLabel = "" config.TokenSerial = "" config.SlotNumber = &randomSlot // Look up slot number for label _, err = Configure(config) require.Equal(t, errTokenNotFound, err) } func TestAccessSameLibraryTwice(t *testing.T) { ctx1, err := ConfigureFromFile("config") require.NoError(t, err) ctx2, err := ConfigureFromFile("config") require.NoError(t, err) // Close the first context, which shouldn't render the second // context unusable err = ctx1.Close() require.NoError(t, err) // Try to find a non-existant key. We are just checking that we can // use the underlying P11 lib. _, err = ctx2.FindKey(randomBytes(), nil) require.NoError(t, err) err = ctx2.Close() require.NoError(t, err) // Check we can open this again and use it without error ctx3, err := ConfigureFromFile("config") require.NoError(t, err) // Try to find a non-existant key. We are just checking that we can // use the underlying P11 lib. _, err = ctx3.FindKey(randomBytes(), nil) require.NoError(t, err) err = ctx3.Close() require.NoError(t, err) } func TestNoLogin(t *testing.T) { // To test that no login is respected, we attempt to perform an operation on our // SoftHSM HSM without logging in and check for the error. cfg, err := getConfig("config") require.NoError(t, err) cfg.LoginNotSupported = true ctx, err := Configure(cfg) require.NoError(t, err) _, err = ctx.GenerateSecretKey(randomBytes(), 256, CipherAES) require.Error(t, err) p11Err, ok := err.(pkcs11.Error) require.True(t, ok) assert.Equal(t, pkcs11.Error(pkcs11.CKR_USER_NOT_LOGGED_IN), p11Err) } func TestInvalidMaxSessions(t *testing.T) { cfg, err := getConfig("config") require.NoError(t, err) cfg.MaxSessions = 1 _, err = Configure(cfg) require.Error(t, err) } // randomBytes returns 32 random bytes. func randomBytes() []byte { result := make([]byte, 32) rand.Read(result) return result } func init() { rand.Seed(time.Now().UnixNano()) } crypto11-1.2.4/dsa.go000066400000000000000000000130511400673520700143020ustar00rootroot00000000000000// 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) } crypto11-1.2.4/dsa_test.go000066400000000000000000000247751400673520700153600ustar00rootroot00000000000000// 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" "crypto/rand" _ "crypto/sha1" _ "crypto/sha256" _ "crypto/sha512" "io" "math/big" "testing" "github.com/stretchr/testify/require" ) // Use pre-cooked groups, making new ones is too slow and doesn't test // the thing we care about right now var dsaSizes = map[dsa.ParameterSizes]*dsa.Parameters{ dsa.L1024N160: dsaParameters("162901807344330501683198681796158038892896087086101563643117520955342956222658660947046551471908694917426639943346403004016664903097104319514472496004393646085043684550200397339750548531631673569299660859981509826530366281432841569092450374450801368309873677511310151482654326760484535441347619035325248971027", "1152435081592794070452857956121091711775772698753", "115452448594884027370996574512176932912212827128999848033626686707424652518655228807676153362807773892059885310384996519508710450365985083911488963280781008704774827093959271338258878644127595592794116557826253235239116215434665533711823763161327640232154970352210500147125324244313495378864886440434350445047"), dsa.L2048N224: dsaParameters("26897309010050677376706120375514893443234451462311845264107450103587616694552775798203063987684778976094649425235043381904342317828282144890959162115530648385936228003908801487852374144335466990519500032005785121648159893054617696809139702264373725369373070626941633457622971603832559810490847598977955584343798411835596790144008113895279879074659082117447812649062019962590823347376506566929271773368811020476842094620754802895105872616973988885001858825172067780326607348935255985451124288300256500395655426867070867964116873929476668281059072072214310998391443703378306986615500676546104049539495719703519940117951", "21279492449681152694989309770053968015769408476251418501127004412991", "806074083423550090996520226918161019531020530678027294640523175956127336741123734330141199877500196483993261275159586661624671147930168776905666558638413092940255649594308870768538528038043504442900678005068039885085228576696098584188275093427119292669713986799663707376358075013805220640084303178400319317901835943666702067023936970119214055180476824871264924202941665618311395109943406576227418395899047272659412653021884375802245946147395739665170684226546861490208766142484366132284564686737604148267296225461433426617875614890925967933759512314096027440880608838689527799562179981619366217116863163848643347517"), dsa.L2048N256: dsaParameters("17112618140558405746569134228451035632946851190522147937285442098259988182250197615427085779160410421439180425324757684372143777133652794766735361927034003821174405167502004454639027705200074749864396322138822913233787614500370035203936072693687262723523490964341964969759901927635271723521044671768174187470434319243240992425833709397541419254550881926245866992880605910541826307493251693926611230487275444183879966706699255763528432543168158080992127766526692139601189110012363846432577767973841827921723244473497325201586745757496510772406382623383426532181890987571204825714955203879088898058005561306135204877009", "91121968019586848097218691760416459853586419261325465835456057472782359091757", "11440584645174460641968590902479650665148631776226218292071277461507925953586967572345732823684968840767915723600616468676971247641561301128304315573382612091463182732020028761013121095491181166498021161181825176128544846296532345606186214409027298873946181476041031383608259830471735783109449938372605549135088084178714254363472647038149745329509161045818621699799741032979200140196670711384411636885603577783522623156072149396954757319730766808882432057418486340662640743658615448774457850848807133015183403659298100852797177921099769325832019636521852355306536644565775804923805884803806962749273871634832496124563"), dsa.L3072N256: dsaParameters("3292223093076756007575154742534996337119773908047183722225998562022708727169602325355570673703977126031767898507744907641046577256340035141288034004031488512406489120451045534249225732833971818604693952828746803787139641400035401666093696260981834564456248118055149793765296502054830103686831403950712801607640861048795967036349397959545638669305551590712409193326920723566244393566858414736680142461926772620503656504255924167986033103929106720448272225556256931951085963366484599500380306971899408684747849004037207957694189418415781274826562470103438926910486414615463061843381314235226091370587221926283099048619624319791834455498858310112354321475086722140598386575332287897366827250193444772749165320615259571588848174295398383162626179797293184955390849951880022388527593205033454738502281212004414696356545085156516647817931381822225669880911822774112408110220283747435924461722168415951571106537524166169188892805803", "108867360151102455324355349853282813937921700957084486520964693771814677113683", "1557152939324444301267588704765106652107942442192133066719901270619999543077285434937381472603966361780723031759112891413917740586179484242767473407407437414309703817996415200985589914186423758047030899237374015161656689079147160316057703866877939220177527975058770672610222076376028225539948249840143348317913168294956877608194868405476013052838117032232844130922725529104417156431908522534312701383683651539112438656112551665554183084212392173005974168567420494917967524105375970433753993335902159248975586830245477125927759707545151441829187496420494441219127113977851955484097152895793192460180527358969782742628457686090239586702325662876026163849101009626548110941786173048851538499449581500118606509607658612519792319018156890523385946253475322407270910172103139620247134330065127064271570620806081501157521483092036300763874199611913859815207922006817632837708771028001952742328571406294474170839325153987075519613561"), } func dsaParameters(p, q, g string) *dsa.Parameters { var params = dsa.Parameters{ P: new(big.Int), Q: new(big.Int), G: new(big.Int), } params.P.SetString(p, 10) params.Q.SetString(q, 10) params.G.SetString(g, 10) return ¶ms } // Make dsa.PrivateKey look like a Crypto.Signer type DSAPrivateKey dsa.PrivateKey func (signer *DSAPrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { key := (*dsa.PrivateKey)(signer) var sig dsaSignature var err error if sig.R, sig.S, err = dsa.Sign(rand, key, digest); err != nil { return nil, err } return sig.marshalDER() } func (signer *DSAPrivateKey) Public() crypto.PublicKey { return crypto.PublicKey(&signer.PublicKey) } func TestNativeDSA(t *testing.T) { var err error for psize, params := range dsaSizes { var key = &dsa.PrivateKey{ PublicKey: dsa.PublicKey{ Parameters: *params, Y: nil, }, X: nil, } if err = dsa.GenerateKey(key, rand.Reader); err != nil { t.Errorf("crypto.dsa.GenerateKey: %v", err) return } testDsaSigning(t, (*DSAPrivateKey)(key), psize, "native") } } func TestHardDSA(t *testing.T) { skipTest(t, skipTestDSA) ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { err = ctx.Close() require.NoError(t, err) }() for pSize, params := range dsaSizes { id := randomBytes() label := randomBytes() key, err := ctx.GenerateDSAKeyPairWithLabel(id, label, params) require.NoError(t, err, "Failed for key size %s", parameterSizeToString(pSize)) defer func(k Signer) { _ = k.Delete() }(key) testDsaSigning(t, key, pSize, "hard1") key2, err := ctx.FindKeyPair(id, nil) require.NoError(t, err) testDsaSigning(t, key2.(*pkcs11PrivateKeyDSA), pSize, "hard2") key3, err := ctx.FindKeyPair(nil, label) require.NoError(t, err) testDsaSigning(t, key3.(crypto.Signer), pSize, "hard3") } } func parameterSizeToString(s dsa.ParameterSizes) string { switch s { case dsa.L1024N160: return "L1024N160" case dsa.L2048N224: return "L2048N224" case dsa.L2048N256: return "L2048N256" case dsa.L3072N256: return "L3072N256" default: return "unknown" } } func testDsaSigning(t *testing.T, key crypto.Signer, psize dsa.ParameterSizes, what string) { testDsaSigningWithHash(t, key, crypto.SHA1, psize, what) testDsaSigningWithHash(t, key, crypto.SHA224, psize, what) testDsaSigningWithHash(t, key, crypto.SHA256, psize, what) testDsaSigningWithHash(t, key, crypto.SHA384, psize, what) testDsaSigningWithHash(t, key, crypto.SHA512, psize, what) } func testDsaSigningWithHash(t *testing.T, key crypto.Signer, hashFunction crypto.Hash, psize dsa.ParameterSizes, what string) { plaintext := []byte("sign me with DSA") h := hashFunction.New() _, err := h.Write(plaintext) require.NoError(t, err) plaintextHash := h.Sum([]byte{}) // weird API // crypto.dsa.Sign doesn't truncate the hash! qbytes := (dsaSizes[psize].Q.BitLen() + 7) / 8 plaintextHash = plaintextHash[:qbytes] sigDER, err := key.Sign(rand.Reader, plaintextHash, hashFunction) require.NoError(t, err) var sig dsaSignature err = sig.unmarshalDER(sigDER) require.NoError(t, err) dsaPubkey := key.Public().(crypto.PublicKey).(*dsa.PublicKey) if !dsa.Verify(dsaPubkey, plaintextHash, sig.R, sig.S) { t.Errorf("DSA %s Verify failed (psize %d hash %v)", what, psize, hashFunction) } } func TestDsaRequiredArgs(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() _, err = ctx.GenerateDSAKeyPair(nil, dsaSizes[dsa.L2048N224]) require.Error(t, err) val := randomBytes() _, err = ctx.GenerateDSAKeyPairWithLabel(nil, val, dsaSizes[dsa.L2048N224]) require.Error(t, err) _, err = ctx.GenerateDSAKeyPairWithLabel(val, nil, dsaSizes[dsa.L2048N224]) require.Error(t, err) } crypto11-1.2.4/ecdsa.go000066400000000000000000000221761400673520700146220ustar00rootroot00000000000000// 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) } crypto11-1.2.4/ecdsa_test.go000066400000000000000000000112661400673520700156570ustar00rootroot00000000000000// 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/ecdsa" "crypto/elliptic" "crypto/rand" _ "crypto/sha1" _ "crypto/sha256" _ "crypto/sha512" "testing" "github.com/miekg/pkcs11" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) var curves = []elliptic.Curve{ elliptic.P224(), elliptic.P256(), elliptic.P384(), elliptic.P521(), // plus something with explicit parameters } func TestNativeECDSA(t *testing.T) { var err error var key *ecdsa.PrivateKey for _, curve := range curves { if key, err = ecdsa.GenerateKey(curve, rand.Reader); err != nil { t.Errorf("crypto.ecdsa.GenerateKey: %v", err) return } testEcdsaSigning(t, key, crypto.SHA1, curve.Params().Name, "SHA-1") testEcdsaSigning(t, key, crypto.SHA224, curve.Params().Name, "SHA-224") testEcdsaSigning(t, key, crypto.SHA256, curve.Params().Name, "SHA-256") testEcdsaSigning(t, key, crypto.SHA384, curve.Params().Name, "SHA-384") testEcdsaSigning(t, key, crypto.SHA512, curve.Params().Name, "SHA-512") } } func TestHardECDSA(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { err = ctx.Close() require.NoError(t, err) }() for _, curve := range curves { id := randomBytes() label := randomBytes() key, err := ctx.GenerateECDSAKeyPairWithLabel(id, label, curve) require.NoError(t, err) require.NotNil(t, key) defer func(k Signer) { _ = k.Delete() }(key) testEcdsaSigning(t, key, crypto.SHA1, curve.Params().Name, "SHA-1") testEcdsaSigning(t, key, crypto.SHA224, curve.Params().Name, "SHA-224") testEcdsaSigning(t, key, crypto.SHA256, curve.Params().Name, "SHA-256") testEcdsaSigning(t, key, crypto.SHA384, curve.Params().Name, "SHA-384") testEcdsaSigning(t, key, crypto.SHA512, curve.Params().Name, "SHA-512") key2, err := ctx.FindKeyPair(id, nil) require.NoError(t, err) testEcdsaSigning(t, key2.(*pkcs11PrivateKeyECDSA), crypto.SHA256, curve.Params().Name, "SHA-256") key3, err := ctx.FindKeyPair(nil, label) require.NoError(t, err) testEcdsaSigning(t, key3.(crypto.Signer), crypto.SHA384, curve.Params().Name, "SHA-384") } } func testEcdsaSigning(t *testing.T, key crypto.Signer, hashFunction crypto.Hash, curveName, hashName string) { plaintext := []byte("sign me with ECDSA") h := hashFunction.New() _, err := h.Write(plaintext) require.NoError(t, err) plaintextHash := h.Sum(nil) sigDER, err := key.Sign(rand.Reader, plaintextHash, nil) p11Err, ok := err.(pkcs11.Error) if ok && p11Err == pkcs11.CKR_KEY_SIZE_RANGE { // Returned by CloudHSM (at least), for key sizes it doesn't support. t.Logf("Skipping unsupported curve %s and hash %s", curveName, hashName) return } assert.NoErrorf(t, err, "Sign failed for curve %s and hash %s", curveName, hashName) if err != nil { // We assert and return, so that errors are more informative over a range of curves // and hashes. return } var sig dsaSignature err = sig.unmarshalDER(sigDER) require.NoError(t, err) ecdsaPubkey := key.Public().(crypto.PublicKey).(*ecdsa.PublicKey) if !ecdsa.Verify(ecdsaPubkey, plaintextHash, sig.R, sig.S) { t.Errorf("ECDSA Verify (hash %v): %v", hashFunction, err) } } func TestEcdsaRequiredArgs(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() _, err = ctx.GenerateECDSAKeyPair(nil, elliptic.P224()) require.Error(t, err) val := randomBytes() _, err = ctx.GenerateECDSAKeyPairWithLabel(nil, val, elliptic.P224()) require.Error(t, err) _, err = ctx.GenerateECDSAKeyPairWithLabel(val, nil, elliptic.P224()) require.Error(t, err) } crypto11-1.2.4/go.mod000066400000000000000000000003501400673520700143100ustar00rootroot00000000000000module 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 ) crypto11-1.2.4/go.sum000066400000000000000000000022331400673520700143370ustar00rootroot00000000000000github.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= crypto11-1.2.4/hmac.go000066400000000000000000000145311400673520700144470ustar00rootroot00000000000000// 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 } crypto11-1.2.4/hmac_test.go000066400000000000000000000103661400673520700155100ustar00rootroot00000000000000// 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 ( "testing" "github.com/miekg/pkcs11" "github.com/stretchr/testify/require" ) func TestHmac(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { err = ctx.Close() require.NoError(t, err) }() info, err := ctx.ctx.GetInfo() require.NoError(t, err) if info.ManufacturerID == "SoftHSM" { t.Skipf("HMAC not implemented on SoftHSM") } t.Run("HMACSHA1", func(t *testing.T) { testHmac(t, ctx, pkcs11.CKK_SHA_1_HMAC, pkcs11.CKM_SHA_1_HMAC, 0, 20, false) }) t.Run("HMACSHA1General", func(t *testing.T) { testHmac(t, ctx, pkcs11.CKK_SHA_1_HMAC, pkcs11.CKM_SHA_1_HMAC_GENERAL, 10, 10, true) }) t.Run("HMACSHA256", func(t *testing.T) { testHmac(t, ctx, pkcs11.CKK_SHA256_HMAC, pkcs11.CKM_SHA256_HMAC, 0, 32, false) }) } func testHmac(t *testing.T, ctx *Context, keytype int, mech int, length int, xlength int, full bool) { skipIfMechUnsupported(t, ctx, uint(mech)) id := randomBytes() key, err := ctx.GenerateSecretKey(id, 256, Ciphers[keytype]) require.NoError(t, err) require.NotNil(t, key) defer key.Delete() t.Run("Short", func(t *testing.T) { input := []byte("a short string") h1, err := key.NewHMAC(mech, length) require.NoError(t, err) n, err := h1.Write(input) require.NoError(t, err) require.Equal(t, len(input), n) r1 := h1.Sum([]byte{}) h2, err := key.NewHMAC(mech, length) require.NoError(t, err) n, err = h2.Write(input) require.NoError(t, err) require.Equal(t, len(input), n) r2 := h2.Sum([]byte{}) require.Equal(t, r1, r2) require.Len(t, r1, xlength) }) if full { // Independent of hash, only do these once t.Run("Empty", func(t *testing.T) { // Must be able to MAC empty inputs without panicing h1, err := key.NewHMAC(mech, length) require.NoError(t, err) h1.Sum([]byte{}) }) t.Run("MultiSum", func(t *testing.T) { input := []byte("a different short string") h1, err := key.NewHMAC(mech, length) require.NoError(t, err) n, err := h1.Write(input) require.NoError(t, err) require.Equal(t, len(input), n) r1 := h1.Sum([]byte{}) r2 := h1.Sum([]byte{}) require.Equal(t, r1, r2) // Can't add more after Sum() _, err = h1.Write(input) require.Equal(t, errHmacClosed, err) // 0-length is special n, err = h1.Write([]byte{}) require.NoError(t, err) require.Zero(t, n) }) t.Run("Reset", func(t *testing.T) { h1, err := key.NewHMAC(mech, length) require.NoError(t, err) n, err := h1.Write([]byte{1}) require.NoError(t, err) require.Equal(t, 1, n) r1 := h1.Sum([]byte{}) h1.Reset() n, err = h1.Write([]byte{2}) require.NoError(t, err) require.Equal(t, 1, n) r2 := h1.Sum([]byte{}) h1.Reset() n, err = h1.Write([]byte{1}) require.NoError(t, err) require.Equal(t, 1, n) r3 := h1.Sum([]byte{}) require.Equal(t, r1, r3) require.NotEqual(t, r1, r2) }) t.Run("ResetFast", func(t *testing.T) { // Reset() immediately after creation should be safe h1, err := key.NewHMAC(mech, length) require.NoError(t, err) h1.Reset() n, err := h1.Write([]byte{2}) require.NoError(t, err) require.Equal(t, 1, n) h1.Sum([]byte{}) }) } } crypto11-1.2.4/keys.go000066400000000000000000000437221400673520700145160ustar00rootroot00000000000000// 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 } crypto11-1.2.4/keys_test.go000066400000000000000000000131211400673520700155430ustar00rootroot00000000000000package crypto11 import ( "crypto/rand" "crypto/rsa" "testing" "github.com/miekg/pkcs11" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // withContext executes a test function with a context. func withContext(t *testing.T, f func(ctx *Context)) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() f(ctx) } func TestFindKeysRequiresIdOrLabel(t *testing.T) { withContext(t, func(ctx *Context) { _, err := ctx.FindKey(nil, nil) assert.Error(t, err) _, err = ctx.FindKeys(nil, nil) assert.Error(t, err) _, err = ctx.FindKeyPair(nil, nil) assert.Error(t, err) _, err = ctx.FindKeyPairs(nil, nil) assert.Error(t, err) }) } func TestFindingKeysWithAttributes(t *testing.T) { withContext(t, func(ctx *Context) { label := randomBytes() label2 := randomBytes() key, err := ctx.GenerateSecretKeyWithLabel(randomBytes(), label, 128, CipherAES) require.NoError(t, err) defer func(k *SecretKey) { _ = k.Delete() }(key) key, err = ctx.GenerateSecretKeyWithLabel(randomBytes(), label2, 128, CipherAES) require.NoError(t, err) defer func(k *SecretKey) { _ = k.Delete() }(key) key, err = ctx.GenerateSecretKeyWithLabel(randomBytes(), label2, 256, CipherAES) require.NoError(t, err) defer func(k *SecretKey) { _ = k.Delete() }(key) attrs := NewAttributeSet() _ = attrs.Set(CkaLabel, label) keys, err := ctx.FindKeysWithAttributes(attrs) require.NoError(t, err) require.Len(t, keys, 1) _ = attrs.Set(CkaLabel, label2) keys, err = ctx.FindKeysWithAttributes(attrs) require.NoError(t, err) require.Len(t, keys, 2) attrs = NewAttributeSet() err = attrs.Set(CkaValueLen, 16) require.NoError(t, err) keys, err = ctx.FindKeysWithAttributes(attrs) require.NoError(t, err) require.Len(t, keys, 2) attrs = NewAttributeSet() err = attrs.Set(CkaValueLen, 32) require.NoError(t, err) keys, err = ctx.FindKeysWithAttributes(attrs) require.NoError(t, err) require.Len(t, keys, 1) }) } func TestFindingKeyPairsWithAttributes(t *testing.T) { withContext(t, func(ctx *Context) { // Note: we use common labels, not IDs in this test code. AWS CloudHSM // does not accept two keys with the same ID. label := randomBytes() label2 := randomBytes() key, err := ctx.GenerateRSAKeyPairWithLabel(randomBytes(), label, rsaSize) require.NoError(t, err) defer func(k Signer) { _ = k.Delete() }(key) key, err = ctx.GenerateRSAKeyPairWithLabel(randomBytes(), label2, rsaSize) require.NoError(t, err) defer func(k Signer) { _ = k.Delete() }(key) key, err = ctx.GenerateRSAKeyPairWithLabel(randomBytes(), label2, rsaSize) require.NoError(t, err) defer func(k Signer) { _ = k.Delete() }(key) attrs := NewAttributeSet() _ = attrs.Set(CkaLabel, label) keys, err := ctx.FindKeyPairsWithAttributes(attrs) require.NoError(t, err) require.Len(t, keys, 1) _ = attrs.Set(CkaLabel, label2) keys, err = ctx.FindKeyPairsWithAttributes(attrs) require.NoError(t, err) require.Len(t, keys, 2) attrs = NewAttributeSet() _ = attrs.Set(CkaKeyType, pkcs11.CKK_RSA) keys, err = ctx.FindKeyPairsWithAttributes(attrs) require.NoError(t, err) require.Len(t, keys, 3) }) } func TestFindingAllKeys(t *testing.T) { withContext(t, func(ctx *Context) { for i := 0; i < 10; i++ { id := randomBytes() key, err := ctx.GenerateSecretKey(id, 128, CipherAES) require.NoError(t, err) defer func(k *SecretKey) { _ = k.Delete() }(key) } keys, err := ctx.FindAllKeys() require.NoError(t, err) require.NotNil(t, keys) require.Len(t, keys, 10) }) } func TestFindingAllKeyPairs(t *testing.T) { withContext(t, func(ctx *Context) { for i := 1; i <= 5; i++ { id := randomBytes() key, err := ctx.GenerateRSAKeyPair(id, rsaSize) require.NoError(t, err) defer func(k Signer) { _ = k.Delete() }(key) } keys, err := ctx.FindAllKeyPairs() require.NoError(t, err) require.NotNil(t, keys) require.Len(t, keys, 5) }) } func TestGettingPrivateKeyAttributes(t *testing.T) { withContext(t, func(ctx *Context) { id := randomBytes() key, err := ctx.GenerateRSAKeyPair(id, rsaSize) require.NoError(t, err) defer func(k Signer) { _ = k.Delete() }(key) attrs, err := ctx.GetAttributes(key, []AttributeType{CkaModulus}) require.NoError(t, err) require.NotNil(t, attrs) require.Len(t, attrs, 1) require.Len(t, attrs[CkaModulus].Value, 256) }) } func TestGettingPublicKeyAttributes(t *testing.T) { withContext(t, func(ctx *Context) { id := randomBytes() key, err := ctx.GenerateRSAKeyPair(id, rsaSize) require.NoError(t, err) defer func(k Signer) { _ = k.Delete() }(key) attrs, err := ctx.GetPubAttributes(key, []AttributeType{CkaModulusBits}) require.NoError(t, err) require.NotNil(t, attrs) require.Len(t, attrs, 1) require.Equal(t, uint(rsaSize), bytesToUlong(attrs[CkaModulusBits].Value)) }) } func TestGettingSecretKeyAttributes(t *testing.T) { withContext(t, func(ctx *Context) { id := randomBytes() key, err := ctx.GenerateSecretKey(id, 128, CipherAES) require.NoError(t, err) defer func(k *SecretKey) { _ = k.Delete() }(key) attrs, err := ctx.GetAttributes(key, []AttributeType{CkaValueLen}) require.NoError(t, err) require.NotNil(t, attrs) require.Len(t, attrs, 1) require.Equal(t, uint(16), bytesToUlong(attrs[CkaValueLen].Value)) }) } func TestGettingUnsupportedKeyTypeAttributes(t *testing.T) { withContext(t, func(ctx *Context) { key, err := rsa.GenerateKey(rand.Reader, rsaSize) require.NoError(t, err) _, err = ctx.GetAttributes(key, []AttributeType{CkaModulusBits}) require.Error(t, err) }) } crypto11-1.2.4/rand.go000066400000000000000000000035401400673520700144610ustar00rootroot00000000000000// 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 } crypto11-1.2.4/rand_test.go000066400000000000000000000031221400673520700155140ustar00rootroot00000000000000// 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 ( "testing" "github.com/stretchr/testify/require" ) func TestRandomReader(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { err = ctx.Close() require.NoError(t, err) }() reader, err := ctx.NewRandomReader() require.NoError(t, err) var a [8192]byte for _, size := range []int{1, 16, 32, 256, 347, 4096, 8192} { n, err := reader.Read(a[:size]) require.NoError(t, err) require.Equal(t, size, n) } } crypto11-1.2.4/rsa.go000066400000000000000000000267311400673520700143310ustar00rootroot00000000000000// 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 } crypto11-1.2.4/rsa_test.go000066400000000000000000000217051400673520700153640ustar00rootroot00000000000000// 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/rand" "crypto/rsa" _ "crypto/sha1" _ "crypto/sha256" _ "crypto/sha512" "testing" "github.com/miekg/pkcs11" "github.com/stretchr/testify/require" ) // Set to 2048, as most tokens will support this. 1024 not supported by some tokens (e.g. Amazon CloudHSM). const rsaSize = 2048 func TestNativeRSA(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() key, err := rsa.GenerateKey(rand.Reader, rsaSize) require.NoError(t, err) err = key.Validate() require.NoError(t, err) t.Run("Sign", func(t *testing.T) { testRsaSigning(t, key, true) }) t.Run("Encrypt", func(t *testing.T) { testRsaEncryption(t, key, true) }) } func TestHardRSA(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() id := randomBytes() label := randomBytes() key, err := ctx.GenerateRSAKeyPairWithLabel(id, label, rsaSize) require.NoError(t, err) require.NotNil(t, key) defer func() { _ = key.Delete() }() var key2, key3 crypto.PrivateKey t.Run("Sign", func(t *testing.T) { testRsaSigning(t, key, false) }) t.Run("Encrypt", func(t *testing.T) { testRsaEncryption(t, key, false) }) t.Run("FindId", func(t *testing.T) { key2, err = ctx.FindKeyPair(id, nil) require.NoError(t, err) }) t.Run("SignId", func(t *testing.T) { if key2 == nil { t.SkipNow() } testRsaSigning(t, key2.(*pkcs11PrivateKeyRSA), false) }) t.Run("FindLabel", func(t *testing.T) { key3, err = ctx.FindKeyPair(nil, label) require.NoError(t, err) }) t.Run("SignLabel", func(t *testing.T) { if key3 == nil { t.SkipNow() } testRsaSigning(t, key3.(crypto.Signer), false) }) } func testRsaSigning(t *testing.T, key crypto.Signer, native bool) { t.Run("SHA1", func(t *testing.T) { testRsaSigningPKCS1v15(t, key, crypto.SHA1) }) t.Run("SHA224", func(t *testing.T) { testRsaSigningPKCS1v15(t, key, crypto.SHA224) }) t.Run("SHA256", func(t *testing.T) { testRsaSigningPKCS1v15(t, key, crypto.SHA256) }) t.Run("SHA384", func(t *testing.T) { testRsaSigningPKCS1v15(t, key, crypto.SHA384) }) t.Run("SHA512", func(t *testing.T) { testRsaSigningPKCS1v15(t, key, crypto.SHA512) }) t.Run("PSSSHA1", func(t *testing.T) { testRsaSigningPSS(t, key, crypto.SHA1, native) }) t.Run("PSSSHA224", func(t *testing.T) { testRsaSigningPSS(t, key, crypto.SHA224, native) }) t.Run("PSSSHA256", func(t *testing.T) { testRsaSigningPSS(t, key, crypto.SHA256, native) }) t.Run("PSSSHA384", func(t *testing.T) { testRsaSigningPSS(t, key, crypto.SHA384, native) }) t.Run("PSSSHA512", func(t *testing.T) { testRsaSigningPSS(t, key, crypto.SHA512, native) }) } func testRsaSigningPKCS1v15(t *testing.T, key crypto.Signer, hashFunction crypto.Hash) { plaintext := []byte("sign me with PKCS#1 v1.5") h := hashFunction.New() _, err := h.Write(plaintext) require.NoError(t, err) plaintextHash := h.Sum([]byte{}) // weird API sig, err := key.Sign(rand.Reader, plaintextHash, hashFunction) require.NoError(t, err) rsaPubkey := key.Public().(crypto.PublicKey).(*rsa.PublicKey) err = rsa.VerifyPKCS1v15(rsaPubkey, hashFunction, plaintextHash, sig) require.NoError(t, err) } func testRsaSigningPSS(t *testing.T, key crypto.Signer, hashFunction crypto.Hash, native bool) { if !native { skipIfMechUnsupported(t, key.(*pkcs11PrivateKeyRSA).context, pkcs11.CKM_RSA_PKCS_PSS) } plaintext := []byte("sign me with PSS") h := hashFunction.New() _, err := h.Write(plaintext) require.NoError(t, err) plaintextHash := h.Sum([]byte{}) // weird API pssOptions := &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: hashFunction, } sig, err := key.Sign(rand.Reader, plaintextHash, pssOptions) require.NoError(t, err) rsaPubkey := key.Public().(crypto.PublicKey).(*rsa.PublicKey) err = rsa.VerifyPSS(rsaPubkey, hashFunction, plaintextHash, sig, pssOptions) require.NoError(t, err) } func testRsaEncryption(t *testing.T, key crypto.Decrypter, native bool) { t.Run("PKCS1v15", func(t *testing.T) { testRsaEncryptionPKCS1v15(t, key) }) t.Run("OAEPSHA1", func(t *testing.T) { testRsaEncryptionOAEP(t, key, crypto.SHA1, []byte{}, native) }) t.Run("OAEPSHA224", func(t *testing.T) { testRsaEncryptionOAEP(t, key, crypto.SHA224, []byte{}, native) }) t.Run("OAEPSHA256", func(t *testing.T) { testRsaEncryptionOAEP(t, key, crypto.SHA256, []byte{}, native) }) t.Run("OAEPSHA384", func(t *testing.T) { testRsaEncryptionOAEP(t, key, crypto.SHA384, []byte{}, native) }) t.Run("OAEPSHA512", func(t *testing.T) { testRsaEncryptionOAEP(t, key, crypto.SHA512, []byte{}, native) }) if !shouldSkipTest(skipTestOAEPLabel) { t.Run("OAEPSHA1Label", func(t *testing.T) { testRsaEncryptionOAEP(t, key, crypto.SHA1, []byte{1, 2, 3, 4}, native) }) t.Run("OAEPSHA224Label", func(t *testing.T) { testRsaEncryptionOAEP(t, key, crypto.SHA224, []byte{5, 6, 7, 8}, native) }) t.Run("OAEPSHA256Label", func(t *testing.T) { testRsaEncryptionOAEP(t, key, crypto.SHA256, []byte{9}, native) }) t.Run("OAEPSHA384Label", func(t *testing.T) { testRsaEncryptionOAEP(t, key, crypto.SHA384, []byte{10, 11, 12, 13, 14, 15}, native) }) t.Run("OAEPSHA512Label", func(t *testing.T) { testRsaEncryptionOAEP(t, key, crypto.SHA512, []byte{16, 17, 18}, native) }) } } func testRsaEncryptionPKCS1v15(t *testing.T, key crypto.Decrypter) { var err error var ciphertext, decrypted []byte plaintext := []byte("encrypt me with old and busted crypto") rsaPubkey := key.Public().(crypto.PublicKey).(*rsa.PublicKey) if ciphertext, err = rsa.EncryptPKCS1v15(rand.Reader, rsaPubkey, plaintext); err != nil { t.Errorf("PKCS#1v1.5 Encrypt: %v", err) return } if decrypted, err = key.Decrypt(rand.Reader, ciphertext, nil); err != nil { t.Errorf("PKCS#1v1.5 Decrypt (nil options): %v", err) return } if !bytes.Equal(plaintext, decrypted) { t.Errorf("PKCS#1v1.5 Decrypt (nil options): wrong answer") return } options := &rsa.PKCS1v15DecryptOptions{ SessionKeyLen: 0, } if decrypted, err = key.Decrypt(rand.Reader, ciphertext, options); err != nil { t.Errorf("PKCS#1v1.5 Decrypt %v", err) return } if !bytes.Equal(plaintext, decrypted) { t.Errorf("PKCS#1v1.5 Decrypt: wrong answer") return } } func testRsaEncryptionOAEP(t *testing.T, key crypto.Decrypter, hashFunction crypto.Hash, label []byte, native bool) { if !native { skipIfMechUnsupported(t, key.(*pkcs11PrivateKeyRSA).context, pkcs11.CKM_RSA_PKCS_OAEP) // Doesn't seem to be a way to query supported MGFs so we do that the hard way. info, err := key.(*pkcs11PrivateKeyRSA).context.ctx.GetInfo() require.NoError(t, err) if info.ManufacturerID == "SoftHSM" && (hashFunction != crypto.SHA1 || len(label) > 0) { t.Skipf("SoftHSM OAEP only supports SHA-1 with no label") } } plaintext := []byte("encrypt me with new hotness") h := hashFunction.New() rsaPubkey := key.Public().(crypto.PublicKey).(*rsa.PublicKey) ciphertext, err := rsa.EncryptOAEP(h, rand.Reader, rsaPubkey, plaintext, label) require.NoError(t, err) options := &rsa.OAEPOptions{ Hash: hashFunction, Label: label, } decrypted, err := key.Decrypt(rand.Reader, ciphertext, options) require.NoError(t, err) require.Equal(t, plaintext, decrypted) } func skipIfMechUnsupported(t *testing.T, ctx *Context, wantMech uint) { mechs, err := ctx.ctx.GetMechanismList(ctx.slot) require.NoError(t, err) for _, mech := range mechs { if mech.Mechanism == wantMech { return } } t.Skipf("mechanism 0x%x not supported", wantMech) } func TestRsaRequiredArgs(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() _, err = ctx.GenerateRSAKeyPair(nil, 2048) require.Error(t, err) val := randomBytes() _, err = ctx.GenerateRSAKeyPairWithLabel(nil, val, 2048) require.Error(t, err) _, err = ctx.GenerateRSAKeyPairWithLabel(val, nil, 2048) require.Error(t, err) } crypto11-1.2.4/sessions.go000066400000000000000000000057141400673520700154100ustar00rootroot00000000000000// 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 } crypto11-1.2.4/skip_test.go000066400000000000000000000012101400673520700155320ustar00rootroot00000000000000package crypto11 import ( "os" "strings" "testing" ) const skipTestEnv = "CRYPTO11_SKIP" const skipTestCert = "CERTS" const skipTestOAEPLabel = "OAEP_LABEL" const skipTestDSA = "DSA" // skipTest tests whether the CRYPTO11_SKIP environment variable contains // flagName. If so, it skips the test. func skipTest(t *testing.T, flagName string) { if shouldSkipTest(flagName) { t.Logf("Skipping test due to %s flag", flagName) t.SkipNow() } } func shouldSkipTest(flagName string) bool { thingsToSkip := strings.Split(os.Getenv(skipTestEnv), ",") for _, s := range thingsToSkip { if s == flagName { return true } } return false } crypto11-1.2.4/symmetric.go000066400000000000000000000251201400673520700155470ustar00rootroot00000000000000// 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() } crypto11-1.2.4/symmetric_test.go000066400000000000000000000226001400673520700166060ustar00rootroot00000000000000// Copyright 2018 Thales e-Security, Inc // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package crypto11 import ( "bytes" "crypto/cipher" "runtime" "testing" "github.com/miekg/pkcs11" "github.com/stretchr/testify/require" ) func TestHardSymmetric(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() t.Run("AES128", func(t *testing.T) { testHardSymmetric(t, ctx, pkcs11.CKK_AES, 128) }) t.Run("AES192", func(t *testing.T) { testHardSymmetric(t, ctx, pkcs11.CKK_AES, 192) }) t.Run("AES256", func(t *testing.T) { testHardSymmetric(t, ctx, pkcs11.CKK_AES, 256) }) t.Run("DES3", func(t *testing.T) { testHardSymmetric(t, ctx, pkcs11.CKK_DES3, 0) }) } func testHardSymmetric(t *testing.T, ctx *Context, keytype int, bits int) { for _, p := range Ciphers[keytype].GenParams { skipIfMechUnsupported(t, ctx, p.GenMech) } id := randomBytes() key, err := ctx.GenerateSecretKey(id, bits, Ciphers[keytype]) require.NoError(t, err) require.NotNil(t, key) defer key.Delete() var key2 *SecretKey t.Run("Find", func(t *testing.T) { key2, err = ctx.FindKey(id, nil) require.NoError(t, err) }) t.Run("Block", func(t *testing.T) { skipIfMechUnsupported(t, key.context, key.Cipher.ECBMech) testSymmetricBlock(t, key, key2) }) iv := make([]byte, key.BlockSize()) for i := range iv { iv[i] = 0xF0 } t.Run("CBC", func(t *testing.T) { // By using cipher.NewCBCEncrypter, this test will actually use ECB mode on the key. skipIfMechUnsupported(t, key2.context, key2.Cipher.ECBMech) testSymmetricMode(t, cipher.NewCBCEncrypter(key2, iv), cipher.NewCBCDecrypter(key2, iv)) }) t.Run("CBCClose", func(t *testing.T) { skipIfMechUnsupported(t, key2.context, key2.Cipher.CBCMech) enc, err := key2.NewCBCEncrypterCloser(iv) require.NoError(t, err) dec, err := key2.NewCBCDecrypterCloser(iv) require.NoError(t, err) testSymmetricMode(t, enc, dec) enc.Close() dec.Close() }) t.Run("CBCNoClose", func(t *testing.T) { skipIfMechUnsupported(t, key2.context, key2.Cipher.CBCMech) enc, err := key2.NewCBCEncrypter(iv) require.NoError(t, err) dec, err := key2.NewCBCDecrypter(iv) require.NoError(t, err) testSymmetricMode(t, enc, dec) // See discussion at BlockModeCloser. runtime.GC() }) t.Run("CBCSealOpen", func(t *testing.T) { aead, err := key2.NewCBC(PaddingNone) require.NoError(t, err) testAEADMode(t, aead, 128, 0) }) t.Run("CBCPKCSSealOpen", func(t *testing.T) { aead, err := key2.NewCBC(PaddingPKCS) require.NoError(t, err) testAEADMode(t, aead, 127, 0) }) if bits == 128 { t.Run("GCMSoft", func(t *testing.T) { aead, err := cipher.NewGCM(key2) require.NoError(t, err) testAEADMode(t, aead, 127, 129) }) t.Run("GCMHard", func(t *testing.T) { aead, err := key2.NewGCM() require.NoError(t, err) skipIfMechUnsupported(t, key2.context, pkcs11.CKM_AES_GCM) testAEADMode(t, aead, 127, 129) }) // TODO check that hard/soft is consistent! } // TODO CFB // TODO OFB // TODO CTR } func testSymmetricBlock(t *testing.T, encryptKey cipher.Block, decryptKey cipher.Block) { // The functions in cipher.Block have no error returns, so they panic if they encounter // a problem. We catch these panics here, so the test can fail nicely defer func() { if cause := recover(); cause != nil { t.Fatalf("Caught panic: %q", cause) } }() b := encryptKey.BlockSize() input := make([]byte, 3*b) middle := make([]byte, 3*b) output := make([]byte, 3*b) // Set a recognizable pattern in the buffers for i := 0; i < 3*b; i++ { input[i] = byte(i) middle[i] = byte(i + 3*b) output[i] = byte(i + 6*b) } encryptKey.Encrypt(middle, input) // middle[:b] = encrypt(input[:b]) if bytes.Equal(input[:b], middle[:b]) { t.Errorf("crypto11.PKCSSecretKey.Encrypt: identity transformation") return } matches := 0 for i := 0; i < b; i++ { if middle[i] == byte(i+3*b) { matches++ } } if matches == b { t.Errorf("crypto11.PKCSSecretKey.Encrypt: didn't modify destination") return } for i := 0; i < 3*b; i++ { if input[i] != byte(i) { t.Errorf("crypto11.PKCSSecretKey.Encrypt: corrupted source") return } if i >= b && middle[i] != byte(i+3*b) { t.Errorf("crypto11.PKCSSecretKey.Encrypt: corrupted destination past blocksize") return } } decryptKey.Decrypt(output, middle) // output[:b] = decrypt(middle[:b]) if !bytes.Equal(input[:b], output[:b]) { t.Errorf("crypto11.PKCSSecretKey.Decrypt: plaintext wrong") return } for i := 0; i < 3*b; i++ { if i >= b && output[i] != byte(i+6*b) { t.Errorf("crypto11.PKCSSecretKey.Decrypt: corrupted destination past blocksize") return } } } func testSymmetricMode(t *testing.T, encrypt cipher.BlockMode, decrypt cipher.BlockMode) { // The functions in cipher.Block have no error returns, so they panic if they encounter // a problem. We catch these panics here, so the test can fail nicely defer func() { if cause := recover(); cause != nil { t.Fatalf("Caught panic: %q", cause) } }() input := make([]byte, 256) middle := make([]byte, 256) output := make([]byte, 256) // Set a recognizable pattern in the buffers for i := 0; i < 256; i++ { input[i] = byte(i) middle[i] = byte(i + 32) output[i] = byte(i + 64) } // Encrypt the first 128 bytes encrypt.CryptBlocks(middle, input[:128]) if bytes.Equal(input[:128], middle[:128]) { t.Errorf("BlockMode.Encrypt: did not modify destination") return } for i := 0; i < 128; i++ { if input[i] != byte(i) { t.Errorf("BlockMode.Encrypt: corrupted source") return } } for i := 128; i < 256; i++ { if middle[i] != byte(i+32) { t.Errorf("BlockMode.Encrypt: corrupted destination past input size") return } } // Encrypt the rest encrypt.CryptBlocks(middle[128:], input[128:]) // Decrypt in a single go decrypt.CryptBlocks(output, middle) if !bytes.Equal(input, output) { t.Errorf("BlockMode.Decrypt: plaintext wrong") return } } func testAEADMode(t *testing.T, aead cipher.AEAD, ptlen int, adlen int) { nonce := make([]byte, aead.NonceSize()) plaintext := make([]byte, ptlen) for i := 0; i < len(plaintext); i++ { plaintext[i] = byte(i) } additionalData := make([]byte, adlen) for i := 0; i < len(additionalData); i++ { additionalData[i] = byte(i + 16) } ciphertext := aead.Seal([]byte{}, nonce, plaintext, additionalData) /* In some HSM configurations the IV may be generated and appended to the ciphertext + tag If this is the case (based on the ciphertext length), the test assumes that the IV has been appended */ if len(ciphertext) == aead.NonceSize()+len(plaintext)+aead.Overhead() { nonce = ciphertext[len(ciphertext)-aead.NonceSize():] ciphertext = ciphertext[:len(ciphertext)-aead.NonceSize()] } decrypted, err := aead.Open([]byte{}, nonce, ciphertext, additionalData) if err != nil { t.Errorf("aead.Open: %s", err) return } if !bytes.Equal(plaintext, decrypted) { t.Errorf("aead.Open: mismatch") return } } func BenchmarkCBC(b *testing.B) { ctx, err := ConfigureFromFile("config") require.NoError(b, err) defer func() { require.NoError(b, ctx.Close()) }() id := randomBytes() key, err := ctx.GenerateSecretKey(id, 128, Ciphers[pkcs11.CKK_AES]) require.NoError(b, err) require.NotNil(b, key) defer key.Delete() iv := make([]byte, 16) plaintext := make([]byte, 65536) ciphertext := make([]byte, 65536) b.Run("Native", func(b *testing.B) { for i := 0; i < b.N; i++ { mode := cipher.NewCBCEncrypter(key, iv) mode.CryptBlocks(ciphertext, plaintext) } }) b.Run("IdiomaticClose", func(b *testing.B) { for i := 0; i < b.N; i++ { mode, err := key.NewCBCEncrypterCloser(iv) if err != nil { panic(err) } mode.CryptBlocks(ciphertext, plaintext) mode.Close() } }) b.Run("Idiomatic", func(b *testing.B) { for i := 0; i < b.N; i++ { mode, err := key.NewCBCEncrypter(iv) if err != nil { panic(err) } mode.CryptBlocks(ciphertext, plaintext) } runtime.GC() }) } func TestSymmetricRequiredArgs(t *testing.T) { ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() _, err = ctx.GenerateSecretKey(nil, 128, CipherAES) require.Error(t, err) val := randomBytes() _, err = ctx.GenerateSecretKeyWithLabel(nil, val, 128, CipherAES) require.Error(t, err) _, err = ctx.GenerateSecretKeyWithLabel(val, nil, 128, CipherAES) require.Error(t, err) } // TODO BenchmarkGCM along the same lines as above crypto11-1.2.4/thread_test.go000066400000000000000000000046771400673520700160570ustar00rootroot00000000000000// 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" "testing" "time" "github.com/stretchr/testify/require" ) var threadCount = 32 var signaturesPerThread = 256 func TestThreadedRSA(t *testing.T) { if testing.Short() { t.Skip() } ctx, err := ConfigureFromFile("config") require.NoError(t, err) defer func() { require.NoError(t, ctx.Close()) }() id := randomBytes() key, err := ctx.GenerateRSAKeyPair(id, rsaSize) require.NoError(t, err) defer func(k Signer) { _ = k.Delete() }(key) done := make(chan int) started := time.Now() t.Logf("Starting %v threads", threadCount) for i := 0; i < threadCount; i++ { go signingRoutine(t, key, done) // CloudHSM falls over if you create sessions too quickly time.Sleep(50 * time.Millisecond) } t.Logf("Waiting for %v threads", threadCount) for i := 0; i < threadCount; i++ { <-done } finished := time.Now() ticks := finished.Sub(started) elapsed := float64(ticks) / 1000000000.0 t.Logf("Made %v signatures in %v elapsed (%v/s)", threadCount*signaturesPerThread, elapsed, float64(threadCount*signaturesPerThread)/elapsed) } func signingRoutine(t *testing.T, key crypto.Signer, done chan int) { for i := 0; i < signaturesPerThread; i++ { testRsaSigningPKCS1v15(t, key, crypto.SHA1) // CloudHSM falls over if you create sessions too quickly time.Sleep(50 * time.Millisecond) } done <- 1 }