diff --git a/internal/authentication/jwt_test.go b/internal/authentication/jwt_test.go index 01d7ae6..8057b72 100644 --- a/internal/authentication/jwt_test.go +++ b/internal/authentication/jwt_test.go @@ -6,8 +6,6 @@ import ( "github.com/golang-jwt/jwt/v5" ) -const testVIN = "0123456789abcdefX" - func TestVerify(t *testing.T) { pkey := []byte{ 0x04, 0x77, 0x5e, 0x2e, 0xf5, 0x70, 0xd2, 0x92, 0xdf, 0x42, 0x4c, 0x09, diff --git a/internal/authentication/peer.go b/internal/authentication/peer.go index 6b10539..0bd35a5 100644 --- a/internal/authentication/peer.go +++ b/internal/authentication/peer.go @@ -78,3 +78,40 @@ func (p *Peer) hmacTag(message *universal.RoutableMessage, hmacData *signatures. } return meta.Checksum(message.GetProtobufMessageAsBytes()), nil } + +func RequestID(message *universal.RoutableMessage) []byte { + sigData := message.GetSignatureData() + if sigData.GetSigType() == nil { + return nil + } + + switch s := sigData.GetSigType().(type) { + case *signatures.SignatureData_AES_GCM_PersonalizedData: + return append( + []byte{byte(signatures.SignatureType_SIGNATURE_TYPE_AES_GCM_PERSONALIZED)}, + s.AES_GCM_PersonalizedData.GetTag()...) + case *signatures.SignatureData_HMAC_PersonalizedData: + tag := s.HMAC_PersonalizedData.GetTag() + if message.GetToDestination().GetDomain() == universal.Domain_DOMAIN_VEHICLE_SECURITY { + tag = tag[:16] + } + return append( + []byte{byte(signatures.SignatureType_SIGNATURE_TYPE_HMAC_PERSONALIZED)}, tag...) + default: + return nil + } +} + +func (p *Peer) responseMetadata(message *universal.RoutableMessage, id []byte, counter uint32) ([]byte, error) { + meta := newMetadata() + meta.Add(signatures.Tag_TAG_SIGNATURE_TYPE, []byte{byte(signatures.SignatureType_SIGNATURE_TYPE_AES_GCM_RESPONSE)}) + meta.Add(signatures.Tag_TAG_DOMAIN, []byte{byte(message.GetFromDestination().GetDomain())}) + if err := meta.Add(signatures.Tag_TAG_PERSONALIZATION, p.verifierName); err != nil { + return nil, err + } + meta.AddUint32(signatures.Tag_TAG_COUNTER, counter) + meta.AddUint32(signatures.Tag_TAG_FLAGS, message.Flags) + meta.Add(signatures.Tag_TAG_REQUEST_HASH, id) + meta.AddUint32(signatures.Tag_TAG_FAULT, uint32(message.GetSignedMessageStatus().GetSignedMessageFault())) + return meta.Checksum(nil), nil +} diff --git a/internal/authentication/peer_test.go b/internal/authentication/peer_test.go index b29e956..1dd3e1e 100644 --- a/internal/authentication/peer_test.go +++ b/internal/authentication/peer_test.go @@ -1,8 +1,10 @@ package authentication import ( + "bytes" "testing" + "github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/signatures" universal "github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/universalmessage" ) @@ -84,3 +86,47 @@ func checkError(t *testing.T, err error, expectedCode universal.MessageFault_E) t.Errorf("Got unexpected error type: %s", err) } } + +func TestRequestID(t *testing.T) { + tag := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18} + message := universal.RoutableMessage{ + ToDestination: &universal.Destination{ + SubDestination: &universal.Destination_Domain{ + Domain: universal.Domain_DOMAIN_VEHICLE_SECURITY, + }, + }, + SubSigData: &universal.RoutableMessage_SignatureData{ + SignatureData: &signatures.SignatureData{ + SigType: &signatures.SignatureData_HMAC_PersonalizedData{ + HMAC_PersonalizedData: &signatures.HMAC_Personalized_Signature_Data{ + Tag: append([]byte{}, tag...), + }, + }, + }, + }, + } + id := RequestID(&message) + if len(id) != 17 { + t.Errorf("Expected 17-byte id, but got %d bytes", len(id)) + } + if id[0] != byte(signatures.SignatureType_SIGNATURE_TYPE_HMAC_PERSONALIZED) { + t.Errorf("Invalid first byte of request ID: %02x", id[0]) + } + if !bytes.Equal(id[1:], tag[:16]) { + t.Errorf("Expected: %02x", tag[:16]) + t.Errorf("Observed: %02x", id[1:]) + } + + message.ToDestination.SubDestination = &universal.Destination_Domain{ + Domain: universal.Domain_DOMAIN_INFOTAINMENT, + } + + id = RequestID(&message) + if id[0] != byte(signatures.SignatureType_SIGNATURE_TYPE_HMAC_PERSONALIZED) { + t.Errorf("Invalid first byte of request ID: %02x", id[0]) + } + if !bytes.Equal(id[1:], tag) { + t.Errorf("Expected: %02x", tag) + t.Errorf("Observed: %02x", id[1:]) + } +} diff --git a/internal/authentication/signer.go b/internal/authentication/signer.go index 8445b40..8c558ee 100644 --- a/internal/authentication/signer.go +++ b/internal/authentication/signer.go @@ -211,3 +211,32 @@ func (s *Signer) AuthorizeHMAC(message *universal.RoutableMessage, expiresIn tim } return nil } + +// Decrypt a Verifier message in place. +// +// Returns the anti-replay counter included in the message, which the client must verify increases +// monotonically for a given id or is inside of a sliding window. +func (s *Signer) Decrypt(message *universal.RoutableMessage, id []byte) (uint32, error) { + gcmInfo := message.GetSignatureData().GetAES_GCM_ResponseData() + if gcmInfo == nil { + return 0, newError(errCodeBadParameter, "missing AES-GCM data") + } + authenticatedData, err := s.responseMetadata(message, id, gcmInfo.Counter) + if err != nil { + return 0, nil + } + plaintext, err := s.session.Decrypt( + gcmInfo.Nonce, + message.GetProtobufMessageAsBytes(), + authenticatedData, + gcmInfo.Tag, + ) + if err != nil { + return 0, err + } + message.Payload = &universal.RoutableMessage_ProtobufMessageAsBytes{ + ProtobufMessageAsBytes: plaintext, + } + message.SubSigData = nil + return gcmInfo.Counter, nil +} diff --git a/internal/authentication/verifier.go b/internal/authentication/verifier.go index 3a728d5..05e1d7d 100644 --- a/internal/authentication/verifier.go +++ b/internal/authentication/verifier.go @@ -19,6 +19,7 @@ type Verifier struct { Peer lock sync.Mutex window uint64 + handle uint32 } // NewVerifier returns a Verifier. @@ -72,6 +73,12 @@ func (v *Verifier) rotateEpochIfNeeded(force bool) error { return nil } +func (v *Verifier) AssignHandle(handle uint32) { + v.lock.Lock() + v.handle = handle + v.lock.Unlock() +} + func (v *Verifier) sessionInfo() (*signatures.SessionInfo, error) { if err := v.adjustClock(); err != nil { return nil, err @@ -81,6 +88,7 @@ func (v *Verifier) sessionInfo() (*signatures.SessionInfo, error) { PublicKey: v.session.LocalPublicBytes(), Epoch: v.epoch[:], ClockTime: v.timestamp(), + Handle: v.handle, } return info, nil } @@ -217,55 +225,6 @@ func (v *Verifier) Verify(message *universal.RoutableMessage) (plaintext []byte, return } -// updateSlidingWindow takes the current counter value (i.e., the highest -// counter value of any authentic message received so far), the current sliding -// window, and the newCounter value from an incoming message. The function -// returns the updated counter and window values and sets ok to true if it -// could confirm that newCounter has never been previously used. If ok is -// false, then updatedCounter = counter and updatedWindow = window. -func updateSlidingWindow(counter uint32, window uint64, newCounter uint32) (updatedCounter uint32, updatedWindow uint64, ok bool) { - // If we exit early due to an error, we want to leave the counter/window - // state unchanged. Therefore we initialize return values to the current - // state. - updatedCounter = counter - updatedWindow = window - ok = false - - if counter == newCounter { - // This counter value has been used before. - return - } - - if newCounter < counter { - // This message arrived out of order. - age := counter - newCounter - if age > windowSize { - // Our history doesn't go back this far, so we can't determine if - // we've seen this newCounter value before. - return - } - if window>>(age-1)&1 == 1 { - // The newCounter value has been used before. - return - } - // Everything looks good. - ok = true - updatedWindow |= (1 << (age - 1)) - return - } - - // If we've reached this point, newCounter > counter, so newCounter is valid. - ok = true - updatedCounter = newCounter - // Compute how far we need to shift our sliding window. - shiftCount := newCounter - counter - updatedWindow <<= shiftCount - // We need to set the bit in our window that corresponds to counter (if - // newCounter = counter + 1, then this is the first [LSB] of the window). - updatedWindow |= uint64(1) << (shiftCount - 1) - return -} - func (v *Verifier) verifyGCM(message *universal.RoutableMessage, gcmData *signatures.AES_GCM_Personalized_Signature_Data) (plaintext []byte, err error) { if err = v.verifySessionInfo(message, gcmData); err != nil { return nil, err @@ -337,3 +296,34 @@ func (v *Verifier) verifySessionInfo(message *universal.RoutableMessage, info se } return nil } + +// Encrypt a message response in place. +// +// The message id must uniquely identify the Signer's request that prompted the message. The +// counter must increase monotonically for a given id. +func (v *Verifier) Encrypt(message *universal.RoutableMessage, id []byte, counter uint32) error { + plaintext := message.GetProtobufMessageAsBytes() + authenticatedData, err := v.responseMetadata(message, id, counter) + if err != nil { + return err + } + nonce, ciphertext, tag, err := v.session.Encrypt(plaintext, authenticatedData) + if err != nil { + return err + } + message.SubSigData = &universal.RoutableMessage_SignatureData{ + SignatureData: &signatures.SignatureData{ + SigType: &signatures.SignatureData_AES_GCM_ResponseData{ + AES_GCM_ResponseData: &signatures.AES_GCM_Response_Signature_Data{ + Counter: counter, + Nonce: nonce, + Tag: tag, + }, + }, + }, + } + message.Payload = &universal.RoutableMessage_ProtobufMessageAsBytes{ + ProtobufMessageAsBytes: ciphertext, + } + return nil +} diff --git a/internal/authentication/verifier_test.go b/internal/authentication/verifier_test.go index 35ec411..ee18952 100644 --- a/internal/authentication/verifier_test.go +++ b/internal/authentication/verifier_test.go @@ -550,86 +550,46 @@ func TestNoSignatureData(t *testing.T) { runVerifyTest(t, verifier, message, errCodeBadParameter, false) } -func TestSlidingWindow(t *testing.T) { - type windowTest struct { - counter uint32 - window uint64 - newCounter uint32 - expectedUpdatedCounter uint32 - expectedUpdatedWindow uint64 - expectedOk bool - } - tests := []windowTest{ - // Update should succeed because newCounter is greater than all previous counters. - windowTest{ - counter: 100, - window: uint64((1 << 0) | (1 << 5)), - newCounter: 101, - expectedUpdatedCounter: 101, - expectedUpdatedWindow: uint64(1 | (1 << 1) | (1 << 6)), - expectedOk: true, - }, - // Update should succeed because newCounter is greater than all previous counters. - // In this test, some messages were skipped and so the expectedUpdatedWindow shifts further. - windowTest{ - counter: 100, - window: uint64((1 << 0) | (1 << 5)), - newCounter: 103, - expectedUpdatedCounter: 103, - expectedUpdatedWindow: uint64((1 << 2) | (1 << 3) | (1 << 8)), - expectedOk: true, - }, - // Update should succeed because newCounter is greater than all previous counters. - // In this test, the previous counter doesn't fit in sliding window. - windowTest{ - counter: 100, - window: uint64((1 << 0) | (1 << 5)), - newCounter: 500, - expectedUpdatedCounter: 500, - expectedUpdatedWindow: 0, - expectedOk: true, - }, - // Update should succeed because newCounter falls in window but isn't set. - windowTest{ - counter: 100, - window: uint64((1 << 0) | (1 << 5)), - newCounter: 98, - expectedUpdatedCounter: 100, - expectedUpdatedWindow: uint64((1 << 0) | (1 << 1) | (1 << 5)), - expectedOk: true, - }, - // Update should fail because newCounter falls in window and is already set. - windowTest{ - counter: 100, - window: uint64((1 << 0) | (1 << 5)), - newCounter: 99, - expectedUpdatedCounter: 100, - expectedUpdatedWindow: uint64((1 << 0) | (1 << 5)), - expectedOk: false, - }, - // Update should fail because newCounter falls outside of window and freshness cannot be validated. - windowTest{ - counter: 100, - window: uint64((1 << 0) | (1 << 5)), - newCounter: 3, - expectedUpdatedCounter: 100, - expectedUpdatedWindow: uint64((1 << 0) | (1 << 5)), - expectedOk: false, - }, - // Update should fail because newCounter == counter. - windowTest{ - counter: 100, - window: uint64((1 << 0) | (1 << 5)), - newCounter: 100, - expectedUpdatedCounter: 100, - expectedUpdatedWindow: uint64((1 << 0) | (1 << 5)), - expectedOk: false, - }, - } - for _, test := range tests { - counter, window, ok := updateSlidingWindow(test.counter, test.window, test.newCounter) - if counter != test.expectedUpdatedCounter || window != test.expectedUpdatedWindow || ok != test.expectedOk { - t.Errorf("Failed window test %+v, got counter=%d, window=%d, ok=%v", test, counter, window, ok) - } +func TestProvideHandle(t *testing.T) { + verifier, _ := getGCMVerifierAndSigner(t) + handle := uint32(0xDEADBEEF) + verifier.AssignHandle(handle) + info, err := verifier.SessionInfo() + if err != nil { + t.Fatal(err) + } + if info.GetHandle() != handle { + t.Fatalf("Invalid handle value: %s", info) + } +} + +func TestVerifierEncryption(t *testing.T) { + verifier, signer := getGCMVerifierAndSigner(t) + message := getTestMessage() + message.FromDestination = &universal.Destination{ + SubDestination: &universal.Destination_Domain{Domain: testVerifierDomain}, + } + plaintext := append([]byte{}, message.GetProtobufMessageAsBytes()...) + id := []byte{1, 2, 3} + counter := uint32(4) + t.Logf("Original message: %+v", message) + if err := verifier.Encrypt(message, id, counter); err != nil { + t.Fatalf("Error encrypting message: %s", err) + } + t.Logf("Encrypted message: %+v", message) + if bytes.Equal(plaintext, message.GetProtobufMessageAsBytes()) { + t.Fatalf("Encryption didn't change payload") + } + + id[0] ^= 1 + if _, err := signer.Decrypt(message, id); err == nil { + t.Fatalf("Decryption should have failed") + } + id[0] ^= 1 + if rxCounter, err := signer.Decrypt(message, id); err != nil || rxCounter != counter { + t.Fatalf("Decryption failed: %s", err) + } + if !bytes.Equal(plaintext, message.GetProtobufMessageAsBytes()) { + t.Fatalf("Decryption failed to recover original plaintext") } } diff --git a/internal/authentication/window.go b/internal/authentication/window.go new file mode 100644 index 0000000..7368ff0 --- /dev/null +++ b/internal/authentication/window.go @@ -0,0 +1,68 @@ +package authentication + +// updateSlidingWindow takes the current counter value (i.e., the highest +// counter value of any authentic message received so far), the current sliding +// window, and the newCounter value from an incoming message. The function +// returns the updated counter and window values and sets ok to true if it +// could confirm that newCounter has never been previously used. If ok is +// false, then updatedCounter = counter and updatedWindow = window. +func updateSlidingWindow(counter uint32, window uint64, newCounter uint32) (updatedCounter uint32, updatedWindow uint64, ok bool) { + // If we exit early due to an error, we want to leave the counter/window + // state unchanged. Therefore we initialize return values to the current + // state. + updatedCounter = counter + updatedWindow = window + ok = false + + if counter == newCounter { + // This counter value has been used before. + return + } + + if newCounter < counter { + // This message arrived out of order. + age := counter - newCounter + if age > windowSize { + // Our history doesn't go back this far, so we can't determine if + // we've seen this newCounter value before. + return + } + if window>>(age-1)&1 == 1 { + // The newCounter value has been used before. + return + } + // Everything looks good. + ok = true + updatedWindow |= (1 << (age - 1)) + return + } + + // If we've reached this point, newCounter > counter, so newCounter is valid. + ok = true + updatedCounter = newCounter + // Compute how far we need to shift our sliding window. + shiftCount := newCounter - counter + updatedWindow <<= shiftCount + // We need to set the bit in our window that corresponds to counter (if + // newCounter = counter + 1, then this is the first [LSB] of the window). + updatedWindow |= uint64(1) << (shiftCount - 1) + return +} + +type SlidingWindow struct { + history uint64 + counter uint32 + used bool +} + +func (w *SlidingWindow) Update(counter uint32) bool { + if !w.used { + w.used = true + w.counter = counter + return true + } + var ok bool + + w.counter, w.history, ok = updateSlidingWindow(w.counter, w.history, counter) + return ok +} diff --git a/internal/authentication/window_test.go b/internal/authentication/window_test.go new file mode 100644 index 0000000..b799e4b --- /dev/null +++ b/internal/authentication/window_test.go @@ -0,0 +1,97 @@ +package authentication + +import ( + "testing" +) + +func TestSlidingWindow(t *testing.T) { + type windowTest struct { + counter uint32 + window uint64 + newCounter uint32 + expectedUpdatedCounter uint32 + expectedUpdatedWindow uint64 + expectedOk bool + } + tests := []windowTest{ + // Update should succeed because newCounter is greater than all previous counters. + windowTest{ + counter: 100, + window: uint64((1 << 0) | (1 << 5)), + newCounter: 101, + expectedUpdatedCounter: 101, + expectedUpdatedWindow: uint64(1 | (1 << 1) | (1 << 6)), + expectedOk: true, + }, + // Update should succeed because newCounter is greater than all previous counters. + // In this test, some messages were skipped and so the expectedUpdatedWindow shifts further. + windowTest{ + counter: 100, + window: uint64((1 << 0) | (1 << 5)), + newCounter: 103, + expectedUpdatedCounter: 103, + expectedUpdatedWindow: uint64((1 << 2) | (1 << 3) | (1 << 8)), + expectedOk: true, + }, + // Update should succeed because newCounter is greater than all previous counters. + // In this test, the previous counter doesn't fit in sliding window. + windowTest{ + counter: 100, + window: uint64((1 << 0) | (1 << 5)), + newCounter: 500, + expectedUpdatedCounter: 500, + expectedUpdatedWindow: 0, + expectedOk: true, + }, + // Update should succeed because newCounter falls in window but isn't set. + windowTest{ + counter: 100, + window: uint64((1 << 0) | (1 << 5)), + newCounter: 98, + expectedUpdatedCounter: 100, + expectedUpdatedWindow: uint64((1 << 0) | (1 << 1) | (1 << 5)), + expectedOk: true, + }, + // Update should fail because newCounter falls in window and is already set. + windowTest{ + counter: 100, + window: uint64((1 << 0) | (1 << 5)), + newCounter: 99, + expectedUpdatedCounter: 100, + expectedUpdatedWindow: uint64((1 << 0) | (1 << 5)), + expectedOk: false, + }, + // Update should fail because newCounter falls outside of window and freshness cannot be validated. + windowTest{ + counter: 100, + window: uint64((1 << 0) | (1 << 5)), + newCounter: 3, + expectedUpdatedCounter: 100, + expectedUpdatedWindow: uint64((1 << 0) | (1 << 5)), + expectedOk: false, + }, + // Update should fail because newCounter == counter. + windowTest{ + counter: 100, + window: uint64((1 << 0) | (1 << 5)), + newCounter: 100, + expectedUpdatedCounter: 100, + expectedUpdatedWindow: uint64((1 << 0) | (1 << 5)), + expectedOk: false, + }, + } + for _, test := range tests { + counter, window, ok := updateSlidingWindow(test.counter, test.window, test.newCounter) + if counter != test.expectedUpdatedCounter || window != test.expectedUpdatedWindow || ok != test.expectedOk { + t.Errorf("Failed window test %+v, got counter=%d, window=%d, ok=%v", test, counter, window, ok) + } + w := SlidingWindow{ + history: test.window, + counter: test.counter, + used: true, + } + if w.Update(test.newCounter) != test.expectedOk { + t.Errorf("Failed window test %+v", test) + } + } +} diff --git a/internal/dispatcher/dispatcher.go b/internal/dispatcher/dispatcher.go index ad6a9ea..12b2628 100644 --- a/internal/dispatcher/dispatcher.go +++ b/internal/dispatcher/dispatcher.go @@ -141,7 +141,7 @@ func (d *Dispatcher) StartSessions(ctx context.Context, domains []universal.Doma return err } -func (d *Dispatcher) createHandler(key *receiverKey) *receiver { +func (d *Dispatcher) createHandler(key *receiverKey, id []byte) *receiver { d.handlerLock.Lock() defer d.handlerLock.Unlock() @@ -152,6 +152,7 @@ func (d *Dispatcher) createHandler(key *receiverKey) *receiver { dispatcher: d, requestSentAt: now, lastActive: now, + requestID: id, } d.handlers[*key] = recv @@ -207,6 +208,25 @@ func (d *Dispatcher) checkForSessionUpdate(message *universal.RoutableMessage, h log.Info("[%02x] Updated session info for %s", message.GetRequestUuid(), domain) } +func (d *Dispatcher) decrypt(message *universal.RoutableMessage, handler *receiver) error { + decryptionData := message.GetSignatureData().GetAES_GCM_ResponseData() + if decryptionData == nil { + return nil + } + + domain := handler.key.domain + + d.sessionLock.Lock() + defer d.sessionLock.Unlock() + + session, ok := d.sessions[domain] + if !ok { + return protocol.ErrNoDecryptionContext + } + + return session.decrypt(message, handler) +} + func (d *Dispatcher) process(message *universal.RoutableMessage) { var key receiverKey @@ -263,6 +283,15 @@ func (d *Dispatcher) process(message *universal.RoutableMessage) { // session info. d.checkForSessionUpdate(message, handler) + // Decryption is a no-op for plaintext messages + if err := d.decrypt(message, handler); err == protocol.ErrReplayedResponse { + log.Info("[%02x] Dropping duplicate vehicle response", requestUUID) + return + } else if err != nil { + log.Warning("[%02x] Error decrypting vehicle response: %s", requestUUID, err) + return + } + select { case handler.ch <- message: default: @@ -386,7 +415,7 @@ func (d *Dispatcher) Send(ctx context.Context, message *universal.RoutableMessag } } - resp := d.createHandler(&key) + resp := d.createHandler(&key, authentication.RequestID(message)) encodedMessage, err := proto.Marshal(message) if err != nil { return nil, err diff --git a/internal/dispatcher/receiver.go b/internal/dispatcher/receiver.go index 4b4170a..f3a94e3 100644 --- a/internal/dispatcher/receiver.go +++ b/internal/dispatcher/receiver.go @@ -4,6 +4,7 @@ import ( "fmt" "time" + "github.com/teslamotors/vehicle-command/internal/authentication" universal "github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/universalmessage" ) @@ -32,6 +33,8 @@ type receiver struct { dispatcher *Dispatcher requestSentAt time.Time lastActive time.Time + antireplay authentication.SlidingWindow + requestID []byte } // Recv returns a channel that receives responses to the command that created the receiver. diff --git a/internal/dispatcher/session.go b/internal/dispatcher/session.go index 2173f87..1b5406c 100644 --- a/internal/dispatcher/session.go +++ b/internal/dispatcher/session.go @@ -8,6 +8,7 @@ import ( "github.com/teslamotors/vehicle-command/internal/authentication" "github.com/teslamotors/vehicle-command/pkg/connector" + "github.com/teslamotors/vehicle-command/pkg/protocol" universal "github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/universalmessage" ) @@ -44,6 +45,19 @@ func NewSession(private authentication.ECDHPrivateKey, vin string) (*session, er }, nil } +func (s *session) decrypt(message *universal.RoutableMessage, handler *receiver) error { + s.lock.Lock() + defer s.lock.Unlock() + counter, err := s.ctx.Decrypt(message, handler.requestID) + if err != nil { + return err + } + if !handler.antireplay.Update(counter) { + return protocol.ErrReplayedResponse + } + return nil +} + func (s *session) Authorize(ctx context.Context, command *universal.RoutableMessage, method connector.AuthMethod) error { var err error lifetime := defaultExpiration diff --git a/pkg/protocol/error.go b/pkg/protocol/error.go index 672da85..a9ab2ed 100644 --- a/pkg/protocol/error.go +++ b/pkg/protocol/error.go @@ -51,6 +51,11 @@ var ( ErrProtocolNotSupported = errors.New("vehicle does not support protocol -- use REST API") ErrRequiresBLE = errors.New("command can only be sent over BLE") ErrRequiresEncryption = errors.New("command should not be sent in plaintext or encrypted with an unauthenticated public key") + ErrNoDecryptionContext = errors.New("could not decrypt vehicle response without a session") + // ErrReplayedResponse indicates the client received multiple responses from the vehicle with + // the same response counter. This could be benign, as the network may have reattempted + // transmission. + ErrReplayedResponse = errors.New("received vehicle response with duplicate counter") ) type CommandError struct { diff --git a/pkg/protocol/protobuf/carserver/car_server.pb.go b/pkg/protocol/protobuf/carserver/car_server.pb.go index 86cc95a..5a7c08b 100644 --- a/pkg/protocol/protobuf/carserver/car_server.pb.go +++ b/pkg/protocol/protobuf/carserver/car_server.pb.go @@ -328,7 +328,6 @@ type Action struct { unknownFields protoimpl.UnknownFields // Types that are assignable to ActionMsg: - // // *Action_VehicleAction ActionMsg isAction_ActionMsg `protobuf_oneof:"action_msg"` } @@ -395,7 +394,6 @@ type VehicleAction struct { unknownFields protoimpl.UnknownFields // Types that are assignable to VehicleActionMsg: - // // *VehicleAction_ChargingSetLimitAction // *VehicleAction_ChargingStartStopAction // *VehicleAction_DrivingClearSpeedLimitPinAction @@ -1196,7 +1194,6 @@ type Response struct { ActionStatus *ActionStatus `protobuf:"bytes,1,opt,name=actionStatus,proto3" json:"actionStatus,omitempty"` // Types that are assignable to ResponseMsg: - // // *Response_GetSessionInfoResponse // *Response_GetNearbyChargingSites // *Response_Ping @@ -1353,7 +1350,6 @@ type ResultReason struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Reason: - // // *ResultReason_PlainText Reason isResultReason_Reason `protobuf_oneof:"reason"` } @@ -1530,7 +1526,6 @@ type ChargingStartStopAction struct { unknownFields protoimpl.UnknownFields // Types that are assignable to ChargingAction: - // // *ChargingStartStopAction_Unknown // *ChargingStartStopAction_Start // *ChargingStartStopAction_StartStandard @@ -2519,7 +2514,6 @@ type MediaUpdateVolume struct { unknownFields protoimpl.UnknownFields // Types that are assignable to MediaVolume: - // // *MediaUpdateVolume_VolumeDelta // *MediaUpdateVolume_VolumeAbsoluteFloat MediaVolume isMediaUpdateVolume_MediaVolume `protobuf_oneof:"media_volume"` @@ -3053,12 +3047,10 @@ type VehicleControlSunroofOpenCloseAction struct { unknownFields protoimpl.UnknownFields // Types that are assignable to SunroofLevel: - // // *VehicleControlSunroofOpenCloseAction_AbsoluteLevel // *VehicleControlSunroofOpenCloseAction_DeltaLevel SunroofLevel isVehicleControlSunroofOpenCloseAction_SunroofLevel `protobuf_oneof:"sunroof_level"` // Types that are assignable to Action: - // // *VehicleControlSunroofOpenCloseAction_Vent // *VehicleControlSunroofOpenCloseAction_Close // *VehicleControlSunroofOpenCloseAction_Open @@ -3247,7 +3239,6 @@ type VehicleControlWindowAction struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Action: - // // *VehicleControlWindowAction_Unknown // *VehicleControlWindowAction_Vent // *VehicleControlWindowAction_Close @@ -4281,7 +4272,6 @@ type HvacSeatHeaterActions_HvacSeatHeaterAction struct { unknownFields protoimpl.UnknownFields // Types that are assignable to SeatHeaterLevel: - // // *HvacSeatHeaterActions_HvacSeatHeaterAction_SEAT_HEATER_UNKNOWN // *HvacSeatHeaterActions_HvacSeatHeaterAction_SEAT_HEATER_OFF // *HvacSeatHeaterActions_HvacSeatHeaterAction_SEAT_HEATER_LOW @@ -4289,7 +4279,6 @@ type HvacSeatHeaterActions_HvacSeatHeaterAction struct { // *HvacSeatHeaterActions_HvacSeatHeaterAction_SEAT_HEATER_HIGH SeatHeaterLevel isHvacSeatHeaterActions_HvacSeatHeaterAction_SeatHeaterLevel `protobuf_oneof:"seat_heater_level"` // Types that are assignable to SeatPosition: - // // *HvacSeatHeaterActions_HvacSeatHeaterAction_CAR_SEAT_UNKNOWN // *HvacSeatHeaterActions_HvacSeatHeaterAction_CAR_SEAT_FRONT_LEFT // *HvacSeatHeaterActions_HvacSeatHeaterAction_CAR_SEAT_FRONT_RIGHT @@ -4628,7 +4617,6 @@ type HvacTemperatureAdjustmentAction_Temperature struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: - // // *HvacTemperatureAdjustmentAction_Temperature_TEMP_UNKNOWN // *HvacTemperatureAdjustmentAction_Temperature_TEMP_MIN // *HvacTemperatureAdjustmentAction_Temperature_TEMP_MAX @@ -4726,7 +4714,6 @@ type HvacTemperatureAdjustmentAction_HvacTemperatureZone struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: - // // *HvacTemperatureAdjustmentAction_HvacTemperatureZone_TEMP_ZONE_UNKNOWN // *HvacTemperatureAdjustmentAction_HvacTemperatureZone_TEMP_ZONE_FRONT_LEFT // *HvacTemperatureAdjustmentAction_HvacTemperatureZone_TEMP_ZONE_FRONT_RIGHT diff --git a/pkg/protocol/protobuf/carserver/common.pb.go b/pkg/protocol/protobuf/carserver/common.pb.go index d2e4c82..2e3eb51 100644 --- a/pkg/protocol/protobuf/carserver/common.pb.go +++ b/pkg/protocol/protobuf/carserver/common.pb.go @@ -162,7 +162,6 @@ type PreconditioningTimes struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Times: - // // *PreconditioningTimes_AllWeek // *PreconditioningTimes_Weekdays Times isPreconditioningTimes_Times `protobuf_oneof:"times"` @@ -243,7 +242,6 @@ type OffPeakChargingTimes struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Times: - // // *OffPeakChargingTimes_AllWeek // *OffPeakChargingTimes_Weekdays Times isOffPeakChargingTimes_Times `protobuf_oneof:"times"` diff --git a/pkg/protocol/protobuf/signatures.proto b/pkg/protocol/protobuf/signatures.proto index b22a591..f9eb4d1 100644 --- a/pkg/protocol/protobuf/signatures.proto +++ b/pkg/protocol/protobuf/signatures.proto @@ -15,6 +15,8 @@ enum Tag TAG_COUNTER = 5; TAG_CHALLENGE = 6; TAG_FLAGS = 7; + TAG_REQUEST_HASH = 8; + TAG_FAULT = 9; TAG_END = 255; } @@ -25,6 +27,7 @@ enum SignatureType SIGNATURE_TYPE_HMAC = 6; reserved 7; SIGNATURE_TYPE_HMAC_PERSONALIZED = 8; + SIGNATURE_TYPE_AES_GCM_RESPONSE = 9; } message KeyIdentity { @@ -43,6 +46,12 @@ message AES_GCM_Personalized_Signature_Data { bytes tag = 5; } +message AES_GCM_Response_Signature_Data { + bytes nonce = 1; + uint32 counter = 2; + bytes tag = 3; +} + message HMAC_Signature_Data { bytes tag = 1; } @@ -61,6 +70,7 @@ message SignatureData { AES_GCM_Personalized_Signature_Data AES_GCM_Personalized_data = 5; HMAC_Signature_Data session_info_tag = 6; HMAC_Personalized_Signature_Data HMAC_Personalized_data = 8; + AES_GCM_Response_Signature_Data AES_GCM_Response_data = 9; } } diff --git a/pkg/protocol/protobuf/signatures/signatures.pb.go b/pkg/protocol/protobuf/signatures/signatures.pb.go index 3aba59a..f2167a4 100644 --- a/pkg/protocol/protobuf/signatures/signatures.pb.go +++ b/pkg/protocol/protobuf/signatures/signatures.pb.go @@ -31,6 +31,8 @@ const ( Tag_TAG_COUNTER Tag = 5 Tag_TAG_CHALLENGE Tag = 6 Tag_TAG_FLAGS Tag = 7 + Tag_TAG_REQUEST_HASH Tag = 8 + Tag_TAG_FAULT Tag = 9 Tag_TAG_END Tag = 255 ) @@ -45,6 +47,8 @@ var ( 5: "TAG_COUNTER", 6: "TAG_CHALLENGE", 7: "TAG_FLAGS", + 8: "TAG_REQUEST_HASH", + 9: "TAG_FAULT", 255: "TAG_END", } Tag_value = map[string]int32{ @@ -56,6 +60,8 @@ var ( "TAG_COUNTER": 5, "TAG_CHALLENGE": 6, "TAG_FLAGS": 7, + "TAG_REQUEST_HASH": 8, + "TAG_FAULT": 9, "TAG_END": 255, } ) @@ -94,6 +100,7 @@ const ( SignatureType_SIGNATURE_TYPE_AES_GCM_PERSONALIZED SignatureType = 5 SignatureType_SIGNATURE_TYPE_HMAC SignatureType = 6 SignatureType_SIGNATURE_TYPE_HMAC_PERSONALIZED SignatureType = 8 + SignatureType_SIGNATURE_TYPE_AES_GCM_RESPONSE SignatureType = 9 ) // Enum value maps for SignatureType. @@ -103,12 +110,14 @@ var ( 5: "SIGNATURE_TYPE_AES_GCM_PERSONALIZED", 6: "SIGNATURE_TYPE_HMAC", 8: "SIGNATURE_TYPE_HMAC_PERSONALIZED", + 9: "SIGNATURE_TYPE_AES_GCM_RESPONSE", } SignatureType_value = map[string]int32{ "SIGNATURE_TYPE_AES_GCM": 0, "SIGNATURE_TYPE_AES_GCM_PERSONALIZED": 5, "SIGNATURE_TYPE_HMAC": 6, "SIGNATURE_TYPE_HMAC_PERSONALIZED": 8, + "SIGNATURE_TYPE_AES_GCM_RESPONSE": 9, } ) @@ -191,7 +200,6 @@ type KeyIdentity struct { unknownFields protoimpl.UnknownFields // Types that are assignable to IdentityType: - // // *KeyIdentity_PublicKey // *KeyIdentity_Handle IdentityType isKeyIdentity_IdentityType `protobuf_oneof:"identity_type"` @@ -345,6 +353,69 @@ func (x *AES_GCM_Personalized_Signature_Data) GetTag() []byte { return nil } +type AES_GCM_Response_Signature_Data struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Nonce []byte `protobuf:"bytes,1,opt,name=nonce,proto3" json:"nonce,omitempty"` + Counter uint32 `protobuf:"varint,2,opt,name=counter,proto3" json:"counter,omitempty"` + Tag []byte `protobuf:"bytes,3,opt,name=tag,proto3" json:"tag,omitempty"` +} + +func (x *AES_GCM_Response_Signature_Data) Reset() { + *x = AES_GCM_Response_Signature_Data{} + if protoimpl.UnsafeEnabled { + mi := &file_signatures_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AES_GCM_Response_Signature_Data) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AES_GCM_Response_Signature_Data) ProtoMessage() {} + +func (x *AES_GCM_Response_Signature_Data) ProtoReflect() protoreflect.Message { + mi := &file_signatures_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AES_GCM_Response_Signature_Data.ProtoReflect.Descriptor instead. +func (*AES_GCM_Response_Signature_Data) Descriptor() ([]byte, []int) { + return file_signatures_proto_rawDescGZIP(), []int{2} +} + +func (x *AES_GCM_Response_Signature_Data) GetNonce() []byte { + if x != nil { + return x.Nonce + } + return nil +} + +func (x *AES_GCM_Response_Signature_Data) GetCounter() uint32 { + if x != nil { + return x.Counter + } + return 0 +} + +func (x *AES_GCM_Response_Signature_Data) GetTag() []byte { + if x != nil { + return x.Tag + } + return nil +} + type HMAC_Signature_Data struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -356,7 +427,7 @@ type HMAC_Signature_Data struct { func (x *HMAC_Signature_Data) Reset() { *x = HMAC_Signature_Data{} if protoimpl.UnsafeEnabled { - mi := &file_signatures_proto_msgTypes[2] + mi := &file_signatures_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -369,7 +440,7 @@ func (x *HMAC_Signature_Data) String() string { func (*HMAC_Signature_Data) ProtoMessage() {} func (x *HMAC_Signature_Data) ProtoReflect() protoreflect.Message { - mi := &file_signatures_proto_msgTypes[2] + mi := &file_signatures_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -382,7 +453,7 @@ func (x *HMAC_Signature_Data) ProtoReflect() protoreflect.Message { // Deprecated: Use HMAC_Signature_Data.ProtoReflect.Descriptor instead. func (*HMAC_Signature_Data) Descriptor() ([]byte, []int) { - return file_signatures_proto_rawDescGZIP(), []int{2} + return file_signatures_proto_rawDescGZIP(), []int{3} } func (x *HMAC_Signature_Data) GetTag() []byte { @@ -406,7 +477,7 @@ type HMAC_Personalized_Signature_Data struct { func (x *HMAC_Personalized_Signature_Data) Reset() { *x = HMAC_Personalized_Signature_Data{} if protoimpl.UnsafeEnabled { - mi := &file_signatures_proto_msgTypes[3] + mi := &file_signatures_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -419,7 +490,7 @@ func (x *HMAC_Personalized_Signature_Data) String() string { func (*HMAC_Personalized_Signature_Data) ProtoMessage() {} func (x *HMAC_Personalized_Signature_Data) ProtoReflect() protoreflect.Message { - mi := &file_signatures_proto_msgTypes[3] + mi := &file_signatures_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -432,7 +503,7 @@ func (x *HMAC_Personalized_Signature_Data) ProtoReflect() protoreflect.Message { // Deprecated: Use HMAC_Personalized_Signature_Data.ProtoReflect.Descriptor instead. func (*HMAC_Personalized_Signature_Data) Descriptor() ([]byte, []int) { - return file_signatures_proto_rawDescGZIP(), []int{3} + return file_signatures_proto_rawDescGZIP(), []int{4} } func (x *HMAC_Personalized_Signature_Data) GetEpoch() []byte { @@ -470,17 +541,17 @@ type SignatureData struct { SignerIdentity *KeyIdentity `protobuf:"bytes,1,opt,name=signer_identity,json=signerIdentity,proto3" json:"signer_identity,omitempty"` // Types that are assignable to SigType: - // // *SignatureData_AES_GCM_PersonalizedData // *SignatureData_SessionInfoTag // *SignatureData_HMAC_PersonalizedData + // *SignatureData_AES_GCM_ResponseData SigType isSignatureData_SigType `protobuf_oneof:"sig_type"` } func (x *SignatureData) Reset() { *x = SignatureData{} if protoimpl.UnsafeEnabled { - mi := &file_signatures_proto_msgTypes[4] + mi := &file_signatures_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -493,7 +564,7 @@ func (x *SignatureData) String() string { func (*SignatureData) ProtoMessage() {} func (x *SignatureData) ProtoReflect() protoreflect.Message { - mi := &file_signatures_proto_msgTypes[4] + mi := &file_signatures_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -506,7 +577,7 @@ func (x *SignatureData) ProtoReflect() protoreflect.Message { // Deprecated: Use SignatureData.ProtoReflect.Descriptor instead. func (*SignatureData) Descriptor() ([]byte, []int) { - return file_signatures_proto_rawDescGZIP(), []int{4} + return file_signatures_proto_rawDescGZIP(), []int{5} } func (x *SignatureData) GetSignerIdentity() *KeyIdentity { @@ -544,6 +615,13 @@ func (x *SignatureData) GetHMAC_PersonalizedData() *HMAC_Personalized_Signature_ return nil } +func (x *SignatureData) GetAES_GCM_ResponseData() *AES_GCM_Response_Signature_Data { + if x, ok := x.GetSigType().(*SignatureData_AES_GCM_ResponseData); ok { + return x.AES_GCM_ResponseData + } + return nil +} + type isSignatureData_SigType interface { isSignatureData_SigType() } @@ -560,12 +638,18 @@ type SignatureData_HMAC_PersonalizedData struct { HMAC_PersonalizedData *HMAC_Personalized_Signature_Data `protobuf:"bytes,8,opt,name=HMAC_Personalized_data,json=HMACPersonalizedData,proto3,oneof"` } +type SignatureData_AES_GCM_ResponseData struct { + AES_GCM_ResponseData *AES_GCM_Response_Signature_Data `protobuf:"bytes,9,opt,name=AES_GCM_Response_data,json=AESGCMResponseData,proto3,oneof"` +} + func (*SignatureData_AES_GCM_PersonalizedData) isSignatureData_SigType() {} func (*SignatureData_SessionInfoTag) isSignatureData_SigType() {} func (*SignatureData_HMAC_PersonalizedData) isSignatureData_SigType() {} +func (*SignatureData_AES_GCM_ResponseData) isSignatureData_SigType() {} + type GetSessionInfoRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -577,7 +661,7 @@ type GetSessionInfoRequest struct { func (x *GetSessionInfoRequest) Reset() { *x = GetSessionInfoRequest{} if protoimpl.UnsafeEnabled { - mi := &file_signatures_proto_msgTypes[5] + mi := &file_signatures_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -590,7 +674,7 @@ func (x *GetSessionInfoRequest) String() string { func (*GetSessionInfoRequest) ProtoMessage() {} func (x *GetSessionInfoRequest) ProtoReflect() protoreflect.Message { - mi := &file_signatures_proto_msgTypes[5] + mi := &file_signatures_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -603,7 +687,7 @@ func (x *GetSessionInfoRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSessionInfoRequest.ProtoReflect.Descriptor instead. func (*GetSessionInfoRequest) Descriptor() ([]byte, []int) { - return file_signatures_proto_rawDescGZIP(), []int{5} + return file_signatures_proto_rawDescGZIP(), []int{6} } func (x *GetSessionInfoRequest) GetKeyIdentity() *KeyIdentity { @@ -629,7 +713,7 @@ type SessionInfo struct { func (x *SessionInfo) Reset() { *x = SessionInfo{} if protoimpl.UnsafeEnabled { - mi := &file_signatures_proto_msgTypes[6] + mi := &file_signatures_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -642,7 +726,7 @@ func (x *SessionInfo) String() string { func (*SessionInfo) ProtoMessage() {} func (x *SessionInfo) ProtoReflect() protoreflect.Message { - mi := &file_signatures_proto_msgTypes[6] + mi := &file_signatures_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -655,7 +739,7 @@ func (x *SessionInfo) ProtoReflect() protoreflect.Message { // Deprecated: Use SessionInfo.ProtoReflect.Descriptor instead. func (*SessionInfo) Descriptor() ([]byte, []int) { - return file_signatures_proto_rawDescGZIP(), []int{6} + return file_signatures_proto_rawDescGZIP(), []int{7} } func (x *SessionInfo) GetCounter() uint32 { @@ -720,95 +804,112 @@ var file_signatures_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x07, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x10, 0x0a, 0x03, - 0x74, 0x61, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x27, - 0x0a, 0x13, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x5f, 0x44, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x83, 0x01, 0x0a, 0x20, 0x48, 0x4d, 0x41, 0x43, - 0x5f, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x53, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x44, 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, - 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x65, 0x70, 0x6f, - 0x63, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, - 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x07, - 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x74, - 0x61, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x84, 0x03, - 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x44, 0x61, 0x74, 0x61, 0x12, - 0x40, 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x52, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x12, 0x6c, 0x0a, 0x19, 0x41, 0x45, 0x53, 0x5f, 0x47, 0x43, 0x4d, 0x5f, 0x50, 0x65, 0x72, - 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x73, 0x2e, 0x41, 0x45, 0x53, 0x5f, 0x47, 0x43, 0x4d, 0x5f, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, - 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x5f, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x16, 0x41, 0x45, 0x53, 0x47, 0x43, 0x4d, 0x50, - 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x12, - 0x4b, 0x0a, 0x10, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, - 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x53, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x0e, 0x73, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x61, 0x67, 0x12, 0x64, 0x0a, 0x16, - 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, - 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x53, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x50, + 0x74, 0x61, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x63, + 0x0a, 0x1f, 0x41, 0x45, 0x53, 0x5f, 0x47, 0x43, 0x4d, 0x5f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x5f, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, + 0x74, 0x61, 0x67, 0x22, 0x27, 0x0a, 0x13, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x53, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x44, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, + 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x83, 0x01, 0x0a, + 0x20, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x69, 0x7a, + 0x65, 0x64, 0x5f, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x07, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41, 0x74, + 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x74, + 0x61, 0x67, 0x22, 0xe6, 0x03, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x40, 0x0a, 0x0f, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x5f, 0x69, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, + 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x4b, 0x65, 0x79, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x6c, 0x0a, 0x19, 0x41, 0x45, 0x53, 0x5f, 0x47, 0x43, + 0x4d, 0x5f, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x53, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x41, 0x45, 0x53, 0x5f, 0x47, 0x43, 0x4d, 0x5f, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x53, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x14, 0x48, 0x4d, - 0x41, 0x43, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x44, 0x61, - 0x74, 0x61, 0x42, 0x0a, 0x0a, 0x08, 0x73, 0x69, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x4a, 0x04, - 0x08, 0x07, 0x10, 0x08, 0x22, 0x53, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, - 0x0c, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, - 0x2e, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x0b, 0x6b, 0x65, - 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x22, 0xcb, 0x01, 0x0a, 0x0b, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x6f, 0x63, 0x6b, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x07, 0x52, 0x09, 0x63, 0x6c, 0x6f, - 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x49, 0x6e, 0x66, 0x6f, - 0x5f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x16, 0x0a, 0x06, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x06, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2a, 0xaa, 0x01, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, - 0x16, 0x0a, 0x12, 0x54, 0x41, 0x47, 0x5f, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x54, 0x55, 0x52, 0x45, - 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x41, 0x47, 0x5f, 0x44, - 0x4f, 0x4d, 0x41, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x41, 0x47, 0x5f, 0x50, - 0x45, 0x52, 0x53, 0x4f, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, - 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x41, 0x47, 0x5f, 0x45, 0x50, 0x4f, 0x43, 0x48, 0x10, 0x03, 0x12, - 0x12, 0x0a, 0x0e, 0x54, 0x41, 0x47, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x53, 0x5f, 0x41, - 0x54, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x41, 0x47, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, - 0x45, 0x52, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x54, 0x41, 0x47, 0x5f, 0x43, 0x48, 0x41, 0x4c, - 0x4c, 0x45, 0x4e, 0x47, 0x45, 0x10, 0x06, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x41, 0x47, 0x5f, 0x46, - 0x4c, 0x41, 0x47, 0x53, 0x10, 0x07, 0x12, 0x0c, 0x0a, 0x07, 0x54, 0x41, 0x47, 0x5f, 0x45, 0x4e, - 0x44, 0x10, 0xff, 0x01, 0x2a, 0x99, 0x01, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x54, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x16, 0x41, 0x45, + 0x53, 0x47, 0x43, 0x4d, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x4b, 0x0a, 0x10, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, + 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x48, 0x4d, 0x41, 0x43, + 0x5f, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x44, 0x61, 0x74, 0x61, 0x48, + 0x00, 0x52, 0x0e, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x54, 0x61, + 0x67, 0x12, 0x64, 0x0a, 0x16, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, + 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2c, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x48, + 0x4d, 0x41, 0x43, 0x5f, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x64, + 0x5f, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x44, 0x61, 0x74, 0x61, 0x48, + 0x00, 0x52, 0x14, 0x48, 0x4d, 0x41, 0x43, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x69, + 0x7a, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61, 0x12, 0x60, 0x0a, 0x15, 0x41, 0x45, 0x53, 0x5f, 0x47, + 0x43, 0x4d, 0x5f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x2e, 0x41, 0x45, 0x53, 0x5f, 0x47, 0x43, 0x4d, 0x5f, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x5f, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x44, + 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x12, 0x41, 0x45, 0x53, 0x47, 0x43, 0x4d, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x44, 0x61, 0x74, 0x61, 0x42, 0x0a, 0x0a, 0x08, 0x73, 0x69, 0x67, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x22, 0x53, 0x0a, 0x15, 0x47, + 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x3a, 0x0a, 0x0c, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x53, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x52, 0x0b, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x22, 0xcb, 0x01, 0x0a, 0x0b, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, + 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, + 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, + 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x1d, + 0x0a, 0x0a, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x07, 0x52, 0x09, 0x63, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x37, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, + 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x5f, 0x49, 0x6e, 0x66, 0x6f, 0x5f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x06, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x2a, 0xcf, + 0x01, 0x0a, 0x03, 0x54, 0x61, 0x67, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x41, 0x47, 0x5f, 0x53, 0x49, + 0x47, 0x4e, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x0e, + 0x0a, 0x0a, 0x54, 0x41, 0x47, 0x5f, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e, 0x10, 0x01, 0x12, 0x17, + 0x0a, 0x13, 0x54, 0x41, 0x47, 0x5f, 0x50, 0x45, 0x52, 0x53, 0x4f, 0x4e, 0x41, 0x4c, 0x49, 0x5a, + 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x41, 0x47, 0x5f, 0x45, + 0x50, 0x4f, 0x43, 0x48, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x54, 0x41, 0x47, 0x5f, 0x45, 0x58, + 0x50, 0x49, 0x52, 0x45, 0x53, 0x5f, 0x41, 0x54, 0x10, 0x04, 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x41, + 0x47, 0x5f, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x45, 0x52, 0x10, 0x05, 0x12, 0x11, 0x0a, 0x0d, 0x54, + 0x41, 0x47, 0x5f, 0x43, 0x48, 0x41, 0x4c, 0x4c, 0x45, 0x4e, 0x47, 0x45, 0x10, 0x06, 0x12, 0x0d, + 0x0a, 0x09, 0x54, 0x41, 0x47, 0x5f, 0x46, 0x4c, 0x41, 0x47, 0x53, 0x10, 0x07, 0x12, 0x14, 0x0a, + 0x10, 0x54, 0x41, 0x47, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x5f, 0x48, 0x41, 0x53, + 0x48, 0x10, 0x08, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x41, 0x47, 0x5f, 0x46, 0x41, 0x55, 0x4c, 0x54, + 0x10, 0x09, 0x12, 0x0c, 0x0a, 0x07, 0x54, 0x41, 0x47, 0x5f, 0x45, 0x4e, 0x44, 0x10, 0xff, 0x01, + 0x2a, 0xbe, 0x01, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, + 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x45, 0x53, 0x5f, 0x47, 0x43, 0x4d, 0x10, 0x00, 0x12, 0x27, + 0x0a, 0x23, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, + 0x5f, 0x41, 0x45, 0x53, 0x5f, 0x47, 0x43, 0x4d, 0x5f, 0x50, 0x45, 0x52, 0x53, 0x4f, 0x4e, 0x41, + 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x05, 0x12, 0x17, 0x0a, 0x13, 0x53, 0x49, 0x47, 0x4e, 0x41, + 0x54, 0x55, 0x52, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x10, 0x06, + 0x12, 0x24, 0x0a, 0x20, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x54, 0x59, + 0x50, 0x45, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x50, 0x45, 0x52, 0x53, 0x4f, 0x4e, 0x41, 0x4c, + 0x49, 0x5a, 0x45, 0x44, 0x10, 0x08, 0x12, 0x23, 0x0a, 0x1f, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x45, 0x53, 0x5f, 0x47, 0x43, 0x4d, - 0x10, 0x00, 0x12, 0x27, 0x0a, 0x23, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x45, 0x53, 0x5f, 0x47, 0x43, 0x4d, 0x5f, 0x50, 0x45, 0x52, - 0x53, 0x4f, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x05, 0x12, 0x17, 0x0a, 0x13, 0x53, - 0x49, 0x47, 0x4e, 0x41, 0x54, 0x55, 0x52, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4d, - 0x41, 0x43, 0x10, 0x06, 0x12, 0x24, 0x0a, 0x20, 0x53, 0x49, 0x47, 0x4e, 0x41, 0x54, 0x55, 0x52, - 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x48, 0x4d, 0x41, 0x43, 0x5f, 0x50, 0x45, 0x52, 0x53, - 0x4f, 0x4e, 0x41, 0x4c, 0x49, 0x5a, 0x45, 0x44, 0x10, 0x08, 0x22, 0x04, 0x08, 0x07, 0x10, 0x07, - 0x2a, 0x5f, 0x0a, 0x13, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x49, 0x6e, 0x66, 0x6f, - 0x5f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x53, 0x45, 0x53, 0x53, 0x49, - 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x4f, - 0x4b, 0x10, 0x00, 0x12, 0x2c, 0x0a, 0x28, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x49, - 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4e, - 0x4f, 0x54, 0x5f, 0x4f, 0x4e, 0x5f, 0x57, 0x48, 0x49, 0x54, 0x45, 0x4c, 0x49, 0x53, 0x54, 0x10, - 0x01, 0x42, 0x69, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x74, 0x65, 0x73, 0x6c, 0x61, 0x2e, 0x67, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x73, 0x5a, 0x47, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x74, 0x65, 0x73, 0x6c, 0x61, 0x6d, 0x6f, 0x74, 0x6f, 0x72, 0x73, 0x2f, 0x76, 0x65, 0x68, 0x69, - 0x63, 0x6c, 0x65, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x70, 0x6b, 0x67, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x09, 0x22, 0x04, 0x08, 0x07, 0x10, + 0x07, 0x2a, 0x5f, 0x0a, 0x13, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x49, 0x6e, 0x66, + 0x6f, 0x5f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x53, 0x45, 0x53, 0x53, + 0x49, 0x4f, 0x4e, 0x5f, 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x2c, 0x0a, 0x28, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x5f, + 0x49, 0x4e, 0x46, 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x4b, 0x45, 0x59, 0x5f, + 0x4e, 0x4f, 0x54, 0x5f, 0x4f, 0x4e, 0x5f, 0x57, 0x48, 0x49, 0x54, 0x45, 0x4c, 0x49, 0x53, 0x54, + 0x10, 0x01, 0x42, 0x69, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x74, 0x65, 0x73, 0x6c, 0x61, 0x2e, + 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x5a, 0x47, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x74, 0x65, 0x73, 0x6c, 0x61, 0x6d, 0x6f, 0x74, 0x6f, 0x72, 0x73, 0x2f, 0x76, 0x65, 0x68, + 0x69, 0x63, 0x6c, 0x65, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x70, 0x6b, 0x67, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -824,31 +925,33 @@ func file_signatures_proto_rawDescGZIP() []byte { } var file_signatures_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_signatures_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_signatures_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_signatures_proto_goTypes = []interface{}{ (Tag)(0), // 0: Signatures.Tag (SignatureType)(0), // 1: Signatures.SignatureType (Session_Info_Status)(0), // 2: Signatures.Session_Info_Status (*KeyIdentity)(nil), // 3: Signatures.KeyIdentity (*AES_GCM_Personalized_Signature_Data)(nil), // 4: Signatures.AES_GCM_Personalized_Signature_Data - (*HMAC_Signature_Data)(nil), // 5: Signatures.HMAC_Signature_Data - (*HMAC_Personalized_Signature_Data)(nil), // 6: Signatures.HMAC_Personalized_Signature_Data - (*SignatureData)(nil), // 7: Signatures.SignatureData - (*GetSessionInfoRequest)(nil), // 8: Signatures.GetSessionInfoRequest - (*SessionInfo)(nil), // 9: Signatures.SessionInfo + (*AES_GCM_Response_Signature_Data)(nil), // 5: Signatures.AES_GCM_Response_Signature_Data + (*HMAC_Signature_Data)(nil), // 6: Signatures.HMAC_Signature_Data + (*HMAC_Personalized_Signature_Data)(nil), // 7: Signatures.HMAC_Personalized_Signature_Data + (*SignatureData)(nil), // 8: Signatures.SignatureData + (*GetSessionInfoRequest)(nil), // 9: Signatures.GetSessionInfoRequest + (*SessionInfo)(nil), // 10: Signatures.SessionInfo } var file_signatures_proto_depIdxs = []int32{ 3, // 0: Signatures.SignatureData.signer_identity:type_name -> Signatures.KeyIdentity 4, // 1: Signatures.SignatureData.AES_GCM_Personalized_data:type_name -> Signatures.AES_GCM_Personalized_Signature_Data - 5, // 2: Signatures.SignatureData.session_info_tag:type_name -> Signatures.HMAC_Signature_Data - 6, // 3: Signatures.SignatureData.HMAC_Personalized_data:type_name -> Signatures.HMAC_Personalized_Signature_Data - 3, // 4: Signatures.GetSessionInfoRequest.key_identity:type_name -> Signatures.KeyIdentity - 2, // 5: Signatures.SessionInfo.status:type_name -> Signatures.Session_Info_Status - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 6, // 2: Signatures.SignatureData.session_info_tag:type_name -> Signatures.HMAC_Signature_Data + 7, // 3: Signatures.SignatureData.HMAC_Personalized_data:type_name -> Signatures.HMAC_Personalized_Signature_Data + 5, // 4: Signatures.SignatureData.AES_GCM_Response_data:type_name -> Signatures.AES_GCM_Response_Signature_Data + 3, // 5: Signatures.GetSessionInfoRequest.key_identity:type_name -> Signatures.KeyIdentity + 2, // 6: Signatures.SessionInfo.status:type_name -> Signatures.Session_Info_Status + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_signatures_proto_init() } @@ -882,7 +985,7 @@ func file_signatures_proto_init() { } } file_signatures_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HMAC_Signature_Data); i { + switch v := v.(*AES_GCM_Response_Signature_Data); i { case 0: return &v.state case 1: @@ -894,7 +997,7 @@ func file_signatures_proto_init() { } } file_signatures_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*HMAC_Personalized_Signature_Data); i { + switch v := v.(*HMAC_Signature_Data); i { case 0: return &v.state case 1: @@ -906,7 +1009,7 @@ func file_signatures_proto_init() { } } file_signatures_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SignatureData); i { + switch v := v.(*HMAC_Personalized_Signature_Data); i { case 0: return &v.state case 1: @@ -918,7 +1021,7 @@ func file_signatures_proto_init() { } } file_signatures_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSessionInfoRequest); i { + switch v := v.(*SignatureData); i { case 0: return &v.state case 1: @@ -930,6 +1033,18 @@ func file_signatures_proto_init() { } } file_signatures_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetSessionInfoRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_signatures_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SessionInfo); i { case 0: return &v.state @@ -946,10 +1061,11 @@ func file_signatures_proto_init() { (*KeyIdentity_PublicKey)(nil), (*KeyIdentity_Handle)(nil), } - file_signatures_proto_msgTypes[4].OneofWrappers = []interface{}{ + file_signatures_proto_msgTypes[5].OneofWrappers = []interface{}{ (*SignatureData_AES_GCM_PersonalizedData)(nil), (*SignatureData_SessionInfoTag)(nil), (*SignatureData_HMAC_PersonalizedData)(nil), + (*SignatureData_AES_GCM_ResponseData)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -957,7 +1073,7 @@ func file_signatures_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_signatures_proto_rawDesc, NumEnums: 3, - NumMessages: 7, + NumMessages: 8, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/protocol/protobuf/universal_message.proto b/pkg/protocol/protobuf/universal_message.proto index 4b46739..146ca3f 100644 --- a/pkg/protocol/protobuf/universal_message.proto +++ b/pkg/protocol/protobuf/universal_message.proto @@ -69,6 +69,7 @@ message SessionInfoRequest enum Flags { FLAG_USER_COMMAND = 0; + FLAG_ENCRYPT_RESPONSE = 1; } message RoutableMessage { diff --git a/pkg/protocol/protobuf/universalmessage/universal_message.pb.go b/pkg/protocol/protobuf/universalmessage/universal_message.pb.go index 6a7d807..3d6af71 100644 --- a/pkg/protocol/protobuf/universalmessage/universal_message.pb.go +++ b/pkg/protocol/protobuf/universalmessage/universal_message.pb.go @@ -234,16 +234,19 @@ func (MessageFault_E) EnumDescriptor() ([]byte, []int) { type Flags int32 const ( - Flags_FLAG_USER_COMMAND Flags = 0 + Flags_FLAG_USER_COMMAND Flags = 0 + Flags_FLAG_ENCRYPT_RESPONSE Flags = 1 ) // Enum value maps for Flags. var ( Flags_name = map[int32]string{ 0: "FLAG_USER_COMMAND", + 1: "FLAG_ENCRYPT_RESPONSE", } Flags_value = map[string]int32{ - "FLAG_USER_COMMAND": 0, + "FLAG_USER_COMMAND": 0, + "FLAG_ENCRYPT_RESPONSE": 1, } ) @@ -280,7 +283,6 @@ type Destination struct { unknownFields protoimpl.UnknownFields // Types that are assignable to SubDestination: - // // *Destination_Domain // *Destination_RoutingAddress SubDestination isDestination_SubDestination `protobuf_oneof:"sub_destination"` @@ -473,13 +475,11 @@ type RoutableMessage struct { ToDestination *Destination `protobuf:"bytes,6,opt,name=to_destination,json=toDestination,proto3" json:"to_destination,omitempty"` FromDestination *Destination `protobuf:"bytes,7,opt,name=from_destination,json=fromDestination,proto3" json:"from_destination,omitempty"` // Types that are assignable to Payload: - // // *RoutableMessage_ProtobufMessageAsBytes // *RoutableMessage_SessionInfoRequest // *RoutableMessage_SessionInfo Payload isRoutableMessage_Payload `protobuf_oneof:"payload"` // Types that are assignable to SubSigData: - // // *RoutableMessage_SignatureData SubSigData isRoutableMessage_SubSigData `protobuf_oneof:"sub_sigData"` SignedMessageStatus *MessageStatus `protobuf:"bytes,12,opt,name=signedMessageStatus,proto3" json:"signedMessageStatus,omitempty"` @@ -782,17 +782,18 @@ var file_universal_message_proto_rawDesc = []byte{ 0x46, 0x41, 0x55, 0x4c, 0x54, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x49, 0x52, 0x45, 0x53, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x44, 0x45, 0x4e, 0x54, 0x49, 0x41, 0x4c, 0x53, - 0x10, 0x17, 0x2a, 0x1e, 0x0a, 0x05, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x15, 0x0a, 0x11, 0x46, + 0x10, 0x17, 0x2a, 0x39, 0x0a, 0x05, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x15, 0x0a, 0x11, 0x46, 0x4c, 0x41, 0x47, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x43, 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0x44, - 0x10, 0x00, 0x42, 0x75, 0x0a, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x74, 0x65, 0x73, 0x6c, 0x61, 0x2e, - 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, - 0x73, 0x61, 0x6c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x65, 0x73, 0x6c, 0x61, 0x6d, 0x6f, 0x74, 0x6f, - 0x72, 0x73, 0x2f, 0x76, 0x65, 0x68, 0x69, 0x63, 0x6c, 0x65, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, - 0x61, 0x6c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x46, 0x4c, 0x41, 0x47, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, + 0x50, 0x54, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f, 0x4e, 0x53, 0x45, 0x10, 0x01, 0x42, 0x75, 0x0a, + 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x74, 0x65, 0x73, 0x6c, 0x61, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, + 0x61, 0x74, 0x65, 0x64, 0x2e, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x74, 0x65, 0x73, 0x6c, 0x61, 0x6d, 0x6f, 0x74, 0x6f, 0x72, 0x73, 0x2f, 0x76, 0x65, + 0x68, 0x69, 0x63, 0x6c, 0x65, 0x2d, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x2f, 0x70, 0x6b, + 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x75, 0x6e, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6c, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/protocol/protobuf/vcsec/vcsec.pb.go b/pkg/protocol/protobuf/vcsec/vcsec.pb.go index 5585b30..a0d7abf 100644 --- a/pkg/protocol/protobuf/vcsec/vcsec.pb.go +++ b/pkg/protocol/protobuf/vcsec/vcsec.pb.go @@ -1143,7 +1143,6 @@ type InformationRequest struct { InformationRequestType InformationRequestType `protobuf:"varint,1,opt,name=informationRequestType,proto3,enum=VCSEC.InformationRequestType" json:"informationRequestType,omitempty"` // Types that are assignable to Key: - // // *InformationRequest_KeyId // *InformationRequest_PublicKey // *InformationRequest_Slot @@ -1411,7 +1410,6 @@ type ReplaceKey struct { unknownFields protoimpl.UnknownFields // Types that are assignable to KeyToReplace: - // // *ReplaceKey_PublicKeyToReplace // *ReplaceKey_SlotToReplace KeyToReplace isReplaceKey_KeyToReplace `protobuf_oneof:"keyToReplace"` @@ -1516,7 +1514,6 @@ type WhitelistOperation struct { unknownFields protoimpl.UnknownFields // Types that are assignable to SubMessage: - // // *WhitelistOperation_AddPublicKeyToWhitelist // *WhitelistOperation_RemovePublicKeyFromWhitelist // *WhitelistOperation_AddPermissionsToPublicKey @@ -1836,7 +1833,6 @@ type CommandStatus struct { OperationStatus OperationStatus_E `protobuf:"varint,1,opt,name=operationStatus,proto3,enum=VCSEC.OperationStatus_E" json:"operationStatus,omitempty"` // Types that are assignable to SubMessage: - // // *CommandStatus_SignedMessageStatus // *CommandStatus_WhitelistOperationStatus SubMessage isCommandStatus_SubMessage `protobuf_oneof:"sub_message"` @@ -1924,7 +1920,6 @@ type UnsignedMessage struct { unknownFields protoimpl.UnknownFields // Types that are assignable to SubMessage: - // // *UnsignedMessage_InformationRequest // *UnsignedMessage_RKEAction // *UnsignedMessage_ClosureMoveRequest @@ -2262,7 +2257,6 @@ type FromVCSECMessage struct { unknownFields protoimpl.UnknownFields // Types that are assignable to SubMessage: - // // *FromVCSECMessage_VehicleStatus // *FromVCSECMessage_CommandStatus // *FromVCSECMessage_WhitelistInfo diff --git a/pkg/protocol/protocol.md b/pkg/protocol/protocol.md index 5009d85..a3c0069 100644 --- a/pkg/protocol/protocol.md +++ b/pkg/protocol/protocol.md @@ -59,10 +59,10 @@ above are for printability. * The vehicle sets `signedMessageStatus` to indicate protocol-layer errors. [Application-layer errors](#Response-handling) appear in `protobuf_message_as_bytes`. - * The `flags` field is a bit mask of `universal_message.Flags` values. There - is currently no need for clients to use this field; future versions of the - protocol may use this field to add authenticated data that is discarded by - older vehicles. + * The `flags` field is a bit mask of `universal_message.Flags` values. + Vehicles authenticate this value, but ignore unrecognized bits. Clients + should always set the `FLAG_ENCRYPT_RESPONSE` bit, which instructs vehicles + with compatible firmware (2024.38+) to encrypt the response. ## Decoding messages @@ -275,7 +275,7 @@ in the remainder of the document. *Do not use this key except to debug implementations using the test vectors included in these documents. Since the private key is published, enrolling the -public key on a vehicle may result in unauthorized access. +public key on a vehicle may result in unauthorized access.* In `vehicle.key`: @@ -464,7 +464,7 @@ fceb679ee7bca756fcd441bf238bf2f338629b41d9eb9c67be1b32c9672ce300 --- -Next, the client [serializes the following metadata](#Metadata serialization): +Next, the client [serializes the following metadata](#Metadata-serialization): * `TAG_SIGNATURE_TYPE`: `Signatures.SIGNATURE_TYPE_HMAC` * `TAG_PERSONALIZATION`: `VIN` @@ -534,6 +534,7 @@ The client serializes the following metadata values into a string `M`: | Epoch | `Signatures.TAG_EPOCH` | Copied from `session_info.epoch` | | Expiration time| `Signatures.TAG_EXPIRES_AT` | Time in seconds according to domain's clock | | Counter | `Signatures.TAG_COUNTER` | Monotonic counter, initially `session_info.counter` | +| Flags | `Signatures.TAG_FLAGS` | Request flag bit mask. Only included if non-zero. | Setting the expiration time requires the client to track the difference between the domain's clock and the local clock. Note that each domain has its own clock. @@ -731,6 +732,65 @@ Error codes and their remediation are summarized in [universal_message.proto](protobuf/universal_message.proto). See comments in the `MessageFault_E` definition. +### Response decryption + +If the client set the `FLAG_ENCRYPT_RESPONSE`, then vehicles running supported +firmware (2024.38+) will encrypt the payload. Vehicles running previous +firmware versions will ignore this flag, so clients do not need to check the +vehicle's firmware version before setting it. + +If the response includes a `signature_data.AES_GCM_Response_data` field, then +the `protobuf_message_as_bytes` payload is encrypted. Otherwise, the payload is +plaintext. + +#### Request hash + +The client must compute the "request hash" of each request it sends. This value +will be used when decrypting the response. The request hash is a single byte +that encodes the authentication method used by the request (either +`Signatures.SIGNATURE_TYPE_HMAC_PERSONALIZED` or +`Signatures.SIGNATURE_TYPE_AES_GCM_PERSONALIZED`) followed by the request's +authentication tag. + + * If the request uses AES-GCM encryption, then the tag is the request's + `signature_data.AES_GCM_Personalized_Signature_Data.tag` field. + * If the request uses HMAC-SHA256 authentication, then the tag is the + request's `signature_data.HMAC_PersonalizedData.tag` field. + +If the request is sent to the Vehicle Security domain, then the request hash is +truncated to 17 bytes (i.e., a single byte encoding the authentication method +and the first 16 bytes of the MAC). Truncation only affects HMAC-SHA256 +requests, since AES-GCM tags are only 16 bytes. + +#### Counter verification + +The client must verify that the counter value contained in the response has +not been previously used in a response for the same request. Note that if a +request does trigger multiple responses, responses may be received out of +order. + +Clients that fail to implement this check are vulnerable to replay attacks. + +#### Response metadata + +To decrypt the response, use AES-GCM with the shared key `K` defined above, the +nonce and tag supplied in the response, and the associated authenticated data +field obtained by [serializing](#Metadata serialization) the following metadata: + +| Value | Tag | Description | +| ----- | --- | ----------- | +| Signature type | `Signatures.TAG_SIGNATURE_TYPE` | `Signatures.SIGNATURE_TYPE_AES_GCM_RESPONSE` | +| Domain | `Signatures.TAG_DOMAIN` | Typically `UniversalMesasge.DOMAIN_VEHICLE_SECURITY` or `UniversalMessage.DOMAIN_INFOTAINMENT` | +| VIN | `Signatures.TAG_PERSONALIZATION`| 17-character vehicle identification number | +| Counter | `Signatures.TAG_COUNTER` | Monotonic counter, initially `session_info.counter` | +| Flags | `Signatures.TAG_FLAGS` | Flags field of the response, not the request | +| Request Hash | `Signatures.TAG_REQUEST_HASH` | See [Request hash](#request-hash) | +| Fault | `Signatures.TAG_FAULT` | Message fault of the response | + +Note that the Flags field is always included in the response metadata, whereas +for outgoing requests the flags are only included if non-zero. Encode the fault +as a 32-bit big-endian integer. + ### Infotainment application-layer responses If a reply comes from the Infotainment domain, the client should parse the diff --git a/pkg/vehicle/vehicle.go b/pkg/vehicle/vehicle.go index dc23c80..49c051f 100644 --- a/pkg/vehicle/vehicle.go +++ b/pkg/vehicle/vehicle.go @@ -18,6 +18,9 @@ import ( universal "github.com/teslamotors/vehicle-command/pkg/protocol/protobuf/universalmessage" ) +// DefaultFlags is a bitmask that controls what flags are set on requests. +var DefaultFlags = uint32(1 << universal.Flags_FLAG_ENCRYPT_RESPONSE) + var ( // ErrNoFleetAPIConnection indicates the client attempted to send a command that terminates on // Tesla's backend (rather than a vehicle), but the Vehicle Connection does not use connector/inet. @@ -59,8 +62,9 @@ type sender interface { // A Vehicle represents a Tesla vehicle. type Vehicle struct { + Flags uint32 + dispatcher sender - Flags uint32 vin string conn connector.Connector @@ -77,6 +81,7 @@ func NewVehicle(conn connector.Connector, privateKey authentication.ECDHPrivateK } vin := conn.VIN() vehicle := &Vehicle{ + Flags: DefaultFlags, dispatcher: dispatch, vin: vin, conn: conn,