diff --git a/gen/go/v1sync/syncservice.pb.go b/gen/go/v1sync/syncservice.pb.go index 51c23f79..87295d4a 100644 --- a/gen/go/v1sync/syncservice.pb.go +++ b/gen/go/v1sync/syncservice.pb.go @@ -918,6 +918,8 @@ type SyncStreamItem struct { // *SyncStreamItem_RequestLog // *SyncStreamItem_ReceiveLogData // *SyncStreamItem_Throttle + // *SyncStreamItem_EstablishSharedSecret + // *SyncStreamItem_Encrypted Action isSyncStreamItem_Action `protobuf_oneof:"action"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -1077,6 +1079,24 @@ func (x *SyncStreamItem) GetThrottle() *SyncStreamItem_SyncActionThrottle { return nil } +func (x *SyncStreamItem) GetEstablishSharedSecret() *SyncStreamItem_SyncEstablishSharedSecret { + if x != nil { + if x, ok := x.Action.(*SyncStreamItem_EstablishSharedSecret); ok { + return x.EstablishSharedSecret + } + } + return nil +} + +func (x *SyncStreamItem) GetEncrypted() *SyncStreamItem_SyncActionEncrypted { + if x != nil { + if x, ok := x.Action.(*SyncStreamItem_Encrypted); ok { + return x.Encrypted + } + } + return nil +} + type isSyncStreamItem_Action interface { isSyncStreamItem_Action() } @@ -1133,6 +1153,14 @@ type SyncStreamItem_Throttle struct { Throttle *SyncStreamItem_SyncActionThrottle `protobuf:"bytes,1000,opt,name=throttle,proto3,oneof"` } +type SyncStreamItem_EstablishSharedSecret struct { + EstablishSharedSecret *SyncStreamItem_SyncEstablishSharedSecret `protobuf:"bytes,2,opt,name=establish_shared_secret,json=establishSharedSecret,proto3,oneof"` +} + +type SyncStreamItem_Encrypted struct { + Encrypted *SyncStreamItem_SyncActionEncrypted `protobuf:"bytes,5,opt,name=encrypted,proto3,oneof"` +} + func (*SyncStreamItem_SignedMessage) isSyncStreamItem_Action() {} func (*SyncStreamItem_Handshake) isSyncStreamItem_Action() {} @@ -1159,6 +1187,10 @@ func (*SyncStreamItem_ReceiveLogData) isSyncStreamItem_Action() {} func (*SyncStreamItem_Throttle) isSyncStreamItem_Action() {} +func (*SyncStreamItem_EstablishSharedSecret) isSyncStreamItem_Action() {} + +func (*SyncStreamItem_Encrypted) isSyncStreamItem_Action() {} + type SyncStreamItem_SyncActionHandshake struct { state protoimpl.MessageState `protogen:"open.v1"` ProtocolVersion int64 `protobuf:"varint,1,opt,name=protocol_version,json=protocolVersion,proto3" json:"protocol_version,omitempty"` @@ -1227,6 +1259,60 @@ func (x *SyncStreamItem_SyncActionHandshake) GetPairingSecret() string { return "" } +// SyncActionEncrypted wraps an encrypted SyncStreamItem. +// After ECDH key exchange, all subsequent messages are sent inside this envelope. +type SyncStreamItem_SyncActionEncrypted struct { + state protoimpl.MessageState `protogen:"open.v1"` + Nonce []byte `protobuf:"bytes,1,opt,name=nonce,proto3" json:"nonce,omitempty"` // 12-byte GCM nonce + Ciphertext []byte `protobuf:"bytes,2,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"` // AES-256-GCM(serialized SyncStreamItem) + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SyncStreamItem_SyncActionEncrypted) Reset() { + *x = SyncStreamItem_SyncActionEncrypted{} + mi := &file_v1sync_syncservice_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SyncStreamItem_SyncActionEncrypted) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncStreamItem_SyncActionEncrypted) ProtoMessage() {} + +func (x *SyncStreamItem_SyncActionEncrypted) ProtoReflect() protoreflect.Message { + mi := &file_v1sync_syncservice_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SyncStreamItem_SyncActionEncrypted.ProtoReflect.Descriptor instead. +func (*SyncStreamItem_SyncActionEncrypted) Descriptor() ([]byte, []int) { + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 1} +} + +func (x *SyncStreamItem_SyncActionEncrypted) GetNonce() []byte { + if x != nil { + return x.Nonce + } + return nil +} + +func (x *SyncStreamItem_SyncActionEncrypted) GetCiphertext() []byte { + if x != nil { + return x.Ciphertext + } + return nil +} + // SyncActionHeartbeat is sent periodically to keep the connection alive. type SyncStreamItem_SyncActionHeartbeat struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -1236,7 +1322,7 @@ type SyncStreamItem_SyncActionHeartbeat struct { func (x *SyncStreamItem_SyncActionHeartbeat) Reset() { *x = SyncStreamItem_SyncActionHeartbeat{} - mi := &file_v1sync_syncservice_proto_msgTypes[15] + mi := &file_v1sync_syncservice_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1248,7 +1334,7 @@ func (x *SyncStreamItem_SyncActionHeartbeat) String() string { func (*SyncStreamItem_SyncActionHeartbeat) ProtoMessage() {} func (x *SyncStreamItem_SyncActionHeartbeat) ProtoReflect() protoreflect.Message { - mi := &file_v1sync_syncservice_proto_msgTypes[15] + mi := &file_v1sync_syncservice_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1261,7 +1347,7 @@ func (x *SyncStreamItem_SyncActionHeartbeat) ProtoReflect() protoreflect.Message // Deprecated: Use SyncStreamItem_SyncActionHeartbeat.ProtoReflect.Descriptor instead. func (*SyncStreamItem_SyncActionHeartbeat) Descriptor() ([]byte, []int) { - return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 1} + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 2} } type SyncStreamItem_SyncActionReceiveConfig struct { @@ -1273,7 +1359,7 @@ type SyncStreamItem_SyncActionReceiveConfig struct { func (x *SyncStreamItem_SyncActionReceiveConfig) Reset() { *x = SyncStreamItem_SyncActionReceiveConfig{} - mi := &file_v1sync_syncservice_proto_msgTypes[16] + mi := &file_v1sync_syncservice_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1285,7 +1371,7 @@ func (x *SyncStreamItem_SyncActionReceiveConfig) String() string { func (*SyncStreamItem_SyncActionReceiveConfig) ProtoMessage() {} func (x *SyncStreamItem_SyncActionReceiveConfig) ProtoReflect() protoreflect.Message { - mi := &file_v1sync_syncservice_proto_msgTypes[16] + mi := &file_v1sync_syncservice_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1298,7 +1384,7 @@ func (x *SyncStreamItem_SyncActionReceiveConfig) ProtoReflect() protoreflect.Mes // Deprecated: Use SyncStreamItem_SyncActionReceiveConfig.ProtoReflect.Descriptor instead. func (*SyncStreamItem_SyncActionReceiveConfig) Descriptor() ([]byte, []int) { - return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 2} + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 3} } func (x *SyncStreamItem_SyncActionReceiveConfig) GetConfig() *RemoteConfig { @@ -1320,7 +1406,7 @@ type SyncStreamItem_SyncActionSetConfig struct { func (x *SyncStreamItem_SyncActionSetConfig) Reset() { *x = SyncStreamItem_SyncActionSetConfig{} - mi := &file_v1sync_syncservice_proto_msgTypes[17] + mi := &file_v1sync_syncservice_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1332,7 +1418,7 @@ func (x *SyncStreamItem_SyncActionSetConfig) String() string { func (*SyncStreamItem_SyncActionSetConfig) ProtoMessage() {} func (x *SyncStreamItem_SyncActionSetConfig) ProtoReflect() protoreflect.Message { - mi := &file_v1sync_syncservice_proto_msgTypes[17] + mi := &file_v1sync_syncservice_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1345,7 +1431,7 @@ func (x *SyncStreamItem_SyncActionSetConfig) ProtoReflect() protoreflect.Message // Deprecated: Use SyncStreamItem_SyncActionSetConfig.ProtoReflect.Descriptor instead. func (*SyncStreamItem_SyncActionSetConfig) Descriptor() ([]byte, []int) { - return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 3} + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 4} } func (x *SyncStreamItem_SyncActionSetConfig) GetRepos() []*v1.Repo { @@ -1384,7 +1470,7 @@ type SyncStreamItem_SyncActionRequestResources struct { func (x *SyncStreamItem_SyncActionRequestResources) Reset() { *x = SyncStreamItem_SyncActionRequestResources{} - mi := &file_v1sync_syncservice_proto_msgTypes[18] + mi := &file_v1sync_syncservice_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1396,7 +1482,7 @@ func (x *SyncStreamItem_SyncActionRequestResources) String() string { func (*SyncStreamItem_SyncActionRequestResources) ProtoMessage() {} func (x *SyncStreamItem_SyncActionRequestResources) ProtoReflect() protoreflect.Message { - mi := &file_v1sync_syncservice_proto_msgTypes[18] + mi := &file_v1sync_syncservice_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1409,7 +1495,7 @@ func (x *SyncStreamItem_SyncActionRequestResources) ProtoReflect() protoreflect. // Deprecated: Use SyncStreamItem_SyncActionRequestResources.ProtoReflect.Descriptor instead. func (*SyncStreamItem_SyncActionRequestResources) Descriptor() ([]byte, []int) { - return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 4} + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 5} } type SyncStreamItem_SyncActionReceiveResources struct { @@ -1422,7 +1508,7 @@ type SyncStreamItem_SyncActionReceiveResources struct { func (x *SyncStreamItem_SyncActionReceiveResources) Reset() { *x = SyncStreamItem_SyncActionReceiveResources{} - mi := &file_v1sync_syncservice_proto_msgTypes[19] + mi := &file_v1sync_syncservice_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1434,7 +1520,7 @@ func (x *SyncStreamItem_SyncActionReceiveResources) String() string { func (*SyncStreamItem_SyncActionReceiveResources) ProtoMessage() {} func (x *SyncStreamItem_SyncActionReceiveResources) ProtoReflect() protoreflect.Message { - mi := &file_v1sync_syncservice_proto_msgTypes[19] + mi := &file_v1sync_syncservice_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1447,7 +1533,7 @@ func (x *SyncStreamItem_SyncActionReceiveResources) ProtoReflect() protoreflect. // Deprecated: Use SyncStreamItem_SyncActionReceiveResources.ProtoReflect.Descriptor instead. func (*SyncStreamItem_SyncActionReceiveResources) Descriptor() ([]byte, []int) { - return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 5} + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 6} } func (x *SyncStreamItem_SyncActionReceiveResources) GetRepos() []*RepoMetadata { @@ -1473,7 +1559,7 @@ type SyncStreamItem_SyncActionConnectRepo struct { func (x *SyncStreamItem_SyncActionConnectRepo) Reset() { *x = SyncStreamItem_SyncActionConnectRepo{} - mi := &file_v1sync_syncservice_proto_msgTypes[20] + mi := &file_v1sync_syncservice_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1485,7 +1571,7 @@ func (x *SyncStreamItem_SyncActionConnectRepo) String() string { func (*SyncStreamItem_SyncActionConnectRepo) ProtoMessage() {} func (x *SyncStreamItem_SyncActionConnectRepo) ProtoReflect() protoreflect.Message { - mi := &file_v1sync_syncservice_proto_msgTypes[20] + mi := &file_v1sync_syncservice_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1498,7 +1584,7 @@ func (x *SyncStreamItem_SyncActionConnectRepo) ProtoReflect() protoreflect.Messa // Deprecated: Use SyncStreamItem_SyncActionConnectRepo.ProtoReflect.Descriptor instead. func (*SyncStreamItem_SyncActionConnectRepo) Descriptor() ([]byte, []int) { - return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 6} + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 7} } func (x *SyncStreamItem_SyncActionConnectRepo) GetRepoId() string { @@ -1518,7 +1604,7 @@ type SyncStreamItem_SyncActionOperationManifest struct { func (x *SyncStreamItem_SyncActionOperationManifest) Reset() { *x = SyncStreamItem_SyncActionOperationManifest{} - mi := &file_v1sync_syncservice_proto_msgTypes[21] + mi := &file_v1sync_syncservice_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1530,7 +1616,7 @@ func (x *SyncStreamItem_SyncActionOperationManifest) String() string { func (*SyncStreamItem_SyncActionOperationManifest) ProtoMessage() {} func (x *SyncStreamItem_SyncActionOperationManifest) ProtoReflect() protoreflect.Message { - mi := &file_v1sync_syncservice_proto_msgTypes[21] + mi := &file_v1sync_syncservice_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1543,7 +1629,7 @@ func (x *SyncStreamItem_SyncActionOperationManifest) ProtoReflect() protoreflect // Deprecated: Use SyncStreamItem_SyncActionOperationManifest.ProtoReflect.Descriptor instead. func (*SyncStreamItem_SyncActionOperationManifest) Descriptor() ([]byte, []int) { - return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 7} + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 8} } func (x *SyncStreamItem_SyncActionOperationManifest) GetOpIds() []int64 { @@ -1569,7 +1655,7 @@ type SyncStreamItem_SyncActionRequestOperationData struct { func (x *SyncStreamItem_SyncActionRequestOperationData) Reset() { *x = SyncStreamItem_SyncActionRequestOperationData{} - mi := &file_v1sync_syncservice_proto_msgTypes[22] + mi := &file_v1sync_syncservice_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1581,7 +1667,7 @@ func (x *SyncStreamItem_SyncActionRequestOperationData) String() string { func (*SyncStreamItem_SyncActionRequestOperationData) ProtoMessage() {} func (x *SyncStreamItem_SyncActionRequestOperationData) ProtoReflect() protoreflect.Message { - mi := &file_v1sync_syncservice_proto_msgTypes[22] + mi := &file_v1sync_syncservice_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1594,7 +1680,7 @@ func (x *SyncStreamItem_SyncActionRequestOperationData) ProtoReflect() protorefl // Deprecated: Use SyncStreamItem_SyncActionRequestOperationData.ProtoReflect.Descriptor instead. func (*SyncStreamItem_SyncActionRequestOperationData) Descriptor() ([]byte, []int) { - return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 8} + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 9} } func (x *SyncStreamItem_SyncActionRequestOperationData) GetOpIds() []int64 { @@ -1613,7 +1699,7 @@ type SyncStreamItem_SyncActionReceiveOperations struct { func (x *SyncStreamItem_SyncActionReceiveOperations) Reset() { *x = SyncStreamItem_SyncActionReceiveOperations{} - mi := &file_v1sync_syncservice_proto_msgTypes[23] + mi := &file_v1sync_syncservice_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1625,7 +1711,7 @@ func (x *SyncStreamItem_SyncActionReceiveOperations) String() string { func (*SyncStreamItem_SyncActionReceiveOperations) ProtoMessage() {} func (x *SyncStreamItem_SyncActionReceiveOperations) ProtoReflect() protoreflect.Message { - mi := &file_v1sync_syncservice_proto_msgTypes[23] + mi := &file_v1sync_syncservice_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1638,7 +1724,7 @@ func (x *SyncStreamItem_SyncActionReceiveOperations) ProtoReflect() protoreflect // Deprecated: Use SyncStreamItem_SyncActionReceiveOperations.ProtoReflect.Descriptor instead. func (*SyncStreamItem_SyncActionReceiveOperations) Descriptor() ([]byte, []int) { - return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 9} + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 10} } func (x *SyncStreamItem_SyncActionReceiveOperations) GetEvent() *v1.OperationEvent { @@ -1657,7 +1743,7 @@ type SyncStreamItem_SyncActionRequestLog struct { func (x *SyncStreamItem_SyncActionRequestLog) Reset() { *x = SyncStreamItem_SyncActionRequestLog{} - mi := &file_v1sync_syncservice_proto_msgTypes[24] + mi := &file_v1sync_syncservice_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1669,7 +1755,7 @@ func (x *SyncStreamItem_SyncActionRequestLog) String() string { func (*SyncStreamItem_SyncActionRequestLog) ProtoMessage() {} func (x *SyncStreamItem_SyncActionRequestLog) ProtoReflect() protoreflect.Message { - mi := &file_v1sync_syncservice_proto_msgTypes[24] + mi := &file_v1sync_syncservice_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1682,7 +1768,7 @@ func (x *SyncStreamItem_SyncActionRequestLog) ProtoReflect() protoreflect.Messag // Deprecated: Use SyncStreamItem_SyncActionRequestLog.ProtoReflect.Descriptor instead. func (*SyncStreamItem_SyncActionRequestLog) Descriptor() ([]byte, []int) { - return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 10} + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 11} } func (x *SyncStreamItem_SyncActionRequestLog) GetLogId() string { @@ -1708,7 +1794,7 @@ type SyncStreamItem_SyncActionReceiveLogData struct { func (x *SyncStreamItem_SyncActionReceiveLogData) Reset() { *x = SyncStreamItem_SyncActionReceiveLogData{} - mi := &file_v1sync_syncservice_proto_msgTypes[25] + mi := &file_v1sync_syncservice_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1720,7 +1806,7 @@ func (x *SyncStreamItem_SyncActionReceiveLogData) String() string { func (*SyncStreamItem_SyncActionReceiveLogData) ProtoMessage() {} func (x *SyncStreamItem_SyncActionReceiveLogData) ProtoReflect() protoreflect.Message { - mi := &file_v1sync_syncservice_proto_msgTypes[25] + mi := &file_v1sync_syncservice_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1733,7 +1819,7 @@ func (x *SyncStreamItem_SyncActionReceiveLogData) ProtoReflect() protoreflect.Me // Deprecated: Use SyncStreamItem_SyncActionReceiveLogData.ProtoReflect.Descriptor instead. func (*SyncStreamItem_SyncActionReceiveLogData) Descriptor() ([]byte, []int) { - return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 11} + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 12} } func (x *SyncStreamItem_SyncActionReceiveLogData) GetLogId() string { @@ -1780,7 +1866,7 @@ type SyncStreamItem_SyncActionThrottle struct { func (x *SyncStreamItem_SyncActionThrottle) Reset() { *x = SyncStreamItem_SyncActionThrottle{} - mi := &file_v1sync_syncservice_proto_msgTypes[26] + mi := &file_v1sync_syncservice_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1792,7 +1878,7 @@ func (x *SyncStreamItem_SyncActionThrottle) String() string { func (*SyncStreamItem_SyncActionThrottle) ProtoMessage() {} func (x *SyncStreamItem_SyncActionThrottle) ProtoReflect() protoreflect.Message { - mi := &file_v1sync_syncservice_proto_msgTypes[26] + mi := &file_v1sync_syncservice_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1805,7 +1891,7 @@ func (x *SyncStreamItem_SyncActionThrottle) ProtoReflect() protoreflect.Message // Deprecated: Use SyncStreamItem_SyncActionThrottle.ProtoReflect.Descriptor instead. func (*SyncStreamItem_SyncActionThrottle) Descriptor() ([]byte, []int) { - return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 12} + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 13} } func (x *SyncStreamItem_SyncActionThrottle) GetDelayMs() int64 { @@ -1815,18 +1901,20 @@ func (x *SyncStreamItem_SyncActionThrottle) GetDelayMs() int64 { return 0 } +// SyncEstablishSharedSecret is exchanged immediately after the handshake. +// Each side sends an ephemeral ECDH P-256 public key. Both sides then perform +// ECDH to derive a shared AES-256-GCM session key. All subsequent messages +// must be wrapped in SyncActionEncrypted. type SyncStreamItem_SyncEstablishSharedSecret struct { - state protoimpl.MessageState `protogen:"open.v1"` - // a one-time-use ECDSA public key with a matching unshared private key. Used to perform a key exchange. - // See https://pkg.go.dev/crypto/ecdh#PrivateKey.ECDH . - EcdsaPub string `protobuf:"bytes,2,opt,name=ecdsa_pub,json=ecdsaPub,proto3" json:"ecdsa_pub,omitempty"` // base64 encoded public key + state protoimpl.MessageState `protogen:"open.v1"` + EcdhPublicKey []byte `protobuf:"bytes,1,opt,name=ecdh_public_key,json=ecdhPublicKey,proto3" json:"ecdh_public_key,omitempty"` // raw ECDH P-256 public key bytes unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *SyncStreamItem_SyncEstablishSharedSecret) Reset() { *x = SyncStreamItem_SyncEstablishSharedSecret{} - mi := &file_v1sync_syncservice_proto_msgTypes[27] + mi := &file_v1sync_syncservice_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1838,7 +1926,7 @@ func (x *SyncStreamItem_SyncEstablishSharedSecret) String() string { func (*SyncStreamItem_SyncEstablishSharedSecret) ProtoMessage() {} func (x *SyncStreamItem_SyncEstablishSharedSecret) ProtoReflect() protoreflect.Message { - mi := &file_v1sync_syncservice_proto_msgTypes[27] + mi := &file_v1sync_syncservice_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1851,14 +1939,14 @@ func (x *SyncStreamItem_SyncEstablishSharedSecret) ProtoReflect() protoreflect.M // Deprecated: Use SyncStreamItem_SyncEstablishSharedSecret.ProtoReflect.Descriptor instead. func (*SyncStreamItem_SyncEstablishSharedSecret) Descriptor() ([]byte, []int) { - return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 13} + return file_v1sync_syncservice_proto_rawDescGZIP(), []int{13, 14} } -func (x *SyncStreamItem_SyncEstablishSharedSecret) GetEcdsaPub() string { +func (x *SyncStreamItem_SyncEstablishSharedSecret) GetEcdhPublicKey() []byte { if x != nil { - return x.EcdsaPub + return x.EcdhPublicKey } - return "" + return nil } var File_v1sync_syncservice_proto protoreflect.FileDescriptor @@ -1922,7 +2010,7 @@ const file_v1sync_syncservice_proto_rawDesc = "" + "\n" + "public_key\x18\x01 \x01(\v2\r.v1.PublicKeyR\tpublicKey\x122\n" + "\vinstance_id\x18\x02 \x01(\v2\x11.v1.SignedMessageR\n" + - "instanceId\"\xff\x13\n" + + "instanceId\"\x8f\x16\n" + "\x0eSyncStreamItem\x12:\n" + "\x0esigned_message\x18\x01 \x01(\v2\x11.v1.SignedMessageH\x00R\rsignedMessage\x12J\n" + "\thandshake\x18\x03 \x01(\v2*.v1sync.SyncStreamItem.SyncActionHandshakeH\x00R\thandshake\x12J\n" + @@ -1938,14 +2026,21 @@ const file_v1sync_syncservice_proto_rawDesc = "" + "\vrequest_log\x18\x1e \x01(\v2+.v1sync.SyncStreamItem.SyncActionRequestLogH\x00R\n" + "requestLog\x12[\n" + "\x10receive_log_data\x18\x1f \x01(\v2/.v1sync.SyncStreamItem.SyncActionReceiveLogDataH\x00R\x0ereceiveLogData\x12H\n" + - "\bthrottle\x18\xe8\a \x01(\v2).v1sync.SyncStreamItem.SyncActionThrottleH\x00R\bthrottle\x1a\xc9\x01\n" + + "\bthrottle\x18\xe8\a \x01(\v2).v1sync.SyncStreamItem.SyncActionThrottleH\x00R\bthrottle\x12j\n" + + "\x17establish_shared_secret\x18\x02 \x01(\v20.v1sync.SyncStreamItem.SyncEstablishSharedSecretH\x00R\x15establishSharedSecret\x12J\n" + + "\tencrypted\x18\x05 \x01(\v2*.v1sync.SyncStreamItem.SyncActionEncryptedH\x00R\tencrypted\x1a\xc9\x01\n" + "\x13SyncActionHandshake\x12)\n" + "\x10protocol_version\x18\x01 \x01(\x03R\x0fprotocolVersion\x12,\n" + "\n" + "public_key\x18\x02 \x01(\v2\r.v1.PublicKeyR\tpublicKey\x122\n" + "\vinstance_id\x18\x03 \x01(\v2\x11.v1.SignedMessageR\n" + "instanceId\x12%\n" + - "\x0epairing_secret\x18\x04 \x01(\tR\rpairingSecret\x1a\x15\n" + + "\x0epairing_secret\x18\x04 \x01(\tR\rpairingSecret\x1aK\n" + + "\x13SyncActionEncrypted\x12\x14\n" + + "\x05nonce\x18\x01 \x01(\fR\x05nonce\x12\x1e\n" + + "\n" + + "ciphertext\x18\x02 \x01(\fR\n" + + "ciphertext\x1a\x15\n" + "\x13SyncActionHeartbeat\x1aG\n" + "\x17SyncActionReceiveConfig\x12,\n" + "\x06config\x18\x01 \x01(\v2\x14.v1sync.RemoteConfigR\x06config\x1a\xa5\x01\n" + @@ -1977,9 +2072,9 @@ const file_v1sync_syncservice_proto_rawDesc = "" + "\x05chunk\x18\x04 \x01(\fR\x05chunk\x12#\n" + "\rerror_message\x18\x05 \x01(\tR\ferrorMessage\x1a/\n" + "\x12SyncActionThrottle\x12\x19\n" + - "\bdelay_ms\x18\x01 \x01(\x03R\adelayMs\x1a8\n" + - "\x19SyncEstablishSharedSecret\x12\x1b\n" + - "\tecdsa_pub\x18\x02 \x01(\tR\becdsaPub\"\xb4\x01\n" + + "\bdelay_ms\x18\x01 \x01(\x03R\adelayMs\x1aC\n" + + "\x19SyncEstablishSharedSecret\x12&\n" + + "\x0fecdh_public_key\x18\x01 \x01(\fR\recdhPublicKey\"\xb4\x01\n" + "\x13RepoConnectionState\x12\x1c\n" + "\x18CONNECTION_STATE_UNKNOWN\x10\x00\x12\x1c\n" + "\x18CONNECTION_STATE_PENDING\x10\x01\x12\x1e\n" + @@ -2016,7 +2111,7 @@ func file_v1sync_syncservice_proto_rawDescGZIP() []byte { } var file_v1sync_syncservice_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_v1sync_syncservice_proto_msgTypes = make([]protoimpl.MessageInfo, 28) +var file_v1sync_syncservice_proto_msgTypes = make([]protoimpl.MessageInfo, 29) var file_v1sync_syncservice_proto_goTypes = []any{ (ConnectionState)(0), // 0: v1sync.ConnectionState (SyncStreamItem_RepoConnectionState)(0), // 1: v1sync.SyncStreamItem.RepoConnectionState @@ -2035,73 +2130,76 @@ var file_v1sync_syncservice_proto_goTypes = []any{ (*AuthorizationToken)(nil), // 14: v1sync.AuthorizationToken (*SyncStreamItem)(nil), // 15: v1sync.SyncStreamItem (*SyncStreamItem_SyncActionHandshake)(nil), // 16: v1sync.SyncStreamItem.SyncActionHandshake - (*SyncStreamItem_SyncActionHeartbeat)(nil), // 17: v1sync.SyncStreamItem.SyncActionHeartbeat - (*SyncStreamItem_SyncActionReceiveConfig)(nil), // 18: v1sync.SyncStreamItem.SyncActionReceiveConfig - (*SyncStreamItem_SyncActionSetConfig)(nil), // 19: v1sync.SyncStreamItem.SyncActionSetConfig - (*SyncStreamItem_SyncActionRequestResources)(nil), // 20: v1sync.SyncStreamItem.SyncActionRequestResources - (*SyncStreamItem_SyncActionReceiveResources)(nil), // 21: v1sync.SyncStreamItem.SyncActionReceiveResources - (*SyncStreamItem_SyncActionConnectRepo)(nil), // 22: v1sync.SyncStreamItem.SyncActionConnectRepo - (*SyncStreamItem_SyncActionOperationManifest)(nil), // 23: v1sync.SyncStreamItem.SyncActionOperationManifest - (*SyncStreamItem_SyncActionRequestOperationData)(nil), // 24: v1sync.SyncStreamItem.SyncActionRequestOperationData - (*SyncStreamItem_SyncActionReceiveOperations)(nil), // 25: v1sync.SyncStreamItem.SyncActionReceiveOperations - (*SyncStreamItem_SyncActionRequestLog)(nil), // 26: v1sync.SyncStreamItem.SyncActionRequestLog - (*SyncStreamItem_SyncActionReceiveLogData)(nil), // 27: v1sync.SyncStreamItem.SyncActionReceiveLogData - (*SyncStreamItem_SyncActionThrottle)(nil), // 28: v1sync.SyncStreamItem.SyncActionThrottle - (*SyncStreamItem_SyncEstablishSharedSecret)(nil), // 29: v1sync.SyncStreamItem.SyncEstablishSharedSecret - (*v1.SignedMessage)(nil), // 30: v1.SignedMessage - (*v1.Plan)(nil), // 31: v1.Plan - (*v1.Repo)(nil), // 32: v1.Repo - (*v1.PublicKey)(nil), // 33: v1.PublicKey - (*v1.OperationEvent)(nil), // 34: v1.OperationEvent + (*SyncStreamItem_SyncActionEncrypted)(nil), // 17: v1sync.SyncStreamItem.SyncActionEncrypted + (*SyncStreamItem_SyncActionHeartbeat)(nil), // 18: v1sync.SyncStreamItem.SyncActionHeartbeat + (*SyncStreamItem_SyncActionReceiveConfig)(nil), // 19: v1sync.SyncStreamItem.SyncActionReceiveConfig + (*SyncStreamItem_SyncActionSetConfig)(nil), // 20: v1sync.SyncStreamItem.SyncActionSetConfig + (*SyncStreamItem_SyncActionRequestResources)(nil), // 21: v1sync.SyncStreamItem.SyncActionRequestResources + (*SyncStreamItem_SyncActionReceiveResources)(nil), // 22: v1sync.SyncStreamItem.SyncActionReceiveResources + (*SyncStreamItem_SyncActionConnectRepo)(nil), // 23: v1sync.SyncStreamItem.SyncActionConnectRepo + (*SyncStreamItem_SyncActionOperationManifest)(nil), // 24: v1sync.SyncStreamItem.SyncActionOperationManifest + (*SyncStreamItem_SyncActionRequestOperationData)(nil), // 25: v1sync.SyncStreamItem.SyncActionRequestOperationData + (*SyncStreamItem_SyncActionReceiveOperations)(nil), // 26: v1sync.SyncStreamItem.SyncActionReceiveOperations + (*SyncStreamItem_SyncActionRequestLog)(nil), // 27: v1sync.SyncStreamItem.SyncActionRequestLog + (*SyncStreamItem_SyncActionReceiveLogData)(nil), // 28: v1sync.SyncStreamItem.SyncActionReceiveLogData + (*SyncStreamItem_SyncActionThrottle)(nil), // 29: v1sync.SyncStreamItem.SyncActionThrottle + (*SyncStreamItem_SyncEstablishSharedSecret)(nil), // 30: v1sync.SyncStreamItem.SyncEstablishSharedSecret + (*v1.SignedMessage)(nil), // 31: v1.SignedMessage + (*v1.Plan)(nil), // 32: v1.Plan + (*v1.Repo)(nil), // 33: v1.Repo + (*v1.PublicKey)(nil), // 34: v1.PublicKey + (*v1.OperationEvent)(nil), // 35: v1.OperationEvent } var file_v1sync_syncservice_proto_depIdxs = []int32{ 0, // 0: v1sync.PeerState.state:type_name -> v1sync.ConnectionState 9, // 1: v1sync.PeerState.known_plans:type_name -> v1sync.PlanMetadata 8, // 2: v1sync.PeerState.known_repos:type_name -> v1sync.RepoMetadata 13, // 3: v1sync.PeerState.remote_config:type_name -> v1sync.RemoteConfig - 30, // 4: v1sync.AuthenticateRequest.instance_id:type_name -> v1.SignedMessage + 31, // 4: v1sync.AuthenticateRequest.instance_id:type_name -> v1.SignedMessage 9, // 5: v1sync.SetAvailableResourcesRequest.repos:type_name -> v1sync.PlanMetadata 8, // 6: v1sync.SetAvailableResourcesRequest.plans:type_name -> v1sync.RepoMetadata - 31, // 7: v1sync.SetConfigRequest.plans:type_name -> v1.Plan - 32, // 8: v1sync.SetConfigRequest.repos:type_name -> v1.Repo - 32, // 9: v1sync.SetRemoteClientConfigRequest.repos:type_name -> v1.Repo - 31, // 10: v1sync.SetRemoteClientConfigRequest.plans:type_name -> v1.Plan - 32, // 11: v1sync.RemoteConfig.repos:type_name -> v1.Repo - 31, // 12: v1sync.RemoteConfig.plans:type_name -> v1.Plan - 33, // 13: v1sync.AuthorizationToken.public_key:type_name -> v1.PublicKey - 30, // 14: v1sync.AuthorizationToken.instance_id:type_name -> v1.SignedMessage - 30, // 15: v1sync.SyncStreamItem.signed_message:type_name -> v1.SignedMessage + 32, // 7: v1sync.SetConfigRequest.plans:type_name -> v1.Plan + 33, // 8: v1sync.SetConfigRequest.repos:type_name -> v1.Repo + 33, // 9: v1sync.SetRemoteClientConfigRequest.repos:type_name -> v1.Repo + 32, // 10: v1sync.SetRemoteClientConfigRequest.plans:type_name -> v1.Plan + 33, // 11: v1sync.RemoteConfig.repos:type_name -> v1.Repo + 32, // 12: v1sync.RemoteConfig.plans:type_name -> v1.Plan + 34, // 13: v1sync.AuthorizationToken.public_key:type_name -> v1.PublicKey + 31, // 14: v1sync.AuthorizationToken.instance_id:type_name -> v1.SignedMessage + 31, // 15: v1sync.SyncStreamItem.signed_message:type_name -> v1.SignedMessage 16, // 16: v1sync.SyncStreamItem.handshake:type_name -> v1sync.SyncStreamItem.SyncActionHandshake - 17, // 17: v1sync.SyncStreamItem.heartbeat:type_name -> v1sync.SyncStreamItem.SyncActionHeartbeat - 23, // 18: v1sync.SyncStreamItem.operation_manifest:type_name -> v1sync.SyncStreamItem.SyncActionOperationManifest - 25, // 19: v1sync.SyncStreamItem.receive_operations:type_name -> v1sync.SyncStreamItem.SyncActionReceiveOperations - 24, // 20: v1sync.SyncStreamItem.request_operation_data:type_name -> v1sync.SyncStreamItem.SyncActionRequestOperationData - 18, // 21: v1sync.SyncStreamItem.receive_config:type_name -> v1sync.SyncStreamItem.SyncActionReceiveConfig - 19, // 22: v1sync.SyncStreamItem.set_config:type_name -> v1sync.SyncStreamItem.SyncActionSetConfig - 20, // 23: v1sync.SyncStreamItem.request_resources:type_name -> v1sync.SyncStreamItem.SyncActionRequestResources - 21, // 24: v1sync.SyncStreamItem.receive_resources:type_name -> v1sync.SyncStreamItem.SyncActionReceiveResources - 26, // 25: v1sync.SyncStreamItem.request_log:type_name -> v1sync.SyncStreamItem.SyncActionRequestLog - 27, // 26: v1sync.SyncStreamItem.receive_log_data:type_name -> v1sync.SyncStreamItem.SyncActionReceiveLogData - 28, // 27: v1sync.SyncStreamItem.throttle:type_name -> v1sync.SyncStreamItem.SyncActionThrottle - 33, // 28: v1sync.SyncStreamItem.SyncActionHandshake.public_key:type_name -> v1.PublicKey - 30, // 29: v1sync.SyncStreamItem.SyncActionHandshake.instance_id:type_name -> v1.SignedMessage - 13, // 30: v1sync.SyncStreamItem.SyncActionReceiveConfig.config:type_name -> v1sync.RemoteConfig - 32, // 31: v1sync.SyncStreamItem.SyncActionSetConfig.repos:type_name -> v1.Repo - 31, // 32: v1sync.SyncStreamItem.SyncActionSetConfig.plans:type_name -> v1.Plan - 8, // 33: v1sync.SyncStreamItem.SyncActionReceiveResources.repos:type_name -> v1sync.RepoMetadata - 9, // 34: v1sync.SyncStreamItem.SyncActionReceiveResources.plans:type_name -> v1sync.PlanMetadata - 34, // 35: v1sync.SyncStreamItem.SyncActionReceiveOperations.event:type_name -> v1.OperationEvent - 15, // 36: v1sync.BackrestSyncService.Sync:input_type -> v1sync.SyncStreamItem - 2, // 37: v1sync.BackrestSyncStateService.GetPeerSyncStatesStream:input_type -> v1sync.SyncStateStreamRequest - 11, // 38: v1sync.BackrestSyncStateService.SetRemoteClientConfig:input_type -> v1sync.SetRemoteClientConfigRequest - 15, // 39: v1sync.BackrestSyncService.Sync:output_type -> v1sync.SyncStreamItem - 3, // 40: v1sync.BackrestSyncStateService.GetPeerSyncStatesStream:output_type -> v1sync.PeerState - 12, // 41: v1sync.BackrestSyncStateService.SetRemoteClientConfig:output_type -> v1sync.SetRemoteClientConfigResponse - 39, // [39:42] is the sub-list for method output_type - 36, // [36:39] is the sub-list for method input_type - 36, // [36:36] is the sub-list for extension type_name - 36, // [36:36] is the sub-list for extension extendee - 0, // [0:36] is the sub-list for field type_name + 18, // 17: v1sync.SyncStreamItem.heartbeat:type_name -> v1sync.SyncStreamItem.SyncActionHeartbeat + 24, // 18: v1sync.SyncStreamItem.operation_manifest:type_name -> v1sync.SyncStreamItem.SyncActionOperationManifest + 26, // 19: v1sync.SyncStreamItem.receive_operations:type_name -> v1sync.SyncStreamItem.SyncActionReceiveOperations + 25, // 20: v1sync.SyncStreamItem.request_operation_data:type_name -> v1sync.SyncStreamItem.SyncActionRequestOperationData + 19, // 21: v1sync.SyncStreamItem.receive_config:type_name -> v1sync.SyncStreamItem.SyncActionReceiveConfig + 20, // 22: v1sync.SyncStreamItem.set_config:type_name -> v1sync.SyncStreamItem.SyncActionSetConfig + 21, // 23: v1sync.SyncStreamItem.request_resources:type_name -> v1sync.SyncStreamItem.SyncActionRequestResources + 22, // 24: v1sync.SyncStreamItem.receive_resources:type_name -> v1sync.SyncStreamItem.SyncActionReceiveResources + 27, // 25: v1sync.SyncStreamItem.request_log:type_name -> v1sync.SyncStreamItem.SyncActionRequestLog + 28, // 26: v1sync.SyncStreamItem.receive_log_data:type_name -> v1sync.SyncStreamItem.SyncActionReceiveLogData + 29, // 27: v1sync.SyncStreamItem.throttle:type_name -> v1sync.SyncStreamItem.SyncActionThrottle + 30, // 28: v1sync.SyncStreamItem.establish_shared_secret:type_name -> v1sync.SyncStreamItem.SyncEstablishSharedSecret + 17, // 29: v1sync.SyncStreamItem.encrypted:type_name -> v1sync.SyncStreamItem.SyncActionEncrypted + 34, // 30: v1sync.SyncStreamItem.SyncActionHandshake.public_key:type_name -> v1.PublicKey + 31, // 31: v1sync.SyncStreamItem.SyncActionHandshake.instance_id:type_name -> v1.SignedMessage + 13, // 32: v1sync.SyncStreamItem.SyncActionReceiveConfig.config:type_name -> v1sync.RemoteConfig + 33, // 33: v1sync.SyncStreamItem.SyncActionSetConfig.repos:type_name -> v1.Repo + 32, // 34: v1sync.SyncStreamItem.SyncActionSetConfig.plans:type_name -> v1.Plan + 8, // 35: v1sync.SyncStreamItem.SyncActionReceiveResources.repos:type_name -> v1sync.RepoMetadata + 9, // 36: v1sync.SyncStreamItem.SyncActionReceiveResources.plans:type_name -> v1sync.PlanMetadata + 35, // 37: v1sync.SyncStreamItem.SyncActionReceiveOperations.event:type_name -> v1.OperationEvent + 15, // 38: v1sync.BackrestSyncService.Sync:input_type -> v1sync.SyncStreamItem + 2, // 39: v1sync.BackrestSyncStateService.GetPeerSyncStatesStream:input_type -> v1sync.SyncStateStreamRequest + 11, // 40: v1sync.BackrestSyncStateService.SetRemoteClientConfig:input_type -> v1sync.SetRemoteClientConfigRequest + 15, // 41: v1sync.BackrestSyncService.Sync:output_type -> v1sync.SyncStreamItem + 3, // 42: v1sync.BackrestSyncStateService.GetPeerSyncStatesStream:output_type -> v1sync.PeerState + 12, // 43: v1sync.BackrestSyncStateService.SetRemoteClientConfig:output_type -> v1sync.SetRemoteClientConfigResponse + 41, // [41:44] is the sub-list for method output_type + 38, // [38:41] is the sub-list for method input_type + 38, // [38:38] is the sub-list for extension type_name + 38, // [38:38] is the sub-list for extension extendee + 0, // [0:38] is the sub-list for field type_name } func init() { file_v1sync_syncservice_proto_init() } @@ -2123,6 +2221,8 @@ func file_v1sync_syncservice_proto_init() { (*SyncStreamItem_RequestLog)(nil), (*SyncStreamItem_ReceiveLogData)(nil), (*SyncStreamItem_Throttle)(nil), + (*SyncStreamItem_EstablishSharedSecret)(nil), + (*SyncStreamItem_Encrypted)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -2130,7 +2230,7 @@ func file_v1sync_syncservice_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_v1sync_syncservice_proto_rawDesc), len(file_v1sync_syncservice_proto_rawDesc)), NumEnums: 2, - NumMessages: 28, + NumMessages: 29, NumExtensions: 0, NumServices: 2, }, diff --git a/internal/api/syncapi/cmdstreamutil.go b/internal/api/syncapi/cmdstreamutil.go index 3db793cb..6e98483e 100644 --- a/internal/api/syncapi/cmdstreamutil.go +++ b/internal/api/syncapi/cmdstreamutil.go @@ -1,6 +1,7 @@ package syncapi import ( + "bytes" "context" "errors" "fmt" @@ -9,6 +10,8 @@ import ( "connectrpc.com/connect" "github.com/garethgeorge/backrest/gen/go/v1sync" + "github.com/garethgeorge/backrest/internal/cryptoutil" + "go.uber.org/zap" ) type syncCommandStreamTrait interface { @@ -16,8 +19,8 @@ type syncCommandStreamTrait interface { Receive() (*v1sync.SyncStreamItem, error) } -var _ syncCommandStreamTrait = (*connect.BidiStream[v1sync.SyncStreamItem, v1sync.SyncStreamItem])(nil) // Ensure that connect.BidiStream implements syncCommandStreamTrait -var _ syncCommandStreamTrait = (*connect.BidiStreamForClient[v1sync.SyncStreamItem, v1sync.SyncStreamItem])(nil) // Ensure that connect.BidiStreamForClient implements syncCommandStreamTrait +var _ syncCommandStreamTrait = (*connect.BidiStream[v1sync.SyncStreamItem, v1sync.SyncStreamItem])(nil) +var _ syncCommandStreamTrait = (*connect.BidiStreamForClient[v1sync.SyncStreamItem, v1sync.SyncStreamItem])(nil) type bidiSyncCommandStream struct { sendChan chan *v1sync.SyncStreamItem @@ -27,7 +30,7 @@ type bidiSyncCommandStream struct { func newBidiSyncCommandStream() *bidiSyncCommandStream { return &bidiSyncCommandStream{ - sendChan: make(chan *v1sync.SyncStreamItem, 256), // Buffered channel to allow sending items without blocking + sendChan: make(chan *v1sync.SyncStreamItem, 256), recvChan: make(chan *v1sync.SyncStreamItem, 1), terminateWithErrChan: make(chan error, 1), } @@ -37,7 +40,6 @@ func (s *bidiSyncCommandStream) Send(item *v1sync.SyncStreamItem) { select { case s.sendChan <- item: default: - // Try again with a timeout, if it fails, send an error to terminate the stream select { case s.sendChan <- item: case <-time.After(100 * time.Millisecond): @@ -46,14 +48,10 @@ func (s *bidiSyncCommandStream) Send(item *v1sync.SyncStreamItem) { } } -// SendErrorAndTerminate sends an error to the termination channel. -// If the error is nil, it terminates only. func (s *bidiSyncCommandStream) SendErrorAndTerminate(err error) { select { case s.terminateWithErrChan <- err: default: - // If the channel is full, we can't send the error, so we just ignore it. - // This is a best-effort termination. } } @@ -66,17 +64,27 @@ func (s *bidiSyncCommandStream) ReceiveWithinDuration(d time.Duration) *v1sync.S case item := <-s.recvChan: return item case <-time.After(d): - return nil // Return nil if no item is received within the duration + return nil } } +// ConnectStream bridges the channel-based bidiSyncCommandStream to a real transport. +// It first performs an ECDH key exchange on the raw transport to establish an encrypted +// session, then starts the send/recv pump loop over the encrypted channel. func (s *bidiSyncCommandStream) ConnectStream(ctx context.Context, stream syncCommandStreamTrait) error { ctx, cancel := context.WithCancel(ctx) defer cancel() + + // Perform ECDH key exchange on the raw transport before starting the pump. + transport, err := establishEncryption(stream) + if err != nil { + return err + } + go func() { defer close(s.recvChan) for { - val, err := stream.Receive() + val, err := transport.Receive() if err != nil { s.SendErrorAndTerminate(NewSyncErrorDisconnected(fmt.Errorf("receiving item: %w", err))) return @@ -95,7 +103,7 @@ func (s *bidiSyncCommandStream) ConnectStream(ctx context.Context, stream syncCo if item == nil { continue } - if err := stream.Send(item); err != nil { + if err := transport.Send(item); err != nil { if errors.Is(err, io.EOF) { err = fmt.Errorf("connection failed or dropped: %w", err) } @@ -103,10 +111,59 @@ func (s *bidiSyncCommandStream) ConnectStream(ctx context.Context, stream syncCo return err } case err := <-s.terminateWithErrChan: - return err // Terminate the stream with the error or nil if no error was sent + return err case <-ctx.Done(): - // Context is done, we should stop processing. return ctx.Err() } } } + +// establishEncryption performs an ECDH key exchange on the raw transport and +// returns an encrypted stream wrapper. Each side generates an ephemeral ECDH P-256 +// key pair, exchanges public keys, and derives a shared AES-256-GCM session key. +// The handshake (identity authentication) runs over the encrypted channel afterward. +func establishEncryption(stream syncCommandStreamTrait) (syncCommandStreamTrait, error) { + keyPair, err := cryptoutil.GenerateECDHKeyPair() + if err != nil { + return nil, NewSyncErrorInternal(fmt.Errorf("generating ephemeral ECDH key: %w", err)) + } + + // Send our ephemeral ECDH public key + if err := stream.Send(&v1sync.SyncStreamItem{ + Action: &v1sync.SyncStreamItem_EstablishSharedSecret{ + EstablishSharedSecret: &v1sync.SyncStreamItem_SyncEstablishSharedSecret{ + EcdhPublicKey: keyPair.Public.Bytes(), + }, + }, + }); err != nil { + return nil, NewSyncErrorProtocol(fmt.Errorf("sending ECDH public key: %w", err)) + } + + // Receive the peer's ephemeral ECDH public key + peerMsg, err := stream.Receive() + if err != nil { + return nil, NewSyncErrorProtocol(fmt.Errorf("receiving ECDH public key: %w", err)) + } + peerSecret := peerMsg.GetEstablishSharedSecret() + if peerSecret == nil { + return nil, NewSyncErrorProtocol(fmt.Errorf("expected ECDH key exchange, got %T", peerMsg.GetAction())) + } + + peerECDHPub, err := cryptoutil.ParseECDHPublicKey(peerSecret.GetEcdhPublicKey()) + if err != nil { + return nil, NewSyncErrorProtocol(fmt.Errorf("parsing peer ECDH public key: %w", err)) + } + + // Derive AES-256-GCM session key + gcm, err := cryptoutil.DeriveSessionKey(keyPair.Private, peerECDHPub) + if err != nil { + return nil, NewSyncErrorProtocol(fmt.Errorf("deriving session key: %w", err)) + } + + // Determine nonce direction: side with smaller public key uses prefix 0x00 + localIsSmaller := bytes.Compare(keyPair.Public.Bytes(), peerECDHPub.Bytes()) < 0 + + zap.L().Info("encrypted sync session established") + + return newEncryptedStream(stream, gcm, localIsSmaller), nil +} diff --git a/internal/api/syncapi/encryption.go b/internal/api/syncapi/encryption.go new file mode 100644 index 00000000..c08c2c7e --- /dev/null +++ b/internal/api/syncapi/encryption.go @@ -0,0 +1,120 @@ +package syncapi + +import ( + "crypto/cipher" + "encoding/binary" + "fmt" + "sync" + + "github.com/garethgeorge/backrest/gen/go/v1sync" + "google.golang.org/protobuf/proto" +) + +// encryptedStream wraps a syncCommandStreamTrait with AES-256-GCM encryption. +// Outgoing SyncStreamItems are serialized, encrypted, and sent as SyncActionEncrypted. +// Incoming SyncActionEncrypted messages are decrypted and deserialized back to SyncStreamItems. +// +// To avoid nonce reuse (since both sides share the same key), each direction +// uses a different nonce prefix byte: the side with the lexicographically smaller +// ECDH public key uses prefix 0x00 for sending and expects 0x01 for receiving, +// and vice versa. +type encryptedStream struct { + inner syncCommandStreamTrait + gcm cipher.AEAD + + sendPrefix byte + recvPrefix byte + + sendMu sync.Mutex + sendCounter uint64 + + recvMu sync.Mutex + recvCounter uint64 +} + +func newEncryptedStream(inner syncCommandStreamTrait, gcm cipher.AEAD, localIsSmaller bool) *encryptedStream { + var sendPrefix, recvPrefix byte + if localIsSmaller { + sendPrefix, recvPrefix = 0x00, 0x01 + } else { + sendPrefix, recvPrefix = 0x01, 0x00 + } + return &encryptedStream{ + inner: inner, + gcm: gcm, + sendPrefix: sendPrefix, + recvPrefix: recvPrefix, + } +} + +func (s *encryptedStream) Send(item *v1sync.SyncStreamItem) error { + plaintext, err := proto.Marshal(item) + if err != nil { + return fmt.Errorf("marshal for encryption: %w", err) + } + + s.sendMu.Lock() + nonce := s.makeNonce(s.sendPrefix, s.sendCounter) + s.sendCounter++ + s.sendMu.Unlock() + + ciphertext := s.gcm.Seal(nil, nonce, plaintext, nil) + + return s.inner.Send(&v1sync.SyncStreamItem{ + Action: &v1sync.SyncStreamItem_Encrypted{ + Encrypted: &v1sync.SyncStreamItem_SyncActionEncrypted{ + Nonce: nonce, + Ciphertext: ciphertext, + }, + }, + }) +} + +func (s *encryptedStream) Receive() (*v1sync.SyncStreamItem, error) { + envelope, err := s.inner.Receive() + if err != nil { + return nil, err + } + + encrypted := envelope.GetEncrypted() + if encrypted == nil { + return nil, fmt.Errorf("expected encrypted message, got %T", envelope.GetAction()) + } + + s.recvMu.Lock() + expectedNonce := s.makeNonce(s.recvPrefix, s.recvCounter) + s.recvCounter++ + s.recvMu.Unlock() + + if len(encrypted.Nonce) != s.gcm.NonceSize() { + return nil, fmt.Errorf("invalid nonce size: got %d, want %d", len(encrypted.Nonce), s.gcm.NonceSize()) + } + + // Verify nonce matches expected counter to prevent replay/reorder attacks + for i := range expectedNonce { + if expectedNonce[i] != encrypted.Nonce[i] { + return nil, fmt.Errorf("nonce mismatch: possible replay or reorder attack") + } + } + + plaintext, err := s.gcm.Open(nil, encrypted.Nonce, encrypted.Ciphertext, nil) + if err != nil { + return nil, fmt.Errorf("decrypt message: %w", err) + } + + var inner v1sync.SyncStreamItem + if err := proto.Unmarshal(plaintext, &inner); err != nil { + return nil, fmt.Errorf("unmarshal decrypted message: %w", err) + } + + return &inner, nil +} + +// makeNonce creates a 12-byte GCM nonce. The first byte is the direction prefix +// (0x00 or 0x01), bytes 1-3 are zero, and bytes 4-11 are the counter in big-endian. +func (s *encryptedStream) makeNonce(prefix byte, counter uint64) []byte { + nonce := make([]byte, s.gcm.NonceSize()) // 12 bytes for GCM + nonce[0] = prefix + binary.BigEndian.PutUint64(nonce[4:], counter) + return nonce +} diff --git a/internal/api/syncapi/encryption_test.go b/internal/api/syncapi/encryption_test.go new file mode 100644 index 00000000..306caaab --- /dev/null +++ b/internal/api/syncapi/encryption_test.go @@ -0,0 +1,186 @@ +package syncapi + +import ( + "sync" + "testing" + + "github.com/garethgeorge/backrest/gen/go/v1sync" + "github.com/garethgeorge/backrest/internal/cryptoutil" +) + +// fakeStream is a pair of in-memory channels simulating a bidirectional transport. +type fakeStream struct { + sendCh chan *v1sync.SyncStreamItem + recvCh chan *v1sync.SyncStreamItem +} + +func (f *fakeStream) Send(item *v1sync.SyncStreamItem) error { + f.sendCh <- item + return nil +} + +func (f *fakeStream) Receive() (*v1sync.SyncStreamItem, error) { + item := <-f.recvCh + return item, nil +} + +// newFakeStreamPair creates two connected fakeStreams (A's send is B's recv and vice versa). +func newFakeStreamPair() (*fakeStream, *fakeStream) { + ab := make(chan *v1sync.SyncStreamItem, 16) + ba := make(chan *v1sync.SyncStreamItem, 16) + return &fakeStream{sendCh: ab, recvCh: ba}, &fakeStream{sendCh: ba, recvCh: ab} +} + +func TestEncryptedStream_RoundTrip(t *testing.T) { + alice, err := cryptoutil.GenerateECDHKeyPair() + if err != nil { + t.Fatal(err) + } + bob, err := cryptoutil.GenerateECDHKeyPair() + if err != nil { + t.Fatal(err) + } + + gcm, err := cryptoutil.DeriveSessionKey(alice.Private, bob.Public) + if err != nil { + t.Fatal(err) + } + + aliceIsSmaller := string(alice.Public.Bytes()) < string(bob.Public.Bytes()) + + transportA, transportB := newFakeStreamPair() + encA := newEncryptedStream(transportA, gcm, aliceIsSmaller) + encB := newEncryptedStream(transportB, gcm, !aliceIsSmaller) + + // Send a heartbeat from A to B + sendItem := &v1sync.SyncStreamItem{ + Action: &v1sync.SyncStreamItem_Heartbeat{ + Heartbeat: &v1sync.SyncStreamItem_SyncActionHeartbeat{}, + }, + } + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + if err := encA.Send(sendItem); err != nil { + t.Errorf("send: %v", err) + } + }() + + recvItem, err := encB.Receive() + if err != nil { + t.Fatalf("receive: %v", err) + } + wg.Wait() + + if recvItem.GetHeartbeat() == nil { + t.Fatalf("expected heartbeat, got %T", recvItem.GetAction()) + } +} + +func TestEncryptedStream_BidirectionalMultiMessage(t *testing.T) { + alice, _ := cryptoutil.GenerateECDHKeyPair() + bob, _ := cryptoutil.GenerateECDHKeyPair() + gcm, _ := cryptoutil.DeriveSessionKey(alice.Private, bob.Public) + + aliceIsSmaller := string(alice.Public.Bytes()) < string(bob.Public.Bytes()) + + transportA, transportB := newFakeStreamPair() + encA := newEncryptedStream(transportA, gcm, aliceIsSmaller) + encB := newEncryptedStream(transportB, gcm, !aliceIsSmaller) + + heartbeat := &v1sync.SyncStreamItem{ + Action: &v1sync.SyncStreamItem_Heartbeat{ + Heartbeat: &v1sync.SyncStreamItem_SyncActionHeartbeat{}, + }, + } + + // Send 5 messages A→B sequentially, then 5 messages B→A sequentially + var wg sync.WaitGroup + + // A→B direction + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < 5; i++ { + if err := encA.Send(heartbeat); err != nil { + t.Errorf("A send %d: %v", i, err) + } + } + }() + for i := 0; i < 5; i++ { + if _, err := encB.Receive(); err != nil { + t.Fatalf("B receive %d: %v", i, err) + } + } + wg.Wait() + + // B→A direction + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < 5; i++ { + if err := encB.Send(heartbeat); err != nil { + t.Errorf("B send %d: %v", i, err) + } + } + }() + for i := 0; i < 5; i++ { + if _, err := encA.Receive(); err != nil { + t.Fatalf("A receive %d: %v", i, err) + } + } + wg.Wait() +} + +func TestEstablishEncryption_Integration(t *testing.T) { + transportA, transportB := newFakeStreamPair() + + var encA, encB syncCommandStreamTrait + var errA, errB error + var wg sync.WaitGroup + + wg.Add(2) + go func() { + defer wg.Done() + encA, errA = establishEncryption(transportA) + }() + go func() { + defer wg.Done() + encB, errB = establishEncryption(transportB) + }() + wg.Wait() + + if errA != nil { + t.Fatalf("establish A: %v", errA) + } + if errB != nil { + t.Fatalf("establish B: %v", errB) + } + + // Verify encrypted communication works + heartbeat := &v1sync.SyncStreamItem{ + Action: &v1sync.SyncStreamItem_Heartbeat{ + Heartbeat: &v1sync.SyncStreamItem_SyncActionHeartbeat{}, + }, + } + + wg.Add(1) + go func() { + defer wg.Done() + if err := encA.Send(heartbeat); err != nil { + t.Errorf("send: %v", err) + } + }() + + recv, err := encB.Receive() + if err != nil { + t.Fatalf("receive: %v", err) + } + wg.Wait() + + if recv.GetHeartbeat() == nil { + t.Fatalf("expected heartbeat, got %T", recv.GetAction()) + } +} diff --git a/internal/api/syncapi/syncserver.go b/internal/api/syncapi/syncserver.go index c4f6e795..d8d14f32 100644 --- a/internal/api/syncapi/syncserver.go +++ b/internal/api/syncapi/syncserver.go @@ -18,7 +18,7 @@ import ( "google.golang.org/protobuf/proto" ) -const SyncProtocolVersion = 1 +const SyncProtocolVersion = 2 type BackrestSyncHandler struct { v1syncconnect.UnimplementedBackrestSyncServiceHandler diff --git a/internal/cryptoutil/ecdh.go b/internal/cryptoutil/ecdh.go new file mode 100644 index 00000000..87f9b707 --- /dev/null +++ b/internal/cryptoutil/ecdh.go @@ -0,0 +1,78 @@ +package cryptoutil + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/ecdh" + "crypto/rand" + "crypto/sha256" + "fmt" + "io" + + "golang.org/x/crypto/hkdf" +) + +// ECDHKeyPair holds an ephemeral ECDH key pair for key exchange. +type ECDHKeyPair struct { + Private *ecdh.PrivateKey + Public *ecdh.PublicKey +} + +// GenerateECDHKeyPair generates an ephemeral ECDH P-256 key pair. +func GenerateECDHKeyPair() (*ECDHKeyPair, error) { + privKey, err := ecdh.P256().GenerateKey(rand.Reader) + if err != nil { + return nil, fmt.Errorf("generate ECDH key: %w", err) + } + return &ECDHKeyPair{ + Private: privKey, + Public: privKey.PublicKey(), + }, nil +} + +// DeriveSessionKey performs ECDH with the peer's public key and derives an +// AES-256-GCM AEAD using HKDF-SHA256. Both ephemeral public keys are included +// as HKDF salt to bind the derived key to this specific exchange. Authentication +// of the peers is provided by the handshake layer (signature verification) which +// runs over the encrypted channel. +func DeriveSessionKey(localPrivate *ecdh.PrivateKey, peerPublic *ecdh.PublicKey) (cipher.AEAD, error) { + sharedSecret, err := localPrivate.ECDH(peerPublic) + if err != nil { + return nil, fmt.Errorf("ECDH key agreement: %w", err) + } + + // Sort public keys so both sides produce the same salt regardless of role + pubA, pubB := localPrivate.PublicKey().Bytes(), peerPublic.Bytes() + if bytes.Compare(pubA, pubB) > 0 { + pubA, pubB = pubB, pubA + } + salt := append(pubA, pubB...) + info := []byte("backrest-sync-v2") + + hkdfReader := hkdf.New(sha256.New, sharedSecret, salt, info) + key := make([]byte, 32) + if _, err := io.ReadFull(hkdfReader, key); err != nil { + return nil, fmt.Errorf("HKDF key derivation: %w", err) + } + + block, err := aes.NewCipher(key) + if err != nil { + return nil, fmt.Errorf("create AES cipher: %w", err) + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("create GCM: %w", err) + } + + return gcm, nil +} + +// ParseECDHPublicKey parses raw ECDH P-256 public key bytes. +func ParseECDHPublicKey(raw []byte) (*ecdh.PublicKey, error) { + pub, err := ecdh.P256().NewPublicKey(raw) + if err != nil { + return nil, fmt.Errorf("parse ECDH public key: %w", err) + } + return pub, nil +} diff --git a/internal/cryptoutil/ecdh_test.go b/internal/cryptoutil/ecdh_test.go new file mode 100644 index 00000000..42a743e8 --- /dev/null +++ b/internal/cryptoutil/ecdh_test.go @@ -0,0 +1,91 @@ +package cryptoutil + +import ( + "testing" +) + +func TestGenerateECDHKeyPair(t *testing.T) { + kp, err := GenerateECDHKeyPair() + if err != nil { + t.Fatalf("GenerateECDHKeyPair: %v", err) + } + if kp.Private == nil || kp.Public == nil { + t.Fatal("key pair has nil fields") + } + if len(kp.Public.Bytes()) == 0 { + t.Fatal("public key bytes are empty") + } +} + +func TestDeriveSessionKey_Symmetric(t *testing.T) { + alice, err := GenerateECDHKeyPair() + if err != nil { + t.Fatal(err) + } + bob, err := GenerateECDHKeyPair() + if err != nil { + t.Fatal(err) + } + + gcmAlice, err := DeriveSessionKey(alice.Private, bob.Public) + if err != nil { + t.Fatalf("DeriveSessionKey (alice): %v", err) + } + gcmBob, err := DeriveSessionKey(bob.Private, alice.Public) + if err != nil { + t.Fatalf("DeriveSessionKey (bob): %v", err) + } + + // Both sides should produce the same key: encrypt with alice, decrypt with bob + plaintext := []byte("hello backrest") + nonce := make([]byte, gcmAlice.NonceSize()) + ciphertext := gcmAlice.Seal(nil, nonce, plaintext, nil) + + decrypted, err := gcmBob.Open(nil, nonce, ciphertext, nil) + if err != nil { + t.Fatalf("bob failed to decrypt alice's message: %v", err) + } + if string(decrypted) != string(plaintext) { + t.Fatalf("decrypted %q, want %q", decrypted, plaintext) + } +} + +func TestDeriveSessionKey_DifferentPairs(t *testing.T) { + a, _ := GenerateECDHKeyPair() + b, _ := GenerateECDHKeyPair() + c, _ := GenerateECDHKeyPair() + + gcmAB, _ := DeriveSessionKey(a.Private, b.Public) + gcmAC, _ := DeriveSessionKey(a.Private, c.Public) + + plaintext := []byte("test") + nonce := make([]byte, gcmAB.NonceSize()) + ciphertext := gcmAB.Seal(nil, nonce, plaintext, nil) + + // AC key should NOT be able to decrypt AB ciphertext + if _, err := gcmAC.Open(nil, nonce, ciphertext, nil); err == nil { + t.Fatal("different key pair should not decrypt") + } +} + +func TestParseECDHPublicKey_RoundTrip(t *testing.T) { + kp, err := GenerateECDHKeyPair() + if err != nil { + t.Fatal(err) + } + + raw := kp.Public.Bytes() + parsed, err := ParseECDHPublicKey(raw) + if err != nil { + t.Fatalf("ParseECDHPublicKey: %v", err) + } + if string(parsed.Bytes()) != string(raw) { + t.Fatal("round-trip failed") + } +} + +func TestParseECDHPublicKey_Invalid(t *testing.T) { + if _, err := ParseECDHPublicKey([]byte("not a key")); err == nil { + t.Fatal("expected error for invalid key") + } +} diff --git a/proto/v1sync/syncservice.proto b/proto/v1sync/syncservice.proto index 00c73437..3833af09 100644 --- a/proto/v1sync/syncservice.proto +++ b/proto/v1sync/syncservice.proto @@ -135,6 +135,9 @@ message SyncStreamItem { SyncActionReceiveLogData receive_log_data = 31; SyncActionThrottle throttle = 1000; + + SyncEstablishSharedSecret establish_shared_secret = 2; + SyncActionEncrypted encrypted = 5; } message SyncActionHandshake { @@ -144,6 +147,13 @@ message SyncStreamItem { string pairing_secret = 4; // optional one-time secret from a pairing token, used to auto-authorize a new client } + // SyncActionEncrypted wraps an encrypted SyncStreamItem. + // After ECDH key exchange, all subsequent messages are sent inside this envelope. + message SyncActionEncrypted { + bytes nonce = 1; // 12-byte GCM nonce + bytes ciphertext = 2; // AES-256-GCM(serialized SyncStreamItem) + } + // SyncActionHeartbeat is sent periodically to keep the connection alive. message SyncActionHeartbeat {} @@ -212,9 +222,11 @@ message SyncStreamItem { int64 delay_ms = 1; } + // SyncEstablishSharedSecret is exchanged immediately after the handshake. + // Each side sends an ephemeral ECDH P-256 public key. Both sides then perform + // ECDH to derive a shared AES-256-GCM session key. All subsequent messages + // must be wrapped in SyncActionEncrypted. message SyncEstablishSharedSecret { - // a one-time-use ECDSA public key with a matching unshared private key. Used to perform a key exchange. - // See https://pkg.go.dev/crypto/ecdh#PrivateKey.ECDH . - string ecdsa_pub = 2 [json_name="ecdsaPub"]; // base64 encoded public key + bytes ecdh_public_key = 1; // raw ECDH P-256 public key bytes } } diff --git a/webui/gen/ts/v1sync/syncservice_pb.ts b/webui/gen/ts/v1sync/syncservice_pb.ts index 96c37b6f..8f7ae557 100644 --- a/webui/gen/ts/v1sync/syncservice_pb.ts +++ b/webui/gen/ts/v1sync/syncservice_pb.ts @@ -21,7 +21,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file v1sync/syncservice.proto. */ export const file_v1sync_syncservice: GenFile = /*@__PURE__*/ - fileDesc("Chh2MXN5bmMvc3luY3NlcnZpY2UucHJvdG8SBnYxc3luYyIrChZTeW5jU3RhdGVTdHJlYW1SZXF1ZXN0EhEKCXN1YnNjcmliZRgBIAEoCCKbAgoJUGVlclN0YXRlEhgKEHBlZXJfaW5zdGFuY2VfaWQYASABKAkSEgoKcGVlcl9rZXlpZBgCIAEoCRImCgVzdGF0ZRgDIAEoDjIXLnYxc3luYy5Db25uZWN0aW9uU3RhdGUSFgoOc3RhdHVzX21lc3NhZ2UYBCABKAkSKQoLa25vd25fcGxhbnMYBSADKAsyFC52MXN5bmMuUGxhbk1ldGFkYXRhEikKC2tub3duX3JlcG9zGAYgAygLMhQudjFzeW5jLlJlcG9NZXRhZGF0YRIrCg1yZW1vdGVfY29uZmlnGAcgASgLMhQudjFzeW5jLlJlbW90ZUNvbmZpZxIdChVsYXN0X2hlYXJ0YmVhdF9taWxsaXMYCCABKAMiPQoTQXV0aGVudGljYXRlUmVxdWVzdBImCgtpbnN0YW5jZV9pZBgBIAEoCzIRLnYxLlNpZ25lZE1lc3NhZ2UiPgocR2V0T3BlcmF0aW9uTWV0YWRhdGFSZXNwb25zZRIOCgZvcF9pZHMYASADKAMSDgoGbW9kbm9zGAIgAygDIl0KDExvZ0RhdGFFbnRyeRIOCgZsb2dfaWQYASABKAkSEgoKb3duZXJfb3BpZBgCIAEoAxIaChJleHBpcmF0aW9uX3RzX3VuaXgYAyABKAMSDQoFY2h1bmsYBCABKAwiaAocU2V0QXZhaWxhYmxlUmVzb3VyY2VzUmVxdWVzdBIjCgVyZXBvcxgBIAMoCzIULnYxc3luYy5QbGFuTWV0YWRhdGESIwoFcGxhbnMYAiADKAsyFC52MXN5bmMuUmVwb01ldGFkYXRhIigKDFJlcG9NZXRhZGF0YRIKCgJpZBgBIAEoCRIMCgRndWlkGAIgASgJIhoKDFBsYW5NZXRhZGF0YRIKCgJpZBgBIAEoCSJ2ChBTZXRDb25maWdSZXF1ZXN0EhcKBXBsYW5zGAEgAygLMggudjEuUGxhbhIXCgVyZXBvcxgCIAMoCzIILnYxLlJlcG8SFwoPcmVwb3NfdG9fZGVsZXRlGAMgAygJEhcKD3BsYW5zX3RvX2RlbGV0ZRgEIAMoCSKWAQocU2V0UmVtb3RlQ2xpZW50Q29uZmlnUmVxdWVzdBISCgpwZWVyX2tleWlkGAEgASgJEhcKBXJlcG9zGAIgAygLMggudjEuUmVwbxIXCgVwbGFucxgDIAMoCzIILnYxLlBsYW4SFwoPcmVwb3NfdG9fZGVsZXRlGAQgAygJEhcKD3BsYW5zX3RvX2RlbGV0ZRgFIAMoCSIfCh1TZXRSZW1vdGVDbGllbnRDb25maWdSZXNwb25zZSJgCgxSZW1vdGVDb25maWcSDQoFbW9kbm8YASABKAUSDwoHdmVyc2lvbhgCIAEoBRIXCgVyZXBvcxgDIAMoCzIILnYxLlJlcG8SFwoFcGxhbnMYBCADKAsyCC52MS5QbGFuIl8KEkF1dGhvcml6YXRpb25Ub2tlbhIhCgpwdWJsaWNfa2V5GAEgASgLMg0udjEuUHVibGljS2V5EiYKC2luc3RhbmNlX2lkGAIgASgLMhEudjEuU2lnbmVkTWVzc2FnZSLIEAoOU3luY1N0cmVhbUl0ZW0SKwoOc2lnbmVkX21lc3NhZ2UYASABKAsyES52MS5TaWduZWRNZXNzYWdlSAASPwoJaGFuZHNoYWtlGAMgASgLMioudjFzeW5jLlN5bmNTdHJlYW1JdGVtLlN5bmNBY3Rpb25IYW5kc2hha2VIABI/CgloZWFydGJlYXQYBCABKAsyKi52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvbkhlYXJ0YmVhdEgAElAKEm9wZXJhdGlvbl9tYW5pZmVzdBgUIAEoCzIyLnYxc3luYy5TeW5jU3RyZWFtSXRlbS5TeW5jQWN0aW9uT3BlcmF0aW9uTWFuaWZlc3RIABJQChJyZWNlaXZlX29wZXJhdGlvbnMYFSABKAsyMi52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvblJlY2VpdmVPcGVyYXRpb25zSAASVwoWcmVxdWVzdF9vcGVyYXRpb25fZGF0YRgWIAEoCzI1LnYxc3luYy5TeW5jU3RyZWFtSXRlbS5TeW5jQWN0aW9uUmVxdWVzdE9wZXJhdGlvbkRhdGFIABJICg5yZWNlaXZlX2NvbmZpZxgXIAEoCzIuLnYxc3luYy5TeW5jU3RyZWFtSXRlbS5TeW5jQWN0aW9uUmVjZWl2ZUNvbmZpZ0gAEkAKCnNldF9jb25maWcYGCABKAsyKi52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvblNldENvbmZpZ0gAEk4KEXJlcXVlc3RfcmVzb3VyY2VzGBkgASgLMjEudjFzeW5jLlN5bmNTdHJlYW1JdGVtLlN5bmNBY3Rpb25SZXF1ZXN0UmVzb3VyY2VzSAASTgoRcmVjZWl2ZV9yZXNvdXJjZXMYGiABKAsyMS52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvblJlY2VpdmVSZXNvdXJjZXNIABJCCgtyZXF1ZXN0X2xvZxgeIAEoCzIrLnYxc3luYy5TeW5jU3RyZWFtSXRlbS5TeW5jQWN0aW9uUmVxdWVzdExvZ0gAEksKEHJlY2VpdmVfbG9nX2RhdGEYHyABKAsyLy52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvblJlY2VpdmVMb2dEYXRhSAASPgoIdGhyb3R0bGUY6AcgASgLMikudjFzeW5jLlN5bmNTdHJlYW1JdGVtLlN5bmNBY3Rpb25UaHJvdHRsZUgAGpIBChNTeW5jQWN0aW9uSGFuZHNoYWtlEhgKEHByb3RvY29sX3ZlcnNpb24YASABKAMSIQoKcHVibGljX2tleRgCIAEoCzINLnYxLlB1YmxpY0tleRImCgtpbnN0YW5jZV9pZBgDIAEoCzIRLnYxLlNpZ25lZE1lc3NhZ2USFgoOcGFpcmluZ19zZWNyZXQYBCABKAkaFQoTU3luY0FjdGlvbkhlYXJ0YmVhdBo/ChdTeW5jQWN0aW9uUmVjZWl2ZUNvbmZpZxIkCgZjb25maWcYASABKAsyFC52MXN5bmMuUmVtb3RlQ29uZmlnGnkKE1N5bmNBY3Rpb25TZXRDb25maWcSFwoFcmVwb3MYASADKAsyCC52MS5SZXBvEhcKBXBsYW5zGAIgAygLMggudjEuUGxhbhIXCg9yZXBvc190b19kZWxldGUYAyADKAkSFwoPcGxhbnNfdG9fZGVsZXRlGAQgAygJGhwKGlN5bmNBY3Rpb25SZXF1ZXN0UmVzb3VyY2VzGmYKGlN5bmNBY3Rpb25SZWNlaXZlUmVzb3VyY2VzEiMKBXJlcG9zGAEgAygLMhQudjFzeW5jLlJlcG9NZXRhZGF0YRIjCgVwbGFucxgCIAMoCzIULnYxc3luYy5QbGFuTWV0YWRhdGEaKAoVU3luY0FjdGlvbkNvbm5lY3RSZXBvEg8KB3JlcG9faWQYASABKAkaPQobU3luY0FjdGlvbk9wZXJhdGlvbk1hbmlmZXN0Eg4KBm9wX2lkcxgBIAMoAxIOCgZtb2Rub3MYAiADKAMaMAoeU3luY0FjdGlvblJlcXVlc3RPcGVyYXRpb25EYXRhEg4KBm9wX2lkcxgBIAMoAxpAChtTeW5jQWN0aW9uUmVjZWl2ZU9wZXJhdGlvbnMSIQoFZXZlbnQYASABKAsyEi52MS5PcGVyYXRpb25FdmVudBomChRTeW5jQWN0aW9uUmVxdWVzdExvZxIOCgZsb2dfaWQYASABKAkagAEKGFN5bmNBY3Rpb25SZWNlaXZlTG9nRGF0YRIOCgZsb2dfaWQYASABKAkSEgoKb3duZXJfb3BpZBgCIAEoAxIaChJleHBpcmF0aW9uX3RzX3VuaXgYAyABKAMSDQoFY2h1bmsYBCABKAwSFQoNZXJyb3JfbWVzc2FnZRgFIAEoCRomChJTeW5jQWN0aW9uVGhyb3R0bGUSEAoIZGVsYXlfbXMYASABKAMaLgoZU3luY0VzdGFibGlzaFNoYXJlZFNlY3JldBIRCgllY2RzYV9wdWIYAiABKAkitAEKE1JlcG9Db25uZWN0aW9uU3RhdGUSHAoYQ09OTkVDVElPTl9TVEFURV9VTktOT1dOEAASHAoYQ09OTkVDVElPTl9TVEFURV9QRU5ESU5HEAESHgoaQ09OTkVDVElPTl9TVEFURV9DT05ORUNURUQQAhIhCh1DT05ORUNUSU9OX1NUQVRFX1VOQVVUSE9SSVpFRBADEh4KGkNPTk5FQ1RJT05fU1RBVEVfTk9UX0ZPVU5EEARCCAoGYWN0aW9uKpwCCg9Db25uZWN0aW9uU3RhdGUSHAoYQ09OTkVDVElPTl9TVEFURV9VTktOT1dOEAASHAoYQ09OTkVDVElPTl9TVEFURV9QRU5ESU5HEAESHgoaQ09OTkVDVElPTl9TVEFURV9DT05ORUNURUQQAhIhCh1DT05ORUNUSU9OX1NUQVRFX0RJU0NPTk5FQ1RFRBADEh8KG0NPTk5FQ1RJT05fU1RBVEVfUkVUUllfV0FJVBAEEh8KG0NPTk5FQ1RJT05fU1RBVEVfRVJST1JfQVVUSBAKEiMKH0NPTk5FQ1RJT05fU1RBVEVfRVJST1JfUFJPVE9DT0wQCxIjCh9DT05ORUNUSU9OX1NUQVRFX0VSUk9SX0lOVEVSTkFMEAwyUwoTQmFja3Jlc3RTeW5jU2VydmljZRI8CgRTeW5jEhYudjFzeW5jLlN5bmNTdHJlYW1JdGVtGhYudjFzeW5jLlN5bmNTdHJlYW1JdGVtIgAoATABMtQBChhCYWNrcmVzdFN5bmNTdGF0ZVNlcnZpY2USUAoXR2V0UGVlclN5bmNTdGF0ZXNTdHJlYW0SHi52MXN5bmMuU3luY1N0YXRlU3RyZWFtUmVxdWVzdBoRLnYxc3luYy5QZWVyU3RhdGUiADABEmYKFVNldFJlbW90ZUNsaWVudENvbmZpZxIkLnYxc3luYy5TZXRSZW1vdGVDbGllbnRDb25maWdSZXF1ZXN0GiUudjFzeW5jLlNldFJlbW90ZUNsaWVudENvbmZpZ1Jlc3BvbnNlIgBCMFouZ2l0aHViLmNvbS9nYXJldGhnZW9yZ2UvYmFja3Jlc3QvZ2VuL2dvL3Yxc3luY2IGcHJvdG8z", [file_v1_config, file_v1_crypto, file_v1_restic, file_v1_service, file_v1_operations, file_types_value, file_google_protobuf_empty, file_google_api_annotations, file_google_protobuf_any]); + fileDesc("Chh2MXN5bmMvc3luY3NlcnZpY2UucHJvdG8SBnYxc3luYyIrChZTeW5jU3RhdGVTdHJlYW1SZXF1ZXN0EhEKCXN1YnNjcmliZRgBIAEoCCKbAgoJUGVlclN0YXRlEhgKEHBlZXJfaW5zdGFuY2VfaWQYASABKAkSEgoKcGVlcl9rZXlpZBgCIAEoCRImCgVzdGF0ZRgDIAEoDjIXLnYxc3luYy5Db25uZWN0aW9uU3RhdGUSFgoOc3RhdHVzX21lc3NhZ2UYBCABKAkSKQoLa25vd25fcGxhbnMYBSADKAsyFC52MXN5bmMuUGxhbk1ldGFkYXRhEikKC2tub3duX3JlcG9zGAYgAygLMhQudjFzeW5jLlJlcG9NZXRhZGF0YRIrCg1yZW1vdGVfY29uZmlnGAcgASgLMhQudjFzeW5jLlJlbW90ZUNvbmZpZxIdChVsYXN0X2hlYXJ0YmVhdF9taWxsaXMYCCABKAMiPQoTQXV0aGVudGljYXRlUmVxdWVzdBImCgtpbnN0YW5jZV9pZBgBIAEoCzIRLnYxLlNpZ25lZE1lc3NhZ2UiPgocR2V0T3BlcmF0aW9uTWV0YWRhdGFSZXNwb25zZRIOCgZvcF9pZHMYASADKAMSDgoGbW9kbm9zGAIgAygDIl0KDExvZ0RhdGFFbnRyeRIOCgZsb2dfaWQYASABKAkSEgoKb3duZXJfb3BpZBgCIAEoAxIaChJleHBpcmF0aW9uX3RzX3VuaXgYAyABKAMSDQoFY2h1bmsYBCABKAwiaAocU2V0QXZhaWxhYmxlUmVzb3VyY2VzUmVxdWVzdBIjCgVyZXBvcxgBIAMoCzIULnYxc3luYy5QbGFuTWV0YWRhdGESIwoFcGxhbnMYAiADKAsyFC52MXN5bmMuUmVwb01ldGFkYXRhIigKDFJlcG9NZXRhZGF0YRIKCgJpZBgBIAEoCRIMCgRndWlkGAIgASgJIhoKDFBsYW5NZXRhZGF0YRIKCgJpZBgBIAEoCSJ2ChBTZXRDb25maWdSZXF1ZXN0EhcKBXBsYW5zGAEgAygLMggudjEuUGxhbhIXCgVyZXBvcxgCIAMoCzIILnYxLlJlcG8SFwoPcmVwb3NfdG9fZGVsZXRlGAMgAygJEhcKD3BsYW5zX3RvX2RlbGV0ZRgEIAMoCSKWAQocU2V0UmVtb3RlQ2xpZW50Q29uZmlnUmVxdWVzdBISCgpwZWVyX2tleWlkGAEgASgJEhcKBXJlcG9zGAIgAygLMggudjEuUmVwbxIXCgVwbGFucxgDIAMoCzIILnYxLlBsYW4SFwoPcmVwb3NfdG9fZGVsZXRlGAQgAygJEhcKD3BsYW5zX3RvX2RlbGV0ZRgFIAMoCSIfCh1TZXRSZW1vdGVDbGllbnRDb25maWdSZXNwb25zZSJgCgxSZW1vdGVDb25maWcSDQoFbW9kbm8YASABKAUSDwoHdmVyc2lvbhgCIAEoBRIXCgVyZXBvcxgDIAMoCzIILnYxLlJlcG8SFwoFcGxhbnMYBCADKAsyCC52MS5QbGFuIl8KEkF1dGhvcml6YXRpb25Ub2tlbhIhCgpwdWJsaWNfa2V5GAEgASgLMg0udjEuUHVibGljS2V5EiYKC2luc3RhbmNlX2lkGAIgASgLMhEudjEuU2lnbmVkTWVzc2FnZSKeEgoOU3luY1N0cmVhbUl0ZW0SKwoOc2lnbmVkX21lc3NhZ2UYASABKAsyES52MS5TaWduZWRNZXNzYWdlSAASPwoJaGFuZHNoYWtlGAMgASgLMioudjFzeW5jLlN5bmNTdHJlYW1JdGVtLlN5bmNBY3Rpb25IYW5kc2hha2VIABI/CgloZWFydGJlYXQYBCABKAsyKi52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvbkhlYXJ0YmVhdEgAElAKEm9wZXJhdGlvbl9tYW5pZmVzdBgUIAEoCzIyLnYxc3luYy5TeW5jU3RyZWFtSXRlbS5TeW5jQWN0aW9uT3BlcmF0aW9uTWFuaWZlc3RIABJQChJyZWNlaXZlX29wZXJhdGlvbnMYFSABKAsyMi52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvblJlY2VpdmVPcGVyYXRpb25zSAASVwoWcmVxdWVzdF9vcGVyYXRpb25fZGF0YRgWIAEoCzI1LnYxc3luYy5TeW5jU3RyZWFtSXRlbS5TeW5jQWN0aW9uUmVxdWVzdE9wZXJhdGlvbkRhdGFIABJICg5yZWNlaXZlX2NvbmZpZxgXIAEoCzIuLnYxc3luYy5TeW5jU3RyZWFtSXRlbS5TeW5jQWN0aW9uUmVjZWl2ZUNvbmZpZ0gAEkAKCnNldF9jb25maWcYGCABKAsyKi52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvblNldENvbmZpZ0gAEk4KEXJlcXVlc3RfcmVzb3VyY2VzGBkgASgLMjEudjFzeW5jLlN5bmNTdHJlYW1JdGVtLlN5bmNBY3Rpb25SZXF1ZXN0UmVzb3VyY2VzSAASTgoRcmVjZWl2ZV9yZXNvdXJjZXMYGiABKAsyMS52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvblJlY2VpdmVSZXNvdXJjZXNIABJCCgtyZXF1ZXN0X2xvZxgeIAEoCzIrLnYxc3luYy5TeW5jU3RyZWFtSXRlbS5TeW5jQWN0aW9uUmVxdWVzdExvZ0gAEksKEHJlY2VpdmVfbG9nX2RhdGEYHyABKAsyLy52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvblJlY2VpdmVMb2dEYXRhSAASPgoIdGhyb3R0bGUY6AcgASgLMikudjFzeW5jLlN5bmNTdHJlYW1JdGVtLlN5bmNBY3Rpb25UaHJvdHRsZUgAElMKF2VzdGFibGlzaF9zaGFyZWRfc2VjcmV0GAIgASgLMjAudjFzeW5jLlN5bmNTdHJlYW1JdGVtLlN5bmNFc3RhYmxpc2hTaGFyZWRTZWNyZXRIABI/CgllbmNyeXB0ZWQYBSABKAsyKi52MXN5bmMuU3luY1N0cmVhbUl0ZW0uU3luY0FjdGlvbkVuY3J5cHRlZEgAGpIBChNTeW5jQWN0aW9uSGFuZHNoYWtlEhgKEHByb3RvY29sX3ZlcnNpb24YASABKAMSIQoKcHVibGljX2tleRgCIAEoCzINLnYxLlB1YmxpY0tleRImCgtpbnN0YW5jZV9pZBgDIAEoCzIRLnYxLlNpZ25lZE1lc3NhZ2USFgoOcGFpcmluZ19zZWNyZXQYBCABKAkaOAoTU3luY0FjdGlvbkVuY3J5cHRlZBINCgVub25jZRgBIAEoDBISCgpjaXBoZXJ0ZXh0GAIgASgMGhUKE1N5bmNBY3Rpb25IZWFydGJlYXQaPwoXU3luY0FjdGlvblJlY2VpdmVDb25maWcSJAoGY29uZmlnGAEgASgLMhQudjFzeW5jLlJlbW90ZUNvbmZpZxp5ChNTeW5jQWN0aW9uU2V0Q29uZmlnEhcKBXJlcG9zGAEgAygLMggudjEuUmVwbxIXCgVwbGFucxgCIAMoCzIILnYxLlBsYW4SFwoPcmVwb3NfdG9fZGVsZXRlGAMgAygJEhcKD3BsYW5zX3RvX2RlbGV0ZRgEIAMoCRocChpTeW5jQWN0aW9uUmVxdWVzdFJlc291cmNlcxpmChpTeW5jQWN0aW9uUmVjZWl2ZVJlc291cmNlcxIjCgVyZXBvcxgBIAMoCzIULnYxc3luYy5SZXBvTWV0YWRhdGESIwoFcGxhbnMYAiADKAsyFC52MXN5bmMuUGxhbk1ldGFkYXRhGigKFVN5bmNBY3Rpb25Db25uZWN0UmVwbxIPCgdyZXBvX2lkGAEgASgJGj0KG1N5bmNBY3Rpb25PcGVyYXRpb25NYW5pZmVzdBIOCgZvcF9pZHMYASADKAMSDgoGbW9kbm9zGAIgAygDGjAKHlN5bmNBY3Rpb25SZXF1ZXN0T3BlcmF0aW9uRGF0YRIOCgZvcF9pZHMYASADKAMaQAobU3luY0FjdGlvblJlY2VpdmVPcGVyYXRpb25zEiEKBWV2ZW50GAEgASgLMhIudjEuT3BlcmF0aW9uRXZlbnQaJgoUU3luY0FjdGlvblJlcXVlc3RMb2cSDgoGbG9nX2lkGAEgASgJGoABChhTeW5jQWN0aW9uUmVjZWl2ZUxvZ0RhdGESDgoGbG9nX2lkGAEgASgJEhIKCm93bmVyX29waWQYAiABKAMSGgoSZXhwaXJhdGlvbl90c191bml4GAMgASgDEg0KBWNodW5rGAQgASgMEhUKDWVycm9yX21lc3NhZ2UYBSABKAkaJgoSU3luY0FjdGlvblRocm90dGxlEhAKCGRlbGF5X21zGAEgASgDGjQKGVN5bmNFc3RhYmxpc2hTaGFyZWRTZWNyZXQSFwoPZWNkaF9wdWJsaWNfa2V5GAEgASgMIrQBChNSZXBvQ29ubmVjdGlvblN0YXRlEhwKGENPTk5FQ1RJT05fU1RBVEVfVU5LTk9XThAAEhwKGENPTk5FQ1RJT05fU1RBVEVfUEVORElORxABEh4KGkNPTk5FQ1RJT05fU1RBVEVfQ09OTkVDVEVEEAISIQodQ09OTkVDVElPTl9TVEFURV9VTkFVVEhPUklaRUQQAxIeChpDT05ORUNUSU9OX1NUQVRFX05PVF9GT1VORBAEQggKBmFjdGlvbiqcAgoPQ29ubmVjdGlvblN0YXRlEhwKGENPTk5FQ1RJT05fU1RBVEVfVU5LTk9XThAAEhwKGENPTk5FQ1RJT05fU1RBVEVfUEVORElORxABEh4KGkNPTk5FQ1RJT05fU1RBVEVfQ09OTkVDVEVEEAISIQodQ09OTkVDVElPTl9TVEFURV9ESVNDT05ORUNURUQQAxIfChtDT05ORUNUSU9OX1NUQVRFX1JFVFJZX1dBSVQQBBIfChtDT05ORUNUSU9OX1NUQVRFX0VSUk9SX0FVVEgQChIjCh9DT05ORUNUSU9OX1NUQVRFX0VSUk9SX1BST1RPQ09MEAsSIwofQ09OTkVDVElPTl9TVEFURV9FUlJPUl9JTlRFUk5BTBAMMlMKE0JhY2tyZXN0U3luY1NlcnZpY2USPAoEU3luYxIWLnYxc3luYy5TeW5jU3RyZWFtSXRlbRoWLnYxc3luYy5TeW5jU3RyZWFtSXRlbSIAKAEwATLUAQoYQmFja3Jlc3RTeW5jU3RhdGVTZXJ2aWNlElAKF0dldFBlZXJTeW5jU3RhdGVzU3RyZWFtEh4udjFzeW5jLlN5bmNTdGF0ZVN0cmVhbVJlcXVlc3QaES52MXN5bmMuUGVlclN0YXRlIgAwARJmChVTZXRSZW1vdGVDbGllbnRDb25maWcSJC52MXN5bmMuU2V0UmVtb3RlQ2xpZW50Q29uZmlnUmVxdWVzdBolLnYxc3luYy5TZXRSZW1vdGVDbGllbnRDb25maWdSZXNwb25zZSIAQjBaLmdpdGh1Yi5jb20vZ2FyZXRoZ2VvcmdlL2JhY2tyZXN0L2dlbi9nby92MXN5bmNiBnByb3RvMw", [file_v1_config, file_v1_crypto, file_v1_restic, file_v1_service, file_v1_operations, file_types_value, file_google_protobuf_empty, file_google_api_annotations, file_google_protobuf_any]); /** * @generated from message v1sync.SyncStateStreamRequest @@ -503,6 +503,18 @@ export type SyncStreamItem = Message<"v1sync.SyncStreamItem"> & { */ value: SyncStreamItem_SyncActionThrottle; case: "throttle"; + } | { + /** + * @generated from field: v1sync.SyncStreamItem.SyncEstablishSharedSecret establish_shared_secret = 2; + */ + value: SyncStreamItem_SyncEstablishSharedSecret; + case: "establishSharedSecret"; + } | { + /** + * @generated from field: v1sync.SyncStreamItem.SyncActionEncrypted encrypted = 5; + */ + value: SyncStreamItem_SyncActionEncrypted; + case: "encrypted"; } | { case: undefined; value?: undefined }; }; @@ -547,6 +559,35 @@ export type SyncStreamItem_SyncActionHandshake = Message<"v1sync.SyncStreamItem. export const SyncStreamItem_SyncActionHandshakeSchema: GenMessage = /*@__PURE__*/ messageDesc(file_v1sync_syncservice, 13, 0); +/** + * SyncActionEncrypted wraps an encrypted SyncStreamItem. + * After ECDH key exchange, all subsequent messages are sent inside this envelope. + * + * @generated from message v1sync.SyncStreamItem.SyncActionEncrypted + */ +export type SyncStreamItem_SyncActionEncrypted = Message<"v1sync.SyncStreamItem.SyncActionEncrypted"> & { + /** + * 12-byte GCM nonce + * + * @generated from field: bytes nonce = 1; + */ + nonce: Uint8Array; + + /** + * AES-256-GCM(serialized SyncStreamItem) + * + * @generated from field: bytes ciphertext = 2; + */ + ciphertext: Uint8Array; +}; + +/** + * Describes the message v1sync.SyncStreamItem.SyncActionEncrypted. + * Use `create(SyncStreamItem_SyncActionEncryptedSchema)` to create a new message. + */ +export const SyncStreamItem_SyncActionEncryptedSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_v1sync_syncservice, 13, 1); + /** * SyncActionHeartbeat is sent periodically to keep the connection alive. * @@ -560,7 +601,7 @@ export type SyncStreamItem_SyncActionHeartbeat = Message<"v1sync.SyncStreamItem. * Use `create(SyncStreamItem_SyncActionHeartbeatSchema)` to create a new message. */ export const SyncStreamItem_SyncActionHeartbeatSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_v1sync_syncservice, 13, 1); + messageDesc(file_v1sync_syncservice, 13, 2); /** * @generated from message v1sync.SyncStreamItem.SyncActionReceiveConfig @@ -577,7 +618,7 @@ export type SyncStreamItem_SyncActionReceiveConfig = Message<"v1sync.SyncStreamI * Use `create(SyncStreamItem_SyncActionReceiveConfigSchema)` to create a new message. */ export const SyncStreamItem_SyncActionReceiveConfigSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_v1sync_syncservice, 13, 2); + messageDesc(file_v1sync_syncservice, 13, 3); /** * @generated from message v1sync.SyncStreamItem.SyncActionSetConfig @@ -609,7 +650,7 @@ export type SyncStreamItem_SyncActionSetConfig = Message<"v1sync.SyncStreamItem. * Use `create(SyncStreamItem_SyncActionSetConfigSchema)` to create a new message. */ export const SyncStreamItem_SyncActionSetConfigSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_v1sync_syncservice, 13, 3); + messageDesc(file_v1sync_syncservice, 13, 4); /** * @generated from message v1sync.SyncStreamItem.SyncActionRequestResources @@ -622,7 +663,7 @@ export type SyncStreamItem_SyncActionRequestResources = Message<"v1sync.SyncStre * Use `create(SyncStreamItem_SyncActionRequestResourcesSchema)` to create a new message. */ export const SyncStreamItem_SyncActionRequestResourcesSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_v1sync_syncservice, 13, 4); + messageDesc(file_v1sync_syncservice, 13, 5); /** * @generated from message v1sync.SyncStreamItem.SyncActionReceiveResources @@ -644,7 +685,7 @@ export type SyncStreamItem_SyncActionReceiveResources = Message<"v1sync.SyncStre * Use `create(SyncStreamItem_SyncActionReceiveResourcesSchema)` to create a new message. */ export const SyncStreamItem_SyncActionReceiveResourcesSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_v1sync_syncservice, 13, 5); + messageDesc(file_v1sync_syncservice, 13, 6); /** * @generated from message v1sync.SyncStreamItem.SyncActionConnectRepo @@ -661,7 +702,7 @@ export type SyncStreamItem_SyncActionConnectRepo = Message<"v1sync.SyncStreamIte * Use `create(SyncStreamItem_SyncActionConnectRepoSchema)` to create a new message. */ export const SyncStreamItem_SyncActionConnectRepoSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_v1sync_syncservice, 13, 6); + messageDesc(file_v1sync_syncservice, 13, 7); /** * @generated from message v1sync.SyncStreamItem.SyncActionOperationManifest @@ -683,7 +724,7 @@ export type SyncStreamItem_SyncActionOperationManifest = Message<"v1sync.SyncStr * Use `create(SyncStreamItem_SyncActionOperationManifestSchema)` to create a new message. */ export const SyncStreamItem_SyncActionOperationManifestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_v1sync_syncservice, 13, 7); + messageDesc(file_v1sync_syncservice, 13, 8); /** * @generated from message v1sync.SyncStreamItem.SyncActionRequestOperationData @@ -700,7 +741,7 @@ export type SyncStreamItem_SyncActionRequestOperationData = Message<"v1sync.Sync * Use `create(SyncStreamItem_SyncActionRequestOperationDataSchema)` to create a new message. */ export const SyncStreamItem_SyncActionRequestOperationDataSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_v1sync_syncservice, 13, 8); + messageDesc(file_v1sync_syncservice, 13, 9); /** * @generated from message v1sync.SyncStreamItem.SyncActionReceiveOperations @@ -717,7 +758,7 @@ export type SyncStreamItem_SyncActionReceiveOperations = Message<"v1sync.SyncStr * Use `create(SyncStreamItem_SyncActionReceiveOperationsSchema)` to create a new message. */ export const SyncStreamItem_SyncActionReceiveOperationsSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_v1sync_syncservice, 13, 9); + messageDesc(file_v1sync_syncservice, 13, 10); /** * @generated from message v1sync.SyncStreamItem.SyncActionRequestLog @@ -734,7 +775,7 @@ export type SyncStreamItem_SyncActionRequestLog = Message<"v1sync.SyncStreamItem * Use `create(SyncStreamItem_SyncActionRequestLogSchema)` to create a new message. */ export const SyncStreamItem_SyncActionRequestLogSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_v1sync_syncservice, 13, 10); + messageDesc(file_v1sync_syncservice, 13, 11); /** * @generated from message v1sync.SyncStreamItem.SyncActionReceiveLogData @@ -781,7 +822,7 @@ export type SyncStreamItem_SyncActionReceiveLogData = Message<"v1sync.SyncStream * Use `create(SyncStreamItem_SyncActionReceiveLogDataSchema)` to create a new message. */ export const SyncStreamItem_SyncActionReceiveLogDataSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_v1sync_syncservice, 13, 11); + messageDesc(file_v1sync_syncservice, 13, 12); /** * @generated from message v1sync.SyncStreamItem.SyncActionThrottle @@ -798,21 +839,23 @@ export type SyncStreamItem_SyncActionThrottle = Message<"v1sync.SyncStreamItem.S * Use `create(SyncStreamItem_SyncActionThrottleSchema)` to create a new message. */ export const SyncStreamItem_SyncActionThrottleSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_v1sync_syncservice, 13, 12); + messageDesc(file_v1sync_syncservice, 13, 13); /** + * SyncEstablishSharedSecret is exchanged immediately after the handshake. + * Each side sends an ephemeral ECDH P-256 public key. Both sides then perform + * ECDH to derive a shared AES-256-GCM session key. All subsequent messages + * must be wrapped in SyncActionEncrypted. + * * @generated from message v1sync.SyncStreamItem.SyncEstablishSharedSecret */ export type SyncStreamItem_SyncEstablishSharedSecret = Message<"v1sync.SyncStreamItem.SyncEstablishSharedSecret"> & { /** - * a one-time-use ECDSA public key with a matching unshared private key. Used to perform a key exchange. - * See https://pkg.go.dev/crypto/ecdh#PrivateKey.ECDH . + * raw ECDH P-256 public key bytes * - * base64 encoded public key - * - * @generated from field: string ecdsa_pub = 2; + * @generated from field: bytes ecdh_public_key = 1; */ - ecdsaPub: string; + ecdhPublicKey: Uint8Array; }; /** @@ -820,7 +863,7 @@ export type SyncStreamItem_SyncEstablishSharedSecret = Message<"v1sync.SyncStrea * Use `create(SyncStreamItem_SyncEstablishSharedSecretSchema)` to create a new message. */ export const SyncStreamItem_SyncEstablishSharedSecretSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_v1sync_syncservice, 13, 13); + messageDesc(file_v1sync_syncservice, 13, 14); /** * @generated from enum v1sync.SyncStreamItem.RepoConnectionState