yubihsm-go/commands/response.go

522 lines
12 KiB
Go
Raw Permalink Normal View History

2018-09-02 05:46:37 -07:00
package commands
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
)
type (
Response interface {
}
Error struct {
Code ErrorCode
}
2022-06-15 16:54:59 -07:00
DeviceInfoResponse struct {
2022-07-12 08:49:49 -07:00
MajorVersion uint8
MinorVersion uint8
BuildVersion uint8
SerialNumber uint32
LogTotal uint8
LogUsed uint8
SupportedAlgorithms []Algorithm
2022-06-15 16:54:59 -07:00
}
2018-09-02 05:46:37 -07:00
CreateSessionResponse struct {
SessionID uint8
CardChallenge []byte
CardCryptogram []byte
}
SessionMessageResponse struct {
SessionID uint8
EncryptedData []byte
MAC []byte
}
CreateAsymmetricKeyResponse struct {
KeyID uint16
}
PutAsymmetricKeyResponse struct {
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
}
2018-09-02 05:46:37 -07:00
SignDataEddsaResponse struct {
Signature []byte
}
2018-09-08 11:43:08 -07:00
2021-04-07 15:05:46 -07:00
SignDataPkcs1Response struct {
Signature []byte
}
2018-11-30 07:49:18 -08:00
SignDataEcdsaResponse struct {
Signature []byte
}
2018-09-08 11:43:08 -07:00
GetPubKeyResponse struct {
Algorithm Algorithm
// KeyData can contain different formats depending on the algorithm according to the YubiHSM2 documentation.
KeyData []byte
}
2018-09-14 03:21:34 -07:00
EchoResponse struct {
Data []byte
}
DeriveEcdhResponse struct {
XCoordinate []byte
}
2021-04-07 15:05:46 -07:00
ChangeAuthenticationKeyResponse struct {
ObjectID uint16
}
2021-03-04 07:46:36 -08:00
2021-04-07 15:05:46 -07:00
PutWrapkeyResponse struct {
ObjectID uint16
}
2021-04-09 10:44:16 -07:00
PutAuthkeyResponse struct {
2021-03-04 07:46:36 -08:00
ObjectID uint16
}
2021-03-05 04:41:57 -08:00
2021-04-07 14:22:09 -07:00
PutOpaqueResponse struct {
2021-03-05 04:41:57 -08:00
ObjectID uint16
}
2021-04-07 14:22:09 -07:00
GetOpaqueResponse struct {
Data []byte
}
SignAttestationCertResponse struct {
Cert []byte
}
2021-06-17 04:28:14 -07:00
ExportWrappedResponse struct {
Nonce []byte
Data []byte
}
2021-06-17 10:59:00 -07:00
ImportWrappedResponse struct {
ObjectType uint8
ObjectID uint16
}
2018-09-02 05:46:37 -07:00
)
// ParseResponse parses the binary response from the card to the relevant Response type.
// If the response is an error zu parses the Error type response and returns an error of the
// type commands.Error with the parsed error message.
func ParseResponse(data []byte) (Response, error) {
if len(data) < 3 {
return nil, errors.New("invalid response")
}
transactionType := CommandType(data[0] + ResponseCommandOffset)
var payloadLength uint16
err := binary.Read(bytes.NewReader(data[1:3]), binary.BigEndian, &payloadLength)
if err != nil {
return nil, err
}
payload := data[3:]
if len(payload) != int(payloadLength) {
return nil, errors.New("response payload length does not equal the given length")
}
switch transactionType {
2022-06-15 16:54:59 -07:00
case CommandTypeDeviceInfo:
return parseDeviceInfoResponse(payload)
2018-09-02 05:46:37 -07:00
case CommandTypeCreateSession:
return parseCreateSessionResponse(payload)
case CommandTypeAuthenticateSession:
return nil, nil
case CommandTypeSessionMessage:
return parseSessionMessage(payload)
case CommandTypeGenerateAsymmetricKey:
return parseCreateAsymmetricKeyResponse(payload)
case CommandTypeSignDataEddsa:
return parseSignDataEddsaResponse(payload)
2018-12-05 05:42:38 -08:00
case CommandTypeSignDataEcdsa:
return parseSignDataEcdsaResponse(payload)
2021-04-07 15:05:46 -07:00
case CommandTypeSignDataPkcs1:
return parseSignDataPkcs1Response(payload)
2018-09-02 05:46:37 -07:00
case CommandTypePutAsymmetric:
return parsePutAsymmetricKeyResponse(payload)
case CommandTypeListObjects:
return parseListObjectsResponse(payload)
case CommandTypeGetObjectInfo:
return parseGetObjectInfoResponse(payload)
2018-09-02 10:26:39 -07:00
case CommandTypeCloseSession:
return nil, nil
2018-09-08 11:43:08 -07:00
case CommandTypeGetPubKey:
return parseGetPubKeyResponse(payload)
2018-12-05 05:42:38 -08:00
case CommandTypeDeleteObject:
return nil, nil
2018-09-14 03:21:34 -07:00
case CommandTypeEcho:
return parseEchoResponse(payload)
case CommandTypeDeriveEcdh:
return parseDeriveEcdhResponse(payload)
case CommandTypeChangeAuthenticationKey:
return parseChangeAuthenticationKeyResponse(payload)
2021-04-07 15:05:46 -07:00
case CommandTypeGetPseudoRandom:
return parseGetPseudoRandomResponse(payload), nil
case CommandTypePutWrapKey:
return parsePutWrapkeyResponse(payload)
2021-04-09 10:44:16 -07:00
case CommandTypePutAuthKey:
return parsePutAuthkeyResponse(payload)
2021-04-07 14:22:09 -07:00
case CommandTypePutOpaque:
return parsePutOpaqueResponse(payload)
case CommandTypeGetOpaque:
return parseGetOpaqueResponse(payload)
case CommandTypeAttestAsymmetric:
return parseAttestationCertResponse(payload)
2021-06-17 04:28:14 -07:00
case CommandTypeExportWrapped:
return parseExportWrappedResponse(payload)
2021-06-17 10:59:00 -07:00
case CommandTypeImportWrapped:
return parseImportWrappedResponse(payload)
2018-09-02 05:46:37 -07:00
case ErrorResponseCode:
return nil, parseErrorResponse(payload)
default:
return nil, errors.New("response type unknown / not implemented")
}
}
func parseErrorResponse(payload []byte) error {
if len(payload) != 1 {
return errors.New("invalid response payload length")
}
return &Error{
Code: ErrorCode(payload[0]),
}
}
func parseSessionMessage(payload []byte) (Response, error) {
return &SessionMessageResponse{
SessionID: payload[0],
EncryptedData: payload[1 : len(payload)-8],
MAC: payload[len(payload)-8:],
}, nil
}
2022-06-15 16:54:59 -07:00
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))
}
2022-06-15 16:54:59 -07:00
return &DeviceInfoResponse{
MajorVersion: payload[0],
MinorVersion: payload[1],
BuildVersion: payload[2],
SerialNumber: serialNumber,
LogTotal: payload[7],
LogUsed: payload[8],
SupportedAlgorithms: supportedAlgorithms,
2022-06-15 16:54:59 -07:00
}, nil
}
2018-09-02 05:46:37 -07:00
func parseCreateSessionResponse(payload []byte) (Response, error) {
if len(payload) != 17 {
return nil, errors.New("invalid response payload length")
}
return &CreateSessionResponse{
SessionID: uint8(payload[0]),
CardChallenge: payload[1:9],
CardCryptogram: payload[9:],
}, nil
}
func parseCreateAsymmetricKeyResponse(payload []byte) (Response, error) {
if len(payload) != 2 {
return nil, errors.New("invalid response payload length")
}
var keyID uint16
err := binary.Read(bytes.NewReader(payload[1:3]), binary.BigEndian, &keyID)
if err != nil {
return nil, err
}
return &CreateAsymmetricKeyResponse{
KeyID: keyID,
}, nil
}
func parseSignDataEddsaResponse(payload []byte) (Response, error) {
return &SignDataEddsaResponse{
Signature: payload,
}, nil
}
2021-04-07 15:05:46 -07:00
func parseSignDataPkcs1Response(payload []byte) (Response, error) {
if len(payload) < 1 {
return nil, errors.New("invalid response payload length")
}
return &SignDataPkcs1Response{
Signature: payload,
}, nil
}
2018-12-05 05:42:38 -08:00
func parseSignDataEcdsaResponse(payload []byte) (Response, error) {
return &SignDataEcdsaResponse{
Signature: payload,
}, nil
}
2018-09-02 05:46:37 -07:00
func parsePutAsymmetricKeyResponse(payload []byte) (Response, error) {
if len(payload) != 2 {
return nil, errors.New("invalid response payload length")
}
var keyID uint16
2018-09-17 12:18:05 -07:00
err := binary.Read(bytes.NewReader(payload), binary.BigEndian, &keyID)
2018-09-02 05:46:37 -07:00
if err != nil {
return nil, err
}
return &PutAsymmetricKeyResponse{
KeyID: keyID,
}, 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
}
2018-09-08 11:43:08 -07:00
func parseGetPubKeyResponse(payload []byte) (Response, error) {
if len(payload) < 1 {
return nil, errors.New("invalid response payload length")
}
return &GetPubKeyResponse{
Algorithm: Algorithm(payload[0]),
KeyData: payload[1:],
}, nil
}
2018-09-14 03:21:34 -07:00
func parseEchoResponse(payload []byte) (Response, error) {
return &EchoResponse{
Data: payload,
}, nil
}
func parseDeriveEcdhResponse(payload []byte) (Response, error) {
return &DeriveEcdhResponse{
XCoordinate: payload,
}, nil
}
2021-04-07 15:05:46 -07:00
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
}
2021-04-07 15:05:46 -07:00
return &ChangeAuthenticationKeyResponse{ObjectID: objectID}, nil
2021-03-01 03:47:30 -08:00
}
2021-04-07 15:05:46 -07:00
func parseGetPseudoRandomResponse(payload []byte) Response {
return payload
}
func parsePutWrapkeyResponse(payload []byte) (Response, error) {
2021-03-04 07:46:36 -08:00
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
}
2021-04-07 15:05:46 -07:00
return &PutWrapkeyResponse{ObjectID: objectID}, nil
}
2021-04-09 10:44:16 -07:00
func parsePutAuthkeyResponse(payload []byte) (Response, error) {
2021-04-07 15:05:46 -07:00
if len(payload) != 2 {
return nil, errors.New("invalid response payload length")
}
2021-04-07 14:22:09 -07:00
2021-04-07 15:05:46 -07:00
var objectID uint16
err := binary.Read(bytes.NewReader(payload), binary.BigEndian, &objectID)
if err != nil {
return nil, err
}
2021-04-09 10:44:16 -07:00
return &PutAuthkeyResponse{ObjectID: objectID}, nil
2021-03-05 04:41:57 -08:00
}
2021-04-07 14:22:09 -07:00
func parsePutOpaqueResponse(payload []byte) (Response, error) {
2021-03-05 04:41:57 -08:00
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
}
2021-04-07 14:22:09 -07:00
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
2021-03-04 07:46:36 -08:00
}
2021-06-17 04:28:14 -07:00
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
}
2021-06-17 10:59:00 -07:00
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
}
2018-09-02 05:46:37 -07:00
// Error formats a card error message into a human readable format
func (e *Error) Error() string {
message := ""
switch e.Code {
case ErrorCodeOK:
message = "OK"
case ErrorCodeInvalidCommand:
message = "Invalid command"
case ErrorCodeInvalidData:
message = "Invalid data"
case ErrorCodeInvalidSession:
message = "Invalid session"
case ErrorCodeAuthFail:
message = "Auth fail"
case ErrorCodeSessionFull:
message = "Session full"
case ErrorCodeSessionFailed:
message = "Session failed"
case ErrorCodeStorageFailed:
message = "Storage failed"
case ErrorCodeWrongLength:
message = "Wrong length"
case ErrorCodeInvalidPermission:
message = "Invalid permission"
case ErrorCodeLogFull:
message = "Log full"
case ErrorCodeObjectNotFound:
message = "Object not found"
2021-04-07 14:22:09 -07:00
case ErrorCodeInvalidID:
message = "Invalid ID"
2021-04-07 15:05:46 -07:00
case ErrorCodeCommandUnexecuted:
message = "Command unexecuted"
2021-04-07 14:22:09 -07:00
case ErrorCodeSSHCAConstraintViolation:
message = "SSH CA constraint violation"
case ErrorCodeInvalidOTP:
message = "Invalid OTP"
case ErrorCodeDemoMode:
message = "Demo mode"
case ErrorCodeObjectExists:
message = "Object exists"
2018-09-17 11:14:11 -07:00
default:
2021-04-07 14:22:09 -07:00
message = "Unknown"
2018-09-02 05:46:37 -07:00
}
return fmt.Sprintf("card responded with error: %s", message)
}