Compare commits

...

46 Commits

Author SHA1 Message Date
ezekielnewren dac1fd2bba
add more algorithm constants (#24)
* add more algorithm constants
2023-01-11 13:49:06 +01:00
kayos c367cca749
Connector: check len(values) to avoid nil pointer dereference (#21) 2023-01-11 13:46:18 +01:00
Hendrik Hofstadt ce1163658f
Merge pull request #20 from yunginnanet/master
Connector: close response bodies
2023-01-11 13:40:59 +01:00
Leopold Schabel e69056d810
Merge pull request #23 from AndrewPau/ap/additional-curve-support
Support Additional Algorithms
2022-10-18 21:46:56 +02:00
Andrew Pau 3ca48f2675
Support P384 and P521 curves 2022-10-10 11:11:52 -04:00
Hendrik Hofstadt 61ec9248b3
Merge pull request #19 from rmeringe/add_get_device_info_command
Adding get device info command
2022-07-18 10:19:34 +02:00
Rohan Meringenti f6e7a787af
add supported algorithms and fix indexing 2022-07-12 12:06:23 -04:00
Rohan Meringenti 7ed80af9d6
add support for complete response 2022-07-12 11:49:49 -04:00
Leopold Schabel 28f4f87122
Merge pull request #22 from yunginnanet/markdown
Docs: add syntax highlighting
2022-06-30 20:36:37 +02:00
kayos@tcp.direct 7e8d2bcf3d
Connector: Close response bodies (squashed)
Connector: adjust response body closure

Connector: be consistent about returns

Connector: continue consistency efforts, fix positioning of defer call

Connector(lint): get rid of useless error check

Connector: again, be consistent

Connector: Prevent suppressing non-nil errors

---

Signed-off-by: kayos@tcp.direct <kayos@tcp.direct>
2022-06-30 07:48:33 -07:00
kayos@tcp.direct 78cb2d8595
Docs: add syntax highlighting 2022-06-30 07:04:49 -07:00
Rohan Meringenti 3f0395de8f
adding get device info command 2022-06-15 17:54:59 -06:00
Leopold Schabel 43f6e607f2
Merge pull request #16 from gdbelvin/import
ImportWrapped
2021-11-02 16:31:04 +01:00
Gary Belvin 88cd174823 ImportWrapped 2021-06-17 13:59:00 -04:00
Gary Belvin d3f55e54bd ExportWrapped 2021-06-17 07:28:14 -04:00
Hendrik Hofstadt 278b0ae2f2
Merge pull request #14 from AndrewPau/ap/add-commands
Add commands, better conform with yubiHSM docs
2021-04-09 20:40:54 +02:00
Andrew Pau def8ee8789
Revert AuthKey changes 2021-04-09 10:48:34 -07:00
Andrew Pau 86670f0fba
Merge conflicts 2021-04-08 16:42:16 -07:00
Andrew Pau 76fb3dd4fd
Add commands 2021-04-07 14:22:09 -07:00
Hendrik Hofstadt f66dd01301
Merge pull request #13 from terrbear/master
add sign pkcs1 command
2021-03-31 08:41:21 +02:00
terry a1855252da add sign pkcs1 command 2021-03-31 17:04:49 +13:00
Hendrik Hofstadt 9938428649
Merge pull request #12 from gdbelvin/putauthkey
PutAuthkey
2021-03-22 10:34:19 +01:00
Gary Belvin 5d97b2b65f PutAuthkey 2021-03-21 20:52:47 +00:00
Hendrik Hofstadt 1734a5c46e
Merge pull request #11 from gdbelvin/putwrapkey
PutWrapKey
2021-03-19 13:18:27 +01:00
Hendrik Hofstadt 7f6fc39faf
Merge pull request #10 from gdbelvin/capability
CapabilityPrimitive
2021-03-19 13:12:23 +01:00
Gary Belvin fe9ad5f86f Wrap Algorithms 2021-03-19 12:06:21 +00:00
Gary Belvin 8ea63b0b20 GitIgnore 2021-03-19 12:06:21 +00:00
Gary Belvin 7d8389a6ff PutWrapkeyCommand 2021-03-19 12:06:21 +00:00
Gary Belvin bcd16c93b1 CapabilityPrimitive 2021-03-19 12:02:35 +00:00
Hendrik Hofstadt fb117c8072
Merge pull request #9 from gdbelvin/getpseudorandom
Suppport for GetPseudoRandom
2021-03-17 14:23:36 +01:00
Gary Belvin 48c7fef69d Update readme 2021-03-05 12:05:04 +00:00
Gary Belvin 11bd9d2fb6 Add GetPseudoRandom Command 2021-03-05 12:01:30 +00:00
Gary Belvin bfa73afdb9 Add vim swap files to .gitignore 2021-03-01 11:46:54 +00:00
Hendrik Hofstadt 3727364517
Merge pull request #8 from jakecraige/jake/upstream-auth
Add ChangeAuthenticationKey command
2020-02-28 19:35:26 +01:00
Jake Craige 44a7c0d1fa
Sort alg 2020-02-28 10:27:06 -08:00
Jake Craige 530348283f
Add ChangeAuthenticationKey command
Introduce the command which is used to change the password to
authenticate to the HSM.

A small refactoring of extracting authkey out was necessary to prevent
an import cycle from securechannel importing commands which (without the
refactor) imported secure channel again.

Documentation for this command: https://developers.yubico.com/YubiHSM2/Commands/Change_Authentication_Key.html
2020-02-28 09:40:45 -08:00
Hendrik Hofstadt d0ca2ed0df
Merge pull request #7 from jakecraige/jake/derive-ecdh
Implement DeriveEcdh command
2019-08-28 12:18:41 +02:00
Jake Craige 7df30b3ded
Implement DeriveEcdh command
Add the implementation fo the DeriveEcdh command alongside a command
name that matches what Yubi calls it.

Left the previously named one alone so this doesn't break backwards
compatibility if anyone was using that.

Docs: https://developers.yubico.com/YubiHSM2/Commands/Derive_Ecdh.html
2019-08-27 14:17:50 -07:00
Hendrik Hofstadt 892fb9b370 Don't reset ping interval as failed commands do not recreate broken sessions 2019-08-14 07:41:44 +02:00
Hendrik Hofstadt 0299fd5d70 Swap dead sessions 2019-06-20 20:34:45 +02:00
Hendrik Hofstadt 3601184c8b
Merge pull request #5 from ecadlabs/master
Support for SignEcdsa and DeleteObject
2019-06-19 21:11:00 +02:00
Simon Boissonneault-Robert b28a514e8f Add ListObject and GetObjectInfo support
Misc: removed pinged print
2019-06-18 16:18:13 -04:00
Simon Boissonneault-Robert 4f80722970 Changing imports to match original repo 2019-06-17 16:41:08 -04:00
Matthew Campbell fa21710f0b
Merge pull request #1 from loomnetwork/secp256k1
Added secp256k1 supporting
2018-12-05 21:26:40 +07:00
linuxdev53 0717d04d07 Added secp256k1 keygen/sign, delete 2018-12-05 15:42:38 +02:00
linuxdev53 8a73cfa938 Added secp256k1 support 2018-11-30 17:52:16 +02:00
11 changed files with 908 additions and 151 deletions

4
.gitignore vendored
View File

@ -1,2 +1,4 @@
.idea
*.iml
*.iml
*.swp
*.swo

View File

@ -6,21 +6,30 @@ messages is depleted.
Currently the following commands are implemented:
* DeviceInfo
* Reset
* GenerateAsymmetricKey
* SignDataEddsa
* SignDataPkcs1
* PutAsymmetricKey
* GetPubKey
* DeriveEcdh
* Echo
* ChangeAuthenticationKey
* PutAuthenticationKey
* GetOpaque
* PutOpaque
* SignAttestationCertificate
* Authentication & Session related commands
* GetPseudoRandom
Implementing new commands is really easy. Please consult `commands/constructors.go` and `commands/response.go` for reference.
Please submit a PR if you have implemented new commands or extended existing constructors.
## Example of usage
```
```go
c := connector.NewHTTPConnector("localhost:1234")
sm, err := yubihsm.NewSessionManager(c, 1, "password", 2)
if err != nil {

View File

@ -1,7 +1,8 @@
package securechannel
package authkey
import (
"crypto/sha256"
"golang.org/x/crypto/pbkdf2"
)
@ -16,17 +17,17 @@ const (
yubicoSeed = "Yubico"
)
// deriveAuthKeyFromPwd derives an AuthKey using pkdf2 as specified in the HSM documentation
func deriveAuthKeyFromPwd(password string) AuthKey {
// NewFromPassword derives an AuthKey using pkdf2 as specified in the HSM documentation
func NewFromPassword(password string) AuthKey {
return pbkdf2.Key([]byte(password), []byte(yubicoSeed), authKeyIterations, authKeyLength, sha256.New)
}
// GetEncKey returns the EncryptionKey part of the AuthKey
func (k AuthKey) GetEncKey() []byte {
return k[:KeyLength]
return k[:authKeyLength/2]
}
// GetEncKey returns the MACKey part of the AuthKey
// GetMacKey returns the MACKey part of the AuthKey
func (k AuthKey) GetMacKey() []byte {
return k[KeyLength:]
return k[authKeyLength/2:]
}

View File

@ -4,8 +4,20 @@ import (
"bytes"
"encoding/binary"
"errors"
"io"
"github.com/certusone/yubihsm-go/authkey"
)
func CreateDeviceInfoCommand() (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeDeviceInfo,
}
return command, nil
}
func CreateCreateSessionCommand(keySetID uint16, hostChallenge []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeCreateSession,
@ -77,6 +89,34 @@ func CreateSignDataEddsaCommand(keyID uint16, data []byte) (*CommandMessage, err
return command, nil
}
func CreateSignDataEcdsaCommand(keyID uint16, data []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeSignDataEcdsa,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, keyID)
payload.Write(data)
command.Data = payload.Bytes()
return command, nil
}
func CreateSignDataPkcs1Command(keyID uint16, data []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeSignDataPkcs1,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, keyID)
payload.Write(data)
command.Data = payload.Bytes()
return command, nil
}
func CreatePutAsymmetricKeyCommand(keyID uint16, label []byte, domains uint16, capabilities uint64, algorithm Algorithm, keyPart1 []byte, keyPart2 []byte) (*CommandMessage, error) {
if len(label) > LabelLength {
return nil, errors.New("label is too long")
@ -104,6 +144,71 @@ func CreatePutAsymmetricKeyCommand(keyID uint16, label []byte, domains uint16, c
return command, nil
}
type ListCommandOption func(w io.Writer)
func NewObjectTypeOption(objectType uint8) ListCommandOption {
return func(w io.Writer) {
binary.Write(w, binary.BigEndian, ListObjectParamType)
binary.Write(w, binary.BigEndian, objectType)
}
}
func NewIDOption(id uint16) ListCommandOption {
return func(w io.Writer) {
binary.Write(w, binary.BigEndian, ListObjectParamID)
binary.Write(w, binary.BigEndian, id)
}
}
func NewDomainOption(domain uint16) ListCommandOption {
return func(w io.Writer) {
binary.Write(w, binary.BigEndian, ListObjectParamDomains)
binary.Write(w, binary.BigEndian, domain)
}
}
func NewLabelOption(label []byte) (ListCommandOption, error) {
if len(label) > LabelLength {
return nil, errors.New("label is too long")
}
if len(label) < LabelLength {
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
}
return func(w io.Writer) {
binary.Write(w, binary.BigEndian, ListObjectParamLabel)
binary.Write(w, binary.BigEndian, label)
}, nil
}
func CreateListObjectsCommand(options ...ListCommandOption) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeListObjects,
}
payload := bytes.NewBuffer([]byte{})
for _, opt := range options {
opt(payload)
}
command.Data = payload.Bytes()
return command, nil
}
func CreateGetObjectInfoCommand(keyID uint16, objectType uint8) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeGetObjectInfo,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, keyID)
binary.Write(payload, binary.BigEndian, objectType)
command.Data = payload.Bytes()
return command, nil
}
func CreateCloseSessionCommand() (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeCloseSession,
@ -124,6 +229,19 @@ func CreateGetPubKeyCommand(keyID uint16) (*CommandMessage, error) {
return command, nil
}
func CreateDeleteObjectCommand(objID uint16, objType uint8) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeDeleteObject,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, objID)
binary.Write(payload, binary.BigEndian, objType)
command.Data = payload.Bytes()
return command, nil
}
func CreateEchoCommand(data []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeEcho,
@ -132,3 +250,212 @@ func CreateEchoCommand(data []byte) (*CommandMessage, error) {
return command, nil
}
func CreateDeriveEcdhCommand(objID uint16, pubkey []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeDeriveEcdh,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, objID)
payload.Write(pubkey)
command.Data = payload.Bytes()
return command, nil
}
func CreateChangeAuthenticationKeyCommand(objID uint16, newPassword string) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeChangeAuthenticationKey,
}
authKey := authkey.NewFromPassword(newPassword)
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, objID)
binary.Write(payload, binary.BigEndian, AlgorithmYubicoAESAuthentication)
payload.Write(authKey.GetEncKey())
payload.Write(authKey.GetMacKey())
command.Data = payload.Bytes()
return command, nil
}
func CreatePutOpaqueCommand(objID uint16, label []byte, domains uint16, capabilities uint64, algorithm Algorithm, data []byte) (*CommandMessage, error) {
if len(label) > LabelLength {
return nil, errors.New("label is too long")
}
if len(label) < LabelLength {
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
}
command := &CommandMessage{
CommandType: CommandTypePutOpaque,
}
payload := bytes.NewBuffer(nil)
binary.Write(payload, binary.BigEndian, objID)
payload.Write(label)
binary.Write(payload, binary.BigEndian, domains)
binary.Write(payload, binary.BigEndian, capabilities)
binary.Write(payload, binary.BigEndian, algorithm)
payload.Write(data)
command.Data = payload.Bytes()
return command, nil
}
func CreateGetOpaqueCommand(objID uint16) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeGetOpaque,
}
payload := bytes.NewBuffer(nil)
binary.Write(payload, binary.BigEndian, objID)
command.Data = payload.Bytes()
return command, nil
}
func CreateGetPseudoRandomCommand(numBytes uint16) *CommandMessage {
command := &CommandMessage{
CommandType: CommandTypeGetPseudoRandom,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, numBytes)
command.Data = payload.Bytes()
return command
}
func CreatePutWrapkeyCommand(objID uint16, label []byte, domains uint16, capabilities uint64, algorithm Algorithm, delegated uint64, wrapkey []byte) (*CommandMessage, error) {
if len(label) > LabelLength {
return nil, errors.New("label is too long")
}
if len(label) < LabelLength {
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
}
switch algorithm {
case AlgorithmAES128CCMWrap:
if keyLen := len(wrapkey); keyLen != 16 {
return nil, errors.New("wrapkey is wrong length")
}
case AlgorithmAES192CCMWrap:
if keyLen := len(wrapkey); keyLen != 24 {
return nil, errors.New("wrapkey is wrong length")
}
case AlgorithmAES256CCMWrap:
if keyLen := len(wrapkey); keyLen != 32 {
return nil, errors.New("wrapkey is wrong length")
}
default:
return nil, errors.New("invalid algorithm")
}
command := &CommandMessage{
CommandType: CommandTypePutWrapKey,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, objID)
payload.Write(label)
binary.Write(payload, binary.BigEndian, domains)
binary.Write(payload, binary.BigEndian, capabilities)
binary.Write(payload, binary.BigEndian, algorithm)
binary.Write(payload, binary.BigEndian, delegated)
payload.Write(wrapkey)
command.Data = payload.Bytes()
return command, nil
}
func CreatePutAuthkeyCommand(objID uint16, label []byte, domains uint16, capabilities, delegated uint64, encKey, macKey []byte) (*CommandMessage, error) {
if len(label) > LabelLength {
return nil, errors.New("label is too long")
}
if len(label) < LabelLength {
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
}
algorithm := AlgorithmYubicoAESAuthentication
// TODO: support P256 Authentication when it is released
// https://github.com/Yubico/yubihsm-shell/blob/1c8e254603e72f3f39cf1c3910996dbfcdba2b12/lib/yubihsm.c#L3110
if len(encKey) != 16 {
return nil, errors.New("invalid encryption key length")
}
if len(macKey) != 16 {
return nil, errors.New("invalid mac key length")
}
command := &CommandMessage{
CommandType: CommandTypePutAuthKey,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, objID)
payload.Write(label)
binary.Write(payload, binary.BigEndian, domains)
binary.Write(payload, binary.BigEndian, capabilities)
binary.Write(payload, binary.BigEndian, algorithm)
binary.Write(payload, binary.BigEndian, delegated)
payload.Write(encKey)
payload.Write(macKey)
command.Data = payload.Bytes()
return command, nil
}
func CreatePutDerivedAuthenticationKeyCommand(objID uint16, label []byte, domains uint16, capabilities uint64, delegated uint64, password string) (*CommandMessage, error) {
authKey := authkey.NewFromPassword(password)
return CreatePutAuthkeyCommand(objID, label, domains, capabilities, delegated, authKey.GetEncKey(), authKey.GetMacKey())
}
func CreateSignAttestationCertCommand(keyObjID, attestationObjID uint16) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeAttestAsymmetric,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, keyObjID)
binary.Write(payload, binary.BigEndian, attestationObjID)
command.Data = payload.Bytes()
return command, nil
}
func CreateExportWrappedCommand(wrapObjID uint16, objType uint8, objID uint16) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeExportWrapped,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, wrapObjID)
binary.Write(payload, binary.BigEndian, objType)
binary.Write(payload, binary.BigEndian, objID)
command.Data = payload.Bytes()
return command, nil
}
// CreateImportWrappedCommand will import a wrapped/encrypted Object that was
// previously exported by an YubiHSM2 device. The imported object will retain
// its metadata (Object ID, Domains, Capabilities …etc), however, the objects
// origin will be marked as imported instead of generated.
func CreateImportWrappedCommand(wrapObjID uint16, nonce, data []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeImportWrapped,
}
if len(nonce) != 13 {
return nil, errors.New("invalid nonce length")
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, wrapObjID)
payload.Write(nonce)
payload.Write(data)
command.Data = payload.Bytes()
return command, nil
}

