Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement GetLogs, SetLogIndex commands #25

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Currently the following commands are implemented:
* SignAttestationCertificate
* Authentication & Session related commands
* GetPseudoRandom
* GetLogs
* SetLogIndex

Implementing new commands is really easy. Please consult `commands/constructors.go` and `commands/response.go` for reference.

Expand Down
35 changes: 34 additions & 1 deletion commands/constructors.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"github.com/certusone/yubihsm-go/authkey"
)


func CreateDeviceInfoCommand() (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeDeviceInfo,
Expand Down Expand Up @@ -117,6 +116,20 @@ func CreateSignDataPkcs1Command(keyID uint16, data []byte) (*CommandMessage, err
return command, nil
}

func CreateHMACDataCommand(keyID uint16, data []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeHMACData,
}

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")
Expand Down Expand Up @@ -459,3 +472,23 @@ func CreateImportWrappedCommand(wrapObjID uint16, nonce, data []byte) (*CommandM

return command, nil
}

func CreateGetLogsCommand() *CommandMessage {
command := &CommandMessage{
CommandType: CommandTypeGetLogs,
}

return command
}

func CreateSetLogIndexCommand(index uint16) *CommandMessage {
command := &CommandMessage{
CommandType: CommandTypeSetLogIndex,
}

payload := bytes.NewBuffer([]byte{})
_ = binary.Write(payload, binary.BigEndian, index)
command.Data = payload.Bytes()

return command
}
147 changes: 147 additions & 0 deletions commands/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ type (
Signature []byte
}

HMACDataResponse struct {
Data []byte
}

SignDataEcdsaResponse struct {
Signature []byte
}
Expand Down Expand Up @@ -127,6 +131,24 @@ type (
ObjectType uint8
ObjectID uint16
}

GetLogsResponse struct {
UnloggedBootEvents uint16
UnloggedAuthenticationEvents uint16
Elements []LogElement
}

LogElement struct {
CommandNumber uint16
CommandType CommandType
CommandLength uint16
SessionID uint16
KeyID uint16
SecondaryKeyID uint16
Result ErrorCode
SysTick uint32
Digest []byte
}
)

// ParseResponse parses the binary response from the card to the relevant Response type.
Expand Down Expand Up @@ -167,6 +189,8 @@ func ParseResponse(data []byte) (Response, error) {
return parseSignDataEcdsaResponse(payload)
case CommandTypeSignDataPkcs1:
return parseSignDataPkcs1Response(payload)
case CommandTypeHMACData:
return parseHMACDataResponse(payload)
case CommandTypePutAsymmetric:
return parsePutAsymmetricKeyResponse(payload)
case CommandTypeListObjects:
Expand Down Expand Up @@ -201,6 +225,10 @@ func ParseResponse(data []byte) (Response, error) {
return parseExportWrappedResponse(payload)
case CommandTypeImportWrapped:
return parseImportWrappedResponse(payload)
case CommandTypeGetLogs:
return parseGetLogsResponse(payload)
case CommandTypeSetLogIndex:
return nil, nil
case ErrorResponseCode:
return nil, parseErrorResponse(payload)
default:
Expand Down Expand Up @@ -292,6 +320,16 @@ func parseSignDataPkcs1Response(payload []byte) (Response, error) {
}, nil
}

func parseHMACDataResponse(payload []byte) (Response, error) {
if len(payload) < 1 {
return nil, errors.New("invalid response payload length")
}

return &HMACDataResponse{
Data: payload,
}, nil
}

func parseSignDataEcdsaResponse(payload []byte) (Response, error) {
return &SignDataEcdsaResponse{
Signature: payload,
Expand Down Expand Up @@ -473,6 +511,115 @@ func parseImportWrappedResponse(payload []byte) (Response, error) {
}, nil
}

const (
LogElementDataLength = 16
LogElementDigestLength = 16
LogElementLength = LogElementDataLength + LogElementDigestLength
)

const (
LogCommandTypeBoot = CommandType(0x00)
LogCommandTypeReset = CommandType(0xff)
)

func parseGetLogsResponse(payload []byte) (Response, error) {
if len(payload) < 5 {
return nil, errors.New("invalid response payload length")
}

var (
unloggedBootEvents uint16
unloggedAuthenticationEvents uint16
numberOfElements uint8
elements []LogElement
)

r := bytes.NewReader(payload)
if err := binary.Read(r, binary.BigEndian, &unloggedBootEvents); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &unloggedAuthenticationEvents); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &numberOfElements); err != nil {
return nil, err
}

if len(payload) != 5+int(numberOfElements)*LogElementLength {
return nil, errors.New("invalid response payload length")
}

for i := 0; i < int(numberOfElements); i++ {
var (
commandNumber uint16
commandType CommandType
commandLength uint16
sessionID uint16
keyID uint16
secondaryKeyID uint16
result ErrorCode
sysTick uint32
digest = make([]byte, 16)
)

if err := binary.Read(r, binary.BigEndian, &commandNumber); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &commandType); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &commandLength); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &sessionID); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &keyID); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &secondaryKeyID); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &result); err != nil {
return nil, err
}
if err := binary.Read(r, binary.BigEndian, &sysTick); err != nil {
return nil, err
}
if n, err := r.Read(digest); err != nil {
return nil, err
} else if n != 16 {
return nil, errors.New("incomplete log entry digest")
}

