all: Add untrusted folders behind feature flag (ref #62) (#7055)

This commit is contained in:
Simon Frei
2020-11-09 15:33:32 +01:00
committed by GitHub
parent 4db5ea5893
commit 31559e908b
59 changed files with 6952 additions and 1111 deletions
+3 -3
View File
@@ -81,9 +81,9 @@ func benchmarkRequestsConnPair(b *testing.B, conn0, conn1 net.Conn) {
// Use c0 and c1 for each alternating request, so we get as much
// data flowing in both directions.
if i%2 == 0 {
buf, err = c0.Request(context.Background(), "folder", "file", int64(i), 128<<10, nil, 0, false)
buf, err = c0.Request(context.Background(), "folder", "file", i, int64(i), 128<<10, nil, 0, false)
} else {
buf, err = c1.Request(context.Background(), "folder", "file", int64(i), 128<<10, nil, 0, false)
buf, err = c1.Request(context.Background(), "folder", "file", i, int64(i), 128<<10, nil, 0, false)
}
if err != nil {
@@ -174,7 +174,7 @@ func (m *fakeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileIn
return nil
}
func (m *fakeModel) Request(deviceID DeviceID, folder, name string, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) {
func (m *fakeModel) Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) {
// We write the offset to the end of the buffer, so the receiver
// can verify that it did in fact get some data back over the
// connection.
+291 -163
View File
@@ -378,6 +378,7 @@ type Device struct {
Introducer bool `protobuf:"varint,7,opt,name=introducer,proto3" json:"introducer" xml:"introducer"`
IndexID IndexID `protobuf:"varint,8,opt,name=index_id,json=indexId,proto3,customtype=IndexID" json:"indexId" xml:"indexId"`
SkipIntroductionRemovals bool `protobuf:"varint,9,opt,name=skip_introduction_removals,json=skipIntroductionRemovals,proto3" json:"skipIntroductionRemovals" xml:"skipIntroductionRemovals"`
EncryptionPasswordToken []byte `protobuf:"bytes,10,opt,name=encryption_password_token,json=encryptionPasswordToken,proto3" json:"encryptionPasswordToken" xml:"encryptionPasswordToken"`
}
func (m *Device) Reset() { *m = Device{} }
@@ -499,6 +500,7 @@ type FileInfo struct {
Blocks []BlockInfo `protobuf:"bytes,16,rep,name=blocks,proto3" json:"blocks" xml:"block"`
SymlinkTarget string `protobuf:"bytes,17,opt,name=symlink_target,json=symlinkTarget,proto3" json:"symlinkTarget" xml:"symlinkTarget"`
BlocksHash []byte `protobuf:"bytes,18,opt,name=blocks_hash,json=blocksHash,proto3" json:"blocksHash" xml:"blocksHash"`
Encrypted []byte `protobuf:"bytes,19,opt,name=encrypted,proto3" json:"encrypted" xml:"encrypted"`
Type FileInfoType `protobuf:"varint,2,opt,name=type,proto3,enum=protocol.FileInfoType" json:"type" xml:"type"`
Permissions uint32 `protobuf:"varint,4,opt,name=permissions,proto3" json:"permissions" xml:"permissions"`
ModifiedNs int `protobuf:"varint,11,opt,name=modified_ns,json=modifiedNs,proto3,casttype=int" json:"modifiedNs" xml:"modifiedNs"`
@@ -671,6 +673,7 @@ type Request struct {
Hash []byte `protobuf:"bytes,6,opt,name=hash,proto3" json:"hash" xml:"hash"`
FromTemporary bool `protobuf:"varint,7,opt,name=from_temporary,json=fromTemporary,proto3" json:"fromTemporary" xml:"fromTemporary"`
WeakHash uint32 `protobuf:"varint,8,opt,name=weak_hash,json=weakHash,proto3" json:"weakHash" xml:"weakHash"`
BlockNo int `protobuf:"varint,9,opt,name=block_no,json=blockNo,proto3,casttype=int" json:"blockNo" xml:"blockNo"`
}
func (m *Request) Reset() { *m = Request{} }
@@ -926,169 +929,175 @@ func init() {
func init() { proto.RegisterFile("lib/protocol/bep.proto", fileDescriptor_311ef540e10d9705) }
var fileDescriptor_311ef540e10d9705 = []byte{
// 2584 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0x4d, 0x6c, 0xe3, 0xc6,
0xf5, 0x17, 0xf5, 0x61, 0xcb, 0x63, 0x7b, 0x23, 0xcf, 0x7e, 0x29, 0xda, 0x8d, 0xa8, 0xff, 0xfc,
0x37, 0xad, 0xe3, 0x34, 0xde, 0xc4, 0x49, 0xda, 0x34, 0x49, 0x13, 0xe8, 0x83, 0xb6, 0x95, 0xd8,
0x92, 0x3b, 0xf2, 0x6e, 0xba, 0x8b, 0x16, 0x04, 0x2d, 0x8e, 0x6d, 0x62, 0x29, 0x52, 0x25, 0x65,
0xef, 0x3a, 0xe8, 0xa9, 0xbd, 0x04, 0x3a, 0x14, 0x45, 0x4e, 0x45, 0x51, 0xa1, 0x69, 0x2f, 0xbd,
0x15, 0xe8, 0xa1, 0x97, 0xdc, 0x7a, 0x29, 0x72, 0x5c, 0x04, 0x28, 0x50, 0xf4, 0x40, 0x20, 0xbb,
0x97, 0x56, 0x47, 0x1d, 0x7b, 0x2a, 0xe6, 0x83, 0xe4, 0xd0, 0xb2, 0x03, 0x27, 0x39, 0xf4, 0xc6,
0xf7, 0x7b, 0x1f, 0x1c, 0xce, 0xfb, 0xbd, 0x37, 0x6f, 0x24, 0x70, 0xcd, 0xb6, 0xf6, 0x6e, 0xf7,
0x3d, 0x77, 0xe0, 0x76, 0x5d, 0xfb, 0xf6, 0x1e, 0xe9, 0xaf, 0x32, 0x01, 0xe6, 0x43, 0xac, 0x34,
0x47, 0x1e, 0x0d, 0x38, 0x58, 0xfa, 0x7f, 0x8f, 0xf4, 0x5d, 0x9f, 0x9b, 0xef, 0x1d, 0xed, 0xdf,
0x3e, 0x70, 0x0f, 0x5c, 0x26, 0xb0, 0x27, 0x6e, 0x84, 0x9e, 0x28, 0x20, 0xb7, 0x49, 0x6c, 0xdb,
0x85, 0x75, 0x30, 0x6f, 0x92, 0x63, 0xab, 0x4b, 0x74, 0xc7, 0xe8, 0x91, 0xa2, 0x52, 0x51, 0x96,
0xe7, 0x6a, 0x68, 0x1c, 0xa8, 0x80, 0xc3, 0x2d, 0xa3, 0x47, 0x26, 0x81, 0x5a, 0x78, 0xd4, 0xb3,
0xdf, 0x44, 0x31, 0x84, 0xb0, 0xa4, 0xa7, 0x41, 0xba, 0xb6, 0x45, 0x9c, 0x01, 0x0f, 0x92, 0x8e,
0x83, 0x70, 0x38, 0x11, 0x24, 0x86, 0x10, 0x96, 0xf4, 0xb0, 0x0d, 0x2e, 0x89, 0x20, 0xc7, 0xc4,
0xf3, 0x2d, 0xd7, 0x29, 0x66, 0x58, 0x9c, 0xe5, 0x71, 0xa0, 0x2e, 0x72, 0xcd, 0x5d, 0xae, 0x98,
0x04, 0xea, 0x65, 0x29, 0x94, 0x40, 0x11, 0x4e, 0x5a, 0xa1, 0x3f, 0x2b, 0x60, 0x66, 0x93, 0x18,
0x26, 0xf1, 0x60, 0x15, 0x64, 0x07, 0x27, 0x7d, 0xfe, 0x79, 0x97, 0xd6, 0xae, 0xae, 0x86, 0x1b,
0xb7, 0xba, 0x4d, 0x7c, 0xdf, 0x38, 0x20, 0xbb, 0x27, 0x7d, 0x52, 0xbb, 0x36, 0x0e, 0x54, 0x66,
0x36, 0x09, 0x54, 0xc0, 0xe2, 0x53, 0x01, 0x61, 0x86, 0x41, 0x13, 0xcc, 0x77, 0xdd, 0x5e, 0xdf,
0x23, 0x3e, 0x5b, 0x5b, 0x9a, 0x45, 0xba, 0x39, 0x15, 0xa9, 0x1e, 0xdb, 0xd4, 0x6e, 0x8d, 0x03,
0x55, 0x76, 0x9a, 0x04, 0xea, 0x12, 0x5f, 0x77, 0x8c, 0x21, 0x2c, 0x5b, 0xa0, 0x1f, 0x83, 0xc5,
0xba, 0x7d, 0xe4, 0x0f, 0x88, 0x57, 0x77, 0x9d, 0x7d, 0xeb, 0x00, 0xbe, 0x0f, 0x66, 0xf7, 0x5d,
0xdb, 0x24, 0x9e, 0x5f, 0x54, 0x2a, 0x99, 0xe5, 0xf9, 0xb5, 0x42, 0xfc, 0xca, 0x75, 0xa6, 0xa8,
0xa9, 0x9f, 0x05, 0x6a, 0x6a, 0x1c, 0xa8, 0xa1, 0xe1, 0x24, 0x50, 0x17, 0xd8, 0x6b, 0xb8, 0x8c,
0x70, 0xa8, 0x40, 0x9f, 0x66, 0xc1, 0x0c, 0x77, 0x82, 0xab, 0x20, 0x6d, 0x99, 0x22, 0xdd, 0xe5,
0x27, 0x81, 0x9a, 0x6e, 0x36, 0xc6, 0x81, 0x9a, 0xb6, 0xcc, 0x49, 0xa0, 0xe6, 0x99, 0xb7, 0x65,
0xa2, 0x8f, 0x1f, 0xdf, 0x4a, 0x37, 0x1b, 0x38, 0x6d, 0x99, 0x70, 0x15, 0xe4, 0x6c, 0x63, 0x8f,
0xd8, 0x22, 0xb9, 0xc5, 0x71, 0xa0, 0x72, 0x60, 0x12, 0xa8, 0xf3, 0xcc, 0x9e, 0x49, 0x08, 0x73,
0x14, 0xbe, 0x05, 0xe6, 0x3c, 0x62, 0x98, 0xba, 0xeb, 0xd8, 0x27, 0x2c, 0x91, 0xf9, 0x5a, 0x79,
0x1c, 0xa8, 0x79, 0x0a, 0xb6, 0x1d, 0xfb, 0x64, 0x12, 0xa8, 0x97, 0x98, 0x5b, 0x08, 0x20, 0x1c,
0xe9, 0xa0, 0x0e, 0xa0, 0x75, 0xe0, 0xb8, 0x1e, 0xd1, 0xfb, 0xc4, 0xeb, 0x59, 0x6c, 0x6b, 0xfc,
0x62, 0x96, 0x45, 0x79, 0x79, 0x1c, 0xa8, 0x4b, 0x5c, 0xbb, 0x13, 0x2b, 0x27, 0x81, 0x7a, 0x9d,
0xaf, 0xfa, 0xb4, 0x06, 0xe1, 0x69, 0x6b, 0xf8, 0x3e, 0x58, 0x14, 0x2f, 0x30, 0x89, 0x4d, 0x06,
0xa4, 0x98, 0x63, 0xb1, 0xbf, 0x35, 0x0e, 0xd4, 0x05, 0xae, 0x68, 0x30, 0x7c, 0x12, 0xa8, 0x50,
0x0a, 0xcb, 0x41, 0x84, 0x13, 0x36, 0xd0, 0x04, 0x57, 0x4c, 0xcb, 0x37, 0xf6, 0x6c, 0xa2, 0x0f,
0x48, 0xaf, 0xaf, 0x5b, 0x8e, 0x49, 0x1e, 0x11, 0xbf, 0x38, 0xc3, 0x62, 0xae, 0x8d, 0x03, 0x15,
0x0a, 0xfd, 0x2e, 0xe9, 0xf5, 0x9b, 0x5c, 0x3b, 0x09, 0xd4, 0x22, 0xaf, 0xa9, 0x29, 0x15, 0xc2,
0x67, 0xd8, 0xc3, 0x35, 0x30, 0xd3, 0x37, 0x8e, 0x7c, 0x62, 0x16, 0x67, 0x59, 0xdc, 0xd2, 0x38,
0x50, 0x05, 0x12, 0x25, 0x9c, 0x8b, 0x08, 0x0b, 0x9c, 0x92, 0x87, 0x57, 0xa9, 0x5f, 0x2c, 0x9c,
0x26, 0x4f, 0x83, 0x29, 0x62, 0xf2, 0x08, 0xc3, 0x28, 0x16, 0x97, 0x11, 0x0e, 0x15, 0xe8, 0x6f,
0x39, 0x30, 0xc3, 0x9d, 0x60, 0x2d, 0x22, 0xcf, 0x42, 0x6d, 0x8d, 0x06, 0xf8, 0x67, 0xa0, 0xe6,
0xb9, 0xae, 0xd9, 0x38, 0x8f, 0x4c, 0x1f, 0x3d, 0xbe, 0xa5, 0x48, 0x84, 0x5a, 0x01, 0x59, 0xa9,
0x59, 0xb0, 0xda, 0x73, 0x78, 0x9b, 0xe0, 0xb5, 0xe7, 0xb0, 0x06, 0xc1, 0x30, 0xf8, 0x36, 0x98,
0x33, 0x4c, 0x93, 0xd6, 0x08, 0xf1, 0x8b, 0x99, 0x4a, 0x86, 0x72, 0x76, 0x1c, 0xa8, 0x31, 0x38,
0x09, 0xd4, 0x45, 0xe6, 0x25, 0x10, 0x84, 0x63, 0x1d, 0xfc, 0x49, 0xb2, 0x72, 0xb3, 0xa7, 0x7b,
0xc0, 0x37, 0x2b, 0x59, 0xca, 0xf4, 0x2e, 0xf1, 0x44, 0xeb, 0xcb, 0xf1, 0x82, 0xa2, 0x4c, 0xa7,
0xa0, 0x68, 0x7c, 0x9c, 0xe9, 0x21, 0x80, 0x70, 0xa4, 0x83, 0x1b, 0x60, 0xa1, 0x67, 0x3c, 0xd2,
0x7d, 0xf2, 0xd3, 0x23, 0xe2, 0x74, 0x09, 0xe3, 0x4c, 0x86, 0xaf, 0xa2, 0x67, 0x3c, 0xea, 0x08,
0x38, 0x5a, 0x85, 0x84, 0x21, 0x2c, 0x5b, 0xc0, 0x1a, 0x00, 0x96, 0x33, 0xf0, 0x5c, 0xf3, 0xa8,
0x4b, 0x3c, 0x41, 0x11, 0xd6, 0x81, 0x63, 0x34, 0xea, 0xc0, 0x31, 0x84, 0xb0, 0xa4, 0x87, 0x07,
0x20, 0xcf, 0xb8, 0xab, 0x5b, 0x66, 0x31, 0x5f, 0x51, 0x96, 0xb3, 0xb5, 0x2d, 0x91, 0xdc, 0x59,
0xc6, 0x42, 0x96, 0xdb, 0xf0, 0x91, 0x72, 0x86, 0x59, 0x37, 0xcd, 0x68, 0xf7, 0x85, 0x4c, 0xfb,
0x46, 0x68, 0xf6, 0x9b, 0xf8, 0x11, 0x87, 0xf6, 0xf0, 0x67, 0xa0, 0xe4, 0x3f, 0xb0, 0x68, 0xa5,
0xf0, 0x77, 0x0f, 0x2c, 0xd7, 0xd1, 0x3d, 0xd2, 0x73, 0x8f, 0x0d, 0xdb, 0x2f, 0xce, 0xb1, 0xc5,
0xbf, 0x33, 0x0e, 0xd4, 0x22, 0xb5, 0x6a, 0x4a, 0x46, 0x58, 0xd8, 0x4c, 0x02, 0xb5, 0xcc, 0xde,
0x78, 0x9e, 0x01, 0xc2, 0xe7, 0xfa, 0xa2, 0x9f, 0x2b, 0x20, 0xc7, 0x96, 0x44, 0x6b, 0x8a, 0xb7,
0x46, 0xd1, 0x08, 0x59, 0x4d, 0x71, 0x64, 0xaa, 0x89, 0x0a, 0x1c, 0x6a, 0x20, 0xb7, 0x6f, 0xd9,
0xc4, 0x2f, 0xa6, 0x59, 0x45, 0x41, 0xa9, 0x1d, 0x5b, 0x36, 0x69, 0x3a, 0xfb, 0x6e, 0xed, 0x86,
0xa8, 0x29, 0x6e, 0x18, 0x31, 0x9a, 0x4a, 0x08, 0x73, 0x10, 0x7d, 0xa4, 0x80, 0x79, 0xb6, 0x88,
0x3b, 0x7d, 0xd3, 0x18, 0x90, 0xff, 0xe5, 0x52, 0x7e, 0x0f, 0x40, 0x3e, 0x74, 0x88, 0xca, 0x52,
0xb9, 0x40, 0x59, 0xae, 0x80, 0xac, 0x6f, 0x7d, 0x48, 0x58, 0x7b, 0xcf, 0x70, 0x5b, 0x2a, 0x47,
0xb6, 0x54, 0x40, 0x98, 0x61, 0xf0, 0x5d, 0x00, 0x7a, 0xae, 0x69, 0xed, 0x5b, 0xc4, 0xd4, 0x7d,
0x56, 0x26, 0x99, 0x5a, 0x85, 0xd6, 0x70, 0x88, 0x76, 0x26, 0x81, 0xfa, 0x0c, 0x27, 0x79, 0x88,
0x20, 0x1c, 0x6b, 0x69, 0x15, 0x47, 0x01, 0xf6, 0x4e, 0x8a, 0x0b, 0x8c, 0x9f, 0x6f, 0x87, 0xfc,
0xec, 0x1c, 0xba, 0xde, 0x80, 0x91, 0x32, 0x7a, 0x4d, 0xed, 0x24, 0x22, 0x7c, 0x0c, 0x21, 0xca,
0x47, 0x61, 0x8c, 0x25, 0x53, 0xb8, 0x05, 0x66, 0xc3, 0xb1, 0x83, 0xf2, 0x2f, 0xd1, 0x2a, 0xef,
0x92, 0xee, 0xc0, 0xf5, 0x6a, 0x95, 0xb0, 0x55, 0x1e, 0x47, 0x63, 0x08, 0xa7, 0xfd, 0x71, 0x38,
0x80, 0x84, 0x1a, 0xf8, 0x26, 0xc8, 0x47, 0x25, 0x0d, 0xd8, 0xb7, 0xb2, 0x96, 0xe0, 0xc7, 0xf5,
0xcc, 0x5b, 0x82, 0x1f, 0x15, 0x73, 0xa4, 0x83, 0xef, 0x81, 0x99, 0x3d, 0xdb, 0xed, 0x3e, 0x08,
0x7b, 0xf6, 0xe5, 0x78, 0x21, 0x35, 0x8a, 0xb3, 0xbc, 0x3e, 0x27, 0xd6, 0x22, 0x4c, 0xa3, 0x43,
0x98, 0x89, 0x08, 0x0b, 0x98, 0xce, 0x54, 0xfe, 0x49, 0xcf, 0xb6, 0x9c, 0x07, 0xfa, 0xc0, 0xf0,
0x0e, 0xc8, 0xa0, 0xb8, 0x14, 0xcf, 0x54, 0x42, 0xb3, 0xcb, 0x14, 0xd1, 0x4c, 0x95, 0x40, 0x11,
0x4e, 0x5a, 0xd1, 0x49, 0x8f, 0x87, 0xd6, 0x0f, 0x0d, 0xff, 0xb0, 0x08, 0xd9, 0x11, 0xc0, 0xfa,
0x0c, 0x87, 0x37, 0x0d, 0xff, 0x30, 0xda, 0xf6, 0x18, 0x42, 0x58, 0xd2, 0xc3, 0x9a, 0x98, 0xc6,
0xf8, 0x0c, 0x75, 0x6d, 0x9a, 0xb6, 0x17, 0x18, 0xc7, 0xd6, 0xc1, 0xfc, 0xe9, 0xd9, 0x60, 0x91,
0xf7, 0xcd, 0x7e, 0x62, 0x2a, 0xe0, 0x7d, 0xb3, 0x2f, 0xcf, 0x03, 0xb2, 0x05, 0x7c, 0x4f, 0xa2,
0x95, 0xe3, 0x17, 0xe7, 0x2b, 0xca, 0x72, 0xae, 0xf6, 0x82, 0xcc, 0xa3, 0x96, 0x3f, 0xc5, 0xa3,
0x96, 0x8f, 0xfe, 0x13, 0xa8, 0x19, 0xcb, 0x19, 0x60, 0xc9, 0x0c, 0xee, 0x03, 0xfe, 0x95, 0x3a,
0xab, 0x8a, 0x45, 0x16, 0x6a, 0xe3, 0x49, 0xa0, 0x2e, 0x60, 0xe3, 0x21, 0x4b, 0x5d, 0xc7, 0xfa,
0x90, 0x50, 0xce, 0xef, 0x85, 0x42, 0xc4, 0xf9, 0x08, 0x09, 0x03, 0x7f, 0xfc, 0xf8, 0x56, 0xc2,
0x0d, 0xc7, 0x4e, 0xb0, 0x01, 0xe6, 0x6d, 0xb7, 0x6b, 0xd8, 0xfa, 0xbe, 0x6d, 0x1c, 0xf8, 0xc5,
0x7f, 0xcd, 0xb2, 0x8f, 0x67, 0x59, 0x60, 0xf8, 0x3a, 0x85, 0xa3, 0x45, 0xc7, 0x10, 0xc2, 0x92,
0x1e, 0x6e, 0x82, 0x05, 0x41, 0x57, 0x9e, 0xcb, 0x7f, 0xcf, 0xb2, 0x64, 0xb2, 0x3d, 0x14, 0x0a,
0x91, 0xcd, 0x25, 0x99, 0xe5, 0x3c, 0x9d, 0xb2, 0x05, 0xfc, 0x2e, 0x1d, 0x33, 0xe8, 0x28, 0x64,
0x8a, 0x99, 0xe7, 0x26, 0x1f, 0x28, 0x18, 0x14, 0x55, 0x89, 0x90, 0xd9, 0x44, 0xc1, 0x9e, 0x20,
0x06, 0xb3, 0x96, 0x73, 0x6c, 0xd8, 0x56, 0x38, 0xd3, 0xbc, 0xf1, 0x24, 0x50, 0x01, 0x36, 0x1e,
0x36, 0x39, 0xca, 0x8f, 0x18, 0xf6, 0x28, 0x1d, 0x31, 0x4c, 0xa6, 0x47, 0x8c, 0x64, 0x89, 0x43,
0x3b, 0xca, 0x78, 0xc7, 0x4d, 0x8c, 0x8d, 0x79, 0x16, 0x9a, 0x31, 0xde, 0x71, 0x93, 0x23, 0x23,
0x67, 0x7c, 0x02, 0x45, 0x38, 0x69, 0xf5, 0x66, 0xf6, 0xd7, 0x9f, 0xa8, 0x29, 0xf4, 0x85, 0x02,
0xe6, 0xa2, 0xea, 0xa3, 0x8d, 0x8f, 0x6d, 0x59, 0x86, 0xed, 0x18, 0x23, 0xea, 0x21, 0xdf, 0x2a,
0x4e, 0xd4, 0x43, 0xb6, 0x47, 0x0c, 0xa3, 0x8d, 0xdd, 0xdd, 0xdf, 0xf7, 0xc9, 0x80, 0xb5, 0xd4,
0x0c, 0x6f, 0xec, 0x1c, 0x89, 0x1a, 0x3b, 0x17, 0x11, 0x16, 0x38, 0x7c, 0x45, 0x34, 0xd6, 0x34,
0xa3, 0xd0, 0x73, 0x67, 0x37, 0xd6, 0x90, 0x81, 0xbc, 0xbf, 0xbe, 0x05, 0xe6, 0x1e, 0x12, 0xe3,
0x01, 0x4f, 0x25, 0xaf, 0x06, 0xd6, 0x72, 0x28, 0x28, 0xd2, 0xc8, 0x5b, 0x4e, 0x08, 0x20, 0x1c,
0xe9, 0xc4, 0x37, 0xde, 0x07, 0x33, 0xbc, 0xd3, 0xc1, 0x1d, 0x90, 0xef, 0xba, 0x47, 0xce, 0x20,
0xbe, 0x75, 0x2c, 0xc9, 0xe3, 0x12, 0xd3, 0xd4, 0xfe, 0x4f, 0xb4, 0xa0, 0xc8, 0x34, 0xca, 0x91,
0x00, 0xe8, 0x9c, 0x23, 0x54, 0xe8, 0x17, 0x0a, 0x98, 0x15, 0x8e, 0x70, 0x33, 0x9a, 0x1e, 0xb3,
0xb5, 0x37, 0x4e, 0x35, 0xf0, 0x2f, 0xbf, 0x89, 0xc8, 0xcd, 0x5b, 0x5c, 0x4a, 0x8e, 0x0d, 0xfb,
0x88, 0x6f, 0x54, 0x96, 0x5f, 0x4a, 0x18, 0x10, 0xf5, 0x43, 0x26, 0x21, 0xcc, 0x51, 0xf4, 0xd7,
0x0c, 0x98, 0xc5, 0xb4, 0xcf, 0xfa, 0x03, 0xf8, 0x7a, 0xb4, 0x8a, 0x5c, 0xed, 0xf9, 0xf3, 0x5e,
0x1b, 0x17, 0x63, 0x38, 0xb6, 0xc6, 0xe7, 0x74, 0xfa, 0xc2, 0xe7, 0x74, 0x78, 0xa6, 0x66, 0x2e,
0x70, 0xa6, 0xc6, 0x74, 0xc9, 0x7e, 0x65, 0xba, 0xe4, 0x2e, 0x4e, 0x97, 0x90, 0xc1, 0x33, 0x17,
0x60, 0x70, 0x1b, 0x5c, 0xda, 0xf7, 0xdc, 0x1e, 0xbb, 0xdc, 0xb8, 0x9e, 0xe1, 0x9d, 0x88, 0x6a,
0x65, 0x25, 0x45, 0x35, 0xbb, 0xa1, 0x22, 0x2a, 0xa9, 0x04, 0x8a, 0x70, 0xd2, 0x2a, 0xc9, 0xd5,
0xfc, 0x57, 0xe3, 0x2a, 0xfa, 0x93, 0x02, 0xf2, 0x98, 0xf8, 0x7d, 0xd7, 0xf1, 0xc9, 0xd7, 0x4d,
0xe2, 0x0a, 0xc8, 0x9a, 0xc6, 0xc0, 0x60, 0x29, 0x14, 0x5f, 0x4f, 0xe5, 0xe8, 0xeb, 0xa9, 0x80,
0x30, 0xc3, 0xe0, 0xbb, 0x20, 0xdb, 0x75, 0x4d, 0x9e, 0xbc, 0x4b, 0xf2, 0x61, 0xac, 0x79, 0x9e,
0xeb, 0xd5, 0x5d, 0x53, 0x9c, 0x54, 0xd4, 0x28, 0x0a, 0x40, 0x05, 0x84, 0x19, 0x86, 0xfe, 0xa8,
0x80, 0x42, 0xc3, 0x7d, 0xe8, 0xd8, 0xae, 0x61, 0xee, 0x78, 0xee, 0x01, 0xbd, 0x37, 0x7c, 0xad,
0x71, 0x4f, 0x07, 0xb3, 0x47, 0x6c, 0x58, 0x0c, 0x07, 0xbe, 0x5b, 0xc9, 0x93, 0xf3, 0xf4, 0x4b,
0xf8, 0x64, 0x19, 0xdf, 0xf0, 0x84, 0x73, 0x14, 0x9f, 0xcb, 0x08, 0x87, 0x0a, 0xf4, 0x87, 0x0c,
0x28, 0x9d, 0x1f, 0x08, 0xf6, 0xc0, 0x3c, 0xb7, 0xd4, 0xa5, 0xdf, 0x52, 0x96, 0x2f, 0xb2, 0x06,
0x76, 0x9e, 0xb3, 0xf3, 0xe9, 0x28, 0x92, 0xa3, 0xf3, 0x29, 0x86, 0x10, 0x96, 0xf4, 0x5f, 0xe9,
0x82, 0x28, 0x4d, 0x6f, 0x99, 0x6f, 0x3e, 0xbd, 0x75, 0xc0, 0x22, 0x3f, 0xc7, 0xc3, 0x9b, 0x7c,
0xb6, 0x92, 0x59, 0xce, 0xd5, 0x56, 0xc7, 0x81, 0xba, 0xb0, 0xc7, 0x0f, 0x81, 0xf0, 0x0e, 0xbf,
0x14, 0x9f, 0xde, 0x1c, 0x0c, 0xd9, 0x56, 0x48, 0xe1, 0x84, 0x2d, 0x5c, 0x4f, 0x0c, 0x07, 0xbc,
0x54, 0xbf, 0x7d, 0xc1, 0x61, 0x40, 0x3a, 0xfc, 0xd1, 0x0c, 0xc8, 0xee, 0x58, 0xce, 0x01, 0x7a,
0x0b, 0xe4, 0xea, 0xb6, 0xeb, 0xb3, 0x8e, 0xe1, 0x11, 0xc3, 0x77, 0x1d, 0x99, 0x4a, 0x1c, 0x89,
0x52, 0xcd, 0x45, 0x84, 0x05, 0xbe, 0xf2, 0x69, 0x06, 0xcc, 0x4b, 0x3f, 0x7d, 0xc1, 0x1f, 0x80,
0x1b, 0xdb, 0x5a, 0xa7, 0x53, 0xdd, 0xd0, 0xf4, 0xdd, 0x7b, 0x3b, 0x9a, 0x5e, 0xdf, 0xba, 0xd3,
0xd9, 0xd5, 0xb0, 0x5e, 0x6f, 0xb7, 0xd6, 0x9b, 0x1b, 0x85, 0x54, 0xe9, 0xe6, 0x70, 0x54, 0x29,
0x4a, 0x1e, 0xc9, 0x1f, 0xa9, 0xbe, 0x03, 0x60, 0xc2, 0xbd, 0xd9, 0x6a, 0x68, 0x3f, 0x2a, 0x28,
0xa5, 0x2b, 0xc3, 0x51, 0xa5, 0x20, 0x79, 0xf1, 0x5b, 0xd7, 0xf7, 0xc1, 0xb3, 0xd3, 0xd6, 0xfa,
0x9d, 0x9d, 0x46, 0x75, 0x57, 0x2b, 0xa4, 0x4b, 0xa5, 0xe1, 0xa8, 0x72, 0xed, 0xb4, 0x93, 0xa0,
0xe0, 0xcb, 0xe0, 0x4a, 0xc2, 0x15, 0x6b, 0x3f, 0xbc, 0xa3, 0x75, 0x76, 0x0b, 0x99, 0xd2, 0xb5,
0xe1, 0xa8, 0x02, 0x25, 0xaf, 0xb0, 0xcd, 0xaf, 0x81, 0xab, 0xa7, 0x3c, 0x3a, 0x3b, 0xed, 0x56,
0x47, 0x2b, 0x64, 0x4b, 0xd7, 0x87, 0xa3, 0xca, 0xe5, 0x84, 0x8b, 0xe8, 0x2a, 0x75, 0x50, 0x4e,
0xf8, 0x34, 0xda, 0x1f, 0xb4, 0xb6, 0xda, 0xd5, 0x86, 0xbe, 0x83, 0xdb, 0x1b, 0x58, 0xeb, 0x74,
0x0a, 0xb9, 0x92, 0x3a, 0x1c, 0x55, 0x6e, 0x48, 0xce, 0x53, 0x15, 0xbe, 0x02, 0x96, 0x12, 0x41,
0x76, 0x9a, 0xad, 0x8d, 0xc2, 0x4c, 0xe9, 0xf2, 0x70, 0x54, 0x79, 0x46, 0xf2, 0xa3, 0xb9, 0x9c,
0xda, 0xbf, 0xfa, 0x56, 0xbb, 0xa3, 0x15, 0x66, 0xa7, 0xf6, 0x8f, 0x25, 0x7c, 0xe5, 0x77, 0x0a,
0x80, 0xd3, 0xbf, 0x36, 0xc2, 0x37, 0x40, 0x31, 0x0c, 0x52, 0x6f, 0x6f, 0xef, 0xd0, 0x75, 0x36,
0xdb, 0x2d, 0xbd, 0xd5, 0x6e, 0x69, 0x85, 0x54, 0x62, 0x57, 0x25, 0xaf, 0x96, 0xeb, 0x10, 0xd8,
0x06, 0xd7, 0xcf, 0xf2, 0xdc, 0xba, 0xff, 0x5a, 0x41, 0x29, 0xad, 0x0d, 0x47, 0x95, 0xab, 0xd3,
0x8e, 0x5b, 0xf7, 0x5f, 0xfb, 0xfc, 0x97, 0xcf, 0x9f, 0xad, 0x58, 0xf9, 0xad, 0x02, 0xe6, 0xe5,
0xa5, 0xbd, 0x02, 0xae, 0xc8, 0x81, 0xb7, 0xb5, 0xdd, 0x6a, 0xa3, 0xba, 0x5b, 0x2d, 0xa4, 0x78,
0x0e, 0x24, 0xd3, 0x6d, 0x32, 0x30, 0x58, 0xdb, 0x7d, 0x11, 0x2c, 0x25, 0xbe, 0x42, 0xbb, 0xab,
0xe1, 0x90, 0x51, 0xf2, 0xfa, 0xc9, 0x31, 0xf1, 0xe0, 0x4b, 0x00, 0xca, 0xc6, 0xd5, 0xad, 0x0f,
0xaa, 0xf7, 0x3a, 0x85, 0x74, 0xe9, 0xea, 0x70, 0x54, 0x59, 0x92, 0xac, 0xab, 0xf6, 0x43, 0xe3,
0xc4, 0x5f, 0xf9, 0x4b, 0x1a, 0x2c, 0xc8, 0x57, 0x0d, 0xf8, 0x12, 0xb8, 0xbc, 0xde, 0xdc, 0xa2,
0x4c, 0x5c, 0x6f, 0xf3, 0x0c, 0x50, 0xb1, 0x90, 0xe2, 0xaf, 0x93, 0x4d, 0xe9, 0x33, 0xfc, 0x1e,
0x28, 0x9e, 0x32, 0x6f, 0x34, 0xb1, 0x56, 0xdf, 0x6d, 0xe3, 0x7b, 0x05, 0xa5, 0xf4, 0x2c, 0xdd,
0x30, 0xd9, 0xa7, 0x61, 0x79, 0xac, 0x05, 0x9d, 0xc0, 0x77, 0xc0, 0x8d, 0x53, 0x8e, 0x9d, 0x7b,
0xdb, 0x5b, 0xcd, 0xd6, 0xfb, 0xfc, 0x7d, 0xe9, 0xd2, 0x73, 0xc3, 0x51, 0xe5, 0xba, 0xec, 0xdb,
0xe1, 0xb7, 0x2f, 0x0a, 0xe5, 0x15, 0xb8, 0x09, 0x2a, 0xe7, 0xf8, 0xc7, 0x0b, 0xc8, 0x94, 0xd0,
0x70, 0x54, 0xb9, 0x79, 0x46, 0x90, 0x68, 0x1d, 0x79, 0x05, 0xbe, 0x0a, 0xae, 0x9d, 0x1d, 0x29,
0xac, 0x8b, 0x33, 0xfc, 0x57, 0xfe, 0xae, 0x80, 0xb9, 0xe8, 0xd4, 0xa3, 0x9b, 0xa6, 0x61, 0xdc,
0xa6, 0x4d, 0xa2, 0xa1, 0xe9, 0xad, 0xb6, 0xce, 0xa4, 0x70, 0xd3, 0x22, 0xbb, 0x96, 0xcb, 0x1e,
0x29, 0xc7, 0x25, 0xf3, 0x0d, 0xad, 0xa5, 0xe1, 0x66, 0x3d, 0xcc, 0x68, 0x64, 0xbd, 0x41, 0x1c,
0xe2, 0x59, 0x5d, 0xf8, 0x1a, 0xb8, 0x9e, 0x0c, 0xde, 0xb9, 0x53, 0xdf, 0x0c, 0x77, 0x89, 0x2d,
0x50, 0x7a, 0x41, 0xe7, 0xa8, 0x7b, 0xc8, 0x12, 0xf3, 0x7a, 0xc2, 0xab, 0xd9, 0xba, 0x5b, 0xdd,
0x6a, 0x36, 0xb8, 0x57, 0xa6, 0x54, 0x1c, 0x8e, 0x2a, 0x57, 0x22, 0x2f, 0x71, 0x71, 0xa0, 0x6e,
0x2b, 0x9f, 0x2b, 0xa0, 0xfc, 0xe5, 0x87, 0x17, 0xfc, 0x00, 0xbc, 0xc0, 0xf6, 0x6b, 0xaa, 0x15,
0x88, 0xbe, 0xc5, 0xf7, 0xb0, 0xba, 0xb3, 0xa3, 0xb5, 0x1a, 0x85, 0x54, 0x69, 0x79, 0x38, 0xaa,
0xdc, 0xfa, 0xf2, 0x90, 0xd5, 0x7e, 0x9f, 0x38, 0xe6, 0x05, 0x03, 0xaf, 0xb7, 0xf1, 0x86, 0xb6,
0x5b, 0x50, 0x2e, 0x12, 0x78, 0xdd, 0xa5, 0x37, 0xf5, 0xda, 0xf6, 0x67, 0x5f, 0x94, 0x53, 0x8f,
0xbf, 0x28, 0xa7, 0x3e, 0x7b, 0x52, 0x56, 0x1e, 0x3f, 0x29, 0x2b, 0xbf, 0x7a, 0x5a, 0x4e, 0x7d,
0xf2, 0xb4, 0xac, 0x3c, 0x7e, 0x5a, 0x4e, 0xfd, 0xe3, 0x69, 0x39, 0x75, 0xff, 0xc5, 0x03, 0x6b,
0x70, 0x78, 0xb4, 0xb7, 0xda, 0x75, 0x7b, 0xb7, 0xfd, 0x13, 0xa7, 0x3b, 0x38, 0xb4, 0x9c, 0x03,
0xe9, 0x49, 0xfe, 0xd7, 0x69, 0x6f, 0x86, 0x3d, 0xbd, 0xfa, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff,
0xf3, 0xce, 0x28, 0x00, 0x8c, 0x1a, 0x00, 0x00,
// 2681 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0x4d, 0x6c, 0x1b, 0xc7,
0xf5, 0xd7, 0x92, 0x94, 0x44, 0x8d, 0x24, 0x87, 0x1a, 0x7f, 0x6d, 0x68, 0x9b, 0xcb, 0xff, 0xc4,
0xf9, 0x57, 0x51, 0x1a, 0x39, 0x51, 0x92, 0x36, 0x4d, 0x52, 0x07, 0xfc, 0x92, 0xc4, 0x44, 0x22,
0xd9, 0x21, 0xed, 0xd4, 0x46, 0x0b, 0x62, 0xc5, 0x1d, 0x51, 0x0b, 0x2f, 0x77, 0xd9, 0x5d, 0x4a,
0xb6, 0x82, 0x5e, 0xda, 0x5e, 0x02, 0x1e, 0x8a, 0x22, 0xa7, 0xa2, 0x28, 0xd1, 0xa0, 0x97, 0xde,
0x0a, 0xf4, 0xd0, 0x4b, 0x4e, 0x3d, 0xe6, 0x68, 0x04, 0x28, 0x50, 0xf4, 0xb0, 0x80, 0xed, 0x4b,
0xcb, 0x23, 0x8f, 0x3d, 0x15, 0xf3, 0xb1, 0xbb, 0xb3, 0xa2, 0x14, 0xc8, 0xc9, 0xa1, 0xb7, 0x7d,
0xbf, 0xf7, 0x7b, 0x6f, 0x86, 0x6f, 0xde, 0x7b, 0xf3, 0x76, 0x09, 0xae, 0x58, 0xe6, 0xde, 0xad,
0xbe, 0xeb, 0x0c, 0x9c, 0x8e, 0x63, 0xdd, 0xda, 0x23, 0xfd, 0x75, 0x26, 0xc0, 0x74, 0x80, 0x65,
0x17, 0xc8, 0xa3, 0x01, 0x07, 0xb3, 0x2f, 0xb9, 0xa4, 0xef, 0x78, 0x9c, 0xbe, 0x77, 0xb8, 0x7f,
0xab, 0xeb, 0x74, 0x1d, 0x26, 0xb0, 0x27, 0x4e, 0x42, 0x4f, 0x15, 0x30, 0xbb, 0x4d, 0x2c, 0xcb,
0x81, 0x25, 0xb0, 0x68, 0x90, 0x23, 0xb3, 0x43, 0xda, 0xb6, 0xde, 0x23, 0xaa, 0x92, 0x57, 0x56,
0x17, 0x8a, 0x68, 0xec, 0x6b, 0x80, 0xc3, 0x35, 0xbd, 0x47, 0x26, 0xbe, 0x96, 0x79, 0xd4, 0xb3,
0xde, 0x45, 0x11, 0x84, 0xb0, 0xa4, 0xa7, 0x4e, 0x3a, 0x96, 0x49, 0xec, 0x01, 0x77, 0x92, 0x88,
0x9c, 0x70, 0x38, 0xe6, 0x24, 0x82, 0x10, 0x96, 0xf4, 0xb0, 0x0e, 0x2e, 0x08, 0x27, 0x47, 0xc4,
0xf5, 0x4c, 0xc7, 0x56, 0x93, 0xcc, 0xcf, 0xea, 0xd8, 0xd7, 0x96, 0xb9, 0xe6, 0x2e, 0x57, 0x4c,
0x7c, 0xed, 0xa2, 0xe4, 0x4a, 0xa0, 0x08, 0xc7, 0x59, 0xe8, 0x2f, 0x0a, 0x98, 0xdb, 0x26, 0xba,
0x41, 0x5c, 0x58, 0x00, 0xa9, 0xc1, 0x71, 0x9f, 0xff, 0xbc, 0x0b, 0x1b, 0x97, 0xd7, 0x83, 0xc0,
0xad, 0xef, 0x12, 0xcf, 0xd3, 0xbb, 0xa4, 0x75, 0xdc, 0x27, 0xc5, 0x2b, 0x63, 0x5f, 0x63, 0xb4,
0x89, 0xaf, 0x01, 0xe6, 0x9f, 0x0a, 0x08, 0x33, 0x0c, 0x1a, 0x60, 0xb1, 0xe3, 0xf4, 0xfa, 0x2e,
0xf1, 0xd8, 0xde, 0x12, 0xcc, 0xd3, 0xf5, 0x29, 0x4f, 0xa5, 0x88, 0x53, 0xbc, 0x39, 0xf6, 0x35,
0xd9, 0x68, 0xe2, 0x6b, 0x2b, 0x7c, 0xdf, 0x11, 0x86, 0xb0, 0xcc, 0x40, 0x3f, 0x01, 0xcb, 0x25,
0xeb, 0xd0, 0x1b, 0x10, 0xb7, 0xe4, 0xd8, 0xfb, 0x66, 0x17, 0x7e, 0x04, 0xe6, 0xf7, 0x1d, 0xcb,
0x20, 0xae, 0xa7, 0x2a, 0xf9, 0xe4, 0xea, 0xe2, 0x46, 0x26, 0x5a, 0x72, 0x93, 0x29, 0x8a, 0xda,
0x97, 0xbe, 0x36, 0x33, 0xf6, 0xb5, 0x80, 0x38, 0xf1, 0xb5, 0x25, 0xb6, 0x0c, 0x97, 0x11, 0x0e,
0x14, 0xe8, 0x8b, 0x14, 0x98, 0xe3, 0x46, 0x70, 0x1d, 0x24, 0x4c, 0x43, 0x1c, 0x77, 0xee, 0xa9,
0xaf, 0x25, 0xaa, 0xe5, 0xb1, 0xaf, 0x25, 0x4c, 0x63, 0xe2, 0x6b, 0x69, 0x66, 0x6d, 0x1a, 0xe8,
0xb3, 0xc7, 0x37, 0x13, 0xd5, 0x32, 0x4e, 0x98, 0x06, 0x5c, 0x07, 0xb3, 0x96, 0xbe, 0x47, 0x2c,
0x71, 0xb8, 0xea, 0xd8, 0xd7, 0x38, 0x30, 0xf1, 0xb5, 0x45, 0xc6, 0x67, 0x12, 0xc2, 0x1c, 0x85,
0xef, 0x81, 0x05, 0x97, 0xe8, 0x46, 0xdb, 0xb1, 0xad, 0x63, 0x76, 0x90, 0xe9, 0x62, 0x6e, 0xec,
0x6b, 0x69, 0x0a, 0xd6, 0x6d, 0xeb, 0x78, 0xe2, 0x6b, 0x17, 0x98, 0x59, 0x00, 0x20, 0x1c, 0xea,
0x60, 0x1b, 0x40, 0xb3, 0x6b, 0x3b, 0x2e, 0x69, 0xf7, 0x89, 0xdb, 0x33, 0x59, 0x68, 0x3c, 0x35,
0xc5, 0xbc, 0xbc, 0x3e, 0xf6, 0xb5, 0x15, 0xae, 0x6d, 0x44, 0xca, 0x89, 0xaf, 0x5d, 0xe5, 0xbb,
0x3e, 0xa9, 0x41, 0x78, 0x9a, 0x0d, 0x3f, 0x02, 0xcb, 0x62, 0x01, 0x83, 0x58, 0x64, 0x40, 0xd4,
0x59, 0xe6, 0xfb, 0xff, 0xc7, 0xbe, 0xb6, 0xc4, 0x15, 0x65, 0x86, 0x4f, 0x7c, 0x0d, 0x4a, 0x6e,
0x39, 0x88, 0x70, 0x8c, 0x03, 0x0d, 0x70, 0xc9, 0x30, 0x3d, 0x7d, 0xcf, 0x22, 0xed, 0x01, 0xe9,
0xf5, 0xdb, 0xa6, 0x6d, 0x90, 0x47, 0xc4, 0x53, 0xe7, 0x98, 0xcf, 0x8d, 0xb1, 0xaf, 0x41, 0xa1,
0x6f, 0x91, 0x5e, 0xbf, 0xca, 0xb5, 0x13, 0x5f, 0x53, 0x79, 0x4d, 0x4d, 0xa9, 0x10, 0x3e, 0x85,
0x0f, 0x37, 0xc0, 0x5c, 0x5f, 0x3f, 0xf4, 0x88, 0xa1, 0xce, 0x33, 0xbf, 0xd9, 0xb1, 0xaf, 0x09,
0x24, 0x3c, 0x70, 0x2e, 0x22, 0x2c, 0x70, 0x9a, 0x3c, 0xbc, 0x4a, 0x3d, 0x35, 0x73, 0x32, 0x79,
0xca, 0x4c, 0x11, 0x25, 0x8f, 0x20, 0x86, 0xbe, 0xb8, 0x8c, 0x70, 0xa0, 0x40, 0x7f, 0x9b, 0x03,
0x73, 0xdc, 0x08, 0x16, 0xc3, 0xe4, 0x59, 0x2a, 0x6e, 0x50, 0x07, 0xff, 0xf4, 0xb5, 0x34, 0xd7,
0x55, 0xcb, 0x67, 0x25, 0xd3, 0xa7, 0x8f, 0x6f, 0x2a, 0x52, 0x42, 0xad, 0x81, 0x94, 0xd4, 0x2c,
0x58, 0xed, 0xd9, 0xbc, 0x4d, 0xf0, 0xda, 0xb3, 0x59, 0x83, 0x60, 0x18, 0x7c, 0x1f, 0x2c, 0xe8,
0x86, 0x41, 0x6b, 0x84, 0x78, 0x6a, 0x32, 0x9f, 0xa4, 0x39, 0x3b, 0xf6, 0xb5, 0x08, 0x9c, 0xf8,
0xda, 0x32, 0xb3, 0x12, 0x08, 0xc2, 0x91, 0x0e, 0xfe, 0x34, 0x5e, 0xb9, 0xa9, 0x93, 0x3d, 0xe0,
0xdb, 0x95, 0x2c, 0xcd, 0xf4, 0x0e, 0x71, 0x45, 0xeb, 0x9b, 0xe5, 0x05, 0x45, 0x33, 0x9d, 0x82,
0xa2, 0xf1, 0xf1, 0x4c, 0x0f, 0x00, 0x84, 0x43, 0x1d, 0xdc, 0x02, 0x4b, 0x3d, 0xfd, 0x51, 0xdb,
0x23, 0x3f, 0x3b, 0x24, 0x76, 0x87, 0xb0, 0x9c, 0x49, 0xf2, 0x5d, 0xf4, 0xf4, 0x47, 0x4d, 0x01,
0x87, 0xbb, 0x90, 0x30, 0x84, 0x65, 0x06, 0x2c, 0x02, 0x60, 0xda, 0x03, 0xd7, 0x31, 0x0e, 0x3b,
0xc4, 0x15, 0x29, 0xc2, 0x3a, 0x70, 0x84, 0x86, 0x1d, 0x38, 0x82, 0x10, 0x96, 0xf4, 0xb0, 0x0b,
0xd2, 0x2c, 0x77, 0xdb, 0xa6, 0xa1, 0xa6, 0xf3, 0xca, 0x6a, 0xaa, 0xb8, 0x23, 0x0e, 0x77, 0x9e,
0x65, 0x21, 0x3b, 0xdb, 0xe0, 0x91, 0xe6, 0x0c, 0x63, 0x57, 0x8d, 0x30, 0xfa, 0x42, 0xa6, 0x7d,
0x23, 0xa0, 0xfd, 0x2e, 0x7a, 0xc4, 0x01, 0x1f, 0xfe, 0x1c, 0x64, 0xbd, 0x07, 0x26, 0xad, 0x14,
0xbe, 0xf6, 0xc0, 0x74, 0xec, 0xb6, 0x4b, 0x7a, 0xce, 0x91, 0x6e, 0x79, 0xea, 0x02, 0xdb, 0xfc,
0xed, 0xb1, 0xaf, 0xa9, 0x94, 0x55, 0x95, 0x48, 0x58, 0x70, 0x26, 0xbe, 0x96, 0x63, 0x2b, 0x9e,
0x45, 0x40, 0xf8, 0x4c, 0x5b, 0xf8, 0x08, 0xbc, 0x48, 0xec, 0x8e, 0x7b, 0xdc, 0x67, 0xcb, 0xf6,
0x75, 0xcf, 0x7b, 0xe8, 0xb8, 0x46, 0x7b, 0xe0, 0x3c, 0x20, 0xb6, 0x0a, 0x58, 0x52, 0xbf, 0x3f,
0xf6, 0xb5, 0xab, 0x11, 0xa9, 0x21, 0x38, 0x2d, 0x4a, 0x99, 0xf8, 0xda, 0x0d, 0xb6, 0xf6, 0x19,
0x7a, 0x84, 0xcf, 0xb2, 0x44, 0xbf, 0x54, 0xc0, 0x2c, 0x0b, 0x06, 0xad, 0x66, 0xde, 0x94, 0x45,
0x0b, 0x66, 0xd5, 0xcc, 0x91, 0xa9, 0xf6, 0x2d, 0x70, 0x58, 0x01, 0xb3, 0xfb, 0xa6, 0x45, 0x3c,
0x35, 0xc1, 0x6a, 0x19, 0x4a, 0x17, 0x81, 0x69, 0x91, 0xaa, 0xbd, 0xef, 0x14, 0xaf, 0x89, 0x6a,
0xe6, 0xc4, 0xb0, 0x96, 0xa8, 0x84, 0x30, 0x07, 0xd1, 0xa7, 0x0a, 0x58, 0x64, 0x9b, 0xb8, 0xd3,
0x37, 0xf4, 0x01, 0xf9, 0x5f, 0x6e, 0xe5, 0x09, 0x00, 0xe9, 0xc0, 0x20, 0x6c, 0x08, 0xca, 0x39,
0x1a, 0xc2, 0x1a, 0x48, 0x79, 0xe6, 0x27, 0x84, 0x5d, 0x2c, 0x49, 0xce, 0xa5, 0x72, 0xc8, 0xa5,
0x02, 0xc2, 0x0c, 0x83, 0x1f, 0x00, 0xd0, 0x73, 0x0c, 0x73, 0xdf, 0x24, 0x46, 0xdb, 0x63, 0x05,
0x9a, 0x2c, 0xe6, 0x69, 0xf7, 0x08, 0xd0, 0xe6, 0xc4, 0xd7, 0x5e, 0xe0, 0xe5, 0x15, 0x20, 0x08,
0x47, 0x5a, 0xda, 0x3f, 0x42, 0x07, 0x7b, 0xc7, 0xea, 0x12, 0xab, 0x8c, 0xf7, 0x83, 0xca, 0x68,
0x1e, 0x38, 0xee, 0x80, 0x95, 0x43, 0xb8, 0x4c, 0xf1, 0x38, 0x2c, 0xb5, 0x08, 0x42, 0xb4, 0x12,
0x04, 0x19, 0x4b, 0x54, 0xb8, 0x03, 0xe6, 0x83, 0x81, 0x87, 0x66, 0x7e, 0xac, 0x49, 0xdf, 0x25,
0x9d, 0x81, 0xe3, 0x16, 0xf3, 0x41, 0x93, 0x3e, 0x0a, 0x07, 0x20, 0x5e, 0x70, 0x47, 0xc1, 0xe8,
0x13, 0x68, 0xe0, 0xbb, 0x20, 0x1d, 0x36, 0x13, 0xc0, 0x7e, 0x2b, 0x6b, 0x46, 0x5e, 0xd4, 0x49,
0x78, 0x33, 0xf2, 0xc2, 0x36, 0x12, 0xea, 0xe0, 0x87, 0x60, 0x6e, 0xcf, 0x72, 0x3a, 0x0f, 0x82,
0xdb, 0xe2, 0x62, 0xb4, 0x91, 0x22, 0xc5, 0xd9, 0xb9, 0xde, 0x10, 0x7b, 0x11, 0xd4, 0xf0, 0xfa,
0x67, 0x22, 0xc2, 0x02, 0xa6, 0xd3, 0x9c, 0x77, 0xdc, 0xb3, 0x4c, 0xfb, 0x41, 0x7b, 0xa0, 0xbb,
0x5d, 0x32, 0x50, 0x57, 0xa2, 0x69, 0x4e, 0x68, 0x5a, 0x4c, 0x11, 0x4e, 0x73, 0x31, 0x14, 0xe1,
0x38, 0x8b, 0xce, 0x98, 0xdc, 0x75, 0xfb, 0x40, 0xf7, 0x0e, 0x54, 0xc8, 0xea, 0x94, 0x75, 0x38,
0x0e, 0x6f, 0xeb, 0xde, 0x41, 0x18, 0xf6, 0x08, 0x42, 0x58, 0xd2, 0xc3, 0xdb, 0x60, 0x41, 0xd4,
0x26, 0x31, 0xd4, 0x8b, 0xcc, 0x05, 0x4b, 0x85, 0x10, 0x0c, 0x53, 0x21, 0x44, 0x10, 0x8e, 0xb4,
0xb0, 0x28, 0xe6, 0x48, 0x3e, 0xfd, 0x5d, 0x99, 0x4e, 0xfb, 0x73, 0x0c, 0x92, 0x9b, 0x60, 0xf1,
0xe4, 0x54, 0xb3, 0xcc, 0x3b, 0x7e, 0x3f, 0x36, 0xcf, 0xf0, 0x8e, 0xdf, 0x97, 0x27, 0x19, 0x99,
0x01, 0x3f, 0x94, 0xd2, 0xd2, 0xf6, 0xd4, 0xc5, 0xbc, 0xb2, 0x3a, 0x5b, 0x7c, 0x45, 0xce, 0xc3,
0x9a, 0x37, 0x95, 0x87, 0x35, 0x0f, 0xfd, 0xc7, 0xd7, 0x92, 0xa6, 0x3d, 0xc0, 0x12, 0x0d, 0xee,
0x03, 0x1e, 0xa5, 0x36, 0xab, 0xaa, 0x65, 0xe6, 0x6a, 0xeb, 0xa9, 0xaf, 0x2d, 0x61, 0xfd, 0x21,
0x3b, 0xfa, 0xa6, 0xf9, 0x09, 0xa1, 0x81, 0xda, 0x0b, 0x84, 0x30, 0x50, 0x21, 0x12, 0x38, 0xfe,
0xec, 0xf1, 0xcd, 0x98, 0x19, 0x8e, 0x8c, 0x60, 0x19, 0x2c, 0x5a, 0x4e, 0x47, 0xb7, 0xda, 0xfb,
0x96, 0xde, 0xf5, 0xd4, 0x7f, 0xcd, 0xb3, 0x1f, 0xcf, 0x4e, 0x91, 0xe1, 0x9b, 0x14, 0x0e, 0x37,
0x1d, 0x41, 0x08, 0x4b, 0x7a, 0xb8, 0x0d, 0x96, 0x44, 0xba, 0xf3, 0x5c, 0xf8, 0xf7, 0x3c, 0x3b,
0x49, 0x16, 0x43, 0xa1, 0x10, 0xd9, 0xb0, 0x22, 0x57, 0x09, 0x4f, 0x07, 0x99, 0x01, 0xbf, 0x47,
0x07, 0x24, 0x3a, 0xc4, 0x19, 0x62, 0x5a, 0xbb, 0xce, 0x47, 0x21, 0x06, 0x85, 0x55, 0x26, 0x64,
0x36, 0x0b, 0xb1, 0x27, 0x88, 0xc1, 0xbc, 0x69, 0x1f, 0xe9, 0x96, 0x19, 0x4c, 0x63, 0xef, 0x3c,
0xf5, 0x35, 0x80, 0xf5, 0x87, 0x55, 0x8e, 0xf2, 0xcb, 0x91, 0x3d, 0x4a, 0x97, 0x23, 0x93, 0xe9,
0xe5, 0x28, 0x31, 0x71, 0xc0, 0xa3, 0x15, 0x63, 0x3b, 0xb1, 0x81, 0x37, 0xcd, 0x5c, 0xb3, 0x8a,
0xb1, 0x9d, 0xf8, 0xb0, 0xcb, 0x2b, 0x26, 0x86, 0x22, 0x1c, 0x67, 0xbd, 0x9b, 0xfa, 0xed, 0xe7,
0xda, 0x0c, 0x7a, 0xa2, 0x80, 0x85, 0xb0, 0x7a, 0x69, 0xe3, 0x64, 0x21, 0x4b, 0xb2, 0x88, 0xb1,
0x44, 0x3d, 0xe0, 0xa1, 0xe2, 0x89, 0x7a, 0xc0, 0x62, 0xc4, 0x30, 0x7a, 0x31, 0x38, 0xfb, 0xfb,
0x1e, 0x19, 0xb0, 0x96, 0x9c, 0xe4, 0x17, 0x03, 0x47, 0xc2, 0x8b, 0x81, 0x8b, 0x08, 0x0b, 0x1c,
0xbe, 0x21, 0x1a, 0x73, 0x82, 0xa5, 0xd0, 0x8d, 0xd3, 0x1b, 0x73, 0x90, 0x81, 0xbc, 0x3f, 0xbf,
0x07, 0x16, 0x1e, 0x12, 0xfd, 0x01, 0x3f, 0x4a, 0x5e, 0x0d, 0xac, 0x65, 0x51, 0x50, 0x1c, 0x23,
0x6f, 0x59, 0x01, 0x80, 0x70, 0xa8, 0x13, 0xbf, 0xf1, 0x3e, 0x98, 0xe3, 0x9d, 0x12, 0x36, 0x40,
0xba, 0xe3, 0x1c, 0xda, 0x83, 0xe8, 0x7d, 0x69, 0x45, 0x1e, 0xf4, 0x98, 0xa6, 0xf8, 0x7f, 0xa2,
0x85, 0x85, 0xd4, 0xf0, 0x8c, 0x04, 0x40, 0x27, 0x34, 0xa1, 0x42, 0xbf, 0x52, 0xc0, 0xbc, 0x30,
0x84, 0xdb, 0xe1, 0xdc, 0x9b, 0x2a, 0xbe, 0x73, 0xe2, 0x02, 0xf8, 0xfa, 0x77, 0x28, 0xb9, 0xf9,
0x8b, 0xd7, 0xa9, 0x23, 0xdd, 0x3a, 0xe4, 0x81, 0x4a, 0xf1, 0xd7, 0x29, 0x06, 0x84, 0xfd, 0x94,
0x49, 0x08, 0x73, 0x14, 0xfd, 0x22, 0x05, 0xe6, 0x31, 0xed, 0xd3, 0xde, 0x00, 0xbe, 0x1d, 0xee,
0x62, 0xb6, 0xf8, 0xf2, 0x59, 0xcb, 0x46, 0xc5, 0x18, 0x0c, 0xdc, 0xd1, 0x3d, 0x9f, 0x38, 0xf7,
0x3d, 0x1f, 0xdc, 0xc9, 0xc9, 0x73, 0xdc, 0xc9, 0x51, 0xba, 0xa4, 0x9e, 0x3b, 0x5d, 0x66, 0xcf,
0x9f, 0x2e, 0x41, 0x06, 0xcf, 0x9d, 0x23, 0x83, 0xeb, 0xe0, 0xc2, 0xbe, 0xeb, 0xf4, 0xd8, 0x6b,
0x99, 0xe3, 0xea, 0xee, 0xb1, 0xa8, 0x56, 0x56, 0x52, 0x54, 0xd3, 0x0a, 0x14, 0x61, 0x49, 0xc5,
0x50, 0x84, 0xe3, 0xac, 0x78, 0xae, 0xa6, 0x9f, 0x2f, 0x57, 0xe1, 0x6d, 0x90, 0xe6, 0x4d, 0xd6,
0x76, 0xd8, 0x4d, 0x3f, 0x5b, 0x7c, 0x89, 0xf6, 0x09, 0x86, 0xd5, 0x9c, 0x30, 0x07, 0x85, 0x1c,
0xfe, 0xec, 0x80, 0x80, 0xfe, 0xac, 0x80, 0x34, 0x26, 0x5e, 0xdf, 0xb1, 0x3d, 0xf2, 0x4d, 0x93,
0x60, 0x0d, 0xa4, 0x0c, 0x7d, 0xa0, 0xb3, 0x14, 0x10, 0xd1, 0xa3, 0x72, 0x18, 0x3d, 0x2a, 0x20,
0xcc, 0x30, 0xf8, 0x01, 0x48, 0x75, 0x1c, 0x83, 0x1f, 0xfe, 0x05, 0x79, 0x18, 0xa8, 0xb8, 0xae,
0xe3, 0x96, 0x1c, 0x43, 0xdc, 0x74, 0x94, 0x14, 0x3a, 0xa0, 0x02, 0xc2, 0x0c, 0x43, 0x7f, 0x52,
0x40, 0xa6, 0xec, 0x3c, 0xb4, 0x2d, 0x47, 0x37, 0x1a, 0xae, 0xd3, 0xa5, 0x6f, 0x4c, 0xdf, 0x68,
0xdc, 0x6c, 0x83, 0xf9, 0x43, 0x36, 0xac, 0x06, 0x03, 0xe7, 0xcd, 0xf8, 0xcd, 0x7b, 0x72, 0x11,
0x3e, 0xd9, 0x46, 0xef, 0xb6, 0xc2, 0x38, 0xf4, 0xcf, 0x65, 0x84, 0x03, 0x05, 0xfa, 0x63, 0x12,
0x64, 0xcf, 0x76, 0x04, 0x7b, 0x60, 0x91, 0x33, 0xdb, 0xd2, 0x57, 0xa4, 0xd5, 0xf3, 0xec, 0x81,
0xcd, 0x03, 0xec, 0x7e, 0x3b, 0x0c, 0xe5, 0xf0, 0x7e, 0x8b, 0x20, 0x84, 0x25, 0xfd, 0x73, 0xbd,
0x1a, 0x4b, 0xd3, 0x63, 0xf2, 0xdb, 0x4f, 0x8f, 0x4d, 0xb0, 0xcc, 0x53, 0x34, 0xf8, 0x86, 0x91,
0xca, 0x27, 0x57, 0x67, 0x8b, 0xeb, 0x63, 0x5f, 0x5b, 0xda, 0xe3, 0x97, 0x48, 0xf0, 0xf5, 0x62,
0x25, 0x4a, 0x56, 0x0e, 0x06, 0xd9, 0x96, 0x99, 0xc1, 0x31, 0x2e, 0xdc, 0x8c, 0x0d, 0x17, 0xbc,
0xd4, 0xbf, 0x73, 0xce, 0x61, 0x42, 0x1a, 0x1e, 0xd0, 0x1c, 0x48, 0x35, 0x4c, 0xbb, 0x8b, 0xde,
0x03, 0xb3, 0x25, 0xcb, 0xf1, 0x58, 0xc7, 0x71, 0x89, 0xee, 0x39, 0xb6, 0x9c, 0x4a, 0x1c, 0x09,
0x8f, 0x9a, 0x8b, 0x08, 0x0b, 0x7c, 0xed, 0x8b, 0x24, 0x58, 0x94, 0x3e, 0xfa, 0xc1, 0x1f, 0x82,
0x6b, 0xbb, 0x95, 0x66, 0xb3, 0xb0, 0x55, 0x69, 0xb7, 0xee, 0x35, 0x2a, 0xed, 0xd2, 0xce, 0x9d,
0x66, 0xab, 0x82, 0xdb, 0xa5, 0x7a, 0x6d, 0xb3, 0xba, 0x95, 0x99, 0xc9, 0x5e, 0x1f, 0x8e, 0xf2,
0xaa, 0x64, 0x11, 0xff, 0x3c, 0xf7, 0x5d, 0x00, 0x63, 0xe6, 0xd5, 0x5a, 0xb9, 0xf2, 0xe3, 0x8c,
0x92, 0xbd, 0x34, 0x1c, 0xe5, 0x33, 0x92, 0x15, 0x7f, 0xeb, 0xfb, 0x01, 0x78, 0x71, 0x9a, 0xdd,
0xbe, 0xd3, 0x28, 0x17, 0x5a, 0x95, 0x4c, 0x22, 0x9b, 0x1d, 0x8e, 0xf2, 0x57, 0x4e, 0x1a, 0x89,
0x14, 0x7c, 0x1d, 0x5c, 0x8a, 0x99, 0xe2, 0xca, 0x8f, 0xee, 0x54, 0x9a, 0xad, 0x4c, 0x32, 0x7b,
0x65, 0x38, 0xca, 0x43, 0xc9, 0x2a, 0xb8, 0x26, 0x36, 0xc0, 0xe5, 0x13, 0x16, 0xcd, 0x46, 0xbd,
0xd6, 0xac, 0x64, 0x52, 0xd9, 0xab, 0xc3, 0x51, 0xfe, 0x62, 0xcc, 0x44, 0x74, 0x95, 0x12, 0xc8,
0xc5, 0x6c, 0xca, 0xf5, 0x8f, 0x6b, 0x3b, 0xf5, 0x42, 0xb9, 0xdd, 0xc0, 0xf5, 0x2d, 0x5c, 0x69,
0x36, 0x33, 0xb3, 0x59, 0x6d, 0x38, 0xca, 0x5f, 0x93, 0x8c, 0xa7, 0x2a, 0x7c, 0x0d, 0xac, 0xc4,
0x9c, 0x34, 0xaa, 0xb5, 0xad, 0xcc, 0x5c, 0xf6, 0xe2, 0x70, 0x94, 0x7f, 0x41, 0xb2, 0xa3, 0x67,
0x39, 0x15, 0xbf, 0xd2, 0x4e, 0xbd, 0x59, 0xc9, 0xcc, 0x4f, 0xc5, 0x8f, 0x1d, 0xf8, 0xda, 0x1f,
0x14, 0x00, 0xa7, 0xbf, 0xb3, 0xc2, 0x77, 0x80, 0x1a, 0x38, 0x29, 0xd5, 0x77, 0x1b, 0x74, 0x9f,
0xd5, 0x7a, 0xad, 0x5d, 0xab, 0xd7, 0x2a, 0x99, 0x99, 0x58, 0x54, 0x25, 0xab, 0x9a, 0x63, 0x13,
0x58, 0x07, 0x57, 0x4f, 0xb3, 0xdc, 0xb9, 0xff, 0x56, 0x46, 0xc9, 0x6e, 0x0c, 0x47, 0xf9, 0xcb,
0xd3, 0x86, 0x3b, 0xf7, 0xdf, 0xfa, 0xea, 0xd7, 0x2f, 0x9f, 0xae, 0x58, 0xfb, 0xbd, 0x02, 0x16,
0xe5, 0xad, 0xbd, 0x01, 0x2e, 0xc9, 0x8e, 0x77, 0x2b, 0xad, 0x42, 0xb9, 0xd0, 0x2a, 0x64, 0x66,
0xf8, 0x19, 0x48, 0xd4, 0x5d, 0x32, 0xd0, 0x59, 0xdb, 0x7d, 0x15, 0xac, 0xc4, 0x7e, 0x45, 0xe5,
0x6e, 0x05, 0x07, 0x19, 0x25, 0xef, 0x9f, 0x1c, 0x11, 0x17, 0xbe, 0x06, 0xa0, 0x4c, 0x2e, 0xec,
0x7c, 0x5c, 0xb8, 0xd7, 0xcc, 0x24, 0xb2, 0x97, 0x87, 0xa3, 0xfc, 0x8a, 0xc4, 0x2e, 0x58, 0x0f,
0xf5, 0x63, 0x6f, 0xed, 0xaf, 0x09, 0xb0, 0x24, 0xbf, 0xaa, 0xc0, 0xd7, 0xc0, 0xc5, 0xcd, 0xea,
0x0e, 0xcd, 0xc4, 0xcd, 0x3a, 0x3f, 0x01, 0x2a, 0x66, 0x66, 0xf8, 0x72, 0x32, 0x95, 0x3e, 0xc3,
0xef, 0x03, 0xf5, 0x04, 0xbd, 0x5c, 0xc5, 0x95, 0x52, 0xab, 0x8e, 0xef, 0x65, 0x94, 0xec, 0x8b,
0x34, 0x60, 0xb2, 0x4d, 0xd9, 0x74, 0x59, 0x0b, 0x3a, 0x86, 0xb7, 0xc1, 0xb5, 0x13, 0x86, 0xcd,
0x7b, 0xbb, 0x3b, 0xd5, 0xda, 0x47, 0x7c, 0xbd, 0x44, 0xf6, 0xc6, 0x70, 0x94, 0xbf, 0x2a, 0xdb,
0x36, 0xf9, 0xdb, 0x1f, 0x85, 0xd2, 0x0a, 0xdc, 0x06, 0xf9, 0x33, 0xec, 0xa3, 0x0d, 0x24, 0xb3,
0x68, 0x38, 0xca, 0x5f, 0x3f, 0xc5, 0x49, 0xb8, 0x8f, 0xb4, 0x02, 0xdf, 0x04, 0x57, 0x4e, 0xf7,
0x14, 0xd4, 0xc5, 0x29, 0xf6, 0x6b, 0x7f, 0x57, 0xc0, 0x42, 0x78, 0xeb, 0xd1, 0xa0, 0x55, 0x30,
0xae, 0xd3, 0x26, 0x51, 0xae, 0xb4, 0x6b, 0xf5, 0x36, 0x93, 0x82, 0xa0, 0x85, 0xbc, 0x9a, 0xc3,
0x1e, 0x69, 0x8e, 0x4b, 0xf4, 0xad, 0x4a, 0xad, 0x82, 0xab, 0xa5, 0xe0, 0x44, 0x43, 0xf6, 0x16,
0xb1, 0x89, 0x6b, 0x76, 0xe0, 0x5b, 0xe0, 0x6a, 0xdc, 0x79, 0xf3, 0x4e, 0x69, 0x3b, 0x88, 0x12,
0xdb, 0xa0, 0xb4, 0x40, 0xf3, 0xb0, 0x73, 0xc0, 0x0e, 0xe6, 0xed, 0x98, 0x55, 0xb5, 0x76, 0xb7,
0xb0, 0x53, 0x2d, 0x73, 0xab, 0x64, 0x56, 0x1d, 0x8e, 0xf2, 0x97, 0x42, 0x2b, 0xf1, 0xe2, 0x41,
0xcd, 0xd6, 0xbe, 0x52, 0x40, 0xee, 0xeb, 0x2f, 0x2f, 0xf8, 0x31, 0x78, 0x85, 0xc5, 0x6b, 0xaa,
0x15, 0x88, 0xbe, 0xc5, 0x63, 0x58, 0x68, 0x34, 0x2a, 0xb5, 0x72, 0x66, 0x26, 0xbb, 0x3a, 0x1c,
0xe5, 0x6f, 0x7e, 0xbd, 0xcb, 0x42, 0xbf, 0x4f, 0x6c, 0xe3, 0x9c, 0x8e, 0x37, 0xeb, 0x78, 0xab,
0xd2, 0xca, 0x28, 0xe7, 0x71, 0xbc, 0xe9, 0xb8, 0x5d, 0x32, 0x28, 0xee, 0x7e, 0xf9, 0x24, 0x37,
0xf3, 0xf8, 0x49, 0x6e, 0xe6, 0xcb, 0xa7, 0x39, 0xe5, 0xf1, 0xd3, 0x9c, 0xf2, 0x9b, 0x67, 0xb9,
0x99, 0xcf, 0x9f, 0xe5, 0x94, 0xc7, 0xcf, 0x72, 0x33, 0xff, 0x78, 0x96, 0x9b, 0xb9, 0xff, 0x6a,
0xd7, 0x1c, 0x1c, 0x1c, 0xee, 0xad, 0x77, 0x9c, 0xde, 0x2d, 0xef, 0xd8, 0xee, 0x0c, 0x0e, 0x4c,
0xbb, 0x2b, 0x3d, 0xc9, 0xff, 0xb7, 0xed, 0xcd, 0xb1, 0xa7, 0x37, 0xff, 0x1b, 0x00, 0x00, 0xff,
0xff, 0xf7, 0xe3, 0xa9, 0xd7, 0x86, 0x1b, 0x00, 0x00,
}
func (m *Hello) Marshal() (dAtA []byte, err error) {
@@ -1328,6 +1337,13 @@ func (m *Device) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if len(m.EncryptionPasswordToken) > 0 {
i -= len(m.EncryptionPasswordToken)
copy(dAtA[i:], m.EncryptionPasswordToken)
i = encodeVarintBep(dAtA, i, uint64(len(m.EncryptionPasswordToken)))
i--
dAtA[i] = 0x52
}
if m.SkipIntroductionRemovals {
i--
if m.SkipIntroductionRemovals {
@@ -1523,6 +1539,15 @@ func (m *FileInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i--
dAtA[i] = 0xc0
}
if len(m.Encrypted) > 0 {
i -= len(m.Encrypted)
copy(dAtA[i:], m.Encrypted)
i = encodeVarintBep(dAtA, i, uint64(len(m.Encrypted)))
i--
dAtA[i] = 0x1
i--
dAtA[i] = 0x9a
}
if len(m.BlocksHash) > 0 {
i -= len(m.BlocksHash)
copy(dAtA[i:], m.BlocksHash)
@@ -1782,6 +1807,11 @@ func (m *Request) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.BlockNo != 0 {
i = encodeVarintBep(dAtA, i, uint64(m.BlockNo))
i--
dAtA[i] = 0x48
}
if m.WeakHash != 0 {
i = encodeVarintBep(dAtA, i, uint64(m.WeakHash))
i--
@@ -2167,6 +2197,10 @@ func (m *Device) ProtoSize() (n int) {
if m.SkipIntroductionRemovals {
n += 2
}
l = len(m.EncryptionPasswordToken)
if l > 0 {
n += 1 + l + sovBep(uint64(l))
}
return n
}
@@ -2267,6 +2301,10 @@ func (m *FileInfo) ProtoSize() (n int) {
if l > 0 {
n += 2 + l + sovBep(uint64(l))
}
l = len(m.Encrypted)
if l > 0 {
n += 2 + l + sovBep(uint64(l))
}
if m.LocalFlags != 0 {
n += 2 + sovBep(uint64(m.LocalFlags))
}
@@ -2362,6 +2400,9 @@ func (m *Request) ProtoSize() (n int) {
if m.WeakHash != 0 {
n += 1 + sovBep(uint64(m.WeakHash))
}
if m.BlockNo != 0 {
n += 1 + sovBep(uint64(m.BlockNo))
}
return n
}
@@ -3290,6 +3331,40 @@ func (m *Device) Unmarshal(dAtA []byte) error {
}
}
m.SkipIntroductionRemovals = bool(v != 0)
case 10:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field EncryptionPasswordToken", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowBep
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthBep
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthBep
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.EncryptionPasswordToken = append(m.EncryptionPasswordToken[:0], dAtA[iNdEx:postIndex]...)
if m.EncryptionPasswordToken == nil {
m.EncryptionPasswordToken = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipBep(dAtA[iNdEx:])
@@ -3958,6 +4033,40 @@ func (m *FileInfo) Unmarshal(dAtA []byte) error {
m.BlocksHash = []byte{}
}
iNdEx = postIndex
case 19:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Encrypted", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowBep
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthBep
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthBep
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Encrypted = append(m.Encrypted[:0], dAtA[iNdEx:postIndex]...)
if m.Encrypted == nil {
m.Encrypted = []byte{}
}
iNdEx = postIndex
case 1000:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field LocalFlags", wireType)
@@ -4580,6 +4689,25 @@ func (m *Request) Unmarshal(dAtA []byte) error {
break
}
}
case 9:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field BlockNo", wireType)
}
m.BlockNo = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowBep
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.BlockNo |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipBep(dAtA[iNdEx:])
+1 -1
View File
@@ -36,7 +36,7 @@ func (t *TestModel) IndexUpdate(deviceID DeviceID, folder string, files []FileIn
return nil
}
func (t *TestModel) Request(deviceID DeviceID, folder, name string, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) {
func (t *TestModel) Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) {
t.folder = folder
t.name = name
t.offset = offset
+547
View File
@@ -0,0 +1,547 @@
// Copyright (C) 2019 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package protocol
import (
"context"
"crypto/rand"
"crypto/sha256"
"encoding/base32"
"errors"
"io"
"strings"
"time"
"github.com/gogo/protobuf/proto"
"github.com/miscreant/miscreant.go"
"golang.org/x/crypto/chacha20poly1305"
"golang.org/x/crypto/hkdf"
"golang.org/x/crypto/scrypt"
)
const (
nonceSize = 24 // chacha20poly1305.NonceSizeX
tagSize = 16 // chacha20poly1305.Overhead()
keySize = 32 // fits both chacha20poly1305 and AES-SIV
minPaddedSize = 1024 // smallest block we'll allow
blockOverhead = tagSize + nonceSize
maxPathComponent = 200 // characters
encryptedDirExtension = ".syncthing-enc" // for top level dirs
miscreantAlgo = "AES-SIV"
)
// The encryptedModel sits between the encrypted device and the model. It
// receives encrypted metadata and requests from the untrusted device, so it
// must decrypt those and answer requests by encrypting the data.
type encryptedModel struct {
model Model
folderKeys map[string]*[keySize]byte // folder ID -> key
}
func (e encryptedModel) Index(deviceID DeviceID, folder string, files []FileInfo) error {
if folderKey, ok := e.folderKeys[folder]; ok {
// incoming index data to be decrypted
if err := decryptFileInfos(files, folderKey); err != nil {
return err
}
}
return e.model.Index(deviceID, folder, files)
}
func (e encryptedModel) IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) error {
if folderKey, ok := e.folderKeys[folder]; ok {
// incoming index data to be decrypted
if err := decryptFileInfos(files, folderKey); err != nil {
return err
}
}
return e.model.IndexUpdate(deviceID, folder, files)
}
func (e encryptedModel) Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) {
folderKey, ok := e.folderKeys[folder]
if !ok {
return e.model.Request(deviceID, folder, name, blockNo, size, offset, hash, weakHash, fromTemporary)
}
// Figure out the real file name, offset and size from the encrypted /
// tweaked values.
realName, err := decryptName(name, folderKey)
if err != nil {
return nil, err
}
realSize := size - blockOverhead
realOffset := offset - int64(blockNo*blockOverhead)
if size < minPaddedSize {
return nil, errors.New("short request")
}
// Perform that request and grab the data. Explicitly zero out the
// hashes which are meaningless.
resp, err := e.model.Request(deviceID, folder, realName, blockNo, realSize, realOffset, nil, 0, false)
if err != nil {
return nil, err
}
// Encrypt the response. Blocks smaller than minPaddedSize are padded
// with random data.
data := resp.Data()
if len(data) < minPaddedSize {
nd := make([]byte, minPaddedSize)
copy(nd, data)
if _, err := rand.Read(nd[len(data):]); err != nil {
panic("catastrophic randomness failure")
}
data = nd
}
fileKey := FileKey(realName, folderKey)
enc := encryptBytes(data, fileKey)
resp.Close()
return rawResponse{enc}, nil
}
func (e encryptedModel) DownloadProgress(deviceID DeviceID, folder string, updates []FileDownloadProgressUpdate) error {
if _, ok := e.folderKeys[folder]; !ok {
return e.model.DownloadProgress(deviceID, folder, updates)
}
// Encrypted devices shouldn't send these - ignore them.
return nil
}
func (e encryptedModel) ClusterConfig(deviceID DeviceID, config ClusterConfig) error {
return e.model.ClusterConfig(deviceID, config)
}
func (e encryptedModel) Closed(conn Connection, err error) {
e.model.Closed(conn, err)
}
// The encryptedConnection sits between the model and the encrypted device. It
// encrypts outgoing metadata and decrypts incoming responses.
type encryptedConnection struct {
conn Connection
folderKeys map[string]*[keySize]byte // folder ID -> key
}
func (e encryptedConnection) Start() {
e.conn.Start()
}
func (e encryptedConnection) ID() DeviceID {
return e.conn.ID()
}
func (e encryptedConnection) Name() string {
return e.conn.Name()
}
func (e encryptedConnection) Index(ctx context.Context, folder string, files []FileInfo) error {
if folderKey, ok := e.folderKeys[folder]; ok {
encryptFileInfos(files, folderKey)
}
return e.conn.Index(ctx, folder, files)
}
func (e encryptedConnection) IndexUpdate(ctx context.Context, folder string, files []FileInfo) error {
if folderKey, ok := e.folderKeys[folder]; ok {
encryptFileInfos(files, folderKey)
}
return e.conn.IndexUpdate(ctx, folder, files)
}
func (e encryptedConnection) Request(ctx context.Context, folder string, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
folderKey, ok := e.folderKeys[folder]
if !ok {
return e.conn.Request(ctx, folder, name, blockNo, offset, size, hash, weakHash, fromTemporary)
}
// Encrypt / adjust the request parameters.
origSize := size
if size < minPaddedSize {
// Make a request for minPaddedSize data instead of the smaller
// block. We'll chop of the extra data later.
size = minPaddedSize
}
encName := encryptName(name, folderKey)
encOffset := offset + int64(blockNo*blockOverhead)
encSize := size + blockOverhead
// Perform that request, getting back and encrypted block.
bs, err := e.conn.Request(ctx, folder, encName, blockNo, encOffset, encSize, nil, 0, false)
if err != nil {
return nil, err
}
// Return the decrypted block (or an error if it fails decryption)
fileKey := FileKey(name, folderKey)
bs, err = DecryptBytes(bs, fileKey)
if err != nil {
return nil, err
}
return bs[:origSize], nil
}
func (e encryptedConnection) DownloadProgress(ctx context.Context, folder string, updates []FileDownloadProgressUpdate) {
if _, ok := e.folderKeys[folder]; !ok {
e.conn.DownloadProgress(ctx, folder, updates)
}
// No need to send these
}
func (e encryptedConnection) ClusterConfig(config ClusterConfig) {
e.conn.ClusterConfig(config)
}
func (e encryptedConnection) Close(err error) {
e.conn.Close(err)
}
func (e encryptedConnection) Closed() bool {
return e.conn.Closed()
}
func (e encryptedConnection) Statistics() Statistics {
return e.conn.Statistics()
}
func encryptFileInfos(files []FileInfo, folderKey *[keySize]byte) {
for i, fi := range files {
files[i] = encryptFileInfo(fi, folderKey)
}
}
// encryptFileInfo encrypts a FileInfo and wraps it into a new fake FileInfo
// with an encrypted name.
func encryptFileInfo(fi FileInfo, folderKey *[keySize]byte) FileInfo {
fileKey := FileKey(fi.Name, folderKey)
// The entire FileInfo is encrypted with a random nonce, and concatenated
// with that nonce.
bs, err := proto.Marshal(&fi)
if err != nil {
panic("impossible serialization mishap: " + err.Error())
}
encryptedFI := encryptBytes(bs, fileKey)
// The vector is set to something that is higher than any other version sent
// previously, assuming people's clocks are correct. We do this because
// there is no way for the insecure device on the other end to do proper
// conflict resolution, so they will simply accept and keep whatever is the
// latest version they see. The secure devices will decrypt the real
// FileInfo, see the real Version, and act appropriately regardless of what
// this fake version happens to be.
version := Vector{
Counters: []Counter{
{
ID: 1,
Value: uint64(time.Now().UnixNano()),
},
},
}
// Construct the fake block list. Each block will be blockOverhead bytes
// larger than the corresponding real one and have an encrypted hash.
// Very small blocks will be padded upwards to minPaddedSize.
//
// The encrypted hash becomes just a "token" for the data -- it doesn't
// help verifying it, but it lets the encrypted device do block level
// diffs and data reuse properly when it gets a new version of a file.
var offset int64
blocks := make([]BlockInfo, len(fi.Blocks))
for i, b := range fi.Blocks {
if b.Size < minPaddedSize {
b.Size = minPaddedSize
}
size := b.Size + blockOverhead
blocks[i] = BlockInfo{
Offset: offset,
Size: size,
Hash: encryptDeterministic(b.Hash, fileKey),
}
offset += int64(size)
}
// Construct the fake FileInfo. This is mostly just a wrapper around the
// encrypted FileInfo and fake block list. We'll represent symlinks as
// directories, because they need some sort of on disk representation
// but have no data outside of the metadata. Deletion and sequence
// numbering are handled as usual.
typ := FileInfoTypeFile
if fi.Type != FileInfoTypeFile {
typ = FileInfoTypeDirectory
}
enc := FileInfo{
Name: encryptName(fi.Name, folderKey),
Type: typ,
Size: offset, // new total file size
Permissions: 0644,
ModifiedS: 1234567890, // Sat Feb 14 00:31:30 CET 2009
Deleted: fi.Deleted,
Version: version,
Sequence: fi.Sequence,
RawBlockSize: fi.RawBlockSize + blockOverhead,
Blocks: blocks,
Encrypted: encryptedFI,
}
return enc
}
func decryptFileInfos(files []FileInfo, folderKey *[keySize]byte) error {
for i, fi := range files {
decFI, err := DecryptFileInfo(fi, folderKey)
if err != nil {
return err
}
files[i] = decFI
}
return nil
}
// DecryptFileInfo extracts the encrypted portion of a FileInfo, decrypts it
// and returns that.
func DecryptFileInfo(fi FileInfo, folderKey *[keySize]byte) (FileInfo, error) {
realName, err := decryptName(fi.Name, folderKey)
if err != nil {
return FileInfo{}, err
}
fileKey := FileKey(realName, folderKey)
dec, err := DecryptBytes(fi.Encrypted, fileKey)
if err != nil {
return FileInfo{}, err
}
var decFI FileInfo
if err := proto.Unmarshal(dec, &decFI); err != nil {
return FileInfo{}, err
}
return decFI, nil
}
// encryptName encrypts the given string in a deterministic manner (the
// result is always the same for any given string) and encodes it in a
// filesystem-friendly manner.
func encryptName(name string, key *[keySize]byte) string {
enc := encryptDeterministic([]byte(name), key)
b32enc := base32.HexEncoding.WithPadding(base32.NoPadding).EncodeToString(enc)
return slashify(b32enc)
}
// decryptName decrypts a string from encryptName
func decryptName(name string, key *[keySize]byte) (string, error) {
name = deslashify(name)
bs, err := base32.HexEncoding.WithPadding(base32.NoPadding).DecodeString(name)
if err != nil {
return "", err
}
dec, err := decryptDeterministic(bs, key)
if err != nil {
return "", err
}
return string(dec), nil
}
// encryptBytes encrypts bytes with a random nonce
func encryptBytes(data []byte, key *[keySize]byte) []byte {
nonce := randomNonce()
return encrypt(data, nonce, key)
}
// encryptDeterministic encrypts bytes using AES-SIV
func encryptDeterministic(data []byte, key *[keySize]byte) []byte {
aead, err := miscreant.NewAEAD(miscreantAlgo, key[:], 0)
if err != nil {
panic("cipher failure: " + err.Error())
}
return aead.Seal(nil, nil, data, nil)
}
// decryptDeterministic decrypts bytes using AES-SIV
func decryptDeterministic(data []byte, key *[keySize]byte) ([]byte, error) {
aead, err := miscreant.NewAEAD(miscreantAlgo, key[:], 0)
if err != nil {
panic("cipher failure: " + err.Error())
}
return aead.Open(nil, nil, data, nil)
}
func encrypt(data []byte, nonce *[nonceSize]byte, key *[keySize]byte) []byte {
aead, err := chacha20poly1305.NewX(key[:])
if err != nil {
// Can only fail if the key is the wrong length
panic("cipher failure: " + err.Error())
}
if aead.NonceSize() != nonceSize || aead.Overhead() != tagSize {
// We want these values to be constant for our type declarations so
// we don't use the values returned by the GCM, but we verify them
// here.
panic("crypto parameter mismatch")
}
// Data is appended to the nonce
return aead.Seal(nonce[:], nonce[:], data, nil)
}
// DecryptBytes returns the decrypted bytes, or an error if decryption
// failed.
func DecryptBytes(data []byte, key *[keySize]byte) ([]byte, error) {
if len(data) < blockOverhead {
return nil, errors.New("data too short")
}
aead, err := chacha20poly1305.NewX(key[:])
if err != nil {
// Can only fail if the key is the wrong length
panic("cipher failure: " + err.Error())
}
if aead.NonceSize() != nonceSize || aead.Overhead() != tagSize {
// We want these values to be constant for our type declarations so
// we don't use the values returned by the GCM, but we verify them
// here.
panic("crypto parameter mismatch")
}
return aead.Open(nil, data[:nonceSize], data[nonceSize:], nil)
}
// randomNonce is a normal, cryptographically random nonce
func randomNonce() *[nonceSize]byte {
var nonce [nonceSize]byte
if _, err := rand.Read(nonce[:]); err != nil {
panic("catastrophic randomness failure: " + err.Error())
}
return &nonce
}
// keysFromPasswords converts a set of folder ID to password into a set of
// folder ID to encryption key, using our key derivation function.
func keysFromPasswords(passwords map[string]string) map[string]*[keySize]byte {
res := make(map[string]*[keySize]byte, len(passwords))
for folder, password := range passwords {
res[folder] = KeyFromPassword(folder, password)
}
return res
}
func knownBytes(folderID string) []byte {
return []byte("syncthing" + folderID)
}
// KeyFromPassword uses key derivation to generate a stronger key from a
// probably weak password.
func KeyFromPassword(folderID, password string) *[keySize]byte {
bs, err := scrypt.Key([]byte(password), knownBytes(folderID), 32768, 8, 1, keySize)
if err != nil {
panic("key derivation failure: " + err.Error())
}
if len(bs) != keySize {
panic("key derivation failure: wrong number of bytes")
}
var key [keySize]byte
copy(key[:], bs)
return &key
}
func FileKey(filename string, folderKey *[keySize]byte) *[keySize]byte {
kdf := hkdf.New(sha256.New, append(folderKey[:], filename...), []byte("syncthing"), nil)
var fileKey [keySize]byte
n, err := io.ReadFull(kdf, fileKey[:])
if err != nil || n != keySize {
panic("hkdf failure")
}
return &fileKey
}
func PasswordToken(folderID, password string) []byte {
return encryptDeterministic(knownBytes(folderID), KeyFromPassword(folderID, password))
}
// slashify inserts slashes (and file extension) in the string to create an
// appropriate tree. ABCDEFGH... => A.syncthing-enc/BC/DEFGH... We can use
// forward slashes here because we're on the outside of native path formats,
// the slash is the wire format.
func slashify(s string) string {
// We somewhat sloppily assume bytes == characters here, but the only
// file names we should deal with are those that come from our base32
// encoding.
comps := make([]string, 0, len(s)/maxPathComponent+3)
comps = append(comps, s[:1]+encryptedDirExtension)
s = s[1:]
comps = append(comps, s[:2])
s = s[2:]
for len(s) > maxPathComponent {
comps = append(comps, s[:maxPathComponent])
s = s[maxPathComponent:]
}
if len(s) > 0 {
comps = append(comps, s)
}
return strings.Join(comps, "/")
}
// deslashify removes slashes and encrypted file extensions from the string.
// This is the inverse of slashify().
func deslashify(s string) string {
s = strings.ReplaceAll(s, encryptedDirExtension, "")
return strings.ReplaceAll(s, "/", "")
}
type rawResponse struct {
data []byte
}
func (r rawResponse) Data() []byte {
return r.data
}
func (r rawResponse) Close() {}
func (r rawResponse) Wait() {}
// IsEncryptedPath returns true if the path points at encrypted data. This is
// determined by checking for a sentinel string in the path.
func IsEncryptedPath(path string) bool {
pathComponents := strings.Split(path, "/")
if len(pathComponents) != 3 {
return false
}
return isEncryptedParentFromComponents(pathComponents[:2])
}
// IsEncryptedParent returns true if the path points at a parent directory of
// encrypted data, i.e. is not a "real" directory. This is determined by
// checking for a sentinel string in the path.
func IsEncryptedParent(path string) bool {
return isEncryptedParentFromComponents(strings.Split(path, "/"))
}
func isEncryptedParentFromComponents(pathComponents []string) bool {
if l := len(pathComponents); l > 2 {
return false
} else if l == 2 && len(pathComponents[1]) != 2 {
return false
}
return pathComponents[0][1:1+len(encryptedDirExtension)] == encryptedDirExtension
}
+97
View File
@@ -0,0 +1,97 @@
// Copyright (C) 2019 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package protocol
import (
"bytes"
"reflect"
"strings"
"testing"
)
func TestEnDecryptName(t *testing.T) {
var key [32]byte
cases := []string{
"",
"foo",
"a longer name/with/slashes and spaces",
}
for _, tc := range cases {
var prev string
for i := 0; i < 5; i++ {
enc := encryptName(tc, &key)
if prev != "" && prev != enc {
t.Error("name should always encrypt the same")
}
prev = enc
if tc != "" && strings.Contains(enc, tc) {
t.Error("shouldn't contain plaintext")
}
dec, err := decryptName(enc, &key)
if err != nil {
t.Error(err)
}
if dec != tc {
t.Error("mismatch after decryption")
}
t.Log(enc)
}
}
}
func TestEnDecryptBytes(t *testing.T) {
var key [32]byte
cases := [][]byte{
{},
{1, 2, 3, 4, 5},
}
for _, tc := range cases {
var prev []byte
for i := 0; i < 5; i++ {
enc := encryptBytes(tc, &key)
if bytes.Equal(enc, prev) {
t.Error("encryption should not repeat")
}
prev = enc
if len(tc) > 0 && bytes.Contains(enc, tc) {
t.Error("shouldn't contain plaintext")
}
dec, err := DecryptBytes(enc, &key)
if err != nil {
t.Error(err)
}
if !bytes.Equal(dec, tc) {
t.Error("mismatch after decryption")
}
}
}
}
func TestEnDecryptFileInfo(t *testing.T) {
var key [32]byte
fi := FileInfo{
Name: "hello",
Size: 45,
Permissions: 0755,
ModifiedS: 8080,
Blocks: []BlockInfo{
{
Size: 45,
Hash: []byte{1, 2, 3},
},
},
}
enc := encryptFileInfo(fi, &key)
dec, err := DecryptFileInfo(enc, &key)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(fi, dec) {
t.Error("mismatch after decryption")
}
}
+2 -2
View File
@@ -26,7 +26,7 @@ func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileI
return m.Model.IndexUpdate(deviceID, folder, files)
}
func (m nativeModel) Request(deviceID DeviceID, folder, name string, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) {
func (m nativeModel) Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) {
name = norm.NFD.String(name)
return m.Model.Request(deviceID, folder, name, size, offset, hash, weakHash, fromTemporary)
return m.Model.Request(deviceID, folder, name, blockNo, size, offset, hash, weakHash, fromTemporary)
}
+2 -2
View File
@@ -26,14 +26,14 @@ func (m nativeModel) IndexUpdate(deviceID DeviceID, folder string, files []FileI
return m.Model.IndexUpdate(deviceID, folder, files)
}
func (m nativeModel) Request(deviceID DeviceID, folder, name string, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) {
func (m nativeModel) Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error) {
if strings.Contains(name, `\`) {
l.Warnf("Dropping request for %s, contains invalid path separator", name)
return nil, ErrNoSuchFile
}
name = filepath.FromSlash(name)
return m.Model.Request(deviceID, folder, name, size, offset, hash, weakHash, fromTemporary)
return m.Model.Request(deviceID, folder, name, blockNo, size, offset, hash, weakHash, fromTemporary)
}
func fixupFiles(files []FileInfo) []FileInfo {
+30 -8
View File
@@ -115,7 +115,7 @@ type Model interface {
// An index update was received from the peer device
IndexUpdate(deviceID DeviceID, folder string, files []FileInfo) error
// A request was made by the peer device
Request(deviceID DeviceID, folder, name string, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error)
Request(deviceID DeviceID, folder, name string, blockNo, size int32, offset int64, hash []byte, weakHash uint32, fromTemporary bool) (RequestResponse, error)
// A cluster configuration message was received
ClusterConfig(deviceID DeviceID, config ClusterConfig) error
// The peer device closed the connection
@@ -137,7 +137,7 @@ type Connection interface {
Name() string
Index(ctx context.Context, folder string, files []FileInfo) error
IndexUpdate(ctx context.Context, folder string, files []FileInfo) error
Request(ctx context.Context, folder string, name string, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error)
Request(ctx context.Context, folder string, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error)
ClusterConfig(config ClusterConfig)
DownloadProgress(ctx context.Context, folder string, updates []FileDownloadProgressUpdate)
Statistics() Statistics
@@ -204,13 +204,36 @@ const (
var CloseTimeout = 10 * time.Second
func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, receiver Model, name string, compress Compression) Connection {
receiver = nativeModel{receiver}
rc := newRawConnection(deviceID, reader, writer, receiver, name, compress)
return wireFormatConnection{rc}
}
func NewEncryptedConnection(passwords map[string]string, deviceID DeviceID, reader io.Reader, writer io.Writer, receiver Model, name string, compress Compression) Connection {
keys := keysFromPasswords(passwords)
// Encryption / decryption is first (outermost) before conversion to
// native path formats.
nm := nativeModel{receiver}
em := encryptedModel{model: nm, folderKeys: keys}
// We do the wire format conversion first (outermost) so that the
// metadata is in wire format when it reaches the encryption step.
rc := newRawConnection(deviceID, reader, writer, em, name, compress)
ec := encryptedConnection{conn: rc, folderKeys: keys}
wc := wireFormatConnection{ec}
return wc
}
func newRawConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, receiver Model, name string, compress Compression) *rawConnection {
cr := &countingReader{Reader: reader}
cw := &countingWriter{Writer: writer}
c := rawConnection{
return &rawConnection{
id: deviceID,
name: name,
receiver: nativeModel{receiver},
receiver: receiver,
cr: cr,
cw: cw,
awaiting: make(map[int]chan asyncResult),
@@ -222,8 +245,6 @@ func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, receiv
closed: make(chan struct{}),
compression: compress,
}
return wireFormatConnection{&c}
}
// Start creates the goroutines for sending and receiving of messages. It must
@@ -281,7 +302,7 @@ func (c *rawConnection) IndexUpdate(ctx context.Context, folder string, idx []Fi
}
// Request returns the bytes for the specified block after fetching them from the connected peer.
func (c *rawConnection) Request(ctx context.Context, folder string, name string, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
func (c *rawConnection) Request(ctx context.Context, folder string, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
c.nextIDMut.Lock()
id := c.nextID
c.nextID++
@@ -302,6 +323,7 @@ func (c *rawConnection) Request(ctx context.Context, folder string, name string,
Name: name,
Offset: offset,
Size: size,
BlockNo: blockNo,
Hash: hash,
WeakHash: weakHash,
FromTemporary: fromTemporary,
@@ -622,7 +644,7 @@ func checkFilename(name string) error {
}
func (c *rawConnection) handleRequest(req Request) {
res, err := c.receiver.Request(c.id, req.Folder, req.Name, int32(req.Size), req.Offset, req.Hash, req.WeakHash, req.FromTemporary)
res, err := c.receiver.Request(c.id, req.Folder, req.Name, int32(req.BlockNo), int32(req.Size), req.Offset, req.Hash, req.WeakHash, req.FromTemporary)
if err != nil {
c.send(context.Background(), &Response{
ID: req.ID,
+7 -1
View File
@@ -80,7 +80,7 @@ func TestClose(t *testing.T) {
c0.Index(ctx, "default", nil)
c0.Index(ctx, "default", nil)
if _, err := c0.Request(ctx, "default", "foo", 0, 0, nil, 0, false); err == nil {
if _, err := c0.Request(ctx, "default", "foo", 0, 0, 0, nil, 0, false); err == nil {
t.Error("Request should return an error")
}
}
@@ -279,6 +279,9 @@ func TestMarshalIndexMessage(t *testing.T) {
if len(f.Version.Counters) == 0 {
m1.Files[i].Version.Counters = nil
}
if len(f.Encrypted) == 0 {
m1.Files[i].Encrypted = nil
}
}
return testMarshal(t, "index", &m1, &Index{})
@@ -340,6 +343,9 @@ func TestMarshalClusterConfigMessage(t *testing.T) {
if len(m1.Folders[i].Devices[j].Addresses) == 0 {
m1.Folders[i].Devices[j].Addresses = nil
}
if len(m1.Folders[i].Devices[j].EncryptionPasswordToken) == 0 {
m1.Folders[i].Devices[j].EncryptionPasswordToken = nil
}
}
}
+2 -2
View File
@@ -35,7 +35,7 @@ func (c wireFormatConnection) IndexUpdate(ctx context.Context, folder string, fs
return c.Connection.IndexUpdate(ctx, folder, myFs)
}
func (c wireFormatConnection) Request(ctx context.Context, folder string, name string, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
func (c wireFormatConnection) Request(ctx context.Context, folder string, name string, blockNo int, offset int64, size int, hash []byte, weakHash uint32, fromTemporary bool) ([]byte, error) {
name = norm.NFC.String(filepath.ToSlash(name))
return c.Connection.Request(ctx, folder, name, offset, size, hash, weakHash, fromTemporary)
return c.Connection.Request(ctx, folder, name, blockNo, offset, size, hash, weakHash, fromTemporary)
}