From cd68ffae315100614f04e4154cb4aaeb9b1db1dc Mon Sep 17 00:00:00 2001 From: cyli Date: Mon, 13 Feb 2017 18:57:27 -0800 Subject: [PATCH 1/3] Update the RootCA object to contain a RootRotation object, which keeps track of the root cert + key we want to rotate to, as well as a cross-signed intermediate. Signed-off-by: cyli --- api/types.pb.go | 892 +++++++++++++++++++++++++++++++++--------------- api/types.proto | 15 + 2 files changed, 632 insertions(+), 275 deletions(-) diff --git a/api/types.pb.go b/api/types.pb.go index c3d9a34177..c72328eac0 100644 --- a/api/types.pb.go +++ b/api/types.pb.go @@ -66,6 +66,7 @@ BlacklistedCertificate HealthConfig MaybeEncryptedRecord + RootRotation NodeSpec ServiceSpec ReplicatedService @@ -1329,6 +1330,8 @@ type ExternalCA struct { // Options is a set of additional key/value pairs whose interpretation // depends on the specified CA type. Options map[string]string `protobuf:"bytes,3,rep,name=options" json:"options,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // CACert specifies which root CA is used by this external CA + CACert []byte `protobuf:"bytes,4,opt,name=ca_cert,json=caCert,proto3" json:"ca_cert,omitempty"` } func (m *ExternalCA) Reset() { *m = ExternalCA{} } @@ -1552,6 +1555,9 @@ type RootCA struct { CACertHash string `protobuf:"bytes,3,opt,name=ca_cert_hash,json=caCertHash,proto3" json:"ca_cert_hash,omitempty"` // JoinTokens contains the join tokens for workers and managers. JoinTokens JoinTokens `protobuf:"bytes,4,opt,name=join_tokens,json=joinTokens" json:"join_tokens"` + // RootRotation contains the new root cert and key we want to rotate to - if this is nil, we are not in the + // middle of a root rotation + RootRotation *RootRotation `protobuf:"bytes,5,opt,name=root_rotation,json=rootRotation" json:"root_rotation,omitempty"` } func (m *RootCA) Reset() { *m = RootCA{} } @@ -1776,6 +1782,17 @@ func (m *MaybeEncryptedRecord) Reset() { *m = MaybeEncryptedR func (*MaybeEncryptedRecord) ProtoMessage() {} func (*MaybeEncryptedRecord) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{46} } +type RootRotation struct { + CACert []byte `protobuf:"bytes,1,opt,name=ca_cert,json=caCert,proto3" json:"ca_cert,omitempty"` + CAKey []byte `protobuf:"bytes,2,opt,name=ca_key,json=caKey,proto3" json:"ca_key,omitempty"` + // cross-signed CA cert is the CACert that has been cross-signed by the previous root + CrossSignedCACert []byte `protobuf:"bytes,3,opt,name=cross_signed_ca_cert,json=crossSignedCaCert,proto3" json:"cross_signed_ca_cert,omitempty"` +} + +func (m *RootRotation) Reset() { *m = RootRotation{} } +func (*RootRotation) ProtoMessage() {} +func (*RootRotation) Descriptor() ([]byte, []int) { return fileDescriptorTypes, []int{47} } + func init() { proto.RegisterType((*Version)(nil), "docker.swarmkit.v1.Version") proto.RegisterType((*IndexEntry)(nil), "docker.swarmkit.v1.IndexEntry") @@ -1830,6 +1847,7 @@ func init() { proto.RegisterType((*BlacklistedCertificate)(nil), "docker.swarmkit.v1.BlacklistedCertificate") proto.RegisterType((*HealthConfig)(nil), "docker.swarmkit.v1.HealthConfig") proto.RegisterType((*MaybeEncryptedRecord)(nil), "docker.swarmkit.v1.MaybeEncryptedRecord") + proto.RegisterType((*RootRotation)(nil), "docker.swarmkit.v1.RootRotation") proto.RegisterEnum("docker.swarmkit.v1.TaskState", TaskState_name, TaskState_value) proto.RegisterEnum("docker.swarmkit.v1.NodeRole", NodeRole_name, NodeRole_value) proto.RegisterEnum("docker.swarmkit.v1.RaftMemberStatus_Reachability", RaftMemberStatus_Reachability_name, RaftMemberStatus_Reachability_value) @@ -2745,6 +2763,10 @@ func (m *RootCA) CopyFrom(src interface{}) { o := src.(*RootCA) *m = *o github_com_docker_swarmkit_api_deepcopy.Copy(&m.JoinTokens, &o.JoinTokens) + if o.RootRotation != nil { + m.RootRotation = &RootRotation{} + github_com_docker_swarmkit_api_deepcopy.Copy(m.RootRotation, o.RootRotation) + } } func (m *Certificate) Copy() *Certificate { @@ -2900,6 +2922,21 @@ func (m *MaybeEncryptedRecord) CopyFrom(src interface{}) { *m = *o } +func (m *RootRotation) Copy() *RootRotation { + if m == nil { + return nil + } + o := &RootRotation{} + o.CopyFrom(m) + return o +} + +func (m *RootRotation) CopyFrom(src interface{}) { + + o := src.(*RootRotation) + *m = *o +} + func (m *Version) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -4286,6 +4323,12 @@ func (m *ExternalCA) MarshalTo(dAtA []byte) (int, error) { i += copy(dAtA[i:], v) } } + if len(m.CACert) > 0 { + dAtA[i] = 0x22 + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.CACert))) + i += copy(dAtA[i:], m.CACert) + } return i, nil } @@ -4658,6 +4701,16 @@ func (m *RootCA) MarshalTo(dAtA []byte) (int, error) { return 0, err } i += n28 + if m.RootRotation != nil { + dAtA[i] = 0x2a + i++ + i = encodeVarintTypes(dAtA, i, uint64(m.RootRotation.Size())) + n29, err := m.RootRotation.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n29 + } return i, nil } @@ -4690,11 +4743,11 @@ func (m *Certificate) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.Status.Size())) - n29, err := m.Status.MarshalTo(dAtA[i:]) + n30, err := m.Status.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n29 + i += n30 if len(m.Certificate) > 0 { dAtA[i] = 0x22 i++ @@ -4822,11 +4875,11 @@ func (m *SecretReference) MarshalTo(dAtA []byte) (int, error) { i += copy(dAtA[i:], m.SecretName) } if m.Target != nil { - nn30, err := m.Target.MarshalTo(dAtA[i:]) + nn31, err := m.Target.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += nn30 + i += nn31 } return i, nil } @@ -4837,11 +4890,11 @@ func (m *SecretReference_File) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.File.Size())) - n31, err := m.File.MarshalTo(dAtA[i:]) + n32, err := m.File.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n31 + i += n32 } return i, nil } @@ -4905,11 +4958,11 @@ func (m *BlacklistedCertificate) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(m.Expiry.Size())) - n32, err := m.Expiry.MarshalTo(dAtA[i:]) + n33, err := m.Expiry.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n32 + i += n33 } return i, nil } @@ -4948,21 +5001,21 @@ func (m *HealthConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x12 i++ i = encodeVarintTypes(dAtA, i, uint64(m.Interval.Size())) - n33, err := m.Interval.MarshalTo(dAtA[i:]) + n34, err := m.Interval.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n33 + i += n34 } if m.Timeout != nil { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(m.Timeout.Size())) - n34, err := m.Timeout.MarshalTo(dAtA[i:]) + n35, err := m.Timeout.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n34 + i += n35 } if m.Retries != 0 { dAtA[i] = 0x20 @@ -4973,11 +5026,11 @@ func (m *HealthConfig) MarshalTo(dAtA []byte) (int, error) { dAtA[i] = 0x2a i++ i = encodeVarintTypes(dAtA, i, uint64(m.StartPeriod.Size())) - n35, err := m.StartPeriod.MarshalTo(dAtA[i:]) + n36, err := m.StartPeriod.MarshalTo(dAtA[i:]) if err != nil { return 0, err } - i += n35 + i += n36 } return i, nil } @@ -5017,6 +5070,42 @@ func (m *MaybeEncryptedRecord) MarshalTo(dAtA []byte) (int, error) { return i, nil } +func (m *RootRotation) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *RootRotation) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.CACert) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.CACert))) + i += copy(dAtA[i:], m.CACert) + } + if len(m.CAKey) > 0 { + dAtA[i] = 0x12 + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.CAKey))) + i += copy(dAtA[i:], m.CAKey) + } + if len(m.CrossSignedCACert) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.CrossSignedCACert))) + i += copy(dAtA[i:], m.CrossSignedCACert) + } + return i, nil +} + func encodeFixed64Types(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) dAtA[offset+1] = uint8(v >> 8) @@ -5646,6 +5735,10 @@ func (m *ExternalCA) Size() (n int) { n += mapEntrySize + 1 + sovTypes(uint64(mapEntrySize)) } } + l = len(m.CACert) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } return n } @@ -5801,6 +5894,10 @@ func (m *RootCA) Size() (n int) { } l = m.JoinTokens.Size() n += 1 + l + sovTypes(uint64(l)) + if m.RootRotation != nil { + l = m.RootRotation.Size() + n += 1 + l + sovTypes(uint64(l)) + } return n } @@ -5967,6 +6064,24 @@ func (m *MaybeEncryptedRecord) Size() (n int) { return n } +func (m *RootRotation) Size() (n int) { + var l int + _ = l + l = len(m.CACert) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.CAKey) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + l = len(m.CrossSignedCACert) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + return n +} + func sovTypes(x uint64) (n int) { for { n++ @@ -6449,6 +6564,7 @@ func (this *ExternalCA) String() string { `Protocol:` + fmt.Sprintf("%v", this.Protocol) + `,`, `URL:` + fmt.Sprintf("%v", this.URL) + `,`, `Options:` + mapStringForOptions + `,`, + `CACert:` + fmt.Sprintf("%v", this.CACert) + `,`, `}`, }, "") return s @@ -6579,6 +6695,7 @@ func (this *RootCA) String() string { `CACert:` + fmt.Sprintf("%v", this.CACert) + `,`, `CACertHash:` + fmt.Sprintf("%v", this.CACertHash) + `,`, `JoinTokens:` + strings.Replace(strings.Replace(this.JoinTokens.String(), "JoinTokens", "JoinTokens", 1), `&`, ``, 1) + `,`, + `RootRotation:` + strings.Replace(fmt.Sprintf("%v", this.RootRotation), "RootRotation", "RootRotation", 1) + `,`, `}`, }, "") return s @@ -6694,6 +6811,18 @@ func (this *MaybeEncryptedRecord) String() string { }, "") return s } +func (this *RootRotation) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&RootRotation{`, + `CACert:` + fmt.Sprintf("%v", this.CACert) + `,`, + `CAKey:` + fmt.Sprintf("%v", this.CAKey) + `,`, + `CrossSignedCACert:` + fmt.Sprintf("%v", this.CrossSignedCACert) + `,`, + `}`, + }, "") + return s +} func valueToStringTypes(v interface{}) string { rv := reflect.ValueOf(v) if rv.IsNil() { @@ -11487,6 +11616,37 @@ func (m *ExternalCA) Unmarshal(dAtA []byte) error { m.Options[mapkey] = mapvalue } iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CACert", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CACert = append(m.CACert[:0], dAtA[iNdEx:postIndex]...) + if m.CACert == nil { + m.CACert = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -12601,6 +12761,39 @@ func (m *RootCA) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RootRotation", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.RootRotation == nil { + m.RootRotation = &RootRotation{} + } + if err := m.RootRotation.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -13804,6 +13997,149 @@ func (m *MaybeEncryptedRecord) Unmarshal(dAtA []byte) error { } return nil } +func (m *RootRotation) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: RootRotation: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: RootRotation: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CACert", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CACert = append(m.CACert[:0], dAtA[iNdEx:postIndex]...) + if m.CACert == nil { + m.CACert = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CAKey", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CAKey = append(m.CAKey[:0], dAtA[iNdEx:postIndex]...) + if m.CAKey == nil { + m.CAKey = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CrossSignedCACert", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CrossSignedCACert = append(m.CrossSignedCACert[:0], dAtA[iNdEx:postIndex]...) + if m.CrossSignedCACert == nil { + m.CrossSignedCACert = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTypes(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 @@ -13912,265 +14248,271 @@ var ( func init() { proto.RegisterFile("types.proto", fileDescriptorTypes) } var fileDescriptorTypes = []byte{ - // 4160 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x5a, 0x4d, 0x6c, 0x1b, 0x49, - 0x76, 0x16, 0x7f, 0x45, 0x3e, 0x52, 0x52, 0xbb, 0xec, 0xf5, 0xca, 0x1c, 0x8f, 0xc4, 0xe9, 0x19, - 0xef, 0xcc, 0x78, 0x0d, 0x8e, 0x2d, 0xef, 0x2c, 0x3c, 0xe3, 0xec, 0x78, 0x9a, 0x3f, 0xb2, 0xb8, - 0x96, 0x48, 0xa2, 0x48, 0xd9, 0x3b, 0x87, 0x84, 0x28, 0x75, 0x97, 0xa8, 0x1e, 0x35, 0xbb, 0x98, - 0xee, 0xa6, 0x64, 0x26, 0x08, 0x62, 0xe4, 0x90, 0x04, 0x3a, 0xe5, 0x18, 0x20, 0x10, 0x72, 0xd8, - 0x9c, 0x72, 0xc8, 0x25, 0x87, 0x00, 0xb9, 0x64, 0x8e, 0x73, 0xcb, 0x26, 0x01, 0x82, 0x45, 0x02, - 0x38, 0x19, 0x05, 0xc8, 0x2d, 0x48, 0x2e, 0x8b, 0x5c, 0x12, 0x20, 0xa8, 0x9f, 0x6e, 0x36, 0x65, - 0x5a, 0xf2, 0x64, 0xf7, 0x22, 0x75, 0xbd, 0xfa, 0xde, 0xab, 0x57, 0x55, 0xaf, 0xaa, 0xbe, 0x57, - 0x45, 0x28, 0x04, 0x93, 0x11, 0xf5, 0x2b, 0x23, 0x8f, 0x05, 0x0c, 0x21, 0x8b, 0x99, 0x87, 0xd4, - 0xab, 0xf8, 0xc7, 0xc4, 0x1b, 0x1e, 0xda, 0x41, 0xe5, 0xe8, 0x5e, 0x69, 0x7d, 0xc0, 0xd8, 0xc0, - 0xa1, 0x1f, 0x09, 0xc4, 0xde, 0x78, 0xff, 0xa3, 0xc0, 0x1e, 0x52, 0x3f, 0x20, 0xc3, 0x91, 0x54, - 0x2a, 0xad, 0x9d, 0x07, 0x58, 0x63, 0x8f, 0x04, 0x36, 0x73, 0x55, 0xfd, 0xb5, 0x01, 0x1b, 0x30, - 0xf1, 0xf9, 0x11, 0xff, 0x92, 0x52, 0x7d, 0x1d, 0x16, 0x9f, 0x52, 0xcf, 0xb7, 0x99, 0x8b, 0xae, - 0x41, 0xc6, 0x76, 0x2d, 0xfa, 0x7c, 0x35, 0x51, 0x4e, 0x7c, 0x90, 0xc6, 0xb2, 0xa0, 0xdf, 0x05, - 0x68, 0xf2, 0x8f, 0x86, 0x1b, 0x78, 0x13, 0xa4, 0x41, 0xea, 0x90, 0x4e, 0x04, 0x22, 0x8f, 0xf9, - 0x27, 0x97, 0x1c, 0x11, 0x67, 0x35, 0x29, 0x25, 0x47, 0xc4, 0xd1, 0xbf, 0x49, 0x40, 0xc1, 0x70, - 0x5d, 0x16, 0x88, 0xd6, 0x7d, 0x84, 0x20, 0xed, 0x92, 0x21, 0x55, 0x4a, 0xe2, 0x1b, 0xd5, 0x20, - 0xeb, 0x90, 0x3d, 0xea, 0xf8, 0xab, 0xc9, 0x72, 0xea, 0x83, 0xc2, 0xc6, 0xf7, 0x2b, 0xaf, 0x76, - 0xb9, 0x12, 0x33, 0x52, 0xd9, 0x16, 0x68, 0xe1, 0x04, 0x56, 0xaa, 0xe8, 0x33, 0x58, 0xb4, 0x5d, - 0xcb, 0x36, 0xa9, 0xbf, 0x9a, 0x16, 0x56, 0xd6, 0xe6, 0x59, 0x99, 0x7a, 0x5f, 0x4d, 0x7f, 0xfd, - 0x72, 0x7d, 0x01, 0x87, 0x4a, 0xa5, 0x4f, 0xa0, 0x10, 0x33, 0x3b, 0xa7, 0x6f, 0xd7, 0x20, 0x73, - 0x44, 0x9c, 0x31, 0x55, 0xbd, 0x93, 0x85, 0x4f, 0x93, 0x0f, 0x12, 0xfa, 0x17, 0x90, 0xc7, 0xd4, - 0x67, 0x63, 0xcf, 0xa4, 0x3e, 0xfa, 0x10, 0xf2, 0x2e, 0x71, 0x59, 0xdf, 0x1c, 0x8d, 0x7d, 0xa1, - 0x9e, 0xaa, 0x16, 0xcf, 0x5e, 0xae, 0xe7, 0x5a, 0xc4, 0x65, 0xb5, 0xce, 0xae, 0x8f, 0x73, 0xbc, - 0xba, 0x36, 0x1a, 0xfb, 0xe8, 0x1d, 0x28, 0x0e, 0xe9, 0x90, 0x79, 0x93, 0xfe, 0xde, 0x24, 0xa0, - 0xbe, 0x30, 0x9c, 0xc2, 0x05, 0x29, 0xab, 0x72, 0x91, 0xfe, 0x47, 0x09, 0xb8, 0x16, 0xda, 0xc6, - 0xf4, 0x37, 0xc7, 0xb6, 0x47, 0x87, 0xd4, 0x0d, 0x7c, 0xf4, 0x31, 0x64, 0x1d, 0x7b, 0x68, 0x07, - 0xb2, 0x8d, 0xc2, 0xc6, 0xdb, 0xf3, 0x7a, 0x1b, 0x79, 0x85, 0x15, 0x18, 0x19, 0x50, 0xf4, 0xa8, - 0x4f, 0xbd, 0x23, 0x39, 0x92, 0xa2, 0xc9, 0x4b, 0x95, 0x67, 0x54, 0xf4, 0x4d, 0xc8, 0x75, 0x1c, - 0x12, 0xec, 0x33, 0x6f, 0x88, 0x74, 0x28, 0x12, 0xcf, 0x3c, 0xb0, 0x03, 0x6a, 0x06, 0x63, 0x2f, - 0x9c, 0xd5, 0x19, 0x19, 0xba, 0x0e, 0x49, 0x26, 0x1b, 0xca, 0x57, 0xb3, 0x67, 0x2f, 0xd7, 0x93, - 0xed, 0x2e, 0x4e, 0x32, 0x5f, 0x7f, 0x08, 0x57, 0x3a, 0xce, 0x78, 0x60, 0xbb, 0x75, 0xea, 0x9b, - 0x9e, 0x3d, 0xe2, 0xd6, 0x79, 0x78, 0xf0, 0xd8, 0x0f, 0xc3, 0x83, 0x7f, 0x47, 0x21, 0x93, 0x9c, - 0x86, 0x8c, 0xfe, 0x07, 0x49, 0xb8, 0xd2, 0x70, 0x07, 0xb6, 0x4b, 0xe3, 0xda, 0xb7, 0x60, 0x99, - 0x0a, 0x61, 0xff, 0x48, 0x86, 0xb1, 0xb2, 0xb3, 0x24, 0xa5, 0x61, 0x6c, 0x37, 0xcf, 0xc5, 0xdb, - 0xbd, 0x79, 0xdd, 0x7f, 0xc5, 0xfa, 0xdc, 0xa8, 0x6b, 0xc0, 0xe2, 0x48, 0x74, 0xc2, 0x5f, 0x4d, - 0x09, 0x5b, 0xb7, 0xe6, 0xd9, 0x7a, 0xa5, 0x9f, 0x61, 0xf0, 0x29, 0xdd, 0x5f, 0x26, 0xf8, 0xfe, - 0x2d, 0x01, 0x2b, 0x2d, 0x66, 0xcd, 0x8c, 0x43, 0x09, 0x72, 0x07, 0xcc, 0x0f, 0x62, 0x0b, 0x2d, - 0x2a, 0xa3, 0x07, 0x90, 0x1b, 0xa9, 0xe9, 0x53, 0xb3, 0x7f, 0x73, 0xbe, 0xcb, 0x12, 0x83, 0x23, - 0x34, 0x7a, 0x08, 0x79, 0x2f, 0x8c, 0x89, 0xd5, 0xd4, 0x9b, 0x04, 0xce, 0x14, 0x8f, 0x7e, 0x04, - 0x59, 0x39, 0x09, 0xab, 0x69, 0xa1, 0x79, 0xeb, 0x8d, 0xc6, 0x1c, 0x2b, 0x25, 0xfd, 0xe7, 0x09, - 0xd0, 0x30, 0xd9, 0x0f, 0x76, 0xe8, 0x70, 0x8f, 0x7a, 0xdd, 0x80, 0x04, 0x63, 0x1f, 0x5d, 0x87, - 0xac, 0x43, 0x89, 0x45, 0x3d, 0xd1, 0xc9, 0x1c, 0x56, 0x25, 0xb4, 0xcb, 0x83, 0x9c, 0x98, 0x07, - 0x64, 0xcf, 0x76, 0xec, 0x60, 0x22, 0xba, 0xb9, 0x3c, 0x7f, 0x96, 0xcf, 0xdb, 0xac, 0xe0, 0x98, - 0x22, 0x9e, 0x31, 0x83, 0x56, 0x61, 0x71, 0x48, 0x7d, 0x9f, 0x0c, 0xa8, 0xe8, 0x7d, 0x1e, 0x87, - 0x45, 0xfd, 0x21, 0x14, 0xe3, 0x7a, 0xa8, 0x00, 0x8b, 0xbb, 0xad, 0x27, 0xad, 0xf6, 0xb3, 0x96, - 0xb6, 0x80, 0x56, 0xa0, 0xb0, 0xdb, 0xc2, 0x0d, 0xa3, 0xb6, 0x65, 0x54, 0xb7, 0x1b, 0x5a, 0x02, - 0x2d, 0x41, 0x7e, 0x5a, 0x4c, 0xea, 0x7f, 0x99, 0x00, 0xe0, 0x13, 0xa8, 0x3a, 0xf5, 0x29, 0x64, - 0xfc, 0x80, 0x04, 0x72, 0xe2, 0x96, 0x37, 0xde, 0x9b, 0xe7, 0xf5, 0x14, 0x5e, 0xe1, 0xff, 0x28, - 0x96, 0x2a, 0x71, 0x0f, 0x93, 0x33, 0x1e, 0xf2, 0x35, 0x44, 0x2c, 0xcb, 0x53, 0x8e, 0x8b, 0x6f, - 0xfd, 0x21, 0x64, 0x84, 0xf6, 0xac, 0xbb, 0x39, 0x48, 0xd7, 0xf9, 0x57, 0x02, 0xe5, 0x21, 0x83, - 0x1b, 0x46, 0xfd, 0x0b, 0x2d, 0x89, 0x34, 0x28, 0xd6, 0x9b, 0xdd, 0x5a, 0xbb, 0xd5, 0x6a, 0xd4, - 0x7a, 0x8d, 0xba, 0x96, 0xd2, 0x6f, 0x41, 0xa6, 0x39, 0xe4, 0x96, 0x6f, 0xf2, 0xa8, 0xd8, 0xa7, - 0x1e, 0x75, 0xcd, 0x30, 0xd8, 0xa6, 0x02, 0xfd, 0x67, 0x79, 0xc8, 0xec, 0xb0, 0xb1, 0x1b, 0xa0, - 0x8d, 0xd8, 0xca, 0x5e, 0x9e, 0xbf, 0x39, 0x0b, 0x60, 0xa5, 0x37, 0x19, 0x51, 0xb5, 0xf2, 0xaf, - 0x43, 0x56, 0xc6, 0x8f, 0xea, 0x8e, 0x2a, 0x71, 0x79, 0x40, 0xbc, 0x01, 0x0d, 0x54, 0x7f, 0x54, - 0x09, 0x7d, 0x00, 0x39, 0x8f, 0x12, 0x8b, 0xb9, 0xce, 0x44, 0x84, 0x59, 0x4e, 0x6e, 0xbd, 0x98, - 0x12, 0xab, 0xed, 0x3a, 0x13, 0x1c, 0xd5, 0xa2, 0x2d, 0x28, 0xee, 0xd9, 0xae, 0xd5, 0x67, 0x23, - 0xb9, 0x0f, 0x66, 0x5e, 0x1f, 0x94, 0xd2, 0xab, 0xaa, 0xed, 0x5a, 0x6d, 0x09, 0xc6, 0x85, 0xbd, - 0x69, 0x01, 0xb5, 0x60, 0xf9, 0x88, 0x39, 0xe3, 0x21, 0x8d, 0x6c, 0x65, 0x85, 0xad, 0xf7, 0x5f, - 0x6f, 0xeb, 0xa9, 0xc0, 0x87, 0xd6, 0x96, 0x8e, 0xe2, 0x45, 0xf4, 0x04, 0x96, 0x82, 0xe1, 0x68, - 0xdf, 0x8f, 0xcc, 0x2d, 0x0a, 0x73, 0xdf, 0xbb, 0x60, 0xc0, 0x38, 0x3c, 0xb4, 0x56, 0x0c, 0x62, - 0xa5, 0xd2, 0xef, 0xa5, 0xa0, 0x10, 0xf3, 0x1c, 0x75, 0xa1, 0x30, 0xf2, 0xd8, 0x88, 0x0c, 0xc4, - 0x5e, 0xae, 0xe6, 0xe2, 0xde, 0x1b, 0xf5, 0xba, 0xd2, 0x99, 0x2a, 0xe2, 0xb8, 0x15, 0xfd, 0x34, - 0x09, 0x85, 0x58, 0x25, 0xba, 0x0d, 0x39, 0xdc, 0xc1, 0xcd, 0xa7, 0x46, 0xaf, 0xa1, 0x2d, 0x94, - 0x6e, 0x9e, 0x9c, 0x96, 0x57, 0x85, 0xb5, 0xb8, 0x81, 0x8e, 0x67, 0x1f, 0xf1, 0xd0, 0xfb, 0x00, - 0x16, 0x43, 0x68, 0xa2, 0xf4, 0xd6, 0xc9, 0x69, 0xf9, 0xbb, 0xe7, 0xa1, 0x31, 0x24, 0xee, 0x6e, - 0x19, 0xb8, 0x51, 0xd7, 0x92, 0xf3, 0x91, 0xb8, 0x7b, 0x40, 0x3c, 0x6a, 0xa1, 0xef, 0x41, 0x56, - 0x01, 0x53, 0xa5, 0xd2, 0xc9, 0x69, 0xf9, 0xfa, 0x79, 0xe0, 0x14, 0x87, 0xbb, 0xdb, 0xc6, 0xd3, - 0x86, 0x96, 0x9e, 0x8f, 0xc3, 0x5d, 0x87, 0x1c, 0x51, 0xf4, 0x1e, 0x64, 0x24, 0x2c, 0x53, 0xba, - 0x71, 0x72, 0x5a, 0xfe, 0xce, 0x2b, 0xe6, 0x38, 0xaa, 0xb4, 0xfa, 0x87, 0x3f, 0x5d, 0x5b, 0xf8, - 0xeb, 0x3f, 0x5b, 0xd3, 0xce, 0x57, 0x97, 0xfe, 0x27, 0x01, 0x4b, 0x33, 0x53, 0x8e, 0x74, 0xc8, - 0xba, 0xcc, 0x64, 0x23, 0xb9, 0xc5, 0xe7, 0xaa, 0x70, 0xf6, 0x72, 0x3d, 0xdb, 0x62, 0x35, 0x36, - 0x9a, 0x60, 0x55, 0x83, 0x9e, 0x9c, 0x3b, 0xa4, 0xee, 0xbf, 0x61, 0x3c, 0xcd, 0x3d, 0xa6, 0x1e, - 0xc1, 0x92, 0xe5, 0xd9, 0x47, 0xd4, 0xeb, 0x9b, 0xcc, 0xdd, 0xb7, 0x07, 0x6a, 0xfb, 0x2e, 0xcd, - 0xb3, 0x59, 0x17, 0x40, 0x5c, 0x94, 0x0a, 0x35, 0x81, 0xff, 0x25, 0x0e, 0xa8, 0xd2, 0x53, 0x28, - 0xc6, 0x23, 0x14, 0xbd, 0x0d, 0xe0, 0xdb, 0xbf, 0x45, 0x15, 0xe7, 0x11, 0x0c, 0x09, 0xe7, 0xb9, - 0x44, 0x30, 0x1e, 0xf4, 0x3e, 0xa4, 0x87, 0xcc, 0x92, 0x76, 0x96, 0xaa, 0x57, 0xf9, 0x39, 0xf9, - 0x4f, 0x2f, 0xd7, 0x0b, 0xcc, 0xaf, 0x6c, 0xda, 0x0e, 0xdd, 0x61, 0x16, 0xc5, 0x02, 0xa0, 0x1f, - 0x41, 0x9a, 0x6f, 0x15, 0xe8, 0x2d, 0x48, 0x57, 0x9b, 0xad, 0xba, 0xb6, 0x50, 0xba, 0x72, 0x72, - 0x5a, 0x5e, 0x12, 0x43, 0xc2, 0x2b, 0x78, 0xec, 0xa2, 0x75, 0xc8, 0x3e, 0x6d, 0x6f, 0xef, 0xee, - 0xf0, 0xf0, 0xba, 0x7a, 0x72, 0x5a, 0x5e, 0x89, 0xaa, 0xe5, 0xa0, 0xa1, 0xb7, 0x21, 0xd3, 0xdb, - 0xe9, 0x6c, 0x76, 0xb5, 0x64, 0x09, 0x9d, 0x9c, 0x96, 0x97, 0xa3, 0x7a, 0xe1, 0x73, 0xe9, 0x8a, - 0x9a, 0xd5, 0x7c, 0x24, 0xd7, 0x7f, 0x91, 0x84, 0x25, 0xcc, 0xc9, 0xb6, 0x17, 0x74, 0x98, 0x63, - 0x9b, 0x13, 0xd4, 0x81, 0xbc, 0xc9, 0x5c, 0xcb, 0x8e, 0xad, 0xa9, 0x8d, 0xd7, 0x1c, 0x8c, 0x53, - 0xad, 0xb0, 0x54, 0x0b, 0x35, 0xf1, 0xd4, 0x08, 0xfa, 0x08, 0x32, 0x16, 0x75, 0xc8, 0x44, 0x9d, - 0xd0, 0x37, 0x2a, 0x92, 0xce, 0x57, 0x42, 0x3a, 0x5f, 0xa9, 0x2b, 0x3a, 0x8f, 0x25, 0x4e, 0x50, - 0x49, 0xf2, 0xbc, 0x4f, 0x82, 0x80, 0x0e, 0x47, 0x81, 0x3c, 0x9e, 0xd3, 0xb8, 0x30, 0x24, 0xcf, - 0x0d, 0x25, 0x42, 0xf7, 0x20, 0x7b, 0x6c, 0xbb, 0x16, 0x3b, 0x56, 0x27, 0xf0, 0x05, 0x46, 0x15, - 0x50, 0x3f, 0xe1, 0xa7, 0xee, 0x39, 0x37, 0xf9, 0x78, 0xb7, 0xda, 0xad, 0x46, 0x38, 0xde, 0xaa, - 0xbe, 0xed, 0xb6, 0x98, 0xcb, 0xd7, 0x0a, 0xb4, 0x5b, 0xfd, 0x4d, 0xa3, 0xb9, 0xbd, 0x8b, 0xf9, - 0x98, 0x5f, 0x3b, 0x39, 0x2d, 0x6b, 0x11, 0x64, 0x93, 0xd8, 0x0e, 0xa7, 0x84, 0x37, 0x20, 0x65, - 0xb4, 0xbe, 0xd0, 0x92, 0x25, 0xed, 0xe4, 0xb4, 0x5c, 0x8c, 0xaa, 0x0d, 0x77, 0x32, 0x5d, 0x46, - 0xe7, 0xdb, 0xd5, 0xff, 0x36, 0x05, 0xc5, 0xdd, 0x91, 0x45, 0x02, 0x2a, 0x63, 0x12, 0x95, 0xa1, - 0x30, 0x22, 0x1e, 0x71, 0x1c, 0xea, 0xd8, 0xfe, 0x50, 0x25, 0x2a, 0x71, 0x11, 0xfa, 0xe4, 0x4d, - 0x87, 0xb1, 0x9a, 0xe3, 0x71, 0xf6, 0xc7, 0xff, 0xb2, 0x9e, 0x08, 0x07, 0x74, 0x17, 0x96, 0xf7, - 0xa5, 0xb7, 0x7d, 0x62, 0x8a, 0x89, 0x4d, 0x89, 0x89, 0xad, 0xcc, 0x9b, 0xd8, 0xb8, 0x5b, 0x15, - 0xd5, 0x49, 0x43, 0x68, 0xe1, 0xa5, 0xfd, 0x78, 0x11, 0xdd, 0x87, 0xc5, 0x21, 0x73, 0xed, 0x80, - 0x79, 0x97, 0xcf, 0x42, 0x88, 0x44, 0xb7, 0xe1, 0x0a, 0x9f, 0xdc, 0xd0, 0x1f, 0x51, 0x2d, 0x4e, - 0xac, 0x24, 0x5e, 0x19, 0x92, 0xe7, 0xaa, 0x41, 0xcc, 0xc5, 0xa8, 0x0a, 0x19, 0xe6, 0x71, 0x4a, - 0x94, 0x15, 0xee, 0xde, 0xb9, 0xd4, 0x5d, 0x59, 0x68, 0x73, 0x1d, 0x2c, 0x55, 0xf5, 0x1f, 0xc2, - 0xd2, 0x4c, 0x27, 0x38, 0x13, 0xe8, 0x18, 0xbb, 0xdd, 0x86, 0xb6, 0x80, 0x8a, 0x90, 0xab, 0xb5, - 0x5b, 0xbd, 0x66, 0x6b, 0x97, 0x53, 0x99, 0x22, 0xe4, 0x70, 0x7b, 0x7b, 0xbb, 0x6a, 0xd4, 0x9e, - 0x68, 0x49, 0xbd, 0x02, 0x85, 0x98, 0x35, 0xb4, 0x0c, 0xd0, 0xed, 0xb5, 0x3b, 0xfd, 0xcd, 0x26, - 0xee, 0xf6, 0x24, 0x11, 0xea, 0xf6, 0x0c, 0xdc, 0x53, 0x82, 0x84, 0xfe, 0x9f, 0xc9, 0x70, 0x46, - 0x15, 0xf7, 0xa9, 0xce, 0x72, 0x9f, 0x0b, 0x9c, 0x57, 0xec, 0x67, 0x5a, 0x88, 0x38, 0xd0, 0x27, - 0x00, 0x22, 0x70, 0xa8, 0xd5, 0x27, 0x81, 0x9a, 0xf8, 0xd2, 0x2b, 0x83, 0xdc, 0x0b, 0xf3, 0x65, - 0x9c, 0x57, 0x68, 0x23, 0x40, 0x3f, 0x82, 0xa2, 0xc9, 0x86, 0x23, 0x87, 0x2a, 0xe5, 0xd4, 0xa5, - 0xca, 0x85, 0x08, 0x6f, 0x04, 0x71, 0xf6, 0x95, 0x9e, 0xe5, 0x87, 0xbf, 0x9f, 0x08, 0x47, 0x66, - 0x0e, 0xe1, 0x2a, 0x42, 0x6e, 0xb7, 0x53, 0x37, 0x7a, 0xcd, 0xd6, 0x63, 0x2d, 0x81, 0x00, 0xb2, - 0x62, 0xa8, 0xeb, 0x5a, 0x92, 0x13, 0xc5, 0x5a, 0x7b, 0xa7, 0xb3, 0xdd, 0x10, 0x94, 0x0b, 0x5d, - 0x03, 0x2d, 0x1c, 0xec, 0xbe, 0x18, 0xc8, 0x46, 0x5d, 0x4b, 0xa3, 0xab, 0xb0, 0x12, 0x49, 0x95, - 0x66, 0x06, 0x5d, 0x07, 0x14, 0x09, 0xa7, 0x26, 0xb2, 0xfa, 0xef, 0xc0, 0x4a, 0x8d, 0xb9, 0x01, - 0xb1, 0xdd, 0x88, 0x44, 0x6f, 0xf0, 0x4e, 0x2b, 0x51, 0xdf, 0xb6, 0xe4, 0x9e, 0x5e, 0x5d, 0x39, - 0x7b, 0xb9, 0x5e, 0x88, 0xa0, 0xcd, 0x3a, 0xef, 0x69, 0x58, 0xb0, 0xf8, 0xfa, 0x1d, 0xd9, 0x96, - 0x18, 0xdc, 0x4c, 0x75, 0xf1, 0xec, 0xe5, 0x7a, 0xaa, 0xd3, 0xac, 0x63, 0x2e, 0x43, 0x6f, 0x41, - 0x9e, 0x3e, 0xb7, 0x83, 0xbe, 0xc9, 0xf7, 0x70, 0x3e, 0x80, 0x19, 0x9c, 0xe3, 0x82, 0x1a, 0xdf, - 0xb2, 0xab, 0x00, 0x1d, 0xe6, 0x05, 0xaa, 0xe5, 0x1f, 0x40, 0x66, 0xc4, 0x3c, 0x91, 0xc1, 0xbe, - 0x36, 0x5f, 0xe7, 0x70, 0x19, 0xa8, 0x58, 0x82, 0xf5, 0xbf, 0x49, 0x02, 0xf4, 0x88, 0x7f, 0xa8, - 0x8c, 0x3c, 0x80, 0x7c, 0x74, 0xf7, 0xa1, 0x52, 0xe1, 0x0b, 0x67, 0x3b, 0x02, 0xa3, 0xfb, 0x61, - 0xb0, 0xc9, 0xf4, 0x60, 0x6e, 0x2a, 0x13, 0x36, 0x34, 0x8f, 0x61, 0xcf, 0xe6, 0x00, 0xfc, 0x48, - 0xa4, 0x9e, 0xa7, 0x66, 0x9e, 0x7f, 0xa2, 0x9a, 0x38, 0x16, 0xe4, 0xa0, 0x29, 0x82, 0xf9, 0xee, - 0xbc, 0x46, 0xce, 0xcd, 0xc8, 0xd6, 0x02, 0x9e, 0xea, 0xa1, 0x47, 0x50, 0xe0, 0xfd, 0xee, 0xfb, - 0xa2, 0x4e, 0x71, 0xcb, 0xd7, 0x0e, 0x95, 0xb4, 0x80, 0x61, 0x14, 0x7d, 0x57, 0x35, 0x58, 0xf6, - 0xc6, 0x2e, 0xef, 0xb6, 0xb2, 0xa1, 0xdb, 0xf0, 0xdd, 0x16, 0x0d, 0x8e, 0x99, 0x77, 0x68, 0x04, - 0x01, 0x31, 0x0f, 0x86, 0xd4, 0x55, 0x63, 0x1c, 0x23, 0xd6, 0x89, 0x19, 0x62, 0xbd, 0x0a, 0x8b, - 0xc4, 0xb1, 0x89, 0x4f, 0x25, 0x1b, 0xc9, 0xe3, 0xb0, 0xc8, 0xe9, 0x3f, 0x4f, 0x26, 0xa8, 0xef, - 0x53, 0x99, 0x02, 0xe7, 0xf1, 0x54, 0xa0, 0xff, 0x43, 0x12, 0xa0, 0xd9, 0x31, 0x76, 0x94, 0xf9, - 0x3a, 0x64, 0xf7, 0xc9, 0xd0, 0x76, 0x26, 0x17, 0x2d, 0xf0, 0x29, 0xbe, 0x62, 0x48, 0x43, 0x9b, - 0x42, 0x07, 0x2b, 0x5d, 0x91, 0x15, 0x8c, 0xf7, 0x5c, 0x1a, 0x44, 0x59, 0x81, 0x28, 0x71, 0x0a, - 0xe2, 0x11, 0x37, 0x9a, 0x19, 0x59, 0xe0, 0xae, 0x0f, 0x48, 0x40, 0x8f, 0xc9, 0x24, 0x5c, 0x95, - 0xaa, 0x88, 0xb6, 0x78, 0xb6, 0xe0, 0x53, 0xef, 0x88, 0x5a, 0xab, 0x19, 0x11, 0x82, 0x97, 0xf9, - 0x83, 0x15, 0x5c, 0x92, 0xab, 0x48, 0xbb, 0xf4, 0x50, 0x30, 0x82, 0x69, 0xd5, 0xb7, 0x4a, 0xe0, - 0xef, 0xc2, 0xd2, 0x4c, 0x3f, 0x5f, 0x49, 0xc7, 0x9a, 0x9d, 0xa7, 0x3f, 0xd0, 0xd2, 0xea, 0xeb, - 0x87, 0x5a, 0x56, 0xff, 0xf3, 0x94, 0x5c, 0x47, 0x6a, 0x54, 0xe7, 0x5f, 0xa9, 0xe5, 0x44, 0xf4, - 0x9b, 0xcc, 0x51, 0xf1, 0xfd, 0xfe, 0xc5, 0xcb, 0x8b, 0xd3, 0x7b, 0x01, 0xc7, 0x91, 0x22, 0x5a, - 0x87, 0x82, 0x9c, 0xff, 0x3e, 0x8f, 0x27, 0x31, 0xac, 0x4b, 0x18, 0xa4, 0x88, 0x6b, 0xa2, 0x5b, - 0xb0, 0x3c, 0x1a, 0xef, 0x39, 0xb6, 0x7f, 0x40, 0x2d, 0x89, 0x49, 0x0b, 0xcc, 0x52, 0x24, 0x15, - 0xb0, 0x1d, 0x28, 0x2a, 0x41, 0x5f, 0x50, 0xbb, 0x8c, 0x70, 0xe8, 0xf6, 0x65, 0x0e, 0x49, 0x15, - 0xc1, 0xf8, 0x0a, 0xa3, 0x69, 0x41, 0xaf, 0x43, 0x2e, 0x74, 0x16, 0xad, 0x42, 0xaa, 0x57, 0xeb, - 0x68, 0x0b, 0xa5, 0x95, 0x93, 0xd3, 0x72, 0x21, 0x14, 0xf7, 0x6a, 0x1d, 0x5e, 0xb3, 0x5b, 0xef, - 0x68, 0x89, 0xd9, 0x9a, 0xdd, 0x7a, 0xa7, 0x94, 0xe6, 0x14, 0x43, 0xdf, 0x87, 0x42, 0xac, 0x05, - 0xf4, 0x2e, 0x2c, 0x36, 0x5b, 0x8f, 0x71, 0xa3, 0xdb, 0xd5, 0x16, 0x4a, 0xd7, 0x4f, 0x4e, 0xcb, - 0x28, 0x56, 0xdb, 0x74, 0x07, 0x7c, 0x7e, 0xd0, 0xdb, 0x90, 0xde, 0x6a, 0xf3, 0xa3, 0x4b, 0x72, - 0xc9, 0x18, 0x62, 0x8b, 0xf9, 0x41, 0xe9, 0xaa, 0xe2, 0x2e, 0x71, 0xc3, 0xfa, 0x9f, 0x24, 0x20, - 0x2b, 0x29, 0xf5, 0xdc, 0x89, 0x32, 0x60, 0x31, 0x4c, 0xf4, 0x24, 0xcf, 0x7f, 0xff, 0xf5, 0x9c, - 0xbc, 0xa2, 0x28, 0xb4, 0x0c, 0xbf, 0x50, 0xaf, 0xf4, 0x29, 0x14, 0xe3, 0x15, 0xdf, 0x2a, 0xf8, - 0x7e, 0x1b, 0x0a, 0x3c, 0xbe, 0x43, 0x6e, 0xbe, 0x01, 0x59, 0x49, 0xfb, 0xa3, 0xad, 0xf4, 0xf5, - 0x09, 0x82, 0x42, 0xa2, 0x07, 0xb0, 0x28, 0x93, 0x8a, 0xf0, 0x0a, 0x6c, 0xed, 0xe2, 0x55, 0x84, - 0x43, 0xb8, 0xfe, 0x08, 0xd2, 0x1d, 0x4a, 0x3d, 0x3e, 0xf6, 0x2e, 0xb3, 0xe8, 0xf4, 0xf4, 0x51, - 0xf9, 0x90, 0x45, 0x9b, 0x75, 0x9e, 0x0f, 0x59, 0xb4, 0x69, 0x45, 0x37, 0x18, 0xc9, 0xd8, 0x0d, - 0x46, 0x0f, 0x8a, 0xcf, 0xa8, 0x3d, 0x38, 0x08, 0xa8, 0x25, 0x0c, 0xdd, 0x81, 0xf4, 0x88, 0x46, - 0xce, 0xaf, 0xce, 0x0d, 0x30, 0x4a, 0x3d, 0x2c, 0x50, 0x7c, 0x1f, 0x39, 0x16, 0xda, 0xea, 0xe2, - 0x55, 0x95, 0xf4, 0xbf, 0x4f, 0xc2, 0x72, 0xd3, 0xf7, 0xc7, 0xc4, 0x35, 0x43, 0x62, 0xf2, 0xd9, - 0x2c, 0x31, 0xf9, 0x60, 0x6e, 0x0f, 0x67, 0x54, 0x66, 0x2f, 0x66, 0xd4, 0xe1, 0x90, 0x8c, 0x0e, - 0x07, 0xfd, 0x3f, 0x12, 0xe1, 0xed, 0xcb, 0xad, 0xd8, 0x72, 0x2f, 0xad, 0x9e, 0x9c, 0x96, 0xaf, - 0xc5, 0x2d, 0xd1, 0x5d, 0xf7, 0xd0, 0x65, 0xc7, 0x2e, 0x7a, 0x07, 0x32, 0xb8, 0xd1, 0x6a, 0x3c, - 0xd3, 0x12, 0x32, 0x3c, 0x67, 0x40, 0x98, 0xba, 0xf4, 0x98, 0x5b, 0xea, 0x34, 0x5a, 0x75, 0x4e, - 0x24, 0x92, 0x73, 0x2c, 0x75, 0xa8, 0x6b, 0xd9, 0xee, 0x00, 0xbd, 0x0b, 0xd9, 0x66, 0xb7, 0xbb, - 0x2b, 0xf2, 0xe3, 0xef, 0x9e, 0x9c, 0x96, 0xaf, 0xce, 0xa0, 0x78, 0x81, 0x5a, 0x1c, 0xc4, 0x59, - 0x3c, 0xa7, 0x18, 0x73, 0x40, 0x9c, 0x1e, 0x4a, 0x10, 0x6e, 0xf7, 0x78, 0xf2, 0x9e, 0x99, 0x03, - 0xc2, 0x8c, 0xff, 0x55, 0xcb, 0xed, 0x9f, 0x93, 0xa0, 0x19, 0xa6, 0x49, 0x47, 0x01, 0xaf, 0x57, - 0x89, 0x53, 0x0f, 0x72, 0x23, 0xfe, 0x65, 0xd3, 0x90, 0x04, 0x3c, 0x98, 0x7b, 0xf5, 0x7f, 0x4e, - 0xaf, 0x82, 0x99, 0x43, 0x0d, 0x6b, 0x68, 0xfb, 0xbe, 0xcd, 0x5c, 0x29, 0xc3, 0x91, 0xa5, 0xd2, - 0x7f, 0x25, 0xe0, 0xea, 0x1c, 0x04, 0xba, 0x0b, 0x69, 0x8f, 0x39, 0xe1, 0x1c, 0xde, 0x7c, 0xdd, - 0xc5, 0x1a, 0x57, 0xc5, 0x02, 0x89, 0xd6, 0x00, 0xc8, 0x38, 0x60, 0x44, 0xb4, 0x2f, 0x66, 0x2f, - 0x87, 0x63, 0x12, 0xf4, 0x0c, 0xb2, 0x3e, 0x35, 0x3d, 0x1a, 0x52, 0xc5, 0x47, 0xff, 0x5f, 0xef, - 0x2b, 0x5d, 0x61, 0x06, 0x2b, 0x73, 0xa5, 0x0a, 0x64, 0xa5, 0x84, 0x87, 0xbd, 0x45, 0x02, 0x22, - 0x9c, 0x2e, 0x62, 0xf1, 0xcd, 0xa3, 0x89, 0x38, 0x83, 0x30, 0x9a, 0x88, 0x33, 0xd0, 0xff, 0x34, - 0x09, 0xd0, 0x78, 0x1e, 0x50, 0xcf, 0x25, 0x4e, 0xcd, 0x40, 0x8d, 0xd8, 0xee, 0x2f, 0x7b, 0xfb, - 0xe1, 0xdc, 0xeb, 0xd6, 0x48, 0xa3, 0x52, 0x33, 0xe6, 0xec, 0xff, 0x37, 0x20, 0x35, 0xf6, 0xd4, - 0x6b, 0x8e, 0xa4, 0x79, 0xbb, 0x78, 0x1b, 0x73, 0x19, 0x6a, 0x4c, 0xb7, 0xad, 0xd4, 0xeb, 0xdf, - 0x6c, 0x62, 0x0d, 0xfc, 0xea, 0xb7, 0xae, 0x3b, 0x00, 0x53, 0xaf, 0xd1, 0x1a, 0x64, 0x6a, 0x9b, - 0xdd, 0xee, 0xb6, 0xb6, 0x20, 0xf7, 0xe6, 0x69, 0x95, 0x10, 0xeb, 0x3f, 0x4d, 0x40, 0xae, 0x66, - 0xa8, 0x13, 0xb3, 0x06, 0x9a, 0xd8, 0x70, 0x4c, 0xea, 0x05, 0x7d, 0xfa, 0x7c, 0x64, 0x7b, 0x13, - 0xb5, 0x67, 0x5c, 0x90, 0x8e, 0x2d, 0x73, 0x95, 0x1a, 0xf5, 0x82, 0x86, 0x50, 0x40, 0x18, 0x8a, - 0x54, 0xf5, 0xaf, 0x6f, 0x92, 0x70, 0xfb, 0x5e, 0xbb, 0x78, 0x1c, 0x24, 0xb1, 0x9e, 0x96, 0x7d, - 0x5c, 0x08, 0x8d, 0xd4, 0x88, 0xaf, 0x3f, 0x85, 0xab, 0x6d, 0xcf, 0x3c, 0xa0, 0x7e, 0x20, 0x1b, - 0x55, 0xfe, 0x3e, 0x82, 0x9b, 0x01, 0xf1, 0x0f, 0xfb, 0x07, 0xb6, 0x1f, 0x30, 0x6f, 0xd2, 0xf7, - 0x68, 0x40, 0x5d, 0x5e, 0xdf, 0x17, 0xcf, 0x3a, 0xea, 0x12, 0xe5, 0x06, 0xc7, 0x6c, 0x49, 0x08, - 0x0e, 0x11, 0xdb, 0x1c, 0xa0, 0x37, 0xa1, 0xc8, 0xa9, 0x6c, 0x9d, 0xee, 0x93, 0xb1, 0x13, 0xf8, - 0x3c, 0x49, 0x72, 0xd8, 0xa0, 0xff, 0xc6, 0x7b, 0x7d, 0xde, 0x61, 0x03, 0xf9, 0xa9, 0xff, 0x04, - 0xb4, 0xba, 0xed, 0x8f, 0x48, 0x60, 0x1e, 0x84, 0xb7, 0x43, 0xa8, 0x0e, 0xda, 0x01, 0x25, 0x5e, - 0xb0, 0x47, 0x49, 0xd0, 0x1f, 0x51, 0xcf, 0x66, 0xd6, 0xe5, 0xe3, 0xb9, 0x12, 0xa9, 0x74, 0x84, - 0x86, 0xfe, 0xdf, 0x09, 0x00, 0x4c, 0xf6, 0x43, 0x5a, 0xf3, 0x7d, 0xb8, 0xe2, 0xbb, 0x64, 0xe4, - 0x1f, 0xb0, 0xa0, 0x6f, 0xbb, 0x01, 0xf5, 0x8e, 0x88, 0xa3, 0x92, 0x7c, 0x2d, 0xac, 0x68, 0x2a, - 0x39, 0xba, 0x03, 0xe8, 0x90, 0xd2, 0x51, 0x9f, 0x39, 0x56, 0x3f, 0xac, 0x94, 0x8f, 0x4e, 0x69, - 0xac, 0xf1, 0x9a, 0xb6, 0x63, 0x75, 0x43, 0x39, 0xaa, 0xc2, 0x1a, 0xef, 0x3e, 0x75, 0x03, 0xcf, - 0xa6, 0x7e, 0x7f, 0x9f, 0x79, 0x7d, 0xdf, 0x61, 0xc7, 0xfd, 0x7d, 0xe6, 0x38, 0xec, 0x98, 0x7a, - 0xe1, 0xfd, 0x49, 0xc9, 0x61, 0x83, 0x86, 0x04, 0x6d, 0x32, 0xaf, 0xeb, 0xb0, 0xe3, 0xcd, 0x10, - 0xc1, 0xb9, 0xcf, 0xb4, 0xcf, 0x81, 0x6d, 0x1e, 0x86, 0xdc, 0x27, 0x92, 0xf6, 0x6c, 0xf3, 0x10, - 0xbd, 0x0b, 0x4b, 0xd4, 0xa1, 0x22, 0x8d, 0x96, 0xa8, 0x8c, 0x40, 0x15, 0x43, 0x21, 0x07, 0xe9, - 0x9f, 0x83, 0xd6, 0x70, 0x4d, 0x6f, 0x32, 0x8a, 0xcd, 0xf9, 0x1d, 0x40, 0x7c, 0xa7, 0xe9, 0x3b, - 0xcc, 0x3c, 0xec, 0x0f, 0x89, 0x4b, 0x06, 0xdc, 0x2f, 0xf9, 0xd0, 0xa1, 0xf1, 0x9a, 0x6d, 0x66, - 0x1e, 0xee, 0x28, 0xb9, 0xfe, 0x09, 0x40, 0x77, 0xe4, 0x51, 0x62, 0xb5, 0xf9, 0x91, 0xcc, 0x87, - 0x4e, 0x94, 0xfa, 0x96, 0x7a, 0x4b, 0x61, 0x9e, 0x5a, 0x54, 0x9a, 0xac, 0xa8, 0x47, 0x72, 0xfd, - 0xd7, 0xe1, 0x6a, 0xc7, 0x21, 0xa6, 0x78, 0x57, 0xec, 0x44, 0x37, 0xf7, 0xe8, 0x01, 0x64, 0x25, - 0x54, 0xcd, 0xe4, 0xdc, 0xc0, 0x9e, 0xb6, 0xb9, 0xb5, 0x80, 0x15, 0xbe, 0x5a, 0x04, 0x98, 0xda, - 0xd1, 0x9f, 0x43, 0x3e, 0x32, 0x8f, 0xca, 0xc0, 0xf3, 0x48, 0x1e, 0xdd, 0xb6, 0xab, 0x12, 0xbf, - 0x3c, 0x8e, 0x8b, 0x50, 0x13, 0x0a, 0xa3, 0x48, 0xf9, 0x42, 0x4e, 0x34, 0xc7, 0x69, 0x1c, 0xd7, - 0xd5, 0x3f, 0x03, 0xf8, 0x31, 0xb3, 0xdd, 0x1e, 0x3b, 0xa4, 0xae, 0x78, 0x2c, 0xe2, 0x29, 0x0f, - 0x0d, 0x07, 0x42, 0x95, 0x44, 0x46, 0x27, 0x47, 0x31, 0x7a, 0x33, 0x91, 0x45, 0xfd, 0xeb, 0x04, - 0x64, 0x31, 0x63, 0x41, 0xcd, 0x40, 0x65, 0xc8, 0x9a, 0xa4, 0x1f, 0x6e, 0x4d, 0xc5, 0x6a, 0xfe, - 0xec, 0xe5, 0x7a, 0xa6, 0x66, 0x3c, 0xa1, 0x13, 0x9c, 0x31, 0xc9, 0x13, 0x3a, 0xe1, 0x1c, 0xc6, - 0x24, 0x62, 0x43, 0x11, 0x66, 0x8a, 0x92, 0xc3, 0xd4, 0x0c, 0xbe, 0x61, 0xe0, 0xac, 0x49, 0xf8, - 0x7f, 0x74, 0x17, 0x8a, 0x0a, 0xd4, 0x3f, 0x20, 0xfe, 0x81, 0x4c, 0x54, 0xaa, 0xcb, 0x67, 0x2f, - 0xd7, 0x41, 0x22, 0xb7, 0x88, 0x7f, 0x80, 0x41, 0xa2, 0xf9, 0x37, 0x6a, 0x40, 0xe1, 0x4b, 0x66, - 0xbb, 0xfd, 0x40, 0x74, 0x42, 0xdd, 0x19, 0xcd, 0x9d, 0x8a, 0x69, 0x57, 0xd5, 0xe3, 0x22, 0x7c, - 0x19, 0x49, 0xf4, 0x7f, 0x4c, 0x40, 0x81, 0xdb, 0xb4, 0xf7, 0x6d, 0x93, 0x73, 0x8e, 0x6f, 0x7f, - 0x14, 0xde, 0x80, 0x94, 0xe9, 0x7b, 0xaa, 0x6f, 0xe2, 0x2c, 0xa8, 0x75, 0x31, 0xe6, 0x32, 0xf4, - 0x39, 0x64, 0x55, 0x76, 0x2a, 0x4f, 0x41, 0xfd, 0x72, 0x76, 0xa4, 0x5c, 0x54, 0x7a, 0x22, 0x2c, - 0xa6, 0xde, 0x89, 0x5e, 0x16, 0x71, 0x5c, 0x84, 0xae, 0x43, 0xd2, 0x74, 0xc5, 0xda, 0x51, 0x8f, - 0xc8, 0xb5, 0x16, 0x4e, 0x9a, 0xae, 0xfe, 0x77, 0x09, 0x58, 0x9a, 0x2e, 0x1d, 0x3e, 0x11, 0x37, - 0x21, 0xef, 0x8f, 0xf7, 0xfc, 0x89, 0x1f, 0xd0, 0x61, 0xf8, 0x1e, 0x15, 0x09, 0x50, 0x13, 0xf2, - 0xc4, 0x19, 0x30, 0xcf, 0x0e, 0x0e, 0x86, 0x2a, 0x31, 0x9a, 0x7f, 0x72, 0xc5, 0x6d, 0x56, 0x8c, - 0x50, 0x05, 0x4f, 0xb5, 0xc3, 0xb3, 0x2a, 0x25, 0x9c, 0x15, 0x67, 0xd5, 0x3b, 0x50, 0x74, 0xc8, - 0x50, 0xa4, 0xeb, 0x3c, 0xdf, 0x16, 0xfd, 0x48, 0xe3, 0x82, 0x92, 0xf5, 0xec, 0x21, 0xd5, 0x75, - 0xc8, 0x47, 0xc6, 0xd0, 0x0a, 0x14, 0x8c, 0x46, 0xb7, 0x7f, 0x6f, 0xe3, 0x41, 0xff, 0x71, 0x6d, - 0x47, 0x5b, 0x50, 0x54, 0xe9, 0xaf, 0x12, 0xb0, 0xa4, 0x16, 0xb6, 0xa2, 0x9f, 0xef, 0xc2, 0xa2, - 0x47, 0xf6, 0x83, 0x90, 0x20, 0xa7, 0x65, 0x70, 0xf1, 0xbd, 0x92, 0x13, 0x64, 0x5e, 0x35, 0x9f, - 0x20, 0xc7, 0x5e, 0x48, 0x53, 0x17, 0xbe, 0x90, 0xa6, 0x7f, 0x25, 0x2f, 0xa4, 0xfa, 0x5f, 0x24, - 0x61, 0x45, 0x31, 0x99, 0x68, 0x1f, 0xf9, 0x10, 0xf2, 0x92, 0xd4, 0x4c, 0xe9, 0xbd, 0x78, 0x94, - 0x93, 0xb8, 0x66, 0x1d, 0xe7, 0x64, 0x75, 0xd3, 0xe2, 0xf9, 0xa6, 0x82, 0xc6, 0xde, 0xfb, 0x41, - 0x8a, 0x5a, 0x3c, 0x59, 0xaa, 0x43, 0x7a, 0xdf, 0x76, 0xa8, 0x8a, 0xb3, 0xb9, 0x57, 0xb1, 0xe7, - 0x9a, 0x17, 0x8f, 0x06, 0x3d, 0x91, 0xb1, 0x6e, 0x2d, 0x60, 0xa1, 0x5d, 0xfa, 0x5d, 0x80, 0xa9, - 0x74, 0x6e, 0x52, 0xc6, 0x89, 0x8f, 0xba, 0xdf, 0x0a, 0x89, 0x4f, 0xb3, 0x8e, 0xb9, 0x8c, 0x57, - 0x0d, 0x6c, 0x4b, 0xad, 0x5c, 0x51, 0xf5, 0x98, 0x57, 0x0d, 0x6c, 0x2b, 0x7a, 0xb9, 0x48, 0x5f, - 0xf2, 0x72, 0x51, 0xcd, 0x85, 0xb7, 0x2c, 0xfa, 0x36, 0x5c, 0xaf, 0x3a, 0xc4, 0x3c, 0x74, 0x6c, - 0x3f, 0xa0, 0x56, 0x7c, 0x85, 0x6e, 0x40, 0x76, 0x86, 0x98, 0x5c, 0x74, 0xa9, 0xa5, 0x90, 0xfa, - 0xbf, 0x27, 0xa0, 0xb8, 0x45, 0x89, 0x13, 0x1c, 0x4c, 0x6f, 0x06, 0x02, 0xea, 0x07, 0x6a, 0x9f, - 0x15, 0xdf, 0xe8, 0x63, 0xc8, 0x45, 0xa7, 0xe9, 0xa5, 0xaf, 0x0b, 0x11, 0x14, 0xdd, 0x87, 0x45, - 0x1e, 0xd3, 0x6c, 0x1c, 0x72, 0xdd, 0x8b, 0x2e, 0xae, 0x15, 0x92, 0xef, 0xad, 0x1e, 0x15, 0xc7, - 0xa7, 0x18, 0x94, 0x0c, 0x0e, 0x8b, 0xe8, 0xd7, 0xa0, 0x28, 0xee, 0x5d, 0x43, 0xb6, 0x90, 0xb9, - 0xcc, 0x66, 0x41, 0x3e, 0x9d, 0x48, 0xa6, 0xf0, 0xbf, 0x09, 0xb8, 0xb6, 0x43, 0x26, 0x7b, 0x54, - 0x2d, 0x53, 0x6a, 0x61, 0x6a, 0x32, 0xcf, 0x42, 0x9d, 0xf8, 0xf2, 0xbe, 0xe0, 0x25, 0x66, 0x9e, - 0xf2, 0xfc, 0x55, 0x1e, 0xf2, 0xef, 0x64, 0x8c, 0x7f, 0x5f, 0x83, 0x8c, 0xcb, 0x5c, 0x93, 0xaa, - 0xb5, 0x2f, 0x0b, 0xba, 0x1d, 0x5f, 0xda, 0xa5, 0xe8, 0x91, 0x44, 0x3c, 0x71, 0xb4, 0x58, 0x10, - 0xb5, 0x86, 0x3e, 0x87, 0x52, 0xb7, 0x51, 0xc3, 0x8d, 0x5e, 0xb5, 0xfd, 0x93, 0x7e, 0xd7, 0xd8, - 0xee, 0x1a, 0x1b, 0x77, 0xfb, 0x9d, 0xf6, 0xf6, 0x17, 0xf7, 0xee, 0xdf, 0xfd, 0x58, 0x4b, 0x94, - 0xca, 0x27, 0xa7, 0xe5, 0x9b, 0x2d, 0xa3, 0xb6, 0x2d, 0x63, 0x79, 0x8f, 0x3d, 0xef, 0x12, 0xc7, - 0x27, 0x1b, 0x77, 0x3b, 0xcc, 0x99, 0x70, 0xcc, 0xed, 0x5f, 0xa4, 0x20, 0x1f, 0x5d, 0x4d, 0xf2, - 0x90, 0xe4, 0x79, 0xa1, 0x6a, 0x2a, 0x92, 0xb7, 0xe8, 0x31, 0x7a, 0x67, 0x9a, 0x11, 0x7e, 0x2e, - 0xdf, 0x62, 0xa2, 0xea, 0x30, 0x1b, 0x7c, 0x0f, 0x72, 0x46, 0xb7, 0xdb, 0x7c, 0xdc, 0x6a, 0xd4, - 0xb5, 0xaf, 0x12, 0xa5, 0xef, 0x9c, 0x9c, 0x96, 0xaf, 0x44, 0x20, 0xc3, 0xf7, 0xed, 0x81, 0x4b, - 0x2d, 0x81, 0xaa, 0xd5, 0x1a, 0x9d, 0x5e, 0xa3, 0xae, 0xbd, 0x48, 0x9e, 0x47, 0x89, 0x0c, 0x47, - 0xbc, 0xa8, 0xe6, 0x3b, 0xb8, 0xd1, 0x31, 0x30, 0x6f, 0xf0, 0xab, 0xa4, 0x4c, 0x54, 0xa7, 0x2d, - 0x7a, 0x74, 0x44, 0x3c, 0xde, 0xe6, 0x5a, 0xf8, 0xcb, 0x82, 0x17, 0x29, 0xf9, 0xea, 0x36, 0xbd, - 0x67, 0xa5, 0xc4, 0x9a, 0xf0, 0xd6, 0xc4, 0x05, 0xb7, 0x30, 0x93, 0x3a, 0xd7, 0x5a, 0x97, 0x07, - 0x02, 0xb7, 0xa2, 0xc3, 0x22, 0xde, 0x6d, 0xb5, 0x38, 0xe8, 0x45, 0xfa, 0x5c, 0xef, 0xf0, 0xd8, - 0x75, 0x39, 0xe6, 0x16, 0xe4, 0xc2, 0xfb, 0x6f, 0xed, 0xab, 0xf4, 0x39, 0x87, 0x6a, 0xe1, 0xe5, - 0xbd, 0x68, 0x70, 0x6b, 0xb7, 0x27, 0x7e, 0xf8, 0xf0, 0x22, 0x73, 0xbe, 0xc1, 0x83, 0x71, 0x60, - 0xf1, 0x14, 0xbc, 0x1c, 0xe5, 0xc4, 0x5f, 0x65, 0x64, 0x96, 0x11, 0x61, 0x54, 0x42, 0xfc, 0x1e, - 0xe4, 0x70, 0xe3, 0xc7, 0xf2, 0x37, 0x12, 0x2f, 0xb2, 0xe7, 0xec, 0x60, 0xfa, 0x25, 0x35, 0x55, - 0x6b, 0x6d, 0xdc, 0xd9, 0x32, 0xc4, 0x90, 0x9f, 0x47, 0xb5, 0xbd, 0xd1, 0x01, 0x71, 0xa9, 0x35, - 0x7d, 0x7a, 0x8c, 0xaa, 0x6e, 0xff, 0x06, 0xe4, 0xc2, 0x63, 0x19, 0xad, 0x41, 0xf6, 0x59, 0x1b, - 0x3f, 0x69, 0x60, 0x6d, 0x41, 0x8e, 0x61, 0x58, 0xf3, 0x4c, 0xf2, 0x9a, 0x32, 0x2c, 0xee, 0x18, - 0x2d, 0xe3, 0x71, 0x03, 0x87, 0xd7, 0x55, 0x21, 0x40, 0x9d, 0x2d, 0x25, 0x4d, 0x35, 0x10, 0xd9, - 0xac, 0xae, 0x7e, 0xfd, 0xcd, 0xda, 0xc2, 0xcf, 0xbf, 0x59, 0x5b, 0x78, 0x71, 0xb6, 0x96, 0xf8, - 0xfa, 0x6c, 0x2d, 0xf1, 0xb3, 0xb3, 0xb5, 0xc4, 0xbf, 0x9e, 0xad, 0x25, 0xf6, 0xb2, 0x62, 0x45, - 0xde, 0xff, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x1a, 0x39, 0x0d, 0xcf, 0x9a, 0x28, 0x00, 0x00, + // 4248 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xac, 0x5a, 0x4d, 0x6c, 0x24, 0x49, + 0x56, 0x76, 0xfd, 0xba, 0xea, 0x55, 0xd9, 0x9d, 0x1d, 0xdd, 0xdb, 0xeb, 0xae, 0xed, 0xb1, 0x6b, + 0x72, 0xa6, 0x77, 0x7e, 0x76, 0x54, 0xd3, 0xed, 0xde, 0x59, 0xf5, 0xcc, 0xb0, 0x33, 0x93, 0xf5, + 0xe3, 0x76, 0x6d, 0xdb, 0x55, 0xa5, 0xa8, 0x72, 0xf7, 0xce, 0x01, 0x52, 0xe1, 0xcc, 0x70, 0x39, + 0xc7, 0x59, 0x19, 0x45, 0x66, 0x96, 0xdd, 0x05, 0x42, 0xb4, 0x38, 0x00, 0xf2, 0x05, 0x8e, 0x48, + 0xc8, 0xa7, 0xe5, 0xc4, 0x81, 0x0b, 0x07, 0x24, 0x2e, 0xcc, 0x81, 0xc3, 0xdc, 0x58, 0x40, 0x42, + 0x2b, 0x90, 0x1a, 0xc6, 0x48, 0xdc, 0x10, 0x5c, 0x56, 0x5c, 0x40, 0x42, 0xf1, 0x93, 0x59, 0x69, + 0x77, 0xd9, 0xee, 0x61, 0xb8, 0xd8, 0x19, 0x2f, 0xbe, 0xf7, 0xe2, 0x45, 0xc4, 0x8b, 0x88, 0xef, + 0x45, 0x14, 0x94, 0xc2, 0xe9, 0x98, 0x06, 0xb5, 0xb1, 0xcf, 0x42, 0x86, 0x90, 0xcd, 0xac, 0x03, + 0xea, 0xd7, 0x82, 0x23, 0xe2, 0x8f, 0x0e, 0x9c, 0xb0, 0x76, 0x78, 0xbf, 0xb2, 0x36, 0x64, 0x6c, + 0xe8, 0xd2, 0xf7, 0x05, 0x62, 0x77, 0xb2, 0xf7, 0x7e, 0xe8, 0x8c, 0x68, 0x10, 0x92, 0xd1, 0x58, + 0x2a, 0x55, 0x56, 0xcf, 0x03, 0xec, 0x89, 0x4f, 0x42, 0x87, 0x79, 0xaa, 0xfe, 0xe6, 0x90, 0x0d, + 0x99, 0xf8, 0x7c, 0x9f, 0x7f, 0x49, 0xa9, 0xbe, 0x06, 0x8b, 0x4f, 0xa8, 0x1f, 0x38, 0xcc, 0x43, + 0x37, 0x21, 0xe7, 0x78, 0x36, 0x7d, 0xb6, 0x92, 0xaa, 0xa6, 0xde, 0xce, 0x62, 0x59, 0xd0, 0xef, + 0x01, 0xb4, 0xf9, 0x47, 0xcb, 0x0b, 0xfd, 0x29, 0xd2, 0x20, 0x73, 0x40, 0xa7, 0x02, 0x51, 0xc4, + 0xfc, 0x93, 0x4b, 0x0e, 0x89, 0xbb, 0x92, 0x96, 0x92, 0x43, 0xe2, 0xea, 0x5f, 0xa7, 0xa0, 0x64, + 0x78, 0x1e, 0x0b, 0x45, 0xeb, 0x01, 0x42, 0x90, 0xf5, 0xc8, 0x88, 0x2a, 0x25, 0xf1, 0x8d, 0x1a, + 0x90, 0x77, 0xc9, 0x2e, 0x75, 0x83, 0x95, 0x74, 0x35, 0xf3, 0x76, 0x69, 0xfd, 0x07, 0xb5, 0x97, + 0xbb, 0x5c, 0x4b, 0x18, 0xa9, 0x6d, 0x09, 0xb4, 0x70, 0x02, 0x2b, 0x55, 0xf4, 0x09, 0x2c, 0x3a, + 0x9e, 0xed, 0x58, 0x34, 0x58, 0xc9, 0x0a, 0x2b, 0xab, 0xf3, 0xac, 0xcc, 0xbc, 0xaf, 0x67, 0xbf, + 0x7a, 0xb1, 0xb6, 0x80, 0x23, 0xa5, 0xca, 0x87, 0x50, 0x4a, 0x98, 0x9d, 0xd3, 0xb7, 0x9b, 0x90, + 0x3b, 0x24, 0xee, 0x84, 0xaa, 0xde, 0xc9, 0xc2, 0x47, 0xe9, 0x87, 0x29, 0xfd, 0x73, 0x28, 0x62, + 0x1a, 0xb0, 0x89, 0x6f, 0xd1, 0x00, 0xbd, 0x03, 0x45, 0x8f, 0x78, 0xcc, 0xb4, 0xc6, 0x93, 0x40, + 0xa8, 0x67, 0xea, 0xe5, 0xd3, 0x17, 0x6b, 0x85, 0x0e, 0xf1, 0x58, 0xa3, 0xb7, 0x13, 0xe0, 0x02, + 0xaf, 0x6e, 0x8c, 0x27, 0x01, 0x7a, 0x1d, 0xca, 0x23, 0x3a, 0x62, 0xfe, 0xd4, 0xdc, 0x9d, 0x86, + 0x34, 0x10, 0x86, 0x33, 0xb8, 0x24, 0x65, 0x75, 0x2e, 0xd2, 0xff, 0x30, 0x05, 0x37, 0x23, 0xdb, + 0x98, 0xfe, 0xfa, 0xc4, 0xf1, 0xe9, 0x88, 0x7a, 0x61, 0x80, 0x3e, 0x80, 0xbc, 0xeb, 0x8c, 0x9c, + 0x50, 0xb6, 0x51, 0x5a, 0x7f, 0x6d, 0x5e, 0x6f, 0x63, 0xaf, 0xb0, 0x02, 0x23, 0x03, 0xca, 0x3e, + 0x0d, 0xa8, 0x7f, 0x28, 0x47, 0x52, 0x34, 0x79, 0xa5, 0xf2, 0x19, 0x15, 0x7d, 0x03, 0x0a, 0x3d, + 0x97, 0x84, 0x7b, 0xcc, 0x1f, 0x21, 0x1d, 0xca, 0xc4, 0xb7, 0xf6, 0x9d, 0x90, 0x5a, 0xe1, 0xc4, + 0x8f, 0x66, 0xf5, 0x8c, 0x0c, 0xdd, 0x82, 0x34, 0x93, 0x0d, 0x15, 0xeb, 0xf9, 0xd3, 0x17, 0x6b, + 0xe9, 0x6e, 0x1f, 0xa7, 0x59, 0xa0, 0x7f, 0x0c, 0xd7, 0x7b, 0xee, 0x64, 0xe8, 0x78, 0x4d, 0x1a, + 0x58, 0xbe, 0x33, 0xe6, 0xd6, 0x79, 0x78, 0xf0, 0xd8, 0x8f, 0xc2, 0x83, 0x7f, 0xc7, 0x21, 0x93, + 0x9e, 0x85, 0x8c, 0xfe, 0x7b, 0x69, 0xb8, 0xde, 0xf2, 0x86, 0x8e, 0x47, 0x93, 0xda, 0x77, 0x61, + 0x99, 0x0a, 0xa1, 0x79, 0x28, 0xc3, 0x58, 0xd9, 0x59, 0x92, 0xd2, 0x28, 0xb6, 0xdb, 0xe7, 0xe2, + 0xed, 0xfe, 0xbc, 0xee, 0xbf, 0x64, 0x7d, 0x6e, 0xd4, 0xb5, 0x60, 0x71, 0x2c, 0x3a, 0x11, 0xac, + 0x64, 0x84, 0xad, 0xbb, 0xf3, 0x6c, 0xbd, 0xd4, 0xcf, 0x28, 0xf8, 0x94, 0xee, 0xb7, 0x09, 0xbe, + 0x7f, 0x4d, 0xc1, 0xb5, 0x0e, 0xb3, 0xcf, 0x8c, 0x43, 0x05, 0x0a, 0xfb, 0x2c, 0x08, 0x13, 0x0b, + 0x2d, 0x2e, 0xa3, 0x87, 0x50, 0x18, 0xab, 0xe9, 0x53, 0xb3, 0x7f, 0x67, 0xbe, 0xcb, 0x12, 0x83, + 0x63, 0x34, 0xfa, 0x18, 0x8a, 0x7e, 0x14, 0x13, 0x2b, 0x99, 0x57, 0x09, 0x9c, 0x19, 0x1e, 0xfd, + 0x18, 0xf2, 0x72, 0x12, 0x56, 0xb2, 0x42, 0xf3, 0xee, 0x2b, 0x8d, 0x39, 0x56, 0x4a, 0xfa, 0x2f, + 0x52, 0xa0, 0x61, 0xb2, 0x17, 0x6e, 0xd3, 0xd1, 0x2e, 0xf5, 0xfb, 0x21, 0x09, 0x27, 0x01, 0xba, + 0x05, 0x79, 0x97, 0x12, 0x9b, 0xfa, 0xa2, 0x93, 0x05, 0xac, 0x4a, 0x68, 0x87, 0x07, 0x39, 0xb1, + 0xf6, 0xc9, 0xae, 0xe3, 0x3a, 0xe1, 0x54, 0x74, 0x73, 0x79, 0xfe, 0x2c, 0x9f, 0xb7, 0x59, 0xc3, + 0x09, 0x45, 0x7c, 0xc6, 0x0c, 0x5a, 0x81, 0xc5, 0x11, 0x0d, 0x02, 0x32, 0xa4, 0xa2, 0xf7, 0x45, + 0x1c, 0x15, 0xf5, 0x8f, 0xa1, 0x9c, 0xd4, 0x43, 0x25, 0x58, 0xdc, 0xe9, 0x3c, 0xee, 0x74, 0x9f, + 0x76, 0xb4, 0x05, 0x74, 0x0d, 0x4a, 0x3b, 0x1d, 0xdc, 0x32, 0x1a, 0x9b, 0x46, 0x7d, 0xab, 0xa5, + 0xa5, 0xd0, 0x12, 0x14, 0x67, 0xc5, 0xb4, 0xfe, 0xe7, 0x29, 0x00, 0x3e, 0x81, 0xaa, 0x53, 0x1f, + 0x41, 0x2e, 0x08, 0x49, 0x28, 0x27, 0x6e, 0x79, 0xfd, 0xcd, 0x79, 0x5e, 0xcf, 0xe0, 0x35, 0xfe, + 0x8f, 0x62, 0xa9, 0x92, 0xf4, 0x30, 0x7d, 0xc6, 0x43, 0xbe, 0x86, 0x88, 0x6d, 0xfb, 0xca, 0x71, + 0xf1, 0xad, 0x7f, 0x0c, 0x39, 0xa1, 0x7d, 0xd6, 0xdd, 0x02, 0x64, 0x9b, 0xfc, 0x2b, 0x85, 0x8a, + 0x90, 0xc3, 0x2d, 0xa3, 0xf9, 0xb9, 0x96, 0x46, 0x1a, 0x94, 0x9b, 0xed, 0x7e, 0xa3, 0xdb, 0xe9, + 0xb4, 0x1a, 0x83, 0x56, 0x53, 0xcb, 0xe8, 0x77, 0x21, 0xd7, 0x1e, 0x71, 0xcb, 0x77, 0x78, 0x54, + 0xec, 0x51, 0x9f, 0x7a, 0x56, 0x14, 0x6c, 0x33, 0x81, 0xfe, 0xf3, 0x22, 0xe4, 0xb6, 0xd9, 0xc4, + 0x0b, 0xd1, 0x7a, 0x62, 0x65, 0x2f, 0xcf, 0xdf, 0x9c, 0x05, 0xb0, 0x36, 0x98, 0x8e, 0xa9, 0x5a, + 0xf9, 0xb7, 0x20, 0x2f, 0xe3, 0x47, 0x75, 0x47, 0x95, 0xb8, 0x3c, 0x24, 0xfe, 0x90, 0x86, 0xaa, + 0x3f, 0xaa, 0x84, 0xde, 0x86, 0x82, 0x4f, 0x89, 0xcd, 0x3c, 0x77, 0x2a, 0xc2, 0xac, 0x20, 0xb7, + 0x5e, 0x4c, 0x89, 0xdd, 0xf5, 0xdc, 0x29, 0x8e, 0x6b, 0xd1, 0x26, 0x94, 0x77, 0x1d, 0xcf, 0x36, + 0xd9, 0x58, 0xee, 0x83, 0xb9, 0x8b, 0x83, 0x52, 0x7a, 0x55, 0x77, 0x3c, 0xbb, 0x2b, 0xc1, 0xb8, + 0xb4, 0x3b, 0x2b, 0xa0, 0x0e, 0x2c, 0x1f, 0x32, 0x77, 0x32, 0xa2, 0xb1, 0xad, 0xbc, 0xb0, 0xf5, + 0xd6, 0xc5, 0xb6, 0x9e, 0x08, 0x7c, 0x64, 0x6d, 0xe9, 0x30, 0x59, 0x44, 0x8f, 0x61, 0x29, 0x1c, + 0x8d, 0xf7, 0x82, 0xd8, 0xdc, 0xa2, 0x30, 0xf7, 0xfd, 0x4b, 0x06, 0x8c, 0xc3, 0x23, 0x6b, 0xe5, + 0x30, 0x51, 0xaa, 0xfc, 0x4e, 0x06, 0x4a, 0x09, 0xcf, 0x51, 0x1f, 0x4a, 0x63, 0x9f, 0x8d, 0xc9, + 0x50, 0xec, 0xe5, 0x6a, 0x2e, 0xee, 0xbf, 0x52, 0xaf, 0x6b, 0xbd, 0x99, 0x22, 0x4e, 0x5a, 0xd1, + 0x4f, 0xd2, 0x50, 0x4a, 0x54, 0xa2, 0x77, 0xa1, 0x80, 0x7b, 0xb8, 0xfd, 0xc4, 0x18, 0xb4, 0xb4, + 0x85, 0xca, 0x9d, 0xe3, 0x93, 0xea, 0x8a, 0xb0, 0x96, 0x34, 0xd0, 0xf3, 0x9d, 0x43, 0x1e, 0x7a, + 0x6f, 0xc3, 0x62, 0x04, 0x4d, 0x55, 0xbe, 0x77, 0x7c, 0x52, 0xfd, 0xee, 0x79, 0x68, 0x02, 0x89, + 0xfb, 0x9b, 0x06, 0x6e, 0x35, 0xb5, 0xf4, 0x7c, 0x24, 0xee, 0xef, 0x13, 0x9f, 0xda, 0xe8, 0xfb, + 0x90, 0x57, 0xc0, 0x4c, 0xa5, 0x72, 0x7c, 0x52, 0xbd, 0x75, 0x1e, 0x38, 0xc3, 0xe1, 0xfe, 0x96, + 0xf1, 0xa4, 0xa5, 0x65, 0xe7, 0xe3, 0x70, 0xdf, 0x25, 0x87, 0x14, 0xbd, 0x09, 0x39, 0x09, 0xcb, + 0x55, 0x6e, 0x1f, 0x9f, 0x54, 0xbf, 0xf3, 0x92, 0x39, 0x8e, 0xaa, 0xac, 0xfc, 0xfe, 0xcf, 0x56, + 0x17, 0xfe, 0xf2, 0x4f, 0x56, 0xb5, 0xf3, 0xd5, 0x95, 0xff, 0x4e, 0xc1, 0xd2, 0x99, 0x29, 0x47, + 0x3a, 0xe4, 0x3d, 0x66, 0xb1, 0xb1, 0xdc, 0xe2, 0x0b, 0x75, 0x38, 0x7d, 0xb1, 0x96, 0xef, 0xb0, + 0x06, 0x1b, 0x4f, 0xb1, 0xaa, 0x41, 0x8f, 0xcf, 0x1d, 0x52, 0x0f, 0x5e, 0x31, 0x9e, 0xe6, 0x1e, + 0x53, 0x9f, 0xc2, 0x92, 0xed, 0x3b, 0x87, 0xd4, 0x37, 0x2d, 0xe6, 0xed, 0x39, 0x43, 0xb5, 0x7d, + 0x57, 0xe6, 0xd9, 0x6c, 0x0a, 0x20, 0x2e, 0x4b, 0x85, 0x86, 0xc0, 0x7f, 0x8b, 0x03, 0xaa, 0xf2, + 0x04, 0xca, 0xc9, 0x08, 0x45, 0xaf, 0x01, 0x04, 0xce, 0x6f, 0x50, 0xc5, 0x79, 0x04, 0x43, 0xc2, + 0x45, 0x2e, 0x11, 0x8c, 0x07, 0xbd, 0x05, 0xd9, 0x11, 0xb3, 0xa5, 0x9d, 0xa5, 0xfa, 0x0d, 0x7e, + 0x4e, 0xfe, 0xe3, 0x8b, 0xb5, 0x12, 0x0b, 0x6a, 0x1b, 0x8e, 0x4b, 0xb7, 0x99, 0x4d, 0xb1, 0x00, + 0xe8, 0x87, 0x90, 0xe5, 0x5b, 0x05, 0xfa, 0x1e, 0x64, 0xeb, 0xed, 0x4e, 0x53, 0x5b, 0xa8, 0x5c, + 0x3f, 0x3e, 0xa9, 0x2e, 0x89, 0x21, 0xe1, 0x15, 0x3c, 0x76, 0xd1, 0x1a, 0xe4, 0x9f, 0x74, 0xb7, + 0x76, 0xb6, 0x79, 0x78, 0xdd, 0x38, 0x3e, 0xa9, 0x5e, 0x8b, 0xab, 0xe5, 0xa0, 0xa1, 0xd7, 0x20, + 0x37, 0xd8, 0xee, 0x6d, 0xf4, 0xb5, 0x74, 0x05, 0x1d, 0x9f, 0x54, 0x97, 0xe3, 0x7a, 0xe1, 0x73, + 0xe5, 0xba, 0x9a, 0xd5, 0x62, 0x2c, 0xd7, 0x7f, 0x99, 0x86, 0x25, 0xcc, 0xc9, 0xb6, 0x1f, 0xf6, + 0x98, 0xeb, 0x58, 0x53, 0xd4, 0x83, 0xa2, 0xc5, 0x3c, 0xdb, 0x49, 0xac, 0xa9, 0xf5, 0x0b, 0x0e, + 0xc6, 0x99, 0x56, 0x54, 0x6a, 0x44, 0x9a, 0x78, 0x66, 0x04, 0xbd, 0x0f, 0x39, 0x9b, 0xba, 0x64, + 0xaa, 0x4e, 0xe8, 0xdb, 0x35, 0x49, 0xe7, 0x6b, 0x11, 0x9d, 0xaf, 0x35, 0x15, 0x9d, 0xc7, 0x12, + 0x27, 0xa8, 0x24, 0x79, 0x66, 0x92, 0x30, 0xa4, 0xa3, 0x71, 0x28, 0x8f, 0xe7, 0x2c, 0x2e, 0x8d, + 0xc8, 0x33, 0x43, 0x89, 0xd0, 0x7d, 0xc8, 0x1f, 0x39, 0x9e, 0xcd, 0x8e, 0xd4, 0x09, 0x7c, 0x89, + 0x51, 0x05, 0xd4, 0x8f, 0xf9, 0xa9, 0x7b, 0xce, 0x4d, 0x3e, 0xde, 0x9d, 0x6e, 0xa7, 0x15, 0x8d, + 0xb7, 0xaa, 0xef, 0x7a, 0x1d, 0xe6, 0xf1, 0xb5, 0x02, 0xdd, 0x8e, 0xb9, 0x61, 0xb4, 0xb7, 0x76, + 0x30, 0x1f, 0xf3, 0x9b, 0xc7, 0x27, 0x55, 0x2d, 0x86, 0x6c, 0x10, 0xc7, 0xe5, 0x94, 0xf0, 0x36, + 0x64, 0x8c, 0xce, 0xe7, 0x5a, 0xba, 0xa2, 0x1d, 0x9f, 0x54, 0xcb, 0x71, 0xb5, 0xe1, 0x4d, 0x67, + 0xcb, 0xe8, 0x7c, 0xbb, 0xfa, 0xdf, 0x64, 0xa0, 0xbc, 0x33, 0xb6, 0x49, 0x48, 0x65, 0x4c, 0xa2, + 0x2a, 0x94, 0xc6, 0xc4, 0x27, 0xae, 0x4b, 0x5d, 0x27, 0x18, 0xa9, 0x44, 0x25, 0x29, 0x42, 0x1f, + 0xbe, 0xea, 0x30, 0xd6, 0x0b, 0x3c, 0xce, 0xfe, 0xe8, 0x9f, 0xd7, 0x52, 0xd1, 0x80, 0xee, 0xc0, + 0xf2, 0x9e, 0xf4, 0xd6, 0x24, 0x96, 0x98, 0xd8, 0x8c, 0x98, 0xd8, 0xda, 0xbc, 0x89, 0x4d, 0xba, + 0x55, 0x53, 0x9d, 0x34, 0x84, 0x16, 0x5e, 0xda, 0x4b, 0x16, 0xd1, 0x03, 0x58, 0x1c, 0x31, 0xcf, + 0x09, 0x99, 0x7f, 0xf5, 0x2c, 0x44, 0x48, 0xf4, 0x2e, 0x5c, 0xe7, 0x93, 0x1b, 0xf9, 0x23, 0xaa, + 0xc5, 0x89, 0x95, 0xc6, 0xd7, 0x46, 0xe4, 0x99, 0x6a, 0x10, 0x73, 0x31, 0xaa, 0x43, 0x8e, 0xf9, + 0x9c, 0x12, 0xe5, 0x85, 0xbb, 0xef, 0x5d, 0xe9, 0xae, 0x2c, 0x74, 0xb9, 0x0e, 0x96, 0xaa, 0xfa, + 0x8f, 0x60, 0xe9, 0x4c, 0x27, 0x38, 0x13, 0xe8, 0x19, 0x3b, 0xfd, 0x96, 0xb6, 0x80, 0xca, 0x50, + 0x68, 0x74, 0x3b, 0x83, 0x76, 0x67, 0x87, 0x53, 0x99, 0x32, 0x14, 0x70, 0x77, 0x6b, 0xab, 0x6e, + 0x34, 0x1e, 0x6b, 0x69, 0xbd, 0x06, 0xa5, 0x84, 0x35, 0xb4, 0x0c, 0xd0, 0x1f, 0x74, 0x7b, 0xe6, + 0x46, 0x1b, 0xf7, 0x07, 0x92, 0x08, 0xf5, 0x07, 0x06, 0x1e, 0x28, 0x41, 0x4a, 0xff, 0x8f, 0x74, + 0x34, 0xa3, 0x8a, 0xfb, 0xd4, 0xcf, 0x72, 0x9f, 0x4b, 0x9c, 0x57, 0xec, 0x67, 0x56, 0x88, 0x39, + 0xd0, 0x87, 0x00, 0x22, 0x70, 0xa8, 0x6d, 0x92, 0x50, 0x4d, 0x7c, 0xe5, 0xa5, 0x41, 0x1e, 0x44, + 0xf9, 0x32, 0x2e, 0x2a, 0xb4, 0x11, 0xa2, 0x1f, 0x43, 0xd9, 0x62, 0xa3, 0xb1, 0x4b, 0x95, 0x72, + 0xe6, 0x4a, 0xe5, 0x52, 0x8c, 0x37, 0xc2, 0x24, 0xfb, 0xca, 0x9e, 0xe5, 0x87, 0xbf, 0x9b, 0x8a, + 0x46, 0x66, 0x0e, 0xe1, 0x2a, 0x43, 0x61, 0xa7, 0xd7, 0x34, 0x06, 0xed, 0xce, 0x23, 0x2d, 0x85, + 0x00, 0xf2, 0x62, 0xa8, 0x9b, 0x5a, 0x9a, 0x13, 0xc5, 0x46, 0x77, 0xbb, 0xb7, 0xd5, 0x12, 0x94, + 0x0b, 0xdd, 0x04, 0x2d, 0x1a, 0x6c, 0x53, 0x0c, 0x64, 0xab, 0xa9, 0x65, 0xd1, 0x0d, 0xb8, 0x16, + 0x4b, 0x95, 0x66, 0x0e, 0xdd, 0x02, 0x14, 0x0b, 0x67, 0x26, 0xf2, 0xfa, 0x6f, 0xc1, 0xb5, 0x06, + 0xf3, 0x42, 0xe2, 0x78, 0x31, 0x89, 0x5e, 0xe7, 0x9d, 0x56, 0x22, 0xd3, 0xb1, 0xe5, 0x9e, 0x5e, + 0xbf, 0x76, 0xfa, 0x62, 0xad, 0x14, 0x43, 0xdb, 0x4d, 0xde, 0xd3, 0xa8, 0x60, 0xf3, 0xf5, 0x3b, + 0x76, 0x6c, 0x31, 0xb8, 0xb9, 0xfa, 0xe2, 0xe9, 0x8b, 0xb5, 0x4c, 0xaf, 0xdd, 0xc4, 0x5c, 0x86, + 0xbe, 0x07, 0x45, 0xfa, 0xcc, 0x09, 0x4d, 0x8b, 0xef, 0xe1, 0x7c, 0x00, 0x73, 0xb8, 0xc0, 0x05, + 0x0d, 0xbe, 0x65, 0xd7, 0x01, 0x7a, 0xcc, 0x0f, 0x55, 0xcb, 0x3f, 0x84, 0xdc, 0x98, 0xf9, 0x22, + 0x83, 0xbd, 0x30, 0x5f, 0xe7, 0x70, 0x19, 0xa8, 0x58, 0x82, 0xf5, 0xbf, 0x4a, 0x03, 0x0c, 0x48, + 0x70, 0xa0, 0x8c, 0x3c, 0x84, 0x62, 0x7c, 0xf7, 0xa1, 0x52, 0xe1, 0x4b, 0x67, 0x3b, 0x06, 0xa3, + 0x07, 0x51, 0xb0, 0xc9, 0xf4, 0x60, 0x6e, 0x2a, 0x13, 0x35, 0x34, 0x8f, 0x61, 0x9f, 0xcd, 0x01, + 0xf8, 0x91, 0x48, 0x7d, 0x5f, 0xcd, 0x3c, 0xff, 0x44, 0x0d, 0x71, 0x2c, 0xc8, 0x41, 0x53, 0x04, + 0xf3, 0x8d, 0x79, 0x8d, 0x9c, 0x9b, 0x91, 0xcd, 0x05, 0x3c, 0xd3, 0x43, 0x9f, 0x42, 0x89, 0xf7, + 0xdb, 0x0c, 0x44, 0x9d, 0xe2, 0x96, 0x17, 0x0e, 0x95, 0xb4, 0x80, 0x61, 0x1c, 0x7f, 0xd7, 0x35, + 0x58, 0xf6, 0x27, 0x1e, 0xef, 0xb6, 0xb2, 0xa1, 0x3b, 0xf0, 0xdd, 0x0e, 0x0d, 0x8f, 0x98, 0x7f, + 0x60, 0x84, 0x21, 0xb1, 0xf6, 0x47, 0xd4, 0x53, 0x63, 0x9c, 0x20, 0xd6, 0xa9, 0x33, 0xc4, 0x7a, + 0x05, 0x16, 0x89, 0xeb, 0x90, 0x80, 0x4a, 0x36, 0x52, 0xc4, 0x51, 0x91, 0xd3, 0x7f, 0x9e, 0x4c, + 0xd0, 0x20, 0xa0, 0x32, 0x05, 0x2e, 0xe2, 0x99, 0x40, 0xff, 0xfb, 0x34, 0x40, 0xbb, 0x67, 0x6c, + 0x2b, 0xf3, 0x4d, 0xc8, 0xef, 0x91, 0x91, 0xe3, 0x4e, 0x2f, 0x5b, 0xe0, 0x33, 0x7c, 0xcd, 0x90, + 0x86, 0x36, 0x84, 0x0e, 0x56, 0xba, 0x22, 0x2b, 0x98, 0xec, 0x7a, 0x34, 0x8c, 0xb3, 0x02, 0x51, + 0xe2, 0x14, 0xc4, 0x27, 0x5e, 0x3c, 0x33, 0xb2, 0xc0, 0x5d, 0x1f, 0x92, 0x90, 0x1e, 0x91, 0x69, + 0xb4, 0x2a, 0x55, 0x11, 0x6d, 0xf2, 0x6c, 0x21, 0xa0, 0xfe, 0x21, 0xb5, 0x57, 0x72, 0x22, 0x04, + 0xaf, 0xf2, 0x07, 0x2b, 0xb8, 0x24, 0x57, 0xb1, 0x76, 0xe5, 0x63, 0xc1, 0x08, 0x66, 0x55, 0xdf, + 0x28, 0x81, 0xbf, 0x07, 0x4b, 0x67, 0xfa, 0xf9, 0x52, 0x3a, 0xd6, 0xee, 0x3d, 0xf9, 0xa1, 0x96, + 0x55, 0x5f, 0x3f, 0xd2, 0xf2, 0xfa, 0x9f, 0x66, 0xe4, 0x3a, 0x52, 0xa3, 0x3a, 0xff, 0x4a, 0xad, + 0x20, 0xa2, 0xdf, 0x62, 0xae, 0x8a, 0xef, 0xb7, 0x2e, 0x5f, 0x5e, 0x9c, 0xde, 0x0b, 0x38, 0x8e, + 0x15, 0xd1, 0x1a, 0x94, 0xe4, 0xfc, 0x9b, 0x3c, 0x9e, 0xc4, 0xb0, 0x2e, 0x61, 0x90, 0x22, 0xae, + 0x89, 0xee, 0xc2, 0xf2, 0x78, 0xb2, 0xeb, 0x3a, 0xc1, 0x3e, 0xb5, 0x25, 0x26, 0x2b, 0x30, 0x4b, + 0xb1, 0x54, 0xc0, 0xb6, 0xa1, 0xac, 0x04, 0xa6, 0xa0, 0x76, 0x39, 0xe1, 0xd0, 0xbb, 0x57, 0x39, + 0x24, 0x55, 0x04, 0xe3, 0x2b, 0x8d, 0x67, 0x05, 0xbd, 0x09, 0x85, 0xc8, 0x59, 0xb4, 0x02, 0x99, + 0x41, 0xa3, 0xa7, 0x2d, 0x54, 0xae, 0x1d, 0x9f, 0x54, 0x4b, 0x91, 0x78, 0xd0, 0xe8, 0xf1, 0x9a, + 0x9d, 0x66, 0x4f, 0x4b, 0x9d, 0xad, 0xd9, 0x69, 0xf6, 0x2a, 0x59, 0x4e, 0x31, 0xf4, 0x3d, 0x28, + 0x25, 0x5a, 0x40, 0x6f, 0xc0, 0x62, 0xbb, 0xf3, 0x08, 0xb7, 0xfa, 0x7d, 0x6d, 0xa1, 0x72, 0xeb, + 0xf8, 0xa4, 0x8a, 0x12, 0xb5, 0x6d, 0x6f, 0xc8, 0xe7, 0x07, 0xbd, 0x06, 0xd9, 0xcd, 0x2e, 0x3f, + 0xba, 0x24, 0x97, 0x4c, 0x20, 0x36, 0x59, 0x10, 0x56, 0x6e, 0x28, 0xee, 0x92, 0x34, 0xac, 0xff, + 0x71, 0x0a, 0xf2, 0x92, 0x52, 0xcf, 0x9d, 0x28, 0x03, 0x16, 0xa3, 0x44, 0x4f, 0xf2, 0xfc, 0xb7, + 0x2e, 0xe6, 0xe4, 0x35, 0x45, 0xa1, 0x65, 0xf8, 0x45, 0x7a, 0x95, 0x8f, 0xa0, 0x9c, 0xac, 0xf8, + 0x46, 0xc1, 0xf7, 0x9b, 0x50, 0xe2, 0xf1, 0x1d, 0x71, 0xf3, 0x75, 0xc8, 0x4b, 0xda, 0x1f, 0x6f, + 0xa5, 0x17, 0x27, 0x08, 0x0a, 0x89, 0x1e, 0xc2, 0xa2, 0x4c, 0x2a, 0xa2, 0x2b, 0xb0, 0xd5, 0xcb, + 0x57, 0x11, 0x8e, 0xe0, 0xfa, 0xa7, 0x90, 0xed, 0x51, 0xea, 0xf3, 0xb1, 0xf7, 0x98, 0x4d, 0x67, + 0xa7, 0x8f, 0xca, 0x87, 0x6c, 0xda, 0x6e, 0xf2, 0x7c, 0xc8, 0xa6, 0x6d, 0x3b, 0xbe, 0xc1, 0x48, + 0x27, 0x6e, 0x30, 0x06, 0x50, 0x7e, 0x4a, 0x9d, 0xe1, 0x7e, 0x48, 0x6d, 0x61, 0xe8, 0x3d, 0xc8, + 0x8e, 0x69, 0xec, 0xfc, 0xca, 0xdc, 0x00, 0xa3, 0xd4, 0xc7, 0x02, 0xc5, 0xf7, 0x91, 0x23, 0xa1, + 0xad, 0x2e, 0x5e, 0x55, 0x49, 0xff, 0xbb, 0x34, 0x2c, 0xb7, 0x83, 0x60, 0x42, 0x3c, 0x2b, 0x22, + 0x26, 0x9f, 0x9c, 0x25, 0x26, 0x6f, 0xcf, 0xed, 0xe1, 0x19, 0x95, 0xb3, 0x17, 0x33, 0xea, 0x70, + 0x48, 0xc7, 0x87, 0x83, 0xfe, 0xef, 0xa9, 0xe8, 0xf6, 0xe5, 0x6e, 0x62, 0xb9, 0x57, 0x56, 0x8e, + 0x4f, 0xaa, 0x37, 0x93, 0x96, 0xe8, 0x8e, 0x77, 0xe0, 0xb1, 0x23, 0x0f, 0xbd, 0x0e, 0x39, 0xdc, + 0xea, 0xb4, 0x9e, 0x6a, 0x29, 0x19, 0x9e, 0x67, 0x40, 0x98, 0x7a, 0xf4, 0x88, 0x5b, 0xea, 0xb5, + 0x3a, 0x4d, 0x4e, 0x24, 0xd2, 0x73, 0x2c, 0xf5, 0xa8, 0x67, 0x3b, 0xde, 0x10, 0xbd, 0x01, 0xf9, + 0x76, 0xbf, 0xbf, 0x23, 0xf2, 0xe3, 0xef, 0x1e, 0x9f, 0x54, 0x6f, 0x9c, 0x41, 0xf1, 0x02, 0xb5, + 0x39, 0x88, 0xb3, 0x78, 0x4e, 0x31, 0xe6, 0x80, 0x38, 0x3d, 0x94, 0x20, 0xdc, 0x1d, 0xf0, 0xe4, + 0x3d, 0x37, 0x07, 0x84, 0x19, 0xff, 0xab, 0x96, 0xdb, 0x3f, 0xa5, 0x41, 0x33, 0x2c, 0x8b, 0x8e, + 0x43, 0x5e, 0xaf, 0x12, 0xa7, 0x01, 0x14, 0xc6, 0xfc, 0xcb, 0xa1, 0x11, 0x09, 0x78, 0x38, 0xf7, + 0xea, 0xff, 0x9c, 0x5e, 0x0d, 0x33, 0x97, 0x1a, 0xf6, 0xc8, 0x09, 0x02, 0x87, 0x79, 0x52, 0x86, + 0x63, 0x4b, 0x95, 0xff, 0x4c, 0xc1, 0x8d, 0x39, 0x08, 0x74, 0x0f, 0xb2, 0x3e, 0x73, 0xa3, 0x39, + 0xbc, 0x73, 0xd1, 0xc5, 0x1a, 0x57, 0xc5, 0x02, 0x89, 0x56, 0x01, 0xc8, 0x24, 0x64, 0x44, 0xb4, + 0x2f, 0x66, 0xaf, 0x80, 0x13, 0x12, 0xf4, 0x14, 0xf2, 0x01, 0xb5, 0x7c, 0x1a, 0x51, 0xc5, 0x4f, + 0xff, 0xaf, 0xde, 0xd7, 0xfa, 0xc2, 0x0c, 0x56, 0xe6, 0x2a, 0x35, 0xc8, 0x4b, 0x09, 0x0f, 0x7b, + 0x9b, 0x84, 0x44, 0x38, 0x5d, 0xc6, 0xe2, 0x9b, 0x47, 0x13, 0x71, 0x87, 0x51, 0x34, 0x11, 0x77, + 0xa8, 0xff, 0x75, 0x1a, 0xa0, 0xf5, 0x2c, 0xa4, 0xbe, 0x47, 0xdc, 0x86, 0x81, 0x5a, 0x89, 0xdd, + 0x5f, 0xf6, 0xf6, 0x9d, 0xb9, 0xd7, 0xad, 0xb1, 0x46, 0xad, 0x61, 0xcc, 0xd9, 0xff, 0x6f, 0x43, + 0x66, 0xe2, 0xab, 0xd7, 0x1c, 0x49, 0xf3, 0x76, 0xf0, 0x16, 0xe6, 0x32, 0xd4, 0x9a, 0x6d, 0x5b, + 0x99, 0x8b, 0xdf, 0x6c, 0x12, 0x0d, 0xcc, 0xdd, 0xba, 0xf8, 0xca, 0xb7, 0x88, 0x69, 0x51, 0x75, + 0x72, 0x94, 0xe5, 0xca, 0x6f, 0x18, 0x0d, 0xea, 0x87, 0x38, 0x6f, 0x11, 0xfe, 0xff, 0x5b, 0xed, + 0x6f, 0xef, 0x01, 0xcc, 0xba, 0x86, 0x56, 0x21, 0xd7, 0xd8, 0xe8, 0xf7, 0xb7, 0xb4, 0x05, 0xb9, + 0x81, 0xcf, 0xaa, 0x84, 0x58, 0xff, 0x59, 0x0a, 0x0a, 0x0d, 0x43, 0x1d, 0xab, 0x0d, 0xd0, 0xc4, + 0xae, 0xc4, 0xbd, 0x33, 0xe9, 0xb3, 0xb1, 0xe3, 0x4f, 0xd5, 0xc6, 0x72, 0x49, 0xce, 0xb6, 0xcc, + 0x55, 0xb8, 0xd7, 0x2d, 0xa1, 0x80, 0x30, 0x94, 0xa9, 0x1a, 0x04, 0xd3, 0x22, 0xd1, 0x1e, 0xbf, + 0x7a, 0xf9, 0x60, 0x49, 0xf6, 0x3d, 0x2b, 0x07, 0xb8, 0x14, 0x19, 0x69, 0x90, 0x40, 0x7f, 0x02, + 0x37, 0xba, 0xbe, 0xb5, 0x4f, 0x83, 0x50, 0x36, 0xaa, 0xfc, 0xfd, 0x14, 0xee, 0x84, 0x24, 0x38, + 0x30, 0xf7, 0x9d, 0x20, 0x64, 0xfe, 0xd4, 0xf4, 0x69, 0x48, 0x3d, 0x5e, 0x6f, 0x8a, 0xb7, 0x1f, + 0x75, 0xd3, 0x72, 0x9b, 0x63, 0x36, 0x25, 0x04, 0x47, 0x88, 0x2d, 0x0e, 0xd0, 0xdb, 0x50, 0xe6, + 0x7c, 0xb7, 0x49, 0xf7, 0xc8, 0xc4, 0x0d, 0x03, 0x9e, 0x49, 0xb9, 0x6c, 0x68, 0xbe, 0xf2, 0x81, + 0x50, 0x74, 0xd9, 0x50, 0x7e, 0xea, 0x3f, 0x05, 0xad, 0xe9, 0x04, 0x63, 0x12, 0x5a, 0xfb, 0xd1, + 0x15, 0x12, 0x6a, 0x82, 0xb6, 0x4f, 0x89, 0x1f, 0xee, 0x52, 0x12, 0x9a, 0x63, 0xea, 0x3b, 0xcc, + 0xbe, 0x7a, 0x3c, 0xaf, 0xc5, 0x2a, 0x3d, 0xa1, 0xa1, 0xff, 0x57, 0x0a, 0x00, 0x93, 0xbd, 0x88, + 0xfb, 0xfc, 0x00, 0xae, 0x07, 0x1e, 0x19, 0x07, 0xfb, 0x2c, 0x34, 0x1d, 0x2f, 0xa4, 0xfe, 0x21, + 0x71, 0xd5, 0x4d, 0x80, 0x16, 0x55, 0xb4, 0x95, 0x1c, 0xbd, 0x07, 0xe8, 0x80, 0xd2, 0xb1, 0xc9, + 0x5c, 0xdb, 0x8c, 0x2a, 0xe5, 0xcb, 0x54, 0x16, 0x6b, 0xbc, 0xa6, 0xeb, 0xda, 0xfd, 0x48, 0x8e, + 0xea, 0xb0, 0xca, 0xbb, 0x4f, 0xbd, 0xd0, 0x77, 0x68, 0x60, 0xee, 0x31, 0xdf, 0x0c, 0x5c, 0x76, + 0x64, 0xee, 0x31, 0xd7, 0x65, 0x47, 0xd4, 0x8f, 0x2e, 0x59, 0x2a, 0x2e, 0x1b, 0xb6, 0x24, 0x68, + 0x83, 0xf9, 0x7d, 0x97, 0x1d, 0x6d, 0x44, 0x08, 0x4e, 0x90, 0x66, 0x7d, 0x0e, 0x1d, 0xeb, 0x20, + 0x22, 0x48, 0xb1, 0x74, 0xe0, 0x58, 0x07, 0xe8, 0x0d, 0x58, 0xa2, 0x2e, 0x15, 0xb9, 0xb6, 0x44, + 0xe5, 0x04, 0xaa, 0x1c, 0x09, 0x39, 0x48, 0xff, 0x0c, 0xb4, 0x96, 0x67, 0xf9, 0xd3, 0x71, 0x62, + 0xce, 0xdf, 0x03, 0xc4, 0xb7, 0x23, 0xd3, 0x65, 0xd6, 0x81, 0x39, 0x22, 0x1e, 0x19, 0x72, 0xbf, + 0xe4, 0x6b, 0x88, 0xc6, 0x6b, 0xb6, 0x98, 0x75, 0xb0, 0xad, 0xe4, 0xfa, 0x87, 0x00, 0xfd, 0xb1, + 0x4f, 0x89, 0xdd, 0xe5, 0xe7, 0x36, 0x1f, 0x3a, 0x51, 0x32, 0x6d, 0xf5, 0xe0, 0xc2, 0x7c, 0xb5, + 0xa8, 0x34, 0x59, 0xd1, 0x8c, 0xe5, 0xfa, 0xaf, 0xc2, 0x8d, 0x9e, 0x4b, 0x2c, 0xf1, 0xf8, 0xd8, + 0x8b, 0xaf, 0xf7, 0xd1, 0x43, 0xc8, 0x4b, 0xa8, 0x9a, 0xc9, 0xb9, 0x81, 0x3d, 0x6b, 0x73, 0x73, + 0x01, 0x2b, 0x7c, 0xbd, 0x0c, 0x30, 0xb3, 0xa3, 0x3f, 0x83, 0x62, 0x6c, 0x1e, 0x55, 0x81, 0x27, + 0x9b, 0x3c, 0xba, 0x1d, 0x4f, 0x65, 0x87, 0x45, 0x9c, 0x14, 0xa1, 0x36, 0x94, 0xc6, 0xb1, 0xf2, + 0xa5, 0xc4, 0x69, 0x8e, 0xd3, 0x38, 0xa9, 0xab, 0x7f, 0x02, 0xf0, 0x13, 0xe6, 0x78, 0x03, 0x76, + 0x40, 0x3d, 0xf1, 0xa2, 0xc4, 0xf3, 0x22, 0x1a, 0x0d, 0x84, 0x2a, 0x89, 0xb4, 0x4f, 0x8e, 0x62, + 0xfc, 0xb0, 0x22, 0x8b, 0xfa, 0x1f, 0xa4, 0x21, 0x8f, 0x19, 0x0b, 0x1b, 0x06, 0xaa, 0x42, 0xde, + 0x22, 0x66, 0xb4, 0x35, 0x95, 0xeb, 0xc5, 0xd3, 0x17, 0x6b, 0xb9, 0x86, 0xf1, 0x98, 0x4e, 0x71, + 0xce, 0x22, 0x8f, 0xe9, 0x34, 0xb9, 0xdd, 0xa5, 0x2f, 0xda, 0xee, 0xd0, 0x3d, 0x28, 0x2b, 0x90, + 0xb9, 0x4f, 0x82, 0x7d, 0x99, 0xcd, 0xd4, 0x97, 0x4f, 0x5f, 0xac, 0x81, 0x44, 0x6e, 0x92, 0x60, + 0x1f, 0x83, 0x44, 0xf3, 0x6f, 0xd4, 0x82, 0xd2, 0x17, 0xcc, 0xf1, 0xcc, 0x50, 0x74, 0x42, 0x5d, + 0x2c, 0xcd, 0x9d, 0x8a, 0x59, 0x57, 0xd5, 0x0b, 0x24, 0x7c, 0x31, 0xeb, 0x7c, 0x0b, 0x96, 0x7c, + 0xc6, 0x42, 0xd3, 0x57, 0xef, 0xec, 0x2a, 0x67, 0xad, 0xce, 0xbd, 0xca, 0x64, 0x2c, 0xc4, 0x0a, + 0x87, 0xcb, 0x7e, 0xa2, 0xa4, 0xff, 0x43, 0x0a, 0x4a, 0xdc, 0x35, 0x67, 0xcf, 0xb1, 0x38, 0xbf, + 0xf9, 0xe6, 0xc7, 0xee, 0x6d, 0xc8, 0x58, 0x81, 0xaf, 0x86, 0x48, 0x9c, 0x3b, 0x8d, 0x3e, 0xc6, + 0x5c, 0x86, 0x3e, 0x83, 0xbc, 0xca, 0x84, 0xe5, 0x89, 0xab, 0x5f, 0xcd, 0xc4, 0x54, 0x4f, 0x95, + 0x9e, 0x88, 0xae, 0x99, 0x77, 0xf2, 0xd8, 0xc1, 0x49, 0x11, 0xba, 0x05, 0x69, 0x4b, 0x76, 0x5e, + 0x3d, 0x58, 0x37, 0x3a, 0x38, 0x6d, 0x79, 0xfa, 0xdf, 0xa6, 0x60, 0x69, 0xb6, 0x02, 0xf9, 0x7c, + 0xde, 0x81, 0x62, 0x30, 0xd9, 0x0d, 0xa6, 0x41, 0x48, 0x47, 0xd1, 0xdb, 0x57, 0x2c, 0x40, 0x6d, + 0x28, 0x12, 0x77, 0xc8, 0x7c, 0x27, 0xdc, 0x1f, 0xa9, 0x24, 0x6c, 0xfe, 0x29, 0x99, 0xb4, 0x59, + 0x33, 0x22, 0x15, 0x3c, 0xd3, 0x8e, 0x8e, 0xbc, 0x8c, 0x70, 0x56, 0x1c, 0x79, 0xaf, 0x43, 0xd9, + 0x25, 0x23, 0x71, 0x35, 0xc0, 0x73, 0x7b, 0xd1, 0x8f, 0x2c, 0x2e, 0x29, 0xd9, 0xc0, 0x19, 0x51, + 0x5d, 0x87, 0x62, 0x6c, 0x0c, 0x5d, 0x83, 0x92, 0xd1, 0xea, 0x9b, 0xf7, 0xd7, 0x1f, 0x9a, 0x8f, + 0x1a, 0xdb, 0xda, 0x82, 0xa2, 0x65, 0x7f, 0x91, 0x82, 0x25, 0xb5, 0x3f, 0x28, 0xaa, 0xfb, 0x06, + 0x2c, 0xfa, 0x64, 0x2f, 0x8c, 0xc8, 0x78, 0x56, 0xc6, 0x28, 0xdf, 0x72, 0x39, 0x19, 0xe7, 0x55, + 0xf3, 0xc9, 0x78, 0xe2, 0x35, 0x36, 0x73, 0xe9, 0x6b, 0x6c, 0xf6, 0xff, 0xe5, 0x35, 0x56, 0xff, + 0xb3, 0x34, 0x5c, 0x53, 0xac, 0x29, 0xde, 0x8e, 0xde, 0x81, 0xa2, 0x24, 0x50, 0xb3, 0x54, 0x42, + 0x3c, 0x00, 0x4a, 0x5c, 0xbb, 0x89, 0x0b, 0xb2, 0xba, 0x6d, 0xf3, 0xdc, 0x56, 0x41, 0x13, 0xbf, + 0x2d, 0x00, 0x29, 0xea, 0xf0, 0xc4, 0xac, 0x09, 0xd9, 0x3d, 0xc7, 0xa5, 0x2a, 0xce, 0xe6, 0x5e, + 0xfb, 0x9e, 0x6b, 0x5e, 0x3c, 0x50, 0x0c, 0x44, 0x76, 0xbc, 0xb9, 0x80, 0x85, 0x76, 0xe5, 0xb7, + 0x01, 0x66, 0xd2, 0xb9, 0x09, 0x20, 0x27, 0x59, 0xea, 0x2e, 0x2d, 0x22, 0x59, 0xed, 0x26, 0xe6, + 0x32, 0x5e, 0x35, 0x74, 0x6c, 0xb5, 0x01, 0x88, 0xaa, 0x47, 0xbc, 0x6a, 0xe8, 0xd8, 0xf1, 0x2b, + 0x49, 0xf6, 0x8a, 0x57, 0x92, 0x7a, 0x21, 0xba, 0xd1, 0xd1, 0xb7, 0xe0, 0x56, 0xdd, 0x25, 0xd6, + 0x81, 0xeb, 0x04, 0x21, 0xb5, 0x93, 0x2b, 0x74, 0x1d, 0xf2, 0x67, 0xf8, 0xcd, 0x65, 0x17, 0x68, + 0x0a, 0xa9, 0xff, 0x5b, 0x0a, 0xca, 0x9b, 0x94, 0xb8, 0xe1, 0xfe, 0xec, 0x16, 0x22, 0xa4, 0x41, + 0xa8, 0xb6, 0x6b, 0xf1, 0x8d, 0x3e, 0x80, 0x42, 0x7c, 0x28, 0x5f, 0xf9, 0x92, 0x11, 0x43, 0xd1, + 0x03, 0x58, 0xe4, 0x31, 0xcd, 0x26, 0x11, 0xaf, 0xbe, 0xec, 0x92, 0x5c, 0x21, 0xf9, 0x16, 0xed, + 0x53, 0x71, 0x0a, 0x8b, 0x41, 0xc9, 0xe1, 0xa8, 0x88, 0x7e, 0x05, 0xca, 0xe2, 0x8e, 0x37, 0x22, + 0x1d, 0xb9, 0xab, 0x6c, 0x96, 0xe4, 0x33, 0x8d, 0x24, 0x1c, 0xff, 0x93, 0x82, 0x9b, 0xdb, 0x64, + 0xba, 0x4b, 0xd5, 0x32, 0xa5, 0x36, 0xa6, 0x16, 0xf3, 0x6d, 0xd4, 0x4b, 0x2e, 0xef, 0x4b, 0x5e, + 0x7d, 0xe6, 0x29, 0xcf, 0x5f, 0xe5, 0x11, 0xd7, 0x4f, 0x27, 0xb8, 0xfe, 0x4d, 0xc8, 0x79, 0xcc, + 0xb3, 0xa8, 0x5a, 0xfb, 0xb2, 0xa0, 0x3b, 0xc9, 0xa5, 0x5d, 0x89, 0x1f, 0x64, 0xc4, 0x73, 0x4a, + 0x87, 0x85, 0x71, 0x6b, 0xe8, 0x33, 0xa8, 0xf4, 0x5b, 0x0d, 0xdc, 0x1a, 0xd4, 0xbb, 0x3f, 0x35, + 0xfb, 0xc6, 0x56, 0xdf, 0x58, 0xbf, 0x67, 0xf6, 0xba, 0x5b, 0x9f, 0xdf, 0x7f, 0x70, 0xef, 0x03, + 0x2d, 0x55, 0xa9, 0x1e, 0x9f, 0x54, 0xef, 0x74, 0x8c, 0xc6, 0x96, 0x8c, 0xe5, 0x5d, 0xf6, 0xac, + 0x4f, 0xdc, 0x80, 0xac, 0xdf, 0xeb, 0x31, 0x77, 0xca, 0x31, 0xfa, 0x49, 0x0a, 0xca, 0xc9, 0xdd, + 0x3e, 0x79, 0x88, 0xa5, 0x2e, 0x3c, 0xc4, 0x66, 0x67, 0x61, 0xfa, 0x82, 0xb3, 0x70, 0x03, 0x6e, + 0x5a, 0x3e, 0x0b, 0x02, 0x33, 0x70, 0x86, 0x1e, 0xb5, 0xcd, 0xc8, 0xa6, 0xe8, 0x67, 0xfd, 0x3b, + 0xa7, 0x2f, 0xd6, 0xae, 0x37, 0x78, 0x7d, 0x5f, 0x54, 0x2b, 0xf3, 0xd7, 0xad, 0x84, 0x48, 0xb4, + 0xf4, 0xee, 0x2f, 0x33, 0x50, 0x8c, 0xaf, 0x69, 0xf9, 0x92, 0xe1, 0x39, 0xb2, 0x1a, 0x8a, 0x58, + 0xde, 0xa1, 0x47, 0xe8, 0xf5, 0x59, 0x76, 0xfc, 0x99, 0x7c, 0x97, 0x8a, 0xab, 0xa3, 0xcc, 0xf8, + 0x4d, 0x28, 0x18, 0xfd, 0x7e, 0xfb, 0x51, 0xa7, 0xd5, 0xd4, 0xbe, 0x4c, 0x55, 0xbe, 0x73, 0x7c, + 0x52, 0xbd, 0x1e, 0x83, 0x8c, 0x40, 0x7a, 0x2a, 0x50, 0x8d, 0x46, 0xab, 0x37, 0x68, 0x35, 0xb5, + 0xe7, 0xe9, 0xf3, 0x28, 0x91, 0xed, 0x89, 0xd7, 0xe5, 0x62, 0x0f, 0xb7, 0x7a, 0x06, 0xe6, 0x0d, + 0x7e, 0x99, 0x96, 0x49, 0xfb, 0xac, 0x45, 0x9f, 0x8e, 0x89, 0xcf, 0xdb, 0x5c, 0x8d, 0x7e, 0x65, + 0xf1, 0x3c, 0x23, 0x5f, 0x20, 0x67, 0x77, 0xce, 0x94, 0xd8, 0x53, 0xde, 0x9a, 0xb8, 0xec, 0x17, + 0x66, 0x32, 0xe7, 0x5a, 0xeb, 0xf3, 0x40, 0xe5, 0x56, 0x74, 0x58, 0xc4, 0x3b, 0x9d, 0x0e, 0x07, + 0x3d, 0xcf, 0x9e, 0xeb, 0x1d, 0x9e, 0x78, 0x1e, 0xc7, 0xdc, 0x85, 0x42, 0xf4, 0x16, 0xa0, 0x7d, + 0x99, 0x3d, 0xe7, 0x50, 0x23, 0x7a, 0xc8, 0x10, 0x0d, 0x6e, 0xee, 0x0c, 0xc4, 0x8f, 0x40, 0x9e, + 0xe7, 0xce, 0x37, 0xb8, 0x3f, 0x09, 0x6d, 0x76, 0xe4, 0xf1, 0x09, 0x56, 0xf7, 0x03, 0x5f, 0xe6, + 0x64, 0x32, 0x15, 0x63, 0xd4, 0xe5, 0xc0, 0x9b, 0x50, 0xc0, 0xad, 0x9f, 0xc8, 0xdf, 0x8b, 0x3c, + 0xcf, 0x9f, 0xb3, 0x83, 0xe9, 0x17, 0xd4, 0x52, 0xad, 0x75, 0x71, 0x6f, 0xd3, 0x10, 0x43, 0x7e, + 0x1e, 0xd5, 0xf5, 0xc7, 0xfb, 0xc4, 0xa3, 0xf6, 0xec, 0x19, 0x36, 0xae, 0x7a, 0xf7, 0xd7, 0xa0, + 0x10, 0xd1, 0x06, 0xb4, 0x0a, 0xf9, 0xa7, 0x5d, 0xfc, 0xb8, 0x85, 0xb5, 0x05, 0x39, 0x86, 0x51, + 0xcd, 0x53, 0x49, 0xdf, 0xaa, 0xb0, 0xb8, 0x6d, 0x74, 0x8c, 0x47, 0x2d, 0x1c, 0x5d, 0xdd, 0x45, + 0x00, 0x75, 0xf6, 0x55, 0x34, 0xd5, 0x40, 0x6c, 0xb3, 0xbe, 0xf2, 0xd5, 0xd7, 0xab, 0x0b, 0xbf, + 0xf8, 0x7a, 0x75, 0xe1, 0xf9, 0xe9, 0x6a, 0xea, 0xab, 0xd3, 0xd5, 0xd4, 0xcf, 0x4f, 0x57, 0x53, + 0xff, 0x72, 0xba, 0x9a, 0xda, 0xcd, 0x8b, 0x1d, 0xe3, 0xc1, 0xff, 0x06, 0x00, 0x00, 0xff, 0xff, + 0x55, 0x06, 0x85, 0xd6, 0xa6, 0x29, 0x00, 0x00, } diff --git a/api/types.proto b/api/types.proto index 36f6f5075d..9e63e67dc7 100644 --- a/api/types.proto +++ b/api/types.proto @@ -655,6 +655,9 @@ message ExternalCA { // Options is a set of additional key/value pairs whose interpretation // depends on the specified CA type. map options = 3; + + // CACert specifies which root CA is used by this external CA + bytes ca_cert = 4 [(gogoproto.customname) = "CACert"]; } message CAConfig { @@ -764,6 +767,10 @@ message RootCA { // JoinTokens contains the join tokens for workers and managers. JoinTokens join_tokens = 4 [(gogoproto.nullable) = false]; + + // RootRotation contains the new root cert and key we want to rotate to - if this is nil, we are not in the + // middle of a root rotation + RootRotation root_rotation = 5; } @@ -909,3 +916,11 @@ message MaybeEncryptedRecord { bytes data = 2; bytes nonce = 3; } + + +message RootRotation { + bytes ca_cert = 1 [(gogoproto.customname) = "CACert"]; + bytes ca_key = 2 [(gogoproto.customname) = "CAKey"]; + // cross-signed CA cert is the CACert that has been cross-signed by the previous root + bytes cross_signed_ca_cert = 3 [(gogoproto.customname) = "CrossSignedCACert"];; +} From 8555c7628a48253901c62f3d2eda3e6fcd508b2c Mon Sep 17 00:00:00 2001 From: cyli Date: Tue, 14 Mar 2017 16:26:08 -0700 Subject: [PATCH 2/3] When updating security config information based on RootCA information in the raft store, take into account whether there is a root rotation object in the RootCA object. Signed-off-by: cyli --- ca/config.go | 34 ++++------- ca/config_test.go | 35 +++++++++-- ca/server.go | 71 +++++++++++++++------- ca/server_test.go | 131 ++++++++++++++++++++++++++++++++++++++++ ca/testutils/cautils.go | 2 + 5 files changed, 225 insertions(+), 48 deletions(-) diff --git a/ca/config.go b/ca/config.go index ca08950580..b42124a5b5 100644 --- a/ca/config.go +++ b/ca/config.go @@ -71,6 +71,8 @@ type SecurityConfig struct { externalCA *ExternalCA keyReadWriter *KeyReadWriter + externalCAClientRootPool *x509.CertPool + ServerTLSCreds *MutableTLSCreds ClientTLSCreds *MutableTLSCreds } @@ -95,11 +97,12 @@ func NewSecurityConfig(rootCA *RootCA, krw *KeyReadWriter, clientTLSCreds, serve } return &SecurityConfig{ - rootCA: rootCA, - keyReadWriter: krw, - externalCA: NewExternalCA(rootCA, externalCATLSConfig), - ClientTLSCreds: clientTLSCreds, - ServerTLSCreds: serverTLSCreds, + rootCA: rootCA, + keyReadWriter: krw, + externalCA: NewExternalCA(rootCA, externalCATLSConfig), + ClientTLSCreds: clientTLSCreds, + ServerTLSCreds: serverTLSCreds, + externalCAClientRootPool: rootCA.Pool, } } @@ -126,24 +129,13 @@ func (s *SecurityConfig) KeyReader() KeyReader { return s.keyReadWriter } -// UpdateRootCA replaces the root CA with a new root CA based on the specified -// certificate, key, and the number of hours the certificates issue should last. -func (s *SecurityConfig) UpdateRootCA(cert, key []byte, certExpiry time.Duration) error { +// UpdateRootCA replaces the root CA with a new root CA +func (s *SecurityConfig) UpdateRootCA(rootCA *RootCA, externalCARootPool *x509.CertPool) error { s.mu.Lock() defer s.mu.Unlock() - // If we have no signing key, then we shouldn't pass a signing cert either (because we don't want a local - // signer at all) - signingCert := cert - if len(key) == 0 { - signingCert = nil - } - rootCA, err := NewRootCA(cert, signingCert, key, certExpiry, nil) - if err != nil { - return err - } - - s.rootCA = &rootCA + s.rootCA = rootCA + s.externalCAClientRootPool = externalCARootPool clientTLSConfig := s.ClientTLSCreds.Config() return s.updateTLSCredentials(clientTLSConfig.Certificates) } @@ -169,7 +161,7 @@ func (s *SecurityConfig) updateTLSCredentials(certificates []tls.Certificate) er // config using a copy without a serverName specified. s.externalCA.UpdateTLSConfig(&tls.Config{ Certificates: certificates, - RootCAs: s.rootCA.Pool, + RootCAs: s.externalCAClientRootPool, MinVersion: tls.VersionTLS12, }) diff --git a/ca/config_test.go b/ca/config_test.go index 7d937566d7..4267b90272 100644 --- a/ca/config_test.go +++ b/ca/config_test.go @@ -356,7 +356,9 @@ func TestSecurityConfigUpdateRootCA(t *testing.T) { // and the "new root" (the testing CA root) rSigner, err := rootCA.Signer() require.NoError(t, err) - err = secConfig.UpdateRootCA(append(rootCA.Certs, tc.RootCA.Certs...), rSigner.Key, ca.DefaultNodeCertExpiration) + updatedRootCA, err := ca.NewRootCA(append(rootCA.Certs, tc.RootCA.Certs...), rSigner.Cert, rSigner.Key, ca.DefaultNodeCertExpiration, nil) + require.NoError(t, err) + err = secConfig.UpdateRootCA(&updatedRootCA, updatedRootCA.Pool) require.NoError(t, err) // can now connect to the test CA using our modified security config, and can cannect to our server using @@ -385,12 +387,29 @@ func TestRenewTLSConfigUpdateRootCARace(t *testing.T) { defer tc.Stop() paths := ca.NewConfigPaths(tc.TempDir) - secConfig, err := tc.WriteNewNodeConfig(ca.WorkerRole) + secConfig, err := tc.WriteNewNodeConfig(ca.ManagerRole) require.NoError(t, err) leafCert, err := ioutil.ReadFile(paths.Node.Cert) require.NoError(t, err) + cert, key, err := testutils.CreateRootCertAndKey("extra root cert for external CA") + require.NoError(t, err) + extraExternalRootCA, err := ca.NewRootCA(append(cert, tc.RootCA.Certs...), cert, key, ca.DefaultNodeCertExpiration, nil) + require.NoError(t, err) + extraExternalServer, err := testutils.NewExternalSigningServer(extraExternalRootCA, tc.TempDir) + require.NoError(t, err) + defer extraExternalServer.Stop() + secConfig.ExternalCA().UpdateURLs(extraExternalServer.URL) + + externalPool := x509.NewCertPool() + externalPool.AppendCertsFromPEM(tc.RootCA.Certs) + externalPool.AppendCertsFromPEM(cert) + + csr, _, err := ca.GenerateNewCSR() + require.NoError(t, err) + signReq := ca.PrepareCSR(csr, "cn", ca.WorkerRole, tc.Organization) + for i := 0; i < 5; i++ { cert, _, err := testutils.CreateRootCertAndKey(fmt.Sprintf("root %d", i+2)) require.NoError(t, err) @@ -402,11 +421,14 @@ func TestRenewTLSConfigUpdateRootCARace(t *testing.T) { rootCA := secConfig.RootCA() go func() { defer close(done1) - var key []byte + s := ca.LocalSigner{} if signer, err := rootCA.Signer(); err == nil { - key = signer.Key + s = *signer } - require.NoError(t, secConfig.UpdateRootCA(append(rootCA.Certs, cert...), key, ca.DefaultNodeCertExpiration)) + updatedRootCA, err := ca.NewRootCA(append(rootCA.Certs, cert...), s.Cert, s.Key, ca.DefaultNodeCertExpiration, nil) + require.NoError(t, err) + externalPool.AppendCertsFromPEM(cert) + require.NoError(t, secConfig.UpdateRootCA(&updatedRootCA, externalPool)) }() go func() { @@ -426,6 +448,9 @@ func TestRenewTLSConfigUpdateRootCARace(t *testing.T) { // at the start of this loop had i+1 certs, afterward should have added one more require.Len(t, secConfig.ClientTLSCreds.Config().RootCAs.Subjects(), i+2) require.Len(t, secConfig.ServerTLSCreds.Config().RootCAs.Subjects(), i+2) + // no matter what, the external CA still has the extra external CA root cert + _, err = secConfig.ExternalCA().Sign(context.Background(), signReq) + require.NoError(t, err) } } diff --git a/ca/server.go b/ca/server.go index 95eb738ce6..31b7e083a0 100644 --- a/ca/server.go +++ b/ca/server.go @@ -1,7 +1,9 @@ package ca import ( + "bytes" "crypto/subtle" + "crypto/x509" "sync" "time" @@ -525,40 +527,59 @@ func (s *Server) UpdateRootCA(ctx context.Context, cluster *api.Cluster) { // If the cluster has a RootCA, let's try to update our SecurityConfig to reflect the latest values rCA := cluster.RootCA - if len(rCA.CACert) != 0 && len(rCA.CAKey) != 0 { + wantedExternalCACert := rCA.CACert // we want to only add external CA URLs that use this cert + if len(rCA.CACert) != 0 { expiry := DefaultNodeCertExpiration + logger := log.G(ctx).WithFields(logrus.Fields{ + "cluster.id": cluster.ID, + "method": "(*Server).UpdateRootCA", + }) if cluster.Spec.CAConfig.NodeCertExpiry != nil { // NodeCertExpiry exists, let's try to parse the duration out of it clusterExpiry, err := gogotypes.DurationFromProto(cluster.Spec.CAConfig.NodeCertExpiry) if err != nil { - log.G(ctx).WithFields(logrus.Fields{ - "cluster.id": cluster.ID, - "method": "(*Server).updateCluster", - }).WithError(err).Warn("failed to parse certificate expiration, using default") + logger.WithError(err).Warn("failed to parse certificate expiration, using default") } else { // We were able to successfully parse the expiration out of the cluster. expiry = clusterExpiry } } else { // NodeCertExpiry seems to be nil - log.G(ctx).WithFields(logrus.Fields{ - "cluster.id": cluster.ID, - "method": "(*Server).updateCluster", - }).WithError(err).Warn("failed to parse certificate expiration, using default") - + logger.WithError(err).Warn("failed to parse certificate expiration, using default") } // Attempt to update our local RootCA with the new parameters - err = s.securityConfig.UpdateRootCA(rCA.CACert, rCA.CAKey, expiry) + var intermediates []byte + signingCert := rCA.CACert + signingKey := rCA.CAKey + if rCA.RootRotation != nil { + signingCert = rCA.RootRotation.CrossSignedCACert + signingKey = rCA.RootRotation.CAKey + intermediates = rCA.RootRotation.CrossSignedCACert + // we're rotating to a new root, so we only want external CAs with the new root cert + wantedExternalCACert = rCA.RootRotation.CACert + } + if signingKey == nil { + signingCert = nil + } + + updatedRootCA, err := NewRootCA(rCA.CACert, signingCert, signingKey, expiry, intermediates) if err != nil { - log.G(ctx).WithFields(logrus.Fields{ - "cluster.id": cluster.ID, - "method": "(*Server).updateCluster", - }).WithError(err).Error("updating Root CA failed") + logger.WithError(err).Error("invalid Root CA object in cluster") + } + + externalCARootPool := updatedRootCA.Pool + if rCA.RootRotation != nil { + // the external CA has to trust the new CA cert + externalCARootPool = x509.NewCertPool() + externalCARootPool.AppendCertsFromPEM(rCA.CACert) + externalCARootPool.AppendCertsFromPEM(rCA.RootRotation.CACert) + } + + // Attempt to update our local RootCA with the new parameters + if err := s.securityConfig.UpdateRootCA(&updatedRootCA, externalCARootPool); err != nil { + logger.WithError(err).Error("updating Root CA failed") } else { - log.G(ctx).WithFields(logrus.Fields{ - "cluster.id": cluster.ID, - "method": "(*Server).updateCluster", - }).Debugf("Root CA updated successfully") + logger.Debugf("Root CA updated successfully") } } @@ -569,9 +590,15 @@ func (s *Server) UpdateRootCA(ctx context.Context, cluster *api.Cluster) { // ExternalCA interface that has different implementations for // different CA types. At the moment, only CFSSL is supported. var cfsslURLs []string - for _, ca := range cluster.Spec.CAConfig.ExternalCAs { - if ca.Protocol == api.ExternalCA_CAProtocolCFSSL { - cfsslURLs = append(cfsslURLs, ca.URL) + for _, extCA := range cluster.Spec.CAConfig.ExternalCAs { + // We want to support old external CA specifications which did not have a CA cert. If there is no cert specified, + // we assume it's the old cert + certForExtCA := extCA.CACert + if certForExtCA == nil { + certForExtCA = rCA.CACert + } + if extCA.Protocol == api.ExternalCA_CAProtocolCFSSL && bytes.Equal(certForExtCA, wantedExternalCACert) { + cfsslURLs = append(cfsslURLs, extCA.URL) } } diff --git a/ca/server_test.go b/ca/server_test.go index 53e1d530a3..d25a906d8c 100644 --- a/ca/server_test.go +++ b/ca/server_test.go @@ -2,12 +2,14 @@ package ca_test import ( "bytes" + "crypto/x509" "fmt" "testing" "time" "golang.org/x/net/context" + "github.com/cloudflare/cfssl/helpers" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/ca" "github.com/docker/swarmkit/ca/testutils" @@ -412,3 +414,132 @@ func TestGetUnlockKey(t *testing.T) { return nil }, 250*time.Millisecond)) } + +type clusterObjToUpdate struct { + clusterObj *api.Cluster + rootCARoots []byte + rootCASigningCert []byte + rootCASigningKey []byte + rootCAIntermediates []byte + externalCertSignedBy []byte +} + +func TestCAServerUpdateRootCA(t *testing.T) { + // this one needs both external CA servers for testing + if !testutils.External { + return + } + + fakeClusterSpec := func(rootCerts, key []byte, rotation *api.RootRotation, externalCAs []*api.ExternalCA) *api.Cluster { + return &api.Cluster{ + RootCA: api.RootCA{ + CACert: rootCerts, + CAKey: key, + CACertHash: "hash", + JoinTokens: api.JoinTokens{ + Worker: "SWMTKN-1-worker", + Manager: "SWMTKN-1-manager", + }, + RootRotation: rotation, + }, + Spec: api.ClusterSpec{ + CAConfig: api.CAConfig{ + ExternalCAs: externalCAs, + }, + }, + } + } + + tc := testutils.NewTestCA(t) + require.NoError(t, tc.CAServer.Stop()) + defer tc.Stop() + + cert, key, err := testutils.CreateRootCertAndKey("new root to rotate to") + require.NoError(t, err) + newRootCA, err := ca.NewRootCA(append(tc.RootCA.Certs, cert...), cert, key, ca.DefaultNodeCertExpiration, nil) + require.NoError(t, err) + externalServer, err := testutils.NewExternalSigningServer(newRootCA, tc.TempDir) + require.NoError(t, err) + defer externalServer.Stop() + crossSigned, err := tc.RootCA.CrossSignCACertificate(cert) + require.NoError(t, err) + + for i, testCase := range []clusterObjToUpdate{ + { + clusterObj: fakeClusterSpec(tc.RootCA.Certs, nil, nil, []*api.ExternalCA{{ + Protocol: api.ExternalCA_CAProtocolCFSSL, + URL: tc.ExternalSigningServer.URL, + // without a CA cert, the URL gets successfully added, and there should be no error connecting to it + }}), + rootCARoots: tc.RootCA.Certs, + externalCertSignedBy: tc.RootCA.Certs, + }, + { + clusterObj: fakeClusterSpec(tc.RootCA.Certs, nil, &api.RootRotation{ + CACert: cert, + CAKey: key, + CrossSignedCACert: crossSigned, + }, []*api.ExternalCA{ + { + Protocol: api.ExternalCA_CAProtocolCFSSL, + URL: tc.ExternalSigningServer.URL, + // without a CA cert, we count this as the old tc.RootCA.Certs, and this should be ignored because we want the new root + }, + }), + rootCARoots: tc.RootCA.Certs, + rootCASigningCert: crossSigned, + rootCASigningKey: key, + rootCAIntermediates: crossSigned, + }, + { + clusterObj: fakeClusterSpec(tc.RootCA.Certs, nil, &api.RootRotation{ + CACert: cert, + CrossSignedCACert: crossSigned, + }, []*api.ExternalCA{ + { + Protocol: api.ExternalCA_CAProtocolCFSSL, + URL: tc.ExternalSigningServer.URL, + // without a CA cert, we count this as the old tc.RootCA.Certs + }, + { + Protocol: api.ExternalCA_CAProtocolCFSSL, + URL: externalServer.URL, + CACert: cert, + }, + }), + rootCARoots: tc.RootCA.Certs, + rootCAIntermediates: crossSigned, + externalCertSignedBy: cert, + }, + } { + tc.CAServer.UpdateRootCA(context.Background(), testCase.clusterObj) + + rootCA := tc.ServingSecurityConfig.RootCA() + require.Equal(t, testCase.rootCARoots, rootCA.Certs) + var signingCert, signingKey []byte + if s, err := rootCA.Signer(); err == nil { + signingCert, signingKey = s.Cert, s.Key + } + require.Equal(t, testCase.rootCARoots, rootCA.Certs) + require.Equal(t, testCase.rootCASigningCert, signingCert, "%d", i) + require.Equal(t, testCase.rootCASigningKey, signingKey, "%d", i) + require.Equal(t, testCase.rootCAIntermediates, rootCA.Intermediates) + + externalCA := tc.ServingSecurityConfig.ExternalCA() + csr, _, err := ca.GenerateNewCSR() + require.NoError(t, err) + signedCert, err := externalCA.Sign(context.Background(), ca.PrepareCSR(csr, "cn", ca.ManagerRole, tc.Organization)) + + if testCase.externalCertSignedBy != nil { + require.NoError(t, err) + parsed, err := helpers.ParseCertificatePEM(signedCert) + require.NoError(t, err) + rootPool := x509.NewCertPool() + rootPool.AppendCertsFromPEM(testCase.externalCertSignedBy) + _, err = parsed.Verify(x509.VerifyOptions{Roots: rootPool}) + require.NoError(t, err) + } else { + require.Equal(t, err, ca.ErrNoExternalCAURLs) + } + } +} diff --git a/ca/testutils/cautils.go b/ca/testutils/cautils.go index 33913a2a07..fb5f02039c 100644 --- a/ca/testutils/cautils.go +++ b/ca/testutils/cautils.go @@ -39,6 +39,7 @@ type TestCA struct { Addr, TempDir, Organization string Paths *ca.SecurityConfigPaths Server *grpc.Server + ServingSecurityConfig *ca.SecurityConfig CAServer *ca.Server Context context.Context NodeCAClients []api.NodeCAClient @@ -233,6 +234,7 @@ func NewTestCAFromRootCA(t *testing.T, tempBaseDir string, rootCA ca.RootCA, krw Conns: conns, Addr: l.Addr().String(), Server: grpcServer, + ServingSecurityConfig: managerConfig, CAServer: caServer, WorkerToken: workerToken, ManagerToken: managerToken, From 563cb1d87b1a329c965ef252d4118543c5bc6777 Mon Sep 17 00:00:00 2001 From: cyli Date: Wed, 22 Mar 2017 16:50:00 -0700 Subject: [PATCH 3/3] Do not update security config unless the actual RootCA object or external CA config has changed Signed-off-by: cyli --- api/equality/equality.go | 38 +++++++++++++ api/equality/equality_test.go | 100 ++++++++++++++++++++++++++++++++++ ca/server.go | 84 +++++++++++++++++++--------- ca/server_test.go | 2 +- 4 files changed, 196 insertions(+), 28 deletions(-) diff --git a/api/equality/equality.go b/api/equality/equality.go index d560631a3b..522c719810 100644 --- a/api/equality/equality.go +++ b/api/equality/equality.go @@ -1,6 +1,7 @@ package equality import ( + "crypto/subtle" "reflect" "github.com/docker/swarmkit/api" @@ -27,3 +28,40 @@ func TaskStatusesEqualStable(a, b *api.TaskStatus) bool { copyA.Timestamp, copyB.Timestamp = nil, nil return reflect.DeepEqual(©A, ©B) } + +// RootCAEqualStable compares RootCAs, excluding join tokens, which are randomly generated +func RootCAEqualStable(a, b *api.RootCA) bool { + if a == nil && b == nil { + return true + } + if a == nil || b == nil { + return false + } + + var aRotationKey, bRotationKey []byte + if a.RootRotation != nil { + aRotationKey = a.RootRotation.CAKey + } + if b.RootRotation != nil { + bRotationKey = b.RootRotation.CAKey + } + if subtle.ConstantTimeCompare(a.CAKey, b.CAKey) != 1 || subtle.ConstantTimeCompare(aRotationKey, bRotationKey) != 1 { + return false + } + + copyA, copyB := *a, *b + copyA.JoinTokens, copyB.JoinTokens = api.JoinTokens{}, api.JoinTokens{} + return reflect.DeepEqual(copyA, copyB) +} + +// ExternalCAsEqualStable compares lists of external CAs and determines whether they are equal. +func ExternalCAsEqualStable(a, b []*api.ExternalCA) bool { + // because DeepEqual will treat an empty list and a nil list differently, we want to manually check this first + if len(a) == 0 && len(b) == 0 { + return true + } + // The assumption is that each individual api.ExternalCA within both lists are created from deserializing from a + // protobuf, so no special affordances are made to treat a nil map and empty map in the Options field of an + // api.ExternalCA as equivalent. + return reflect.DeepEqual(a, b) +} diff --git a/api/equality/equality_test.go b/api/equality/equality_test.go index 9f7837fde8..417cd33f3a 100644 --- a/api/equality/equality_test.go +++ b/api/equality/equality_test.go @@ -5,6 +5,7 @@ import ( "github.com/docker/swarmkit/api" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestTasksEqualStable(t *testing.T) { @@ -53,3 +54,102 @@ func TestTasksEqualStable(t *testing.T) { assert.Equal(t, TasksEqualStable(tasks[0], test.task), test.expected, test.failureText) } } + +func TestRootCAEqualStable(t *testing.T) { + root1 := api.RootCA{ + CACert: []byte("1"), + CAKey: []byte("2"), + CACertHash: "hash", + } + root2 := root1 + root2.JoinTokens = api.JoinTokens{ + Worker: "worker", + Manager: "manager", + } + root3 := root1 + root3.RootRotation = &api.RootRotation{ + CACert: []byte("3"), + CAKey: []byte("4"), + CrossSignedCACert: []byte("5"), + } + + for _, v := range []struct{ a, b *api.RootCA }{ + {a: nil, b: nil}, + {a: &root1, b: &root1}, + {a: &root1, b: &root2}, + {a: &root3, b: &root3}, + } { + require.True(t, RootCAEqualStable(v.a, v.b), "should be equal:\n%v\n%v\n", v.a, v.b) + } + + root1Permutations := []api.RootCA{root1, root1, root1} + root3Permutations := []api.RootCA{root3, root3, root3} + for _, r := range root3Permutations { + copy := *r.RootRotation + root3.RootRotation = © + } + root1Permutations[0].CACert = []byte("nope") + root1Permutations[1].CAKey = []byte("nope") + root1Permutations[2].CACertHash = "nope" + root3Permutations[0].RootRotation.CACert = []byte("nope") + root3Permutations[1].RootRotation.CAKey = []byte("nope") + root3Permutations[2].RootRotation.CrossSignedCACert = []byte("nope") + + for _, v := range []struct{ a, b *api.RootCA }{ + {a: &root1, b: &root3}, + {a: &root1, b: &root1Permutations[0]}, + {a: &root1, b: &root1Permutations[1]}, + {a: &root1, b: &root1Permutations[2]}, + {a: &root3, b: &root3Permutations[0]}, + {a: &root3, b: &root3Permutations[1]}, + {a: &root3, b: &root3Permutations[2]}, + } { + require.False(t, RootCAEqualStable(v.a, v.b), "should not be equal:\n%v\n%v\n", v.a, v.b) + } +} + +func TestExternalCAsEqualStable(t *testing.T) { + externals := []*api.ExternalCA{ + {URL: "1"}, + { + URL: "1", + CACert: []byte("cacert"), + }, + { + URL: "1", + CACert: []byte("cacert"), + Protocol: 1, + }, + { + URL: "1", + CACert: []byte("cacert"), + Options: map[string]string{ + "hello": "there", + }, + }, + { + URL: "1", + CACert: []byte("cacert"), + Options: map[string]string{ + "hello": "world", + }, + }, + } + // equal + for _, v := range []struct{ a, b []*api.ExternalCA }{ + {a: nil, b: []*api.ExternalCA{}}, + {a: externals, b: externals}, + {a: externals[0:1], b: externals[0:1]}, + } { + require.True(t, ExternalCAsEqualStable(v.a, v.b), "should be equal:\n%v\n%v\n", v.a, v.b) + } + // not equal + for _, v := range []struct{ a, b []*api.ExternalCA }{ + {a: nil, b: externals}, + {a: externals[2:3], b: externals[3:4]}, + {a: externals[2:3], b: externals[4:5]}, + {a: externals[3:4], b: externals[4:5]}, + } { + require.False(t, ExternalCAsEqualStable(v.a, v.b), "should not be equal:\n%v\n%v\n", v.a, v.b) + } +} diff --git a/ca/server.go b/ca/server.go index 31b7e083a0..ec33e68ef5 100644 --- a/ca/server.go +++ b/ca/server.go @@ -9,6 +9,7 @@ import ( "github.com/Sirupsen/logrus" "github.com/docker/swarmkit/api" + "github.com/docker/swarmkit/api/equality" "github.com/docker/swarmkit/identity" "github.com/docker/swarmkit/log" "github.com/docker/swarmkit/manager/state" @@ -45,6 +46,14 @@ type Server struct { // started is a channel which gets closed once the server is running // and able to service RPCs. started chan struct{} + + // these are cached values to ensure we only update the security config when + // the cluster root CA and external CAs have changed - the cluster object + // can change for other reasons, and it would not be necessary to update + // the security config as a result + lastSeenClusterRootCA *api.RootCA + lastSeenExternalCAs []*api.ExternalCA + secConfigMu sync.Mutex } // DefaultCAConfig returns the default CA Config, with a default expiration. @@ -523,17 +532,21 @@ func (s *Server) UpdateRootCA(ctx context.Context, cluster *api.Cluster) { s.mu.Lock() s.joinTokens = cluster.RootCA.JoinTokens.Copy() s.mu.Unlock() - var err error - // If the cluster has a RootCA, let's try to update our SecurityConfig to reflect the latest values + s.secConfigMu.Lock() + defer s.secConfigMu.Unlock() + var err error rCA := cluster.RootCA - wantedExternalCACert := rCA.CACert // we want to only add external CA URLs that use this cert - if len(rCA.CACert) != 0 { + rootCAChanged := len(rCA.CACert) != 0 && !equality.RootCAEqualStable(s.lastSeenClusterRootCA, &cluster.RootCA) + externalCAChanged := !equality.ExternalCAsEqualStable(s.lastSeenExternalCAs, cluster.Spec.CAConfig.ExternalCAs) + logger := log.G(ctx).WithFields(logrus.Fields{ + "cluster.id": cluster.ID, + "method": "(*Server).UpdateRootCA", + }) + + if rootCAChanged { + logger.Debug("Updating security config due to change in cluster Root CA") expiry := DefaultNodeCertExpiration - logger := log.G(ctx).WithFields(logrus.Fields{ - "cluster.id": cluster.ID, - "method": "(*Server).UpdateRootCA", - }) if cluster.Spec.CAConfig.NodeCertExpiry != nil { // NodeCertExpiry exists, let's try to parse the duration out of it clusterExpiry, err := gogotypes.DurationFromProto(cluster.Spec.CAConfig.NodeCertExpiry) @@ -555,8 +568,6 @@ func (s *Server) UpdateRootCA(ctx context.Context, cluster *api.Cluster) { signingCert = rCA.RootRotation.CrossSignedCACert signingKey = rCA.RootRotation.CAKey intermediates = rCA.RootRotation.CrossSignedCACert - // we're rotating to a new root, so we only want external CAs with the new root cert - wantedExternalCACert = rCA.RootRotation.CACert } if signingKey == nil { signingCert = nil @@ -579,30 +590,49 @@ func (s *Server) UpdateRootCA(ctx context.Context, cluster *api.Cluster) { if err := s.securityConfig.UpdateRootCA(&updatedRootCA, externalCARootPool); err != nil { logger.WithError(err).Error("updating Root CA failed") } else { - logger.Debugf("Root CA updated successfully") + // only update the server cache if we've successfully updated the root CA + logger.Debug("Root CA updated successfully") + s.lastSeenClusterRootCA = cluster.RootCA.Copy() } } - // Update our security config with the list of External CA URLs - // from the new cluster state. - - // TODO(aaronl): In the future, this will be abstracted with an - // ExternalCA interface that has different implementations for - // different CA types. At the moment, only CFSSL is supported. - var cfsslURLs []string - for _, extCA := range cluster.Spec.CAConfig.ExternalCAs { - // We want to support old external CA specifications which did not have a CA cert. If there is no cert specified, - // we assume it's the old cert - certForExtCA := extCA.CACert - if certForExtCA == nil { - certForExtCA = rCA.CACert + // we want to update if the external CA changed, or if the root CA changed because the root CA could affect what + // certificate for external CAs we want to filter by + if rootCAChanged || externalCAChanged { + logger.Debug("Updating security config due to change in cluster Root CA or cluster spec") + wantedExternalCACert := rCA.CACert // we want to only add external CA URLs that use this cert + if rCA.RootRotation != nil { + // we're rotating to a new root, so we only want external CAs with the new root cert + wantedExternalCACert = rCA.RootRotation.CACert } - if extCA.Protocol == api.ExternalCA_CAProtocolCFSSL && bytes.Equal(certForExtCA, wantedExternalCACert) { + // Update our security config with the list of External CA URLs + // from the new cluster state. + + // TODO(aaronl): In the future, this will be abstracted with an + // ExternalCA interface that has different implementations for + // different CA types. At the moment, only CFSSL is supported. + var cfsslURLs []string + for i, extCA := range cluster.Spec.CAConfig.ExternalCAs { + // We want to support old external CA specifications which did not have a CA cert. If there is no cert specified, + // we assume it's the old cert + certForExtCA := extCA.CACert + if len(certForExtCA) == 0 { + certForExtCA = rCA.CACert + } + if extCA.Protocol != api.ExternalCA_CAProtocolCFSSL { + logger.Debugf("skipping external CA %d (url: %s) due to unknown protocol type", i, extCA.URL) + continue + } + if !bytes.Equal(certForExtCA, wantedExternalCACert) { + logger.Debugf("skipping external CA %d (url: %s) because it has the wrong CA cert", i, extCA.URL) + continue + } cfsslURLs = append(cfsslURLs, extCA.URL) } - } - s.securityConfig.externalCA.UpdateURLs(cfsslURLs...) + s.securityConfig.externalCA.UpdateURLs(cfsslURLs...) + s.lastSeenExternalCAs = cluster.Spec.CAConfig.Copy().ExternalCAs + } } // evaluateAndSignNodeCert implements the logic of which certificates to sign diff --git a/ca/server_test.go b/ca/server_test.go index d25a906d8c..42b26e6dc0 100644 --- a/ca/server_test.go +++ b/ca/server_test.go @@ -539,7 +539,7 @@ func TestCAServerUpdateRootCA(t *testing.T) { _, err = parsed.Verify(x509.VerifyOptions{Roots: rootPool}) require.NoError(t, err) } else { - require.Equal(t, err, ca.ErrNoExternalCAURLs) + require.Equal(t, ca.ErrNoExternalCAURLs, err) } } }