diff --git a/README.md b/README.md index 05aa9a2..353bd56 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/commands/constructors.go b/commands/constructors.go index 9b95a4f..5ec39b7 100644 --- a/commands/constructors.go +++ b/commands/constructors.go @@ -459,3 +459,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 +} diff --git a/commands/response.go b/commands/response.go index 87bcaee..f8f1a2e 100644 --- a/commands/response.go +++ b/commands/response.go @@ -127,6 +127,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. @@ -201,6 +219,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: @@ -473,6 +495,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 := ""