Compare commits

...

23 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
8 changed files with 518 additions and 89 deletions

View File

@ -6,14 +6,20 @@ 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
@ -23,7 +29,7 @@ Please submit a PR if you have implemented new commands or extended existing con
## Example of usage
```
```go
c := connector.NewHTTPConnector("localhost:1234")
sm, err := yubihsm.NewSessionManager(c, 1, "password", 2)
if err != nil {

View File

@ -9,6 +9,15 @@ import (
"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,
@ -94,6 +103,20 @@ func CreateSignDataEcdsaCommand(keyID uint16, data []byte) (*CommandMessage, err
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")
@ -137,6 +160,26 @@ func NewIDOption(id uint16) ListCommandOption {
}
}
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,
@ -237,6 +280,43 @@ func CreateChangeAuthenticationKeyCommand(objID uint16, newPassword string) (*Co
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,
@ -290,3 +370,92 @@ func CreatePutWrapkeyCommand(objID uint16, label []byte, domains uint16, capabil
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
@ -62,6 +72,10 @@ type (
Signature []byte
}
SignDataPkcs1Response struct {
Signature []byte
}
SignDataEcdsaResponse struct {
Signature []byte
}
@ -87,6 +101,32 @@ type (
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.
@ -111,6 +151,8 @@ func ParseResponse(data []byte) (Response, error) {
}
switch transactionType {
case CommandTypeDeviceInfo:
return parseDeviceInfoResponse(payload)
case CommandTypeCreateSession:
return parseCreateSessionResponse(payload)
case CommandTypeAuthenticateSession:
@ -123,6 +165,8 @@ func ParseResponse(data []byte) (Response, error) {
return parseSignDataEddsaResponse(payload)
case CommandTypeSignDataEcdsa:
return parseSignDataEcdsaResponse(payload)
case CommandTypeSignDataPkcs1:
return parseSignDataPkcs1Response(payload)
case CommandTypePutAsymmetric:
return parsePutAsymmetricKeyResponse(payload)
case CommandTypeListObjects:
@ -145,6 +189,18 @@ func ParseResponse(data []byte) (Response, error) {
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:
@ -170,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")
@ -204,6 +282,16 @@ 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,
@ -307,6 +395,84 @@ func parsePutWrapkeyResponse(payload []byte) (Response, error) {
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 := ""
@ -335,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

@ -64,78 +64,124 @@ const (
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
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
AlgorithmYubicoAESAuthentication Algorithm = 38
AlgorighmED25519 Algorithm = 46
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 // here for backwards compatibility
CapabilityAsymmetricDeriveEcdh 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
@ -165,8 +211,12 @@ const (
ObjectTypeOtpAeadKey uint8 = 0x07
// list objects params
ListObjectParamID uint8 = 0x01
ListObjectParamType uint8 = 0x02
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.

View File

@ -2,6 +2,7 @@ package connector
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"net/http"
@ -10,6 +11,8 @@ import (
"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 {
@ -25,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")
@ -67,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]
@ -75,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

@ -85,6 +85,8 @@ 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) {
@ -143,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