From 86670f0fbaf8c7ebe62cfe5e9a1969d1949da245 Mon Sep 17 00:00:00 2001 From: Andrew Pau Date: Wed, 7 Apr 2021 15:05:46 -0700 Subject: [PATCH] Merge conflicts --- README.md | 3 +- commands/constructors.go | 128 +++++++++++++++++++++++-------- commands/response.go | 95 ++++++++++++++--------- commands/types.go | 159 +++++++++++++++++++++------------------ 4 files changed, 247 insertions(+), 138 deletions(-) diff --git a/README.md b/README.md index 638373c..2b5817e 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Currently the following commands are implemented: * Reset * GenerateAsymmetricKey * SignDataEddsa + * SignDataPkcs1 * PutAsymmetricKey * GetPubKey * DeriveEcdh @@ -17,9 +18,9 @@ Currently the following commands are implemented: * PutAuthenticationKey * GetOpaque * PutOpaque - * SignDataPkcs1 * SignAttestationCertificate * Authentication & Session related commands + * GetPseudoRandom Implementing new commands is really easy. Please consult `commands/constructors.go` and `commands/response.go` for reference. diff --git a/commands/constructors.go b/commands/constructors.go index d269f9d..0c7825f 100644 --- a/commands/constructors.go +++ b/commands/constructors.go @@ -94,6 +94,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") @@ -241,33 +255,6 @@ func CreateDeriveEcdhCommand(objID uint16, pubkey []byte) (*CommandMessage, erro return command, nil } -func CreatePutAuthenticationKeyCommand(objID uint16, label []byte, domains uint16, capabilities uint64, delegated uint64, password string) (*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: CommandTypePutAuthKey, - } - - authKey := authkey.NewFromPassword(password) - 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, AlgorithmYubicoAESAuthentication) - binary.Write(payload, binary.BigEndian, delegated) - payload.Write(authKey.GetEncKey()) - payload.Write(authKey.GetMacKey()) - command.Data = payload.Bytes() - - return command, nil -} - func CreateChangeAuthenticationKeyCommand(objID uint16, newPassword string) (*CommandMessage, error) { command := &CommandMessage{ CommandType: CommandTypeChangeAuthenticationKey, @@ -321,20 +308,101 @@ func CreateGetOpaqueCommand(objID uint16) (*CommandMessage, error) { return command, nil } -func CreateSignDataPkcs1Command(objID uint16, data []byte) (*CommandMessage, error) { +func CreateGetPseudoRandomCommand(numBytes uint16) *CommandMessage { command := &CommandMessage{ - CommandType: CommandTypeSignDataPkcs1, + 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(data) + 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 CreatePutAuthenticationKeyCommand(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: CommandTypePutAuthenticationKey, + } + + 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 CreatePutAuthenticationKeyCommand(objID, label, domains, capabilities, delegated, authKey.GetEncKey(), authKey.GetMacKey()) +} + func CreateSignAttestationCertCommand(keyObjID, attestationObjID uint16) (*CommandMessage, error) { command := &CommandMessage{ CommandType: CommandTypeAttestAsymmetric, diff --git a/commands/response.go b/commands/response.go index c8d5b73..6a89d27 100644 --- a/commands/response.go +++ b/commands/response.go @@ -62,6 +62,10 @@ type ( Signature []byte } + SignDataPkcs1Response struct { + Signature []byte + } + SignDataEcdsaResponse struct { Signature []byte } @@ -80,11 +84,15 @@ type ( XCoordinate []byte } - PutAuthenticationKeyResponse struct { + ChangeAuthenticationKeyResponse struct { ObjectID uint16 } - ChangeAuthenticationKeyResponse struct { + PutWrapkeyResponse struct { + ObjectID uint16 + } + + PutAuthenticationKeyResponse struct { ObjectID uint16 } @@ -96,10 +104,6 @@ type ( Data []byte } - SignDataPkcs1Response struct { - Signature []byte - } - SignAttestationCertResponse struct { Cert []byte } @@ -139,6 +143,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: @@ -155,16 +161,18 @@ func ParseResponse(data []byte) (Response, error) { return parseEchoResponse(payload) case CommandTypeDeriveEcdh: return parseDeriveEcdhResponse(payload) - case CommandTypePutAuthKey: - return parsePutAuthenticationKeyResponse(payload) case CommandTypeChangeAuthenticationKey: return parseChangeAuthenticationKeyResponse(payload) + case CommandTypeGetPseudoRandom: + return parseGetPseudoRandomResponse(payload), nil + case CommandTypePutWrapKey: + return parsePutWrapkeyResponse(payload) + case CommandTypePutAuthenticationKey: + return parsePutAuthenticationKeyResponse(payload) case CommandTypePutOpaque: return parsePutOpaqueResponse(payload) case CommandTypeGetOpaque: return parseGetOpaqueResponse(payload) - case CommandTypeSignDataPkcs1: - return parseSignDataPkcs1Response(payload) case CommandTypeAttestAsymmetric: return parseAttestationCertResponse(payload) case ErrorResponseCode: @@ -226,6 +234,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, @@ -298,20 +316,6 @@ func parseDeriveEcdhResponse(payload []byte) (Response, error) { }, nil } -func parsePutAuthenticationKeyResponse(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 &PutAuthenticationKeyResponse{ObjectID: objectID}, nil -} - func parseChangeAuthenticationKeyResponse(payload []byte) (Response, error) { if len(payload) != 2 { return nil, errors.New("invalid response payload length") @@ -326,6 +330,37 @@ func parseChangeAuthenticationKeyResponse(payload []byte) (Response, error) { 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 parsePutAuthenticationKeyResponse(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 &PutAuthenticationKeyResponse{ObjectID: objectID}, nil +} + func parsePutOpaqueResponse(payload []byte) (Response, error) { if len(payload) != 2 { return nil, errors.New("invalid response payload length") @@ -352,16 +387,6 @@ func parseGetOpaqueResponse(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 parseAttestationCertResponse(payload []byte) (Response, error) { if len(payload) < 1 { return nil, errors.New("invalid response payload length") @@ -402,6 +427,8 @@ func (e *Error) Error() string { message = "Object not found" case ErrorCodeInvalidID: message = "Invalid ID" + case ErrorCodeCommandUnexecuted: + message = "Command unexecuted" case ErrorCodeSSHCAConstraintViolation: message = "SSH CA constraint violation" case ErrorCodeInvalidOTP: diff --git a/commands/types.go b/commands/types.go index f65aac3..9bc55a5 100644 --- a/commands/types.go +++ b/commands/types.go @@ -23,7 +23,7 @@ const ( CommandTypeStorageStatus CommandType = 0x41 CommandTypePutOpaque CommandType = 0x42 CommandTypeGetOpaque CommandType = 0x43 - CommandTypePutAuthKey CommandType = 0x44 + CommandTypePutAuthenticationKey CommandType = 0x44 CommandTypePutAsymmetric CommandType = 0x45 CommandTypeGenerateAsymmetricKey CommandType = 0x46 CommandTypeSignDataPkcs1 CommandType = 0x47 @@ -64,23 +64,24 @@ 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 - ErrorCodeInvalidID ErrorCode = 0x0c - ErrorCodeSSHCAConstraintViolation ErrorCode = 0x0e - ErrorCodeInvalidOTP ErrorCode = 0x0f - ErrorCodeDemoMode ErrorCode = 0x10 - ErrorCodeObjectExists ErrorCode = 0x11 + 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 AlgorithmRSA2048 Algorithm = 9 @@ -89,58 +90,61 @@ const ( AlgorithmOpaqueData Algorithm = 30 AlgorithmOpaqueX509Certificate Algorithm = 31 AlgorithmYubicoAESAuthentication Algorithm = 38 + AlgorithmAES128CCMWrap Algorithm = 29 + AlgorithmAES192CCMWrap Algorithm = 41 + AlgorithmAES256CCMWrap Algorithm = 42 AlgorithmED25519 Algorithm = 46 // Capabilities - CapabilityNone uint64 = 0x0000000000000000 - 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 - 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 - CapabilityChangeAuthKey uint64 = 0x0000400000000000 + 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 @@ -170,10 +174,19 @@ const ( ObjectTypeOtpAeadKey uint8 = 0x07 // list objects params - ListObjectParamID uint8 = 0x01 - ListObjectParamType uint8 = 0x02 - ListObjectParamDomains uint8 = 0x03 - ListObjectParamCapabilities uint8 = 0x04 - ListObjectParamAlgorithm uint8 = 0x05 - ListObjectParamLabel uint8 = 0x06 + 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 +}