View File

@ -15,6 +15,16 @@ type (
Code ErrorCode
}
DeviceInfoResponse struct {
MajorVersion uint8
MinorVersion uint8
BuildVersion uint8
SerialNumber uint32
LogTotal uint8
LogUsed uint8
SupportedAlgorithms []Algorithm
}
CreateSessionResponse struct {
SessionID uint8
CardChallenge []byte
@ -35,10 +45,41 @@ type (
KeyID uint16
}
ObjectInfoResponse struct {
Capabilities uint64
ObjectID uint16
Length uint16
Domains uint16
Type uint8
Algorithm Algorithm
Sequence uint8
Origin uint8
Label [40]byte
DelegatedCapabilites uint64
}
Object struct {
ObjectID uint16
ObjectType uint8
Sequence uint8
}
ListObjectsResponse struct {
Objects []Object
}
SignDataEddsaResponse struct {
Signature []byte
}
SignDataPkcs1Response struct {
Signature []byte
}
SignDataEcdsaResponse struct {
Signature []byte
}
GetPubKeyResponse struct {
Algorithm Algorithm
// KeyData can contain different formats depending on the algorithm according to the YubiHSM2 documentation.
@ -48,6 +89,44 @@ type (
EchoResponse struct {
Data []byte
}
DeriveEcdhResponse struct {
XCoordinate []byte
}
ChangeAuthenticationKeyResponse struct {
ObjectID uint16
}
PutWrapkeyResponse struct {
ObjectID uint16
}
PutAuthkeyResponse struct {
ObjectID uint16
}
PutOpaqueResponse struct {
ObjectID uint16
}
GetOpaqueResponse struct {
Data []byte
}
SignAttestationCertResponse struct {
Cert []byte
}
ExportWrappedResponse struct {
Nonce []byte
Data []byte
}
ImportWrappedResponse struct {
ObjectType uint8
ObjectID uint16
}
)
// ParseResponse parses the binary response from the card to the relevant Response type.
@ -72,6 +151,8 @@ func ParseResponse(data []byte) (Response, error) {
}
switch transactionType {
case CommandTypeDeviceInfo:
return parseDeviceInfoResponse(payload)
case CommandTypeCreateSession:
return parseCreateSessionResponse(payload)
case CommandTypeAuthenticateSession:
@ -82,14 +163,44 @@ func ParseResponse(data []byte) (Response, error) {
return parseCreateAsymmetricKeyResponse(payload)
case CommandTypeSignDataEddsa:
return parseSignDataEddsaResponse(payload)
case CommandTypeSignDataEcdsa:
return parseSignDataEcdsaResponse(payload)
case CommandTypeSignDataPkcs1:
return parseSignDataPkcs1Response(payload)
case CommandTypePutAsymmetric:
return parsePutAsymmetricKeyResponse(payload)
case CommandTypeListObjects:
return parseListObjectsResponse(payload)
case CommandTypeGetObjectInfo:
return parseGetObjectInfoResponse(payload)
case CommandTypeCloseSession:
return nil, nil
case CommandTypeGetPubKey:
return parseGetPubKeyResponse(payload)
case CommandTypeDeleteObject:
return nil, nil
case CommandTypeEcho:
return parseEchoResponse(payload)
case CommandTypeDeriveEcdh:
return parseDeriveEcdhResponse(payload)
case CommandTypeChangeAuthenticationKey:
return parseChangeAuthenticationKeyResponse(payload)
case CommandTypeGetPseudoRandom:
return parseGetPseudoRandomResponse(payload), nil
case CommandTypePutWrapKey:
return parsePutWrapkeyResponse(payload)
case CommandTypePutAuthKey:
return parsePutAuthkeyResponse(payload)
case CommandTypePutOpaque:
return parsePutOpaqueResponse(payload)
case CommandTypeGetOpaque:
return parseGetOpaqueResponse(payload)
case CommandTypeAttestAsymmetric:
return parseAttestationCertResponse(payload)
case CommandTypeExportWrapped:
return parseExportWrappedResponse(payload)
case CommandTypeImportWrapped:
return parseImportWrappedResponse(payload)
case ErrorResponseCode:
return nil, parseErrorResponse(payload)
default:
@ -115,6 +226,28 @@ func parseSessionMessage(payload []byte) (Response, error) {
}, nil
}
func parseDeviceInfoResponse(payload []byte) (Response, error) {
var serialNumber uint32
err := binary.Read(bytes.NewReader(payload[3:7]), binary.BigEndian, &serialNumber)
if err != nil {
return nil, err
}
var supportedAlgorithms []Algorithm
for _, alg := range payload[9:] {
supportedAlgorithms = append(supportedAlgorithms, Algorithm(alg))
}
return &DeviceInfoResponse{
MajorVersion: payload[0],
MinorVersion: payload[1],
BuildVersion: payload[2],
SerialNumber: serialNumber,
LogTotal: payload[7],
LogUsed: payload[8],
SupportedAlgorithms: supportedAlgorithms,
}, nil
}
func parseCreateSessionResponse(payload []byte) (Response, error) {
if len(payload) != 17 {
return nil, errors.New("invalid response payload length")
@ -149,6 +282,22 @@ func parseSignDataEddsaResponse(payload []byte) (Response, error) {
}, nil
}
func parseSignDataPkcs1Response(payload []byte) (Response, error) {
if len(payload) < 1 {
return nil, errors.New("invalid response payload length")
}
return &SignDataPkcs1Response{
Signature: payload,
}, nil
}
func parseSignDataEcdsaResponse(payload []byte) (Response, error) {
return &SignDataEcdsaResponse{
Signature: payload,
}, nil
}
func parsePutAsymmetricKeyResponse(payload []byte) (Response, error) {
if len(payload) != 2 {
return nil, errors.New("invalid response payload length")
@ -165,6 +314,34 @@ func parsePutAsymmetricKeyResponse(payload []byte) (Response, error) {
}, nil
}
func parseListObjectsResponse(payload []byte) (Response, error) {
if len(payload)%4 != 0 {
return nil, errors.New("invalid response payload length")
}
response := ListObjectsResponse{
Objects: make([]Object, len(payload)/4),
}
err := binary.Read(bytes.NewReader(payload), binary.BigEndian, &response.Objects)
if err != nil {
return nil, err
}
return &response, nil
}
func parseGetObjectInfoResponse(payload []byte) (Response, error) {
response := ObjectInfoResponse{}
err := binary.Read(bytes.NewReader(payload), binary.BigEndian, &response)
if err != nil {
return nil, err
}
return &response, nil
}
func parseGetPubKeyResponse(payload []byte) (Response, error) {
if len(payload) < 1 {
return nil, errors.New("invalid response payload length")
@ -181,6 +358,121 @@ func parseEchoResponse(payload []byte) (Response, error) {
}, nil
}
func parseDeriveEcdhResponse(payload []byte) (Response, error) {
return &DeriveEcdhResponse{
XCoordinate: payload,
}, nil
}
func parseChangeAuthenticationKeyResponse(payload []byte) (Response, error) {
if len(payload) != 2 {
return nil, errors.New("invalid response payload length")
}
var objectID uint16
err := binary.Read(bytes.NewReader(payload), binary.BigEndian, &objectID)
if err != nil {
return nil, err
}
return &ChangeAuthenticationKeyResponse{ObjectID: objectID}, nil
}
func parseGetPseudoRandomResponse(payload []byte) Response {
return payload
}
func parsePutWrapkeyResponse(payload []byte) (Response, error) {
if len(payload) != 2 {
return nil, errors.New("invalid response payload length")
}
var objectID uint16
err := binary.Read(bytes.NewReader(payload), binary.BigEndian, &objectID)
if err != nil {
return nil, err
}
return &PutWrapkeyResponse{ObjectID: objectID}, nil
}
func parsePutAuthkeyResponse(payload []byte) (Response, error) {
if len(payload) != 2 {
return nil, errors.New("invalid response payload length")
}
var objectID uint16
err := binary.Read(bytes.NewReader(payload), binary.BigEndian, &objectID)
if err != nil {
return nil, err
}
return &PutAuthkeyResponse{ObjectID: objectID}, nil
}
func parsePutOpaqueResponse(payload []byte) (Response, error) {
if len(payload) != 2 {
return nil, errors.New("invalid response payload length")
}
var objectID uint16
err := binary.Read(bytes.NewReader(payload), binary.BigEndian, &objectID)
if err != nil {
return nil, err
}
return &PutOpaqueResponse{
ObjectID: objectID,
}, nil
}
func parseGetOpaqueResponse(payload []byte) (Response, error) {
if len(payload) < 1 {
return nil, errors.New("invalid response payload length")
}
return &GetOpaqueResponse{
Data: payload,
}, nil
}
func parseAttestationCertResponse(payload []byte) (Response, error) {
if len(payload) < 1 {
return nil, errors.New("invalid response payload length")
}
return &SignAttestationCertResponse{
Cert: payload,
}, nil
}
func parseExportWrappedResponse(payload []byte) (Response, error) {
if len(payload) < 13 {
return nil, errors.New("invalid response payload length")
}
return &ExportWrappedResponse{
Nonce: payload[:13],
Data: payload[13:],
}, nil
}
func parseImportWrappedResponse(payload []byte) (Response, error) {
if len(payload) != 3 {
return nil, errors.New("invalid response payload length")
}
var objID uint16
err := binary.Read(bytes.NewReader(payload[1:3]), binary.BigEndian, &objID)
if err != nil {
return nil, err
}
return &ImportWrappedResponse{
ObjectType: uint8(payload[0]),
ObjectID: objID,
}, nil
}
// Error formats a card error message into a human readable format
func (e *Error) Error() string {
message := ""
@ -209,12 +501,20 @@ func (e *Error) Error() string {
message = "Log full"
case ErrorCodeObjectNotFound:
message = "Object not found"
case ErrorCodeIDIllegal:
message = "ID illegal"
case ErrorCodeInvalidID:
message = "Invalid ID"
case ErrorCodeCommandUnexecuted:
message = "Command unexecuted"
case ErrorCodeSSHCAConstraintViolation:
message = "SSH CA constraint violation"
case ErrorCodeInvalidOTP:
message = "Invalid OTP"
case ErrorCodeDemoMode:
message = "Demo mode"
case ErrorCodeObjectExists:
message = "Object exists"
default:
message = "unknown"
message = "Unknown"
}
return fmt.Sprintf("card responded with error: %s", message)

View File

@ -13,120 +13,175 @@ const (
// LabelLength is the max length of a label
LabelLength = 40
CommandTypeEcho CommandType = 0x01
CommandTypeCreateSession CommandType = 0x03
CommandTypeAuthenticateSession CommandType = 0x04
CommandTypeSessionMessage CommandType = 0x05
CommandTypeDeviceInfo CommandType = 0x06
CommandTypeReset CommandType = 0x08
CommandTypeCloseSession CommandType = 0x40
CommandTypeStorageStatus CommandType = 0x41
CommandTypePutOpaque CommandType = 0x42
CommandTypeGetOpaque CommandType = 0x43
CommandTypePutAuthKey CommandType = 0x44
CommandTypePutAsymmetric CommandType = 0x45
CommandTypeGenerateAsymmetricKey CommandType = 0x46
CommandTypeSignDataPkcs1 CommandType = 0x47
CommandTypeListObjects CommandType = 0x48
CommandTypeDecryptPkcs1 CommandType = 0x49
CommandTypeExportWrapped CommandType = 0x4a
CommandTypeImportWrapped CommandType = 0x4b
CommandTypePutWrapKey CommandType = 0x4c
CommandTypeGetLogs CommandType = 0x4d
CommandTypeGetObjectInfo CommandType = 0x4e
CommandTypePutOption CommandType = 0x4f
CommandTypeGetOption CommandType = 0x50
CommandTypeGetPseudoRandom CommandType = 0x51
CommandTypePutHMACKey CommandType = 0x52
CommandTypeHMACData CommandType = 0x53
CommandTypeGetPubKey CommandType = 0x54
CommandTypeSignDataPss CommandType = 0x55
CommandTypeSignDataEcdsa CommandType = 0x56
CommandTypeDecryptEcdh CommandType = 0x57
CommandTypeDeleteObject CommandType = 0x58
CommandTypeDecryptOaep CommandType = 0x59
CommandTypeGenerateHMACKey CommandType = 0x5a
CommandTypeGenerateWrapKey CommandType = 0x5b
CommandTypeVerifyHMAC CommandType = 0x5c
CommandTypeOTPDecrypt CommandType = 0x60
CommandTypeOTPAeadCreate CommandType = 0x61
CommandTypeOTPAeadRandom CommandType = 0x62
CommandTypeOTPAeadRewrap CommandType = 0x63
CommandTypeAttestAsymmetric CommandType = 0x64
CommandTypePutOTPAeadKey CommandType = 0x65
CommandTypeGenerateOTPAeadKey CommandType = 0x66
CommandTypeSetLogIndex CommandType = 0x67
CommandTypeWrapData CommandType = 0x68
CommandTypeUnwrapData CommandType = 0x69
CommandTypeSignDataEddsa CommandType = 0x6a
CommandTypeSetBlink CommandType = 0x6b
CommandTypeEcho CommandType = 0x01
CommandTypeCreateSession CommandType = 0x03
CommandTypeAuthenticateSession CommandType = 0x04
CommandTypeSessionMessage CommandType = 0x05
CommandTypeDeviceInfo CommandType = 0x06
CommandTypeReset CommandType = 0x08
CommandTypeCloseSession CommandType = 0x40
CommandTypeStorageStatus CommandType = 0x41
CommandTypePutOpaque CommandType = 0x42
CommandTypeGetOpaque CommandType = 0x43
CommandTypePutAuthKey CommandType = 0x44
CommandTypePutAsymmetric CommandType = 0x45
CommandTypeGenerateAsymmetricKey CommandType = 0x46
CommandTypeSignDataPkcs1 CommandType = 0x47
CommandTypeListObjects CommandType = 0x48
CommandTypeDecryptPkcs1 CommandType = 0x49
CommandTypeExportWrapped CommandType = 0x4a
CommandTypeImportWrapped CommandType = 0x4b
CommandTypePutWrapKey CommandType = 0x4c
CommandTypeGetLogs CommandType = 0x4d
CommandTypeGetObjectInfo CommandType = 0x4e
CommandTypePutOption CommandType = 0x4f
CommandTypeGetOption CommandType = 0x50
CommandTypeGetPseudoRandom CommandType = 0x51
CommandTypePutHMACKey CommandType = 0x52
CommandTypeHMACData CommandType = 0x53
CommandTypeGetPubKey CommandType = 0x54
CommandTypeSignDataPss CommandType = 0x55
CommandTypeSignDataEcdsa CommandType = 0x56
CommandTypeDecryptEcdh CommandType = 0x57 // here for backwards compatibility
CommandTypeDeriveEcdh CommandType = 0x57
CommandTypeDeleteObject CommandType = 0x58
CommandTypeDecryptOaep CommandType = 0x59
CommandTypeGenerateHMACKey CommandType = 0x5a
CommandTypeGenerateWrapKey CommandType = 0x5b
CommandTypeVerifyHMAC CommandType = 0x5c
CommandTypeOTPDecrypt CommandType = 0x60
CommandTypeOTPAeadCreate CommandType = 0x61
CommandTypeOTPAeadRandom CommandType = 0x62
CommandTypeOTPAeadRewrap CommandType = 0x63
CommandTypeAttestAsymmetric CommandType = 0x64
CommandTypePutOTPAeadKey CommandType = 0x65
CommandTypeGenerateOTPAeadKey CommandType = 0x66
CommandTypeSetLogIndex CommandType = 0x67
CommandTypeWrapData CommandType = 0x68
CommandTypeUnwrapData CommandType = 0x69
CommandTypeSignDataEddsa CommandType = 0x6a
CommandTypeSetBlink CommandType = 0x6b
CommandTypeChangeAuthenticationKey CommandType = 0x6c
// Errors
ErrorCodeOK ErrorCode = 0x00
ErrorCodeInvalidCommand ErrorCode = 0x01
ErrorCodeInvalidData ErrorCode = 0x02
ErrorCodeInvalidSession ErrorCode = 0x03
ErrorCodeAuthFail ErrorCode = 0x04
ErrorCodeSessionFull ErrorCode = 0x05
ErrorCodeSessionFailed ErrorCode = 0x06
ErrorCodeStorageFailed ErrorCode = 0x07
ErrorCodeWrongLength ErrorCode = 0x08
ErrorCodeInvalidPermission ErrorCode = 0x09
ErrorCodeLogFull ErrorCode = 0x0a
ErrorCodeObjectNotFound ErrorCode = 0x0b
ErrorCodeIDIllegal ErrorCode = 0x0c
ErrorCodeCommandUnexecuted ErrorCode = 0xff
ErrorCodeOK ErrorCode = 0x00
ErrorCodeInvalidCommand ErrorCode = 0x01
ErrorCodeInvalidData ErrorCode = 0x02
ErrorCodeInvalidSession ErrorCode = 0x03
ErrorCodeAuthFail ErrorCode = 0x04
ErrorCodeSessionFull ErrorCode = 0x05
ErrorCodeSessionFailed ErrorCode = 0x06
ErrorCodeStorageFailed ErrorCode = 0x07
ErrorCodeWrongLength ErrorCode = 0x08
ErrorCodeInvalidPermission ErrorCode = 0x09
ErrorCodeLogFull ErrorCode = 0x0a
ErrorCodeObjectNotFound ErrorCode = 0x0b
ErrorCodeInvalidID ErrorCode = 0x0c
ErrorCodeSSHCAConstraintViolation ErrorCode = 0x0e
ErrorCodeInvalidOTP ErrorCode = 0x0f
ErrorCodeDemoMode ErrorCode = 0x10
ErrorCodeObjectExists ErrorCode = 0x11
ErrorCodeCommandUnexecuted ErrorCode = 0xff
// Algorithms
AlgorighmED25519 Algorithm = 46
AlgorithmRSAPKCS1SHA1 Algorithm = 1
AlgorithmRSAPKCS1SHA256 Algorithm = 2
AlgorithmRSAPKCS1SHA384 Algorithm = 3
AlgorithmRSAPKCS1SHA512 Algorithm = 4
AlgorithmRSAPSSSHA1 Algorithm = 5
AlgorithmRSAPSSSHA256 Algorithm = 6
AlgorithmRSAPSSSHA384 Algorithm = 7
AlgorithmRSAPSSSHA512 Algorithm = 8
AlgorithmRSA2048 Algorithm = 9
AlgorithmRSA3072 Algorithm = 10
AlgorithmRSA4096 Algorithm = 11
AlgorithmP256 Algorithm = 12
AlgorithmP384 Algorithm = 13
AlgorithmP521 Algorithm = 14
AlgorithmSecp256k1 Algorithm = 15
AlgorithmECBP256 Algorithm = 16
AlgorithmECBP384 Algorithm = 17
AlgorithmECBP512 Algorithm = 18
AlgorithmHMACSHA1 Algorithm = 19
AlgorithmHMACSHA256 Algorithm = 20
AlgorithmHMACSHA384 Algorithm = 21
AlgorithmHMACSHA512 Algorithm = 22
AlgorithmECECDSASHA1 Algorithm = 23
AlgorithmECECDH Algorithm = 24
AlgorithmRSAOAEPSHA1 Algorithm = 25
AlgorithmRSAOAEPSHA256 Algorithm = 26
AlgorithmRSAOAEPSHA384 Algorithm = 27
AlgorithmRSAOAEPSHA512 Algorithm = 28
AlgorithmAES128CCMWrap Algorithm = 29
AlgorithmOpaqueData Algorithm = 30
AlgorithmOpaqueX509Certificate Algorithm = 31
AlgorithmRSAMGF1SHA1 Algorithm = 32
AlgorithmRSAMGF1SHA256 Algorithm = 33
AlgorithmRSAMGF1SHA384 Algorithm = 34
AlgorithmRSAMGF1SHA512 Algorithm = 35
AlgorithmTEMPLATESSH Algorithm = 36
AlgorithmAES128YUBICOOTP Algorithm = 37
AlgorithmYubicoAESAuthentication Algorithm = 38
AlgorithmAES192YUBICOOTP Algorithm = 39
AlgorithmAES256YUBICOOTP Algorithm = 40
AlgorithmAES192CCMWrap Algorithm = 41
AlgorithmAES256CCMWrap Algorithm = 42
AlgorithmECECDSASHA256 Algorithm = 43
AlgorithmECECDSASHA384 Algorithm = 44
AlgorithmECECDSASHA512 Algorithm = 45
AlgorithmED25519 Algorithm = 46
AlgorithmECP224 Algorithm = 47
// Capabilities
CapabilityGetOpaque uint64 = 0x0000000000000001
CapabilityPutOpaque uint64 = 0x0000000000000002
CapabilityPutAuthKey uint64 = 0x0000000000000004
CapabilityPutAsymmetric uint64 = 0x0000000000000008
CapabilityAsymmetricGen uint64 = 0x0000000000000010
CapabilityAsymmetricSignPkcs uint64 = 0x0000000000000020
CapabilityAsymmetricSignPss uint64 = 0x0000000000000040
CapabilityAsymmetricSignEcdsa uint64 = 0x0000000000000080
CapabilityAsymmetricSignEddsa uint64 = 0x0000000000000100
CapabilityAsymmetricDecryptPkcs uint64 = 0x0000000000000200
CapabilityAsymmetricDecryptOaep uint64 = 0x0000000000000400
CapabilityAsymmetricDecryptEcdh uint64 = 0x0000000000000800
CapabilityExportWrapped uint64 = 0x0000000000001000
CapabilityImportWrapped uint64 = 0x0000000000002000
CapabilityPutWrapKey uint64 = 0x0000000000004000
CapabilityGenerateWrapKey uint64 = 0x0000000000008000
CapabilityExportUnderWrap uint64 = 0x0000000000010000
CapabilityPutOption uint64 = 0x0000000000020000
CapabilityGetOption uint64 = 0x0000000000040000
CapabilityGetRandomness uint64 = 0x0000000000080000
CapabilityPutHmacKey uint64 = 0x0000000000100000
CapabilityHmacKeyGenerate uint64 = 0x0000000000200000
CapabilityHmacData uint64 = 0x0000000000400000
CapabilityHmacVerify uint64 = 0x0000000000800000
CapabilityAudit uint64 = 0x0000000001000000
CapabilitySshCertify uint64 = 0x0000000002000000
CapabilityGetTemplate uint64 = 0x0000000004000000
CapabilityPutTemplate uint64 = 0x0000000008000000
CapabilityReset uint64 = 0x0000000010000000
CapabilityOtpDecrypt uint64 = 0x0000000020000000
CapabilityOtpAeadCreate uint64 = 0x0000000040000000
CapabilityOtpAeadRandom uint64 = 0x0000000080000000
CapabilityOtpAeadRewrapFrom uint64 = 0x0000000100000000
CapabilityOtpAeadRewrapTo uint64 = 0x0000000200000000
CapabilityAttest uint64 = 0x0000000400000000
CapabilityPutOtpAeadKey uint64 = 0x0000000800000000
CapabilityGenerateOtpAeadKey uint64 = 0x0000001000000000
CapabilityWrapData uint64 = 0x0000002000000000
CapabilityUnwrapData uint64 = 0x0000004000000000
CapabilityDeleteOpaque uint64 = 0x0000008000000000
CapabilityDeleteAuthKey uint64 = 0x0000010000000000
CapabilityDeleteAsymmetric uint64 = 0x0000020000000000
CapabilityDeleteWrapKey uint64 = 0x0000040000000000
CapabilityDeleteHmacKey uint64 = 0x0000080000000000
CapabilityDeleteTemplate uint64 = 0x0000100000000000
CapabilityDeleteOtpAeadKey uint64 = 0x0000200000000000
CapabilityNone uint64 = 0x0000000000000000
CapabilityGetOpaque uint64 = 0x0000000000000001
CapabilityPutOpaque uint64 = 0x0000000000000002
CapabilityPutAuthenticationKey uint64 = 0x0000000000000004
CapabilityPutAsymmetric uint64 = 0x0000000000000008
CapabilityAsymmetricGen uint64 = 0x0000000000000010
CapabilityAsymmetricSignPkcs uint64 = 0x0000000000000020
CapabilityAsymmetricSignPss uint64 = 0x0000000000000040
CapabilityAsymmetricSignEcdsa uint64 = 0x0000000000000080
CapabilityAsymmetricSignEddsa uint64 = 0x0000000000000100
CapabilityAsymmetricDecryptPkcs uint64 = 0x0000000000000200
CapabilityAsymmetricDecryptOaep uint64 = 0x0000000000000400
CapabilityAsymmetricDecryptEcdh uint64 = 0x0000000000000800 // here for backwards compatibility
CapabilityAsymmetricDeriveEcdh uint64 = 0x0000000000000800
CapabilityExportWrapped uint64 = 0x0000000000001000
CapabilityImportWrapped uint64 = 0x0000000000002000
CapabilityPutWrapKey uint64 = 0x0000000000004000
CapabilityGenerateWrapKey uint64 = 0x0000000000008000
CapabilityExportableUnderWrap uint64 = 0x0000000000010000
CapabilityPutOption uint64 = 0x0000000000020000
CapabilityGetOption uint64 = 0x0000000000040000
CapabilityGetRandomness uint64 = 0x0000000000080000
CapabilityPutHmacKey uint64 = 0x0000000000100000
CapabilityHmacKeyGenerate uint64 = 0x0000000000200000
CapabilityHmacData uint64 = 0x0000000000400000
CapabilityHmacVerify uint64 = 0x0000000000800000
CapabilityAudit uint64 = 0x0000000001000000
CapabilitySshCertify uint64 = 0x0000000002000000
CapabilityGetTemplate uint64 = 0x0000000004000000
CapabilityPutTemplate uint64 = 0x0000000008000000
CapabilityReset uint64 = 0x0000000010000000
CapabilityOtpDecrypt uint64 = 0x0000000020000000
CapabilityOtpAeadCreate uint64 = 0x0000000040000000
CapabilityOtpAeadRandom uint64 = 0x0000000080000000
CapabilityOtpAeadRewrapFrom uint64 = 0x0000000100000000
CapabilityOtpAeadRewrapTo uint64 = 0x0000000200000000
CapabilityAttest uint64 = 0x0000000400000000
CapabilityPutOtpAeadKey uint64 = 0x0000000800000000
CapabilityGenerateOtpAeadKey uint64 = 0x0000001000000000
CapabilityWrapData uint64 = 0x0000002000000000
CapabilityUnwrapData uint64 = 0x0000004000000000
CapabilityDeleteOpaque uint64 = 0x0000008000000000
CapabilityDeleteAuthKey uint64 = 0x0000010000000000
CapabilityDeleteAsymmetric uint64 = 0x0000020000000000
CapabilityDeleteWrapKey uint64 = 0x0000040000000000
CapabilityDeleteHmacKey uint64 = 0x0000080000000000
CapabilityDeleteTemplate uint64 = 0x0000100000000000
CapabilityDeleteOtpAeadKey uint64 = 0x0000200000000000
CapabilityChangeAuthenticationKey uint64 = 0x0000400000000000
// Domains
Domain1 uint16 = 0x0001
@ -145,4 +200,30 @@ const (
Domain14 uint16 = 0x2000
Domain15 uint16 = 0x4000
Domain16 uint16 = 0x8000
// object types
ObjectTypeOpaque uint8 = 0x01
ObjectTypeAuthenticationKey uint8 = 0x02
ObjectTypeAsymmetricKey uint8 = 0x03
ObjectTypeWrapKey uint8 = 0x04
ObjectTypeHmacKey uint8 = 0x05
ObjectTypeTemplate uint8 = 0x06
ObjectTypeOtpAeadKey uint8 = 0x07
// list objects params
ListObjectParamID uint8 = 0x01
ListObjectParamType uint8 = 0x02
ListObjectParamDomains uint8 = 0x03
ListObjectParamCapabilities uint8 = 0x04
ListObjectParamAlgorithm uint8 = 0x05
ListObjectParamLabel uint8 = 0x06
)
// CapabilityPrimitiveFromSlice OR's all the capabilitites together.
func CapabilityPrimitiveFromSlice(capabilitites []uint64) uint64 {
var primitive uint64
for _, c := range capabilitites {
primitive |= c
}
return primitive
}

View File

@ -2,13 +2,17 @@ package connector
import (
"bytes"
"errors"
"fmt"
"github.com/certusone/yubihsm-go/commands"
"io/ioutil"
"net/http"
"strings"
"github.com/certusone/yubihsm-go/commands"
)
var ErrInvalidResponseValueLength = errors.New("invalid response value length")
type (
// HTTPConnector implements the HTTP based connection with the YubiHSM2 connector
HTTPConnector struct {
@ -24,40 +28,50 @@ func NewHTTPConnector(url string) *HTTPConnector {
}
// Request encodes and executes a command on the HSM and returns the binary response
func (c *HTTPConnector) Request(command *commands.CommandMessage) ([]byte, error) {
requestData, err := command.Serialize()
func (c *HTTPConnector) Request(command *commands.CommandMessage) (data []byte, err error) {
var requestData []byte
requestData, err = command.Serialize()
if err != nil {
return nil, err
return
}
res, err := http.DefaultClient.Post("http://"+c.URL+"/connector/api", "application/octet-stream", bytes.NewReader(requestData))
var res *http.Response
res, err = http.DefaultClient.Post("http://"+c.URL+"/connector/api", "application/octet-stream", bytes.NewReader(requestData))
if err != nil {
return nil, err
return
}
defer func() {
closeErr := res.Body.Close()
if err == nil {
err = closeErr
}
}()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("server returned non OK status code %d", res.StatusCode)
err = fmt.Errorf("server returned non OK status code %d", res.StatusCode)
return
}
data, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
data, err = ioutil.ReadAll(res.Body)
return data, nil
return
}
// GetStatus requests the status of the HSM connector route /connector/status
func (c *HTTPConnector) GetStatus() (*StatusResponse, error) {
res, err := http.DefaultClient.Get("http://" + c.URL + "/connector/status")
func (c *HTTPConnector) GetStatus() (status *StatusResponse, err error) {
var res *http.Response
res, err = http.DefaultClient.Get("http://" + c.URL + "/connector/status")
if err != nil {
return nil, err
return
}
data, err := ioutil.ReadAll(res.Body)
var data []byte
data, err = ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
return
}
bodyString := string(data)
pairs := strings.Split(bodyString, "\n")
@ -66,7 +80,11 @@ func (c *HTTPConnector) GetStatus() (*StatusResponse, error) {
values = append(values, strings.Split(pair, "=")...)
}
status := &StatusResponse{}
if values == nil || len(values) < 12 {
return nil, ErrInvalidResponseValueLength
}
status = &StatusResponse{}
status.Status = Status(values[1])
status.Serial = values[3]
status.Version = values[5]
@ -74,5 +92,7 @@ func (c *HTTPConnector) GetStatus() (*StatusResponse, error) {
status.Address = values[9]
status.Port = values[11]
return status, nil
err = res.Body.Close()
return
}

4
go.mod
View File

@ -1,6 +1,8 @@
module github.com/certusone/yubihsm-go
go 1.14
require (
github.com/enceve/crypto v0.0.0-20160707101852-34d48bb93815
golang.org/x/crypto v0.0.0-20180830192347-182538f80094
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
)

7
go.sum
View File

@ -2,3 +2,10 @@ github.com/enceve/crypto v0.0.0-20160707101852-34d48bb93815 h1:D22EM5TeYZJp43hGD
github.com/enceve/crypto v0.0.0-20160707101852-34d48bb93815/go.mod h1:wYFFK4LYXbX7j+76mOq7aiC/EAw2S22CrzPHqgsisPw=
golang.org/x/crypto v0.0.0-20180830192347-182538f80094 h1:rVTAlhYa4+lCfNxmAIEOGQRoD23UqP72M3+rSWVGDTg=
golang.org/x/crypto v0.0.0-20180830192347-182538f80094/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -3,11 +3,13 @@ package yubihsm
import (
"bytes"
"errors"
"log"
"sync"
"time"
"github.com/certusone/yubihsm-go/commands"
"github.com/certusone/yubihsm-go/connector"
"github.com/certusone/yubihsm-go/securechannel"
"sync"
"time"
)
type (
@ -68,9 +70,14 @@ func (s *SessionManager) pingRoutine() {
if !bytes.Equal(parsedResp.Data, echoPayload) {
err = errors.New("echoed data is invalid")
}
} else {
// Session seems to be dead - reconnect and swap
err = s.swapSession()
if err != nil {
log.Printf("swapping dead session failed; err=%v", err)
}
}
println("pinged")
s.keepAlive.Reset(pingInterval)
}
}
@ -125,9 +132,6 @@ func (s *SessionManager) SendEncryptedCommand(c *commands.CommandMessage) (comma
return nil, errors.New("no session available")
}
// Reset keepalive since we are resetting the timeout by sending a command
s.keepAlive.Reset(pingInterval)
return s.session.SendEncryptedCommand(c)
}

View File

@ -7,10 +7,12 @@ import (
"crypto/rand"
"encoding/binary"
"errors"
"sync"
"github.com/enceve/crypto/cmac"
"github.com/certusone/yubihsm-go/authkey"
"github.com/certusone/yubihsm-go/commands"
"github.com/certusone/yubihsm-go/connector"
"github.com/enceve/crypto/cmac"
"sync"
)
type (
@ -38,7 +40,7 @@ type (
DeviceChallenge []byte
// AuthKey to authenticate against the HSM; must match authKeySlot
AuthKey AuthKey
AuthKey authkey.AuthKey
// MACChainValue is the last MAC to allow MAC chaining
MACChainValue []byte
@ -83,12 +85,14 @@ const (
MaxMessagesPerSession = 10000
)
var ErrAuthCryptogram = errors.New("authentication failed: device sent wrong cryptogram")
// NewSecureChannel initiates a new secure channel to communicate with an HSM using the given authKey
// Call Authenticate next to establish a session.
func NewSecureChannel(connector connector.Connector, authKeySlot uint16, password string) (*SecureChannel, error) {
channel := &SecureChannel{
ID: 0,
AuthKey: deriveAuthKeyFromPwd(password),
AuthKey: authkey.NewFromPassword(password),
MACChainValue: make([]byte, 16),
SecurityLevel: SecurityLevelUnauthenticated,
authKeySlot: authKeySlot,
@ -141,7 +145,7 @@ func (s *SecureChannel) Authenticate() error {
}
if !bytes.Equal(deviceCryptogram, createSessionResp.CardCryptogram) {
return errors.New("authentication failed: device sent wrong cryptogram")
return ErrAuthCryptogram
}
// Create host cryptogram