elements = append(elements, LogElement{
CommandNumber: commandNumber,
CommandType: commandType,
CommandLength: commandLength,
SessionID: sessionID,
KeyID: keyID,
SecondaryKeyID: secondaryKeyID,
Result: result,
SysTick: sysTick,
Digest: digest,
})
}

return &GetLogsResponse{
UnloggedBootEvents: unloggedBootEvents,
UnloggedAuthenticationEvents: unloggedAuthenticationEvents,
Elements: elements,
}, nil
}

func (le LogElement) IsBoot() bool {
return le.CommandType == LogCommandTypeBoot
}

func (le LogElement) IsReset() bool {
return le.CommandType == LogCommandTypeReset
}

// Error formats a card error message into a human readable format
func (e *Error) Error() string {
message := ""
Expand Down
20 changes: 19 additions & 1 deletion commands/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ const (
CapabilityDeleteTemplate uint64 = 0x0000100000000000
CapabilityDeleteOtpAeadKey uint64 = 0x0000200000000000
CapabilityChangeAuthenticationKey uint64 = 0x0000400000000000
CapabilityPutSymmetricKey uint64 = 0x0000800000000000
CapabilityGenerateSymmetricKey uint64 = 0x0001000000000000
CapabilityDeleteSymmetricKey uint64 = 0x0002000000000000
CapabilityDecryptEcb uint64 = 0x0004000000000000
CapabilityEncryptEcb uint64 = 0x0008000000000000
CapabilityDecryptCbc uint64 = 0x0010000000000000
CapabilityEncryptCbc uint64 = 0x0020000000000000

// Domains
Domain1 uint16 = 0x0001
Expand Down Expand Up @@ -209,6 +216,7 @@ const (
ObjectTypeHmacKey uint8 = 0x05
ObjectTypeTemplate uint8 = 0x06
ObjectTypeOtpAeadKey uint8 = 0x07
ObjectTypeSymmetricKey uint8 = 0x08

// list objects params
ListObjectParamID uint8 = 0x01
Expand All @@ -219,11 +227,21 @@ const (
ListObjectParamLabel uint8 = 0x06
)

// CapabilityPrimitiveFromSlice OR's all the capabilitites together.
// CapabilityPrimitiveFromSlice OR's all the capabilities together.
func CapabilityPrimitiveFromSlice(capabilitites []uint64) uint64 {
var primitive uint64
for _, c := range capabilitites {
primitive |= c
}
return primitive
}

func SliceForCapabilityPrimitive(primitive uint64) (capabilities []uint64) {
for i := 0; i < 64; i++ {
c := uint64(1) << i
if c&primitive > 0 {
capabilities = append(capabilities, c)
}
}
return
}