Compare commits
46 Commits
Author | SHA1 | Date |
---|---|---|
ezekielnewren | dac1fd2bba | |
kayos | c367cca749 | |
Hendrik Hofstadt | ce1163658f | |
Leopold Schabel | e69056d810 | |
Andrew Pau | 3ca48f2675 | |
Hendrik Hofstadt | 61ec9248b3 | |
Rohan Meringenti | f6e7a787af | |
Rohan Meringenti | 7ed80af9d6 | |
Leopold Schabel | 28f4f87122 | |
kayos@tcp.direct | 7e8d2bcf3d | |
kayos@tcp.direct | 78cb2d8595 | |
Rohan Meringenti | 3f0395de8f | |
Leopold Schabel | 43f6e607f2 | |
Gary Belvin | 88cd174823 | |
Gary Belvin | d3f55e54bd | |
Hendrik Hofstadt | 278b0ae2f2 | |
Andrew Pau | def8ee8789 | |
Andrew Pau | 86670f0fba | |
Andrew Pau | 76fb3dd4fd | |
Hendrik Hofstadt | f66dd01301 | |
terry | a1855252da | |
Hendrik Hofstadt | 9938428649 | |
Gary Belvin | 5d97b2b65f | |
Hendrik Hofstadt | 1734a5c46e | |
Hendrik Hofstadt | 7f6fc39faf | |
Gary Belvin | fe9ad5f86f | |
Gary Belvin | 8ea63b0b20 | |
Gary Belvin | 7d8389a6ff | |
Gary Belvin | bcd16c93b1 | |
Hendrik Hofstadt | fb117c8072 | |
Gary Belvin | 48c7fef69d | |
Gary Belvin | 11bd9d2fb6 | |
Gary Belvin | bfa73afdb9 | |
Hendrik Hofstadt | 3727364517 | |
Jake Craige | 44a7c0d1fa | |
Jake Craige | 530348283f | |
Hendrik Hofstadt | d0ca2ed0df | |
Jake Craige | 7df30b3ded | |
Hendrik Hofstadt | 892fb9b370 | |
Hendrik Hofstadt | 0299fd5d70 | |
Hendrik Hofstadt | 3601184c8b | |
Simon Boissonneault-Robert | b28a514e8f | |
Simon Boissonneault-Robert | 4f80722970 | |
Matthew Campbell | fa21710f0b | |
linuxdev53 | 0717d04d07 | |
linuxdev53 | 8a73cfa938 |
|
@ -1,2 +1,4 @@
|
|||
.idea
|
||||
*.iml
|
||||
*.iml
|
||||
*.swp
|
||||
*.swo
|
||||
|
|
13
README.md
13
README.md
|
@ -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 {
|
||||
|
|
|
@ -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:]
|
||||
}
|
|
@ -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 object’s
|
||||
// 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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
4
go.mod
|
@ -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
7
go.sum
|
@ -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=
|
||||
|
|
16
manager.go
16
manager.go
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue