diff --git a/api/client/webclient/webclient.go b/api/client/webclient/webclient.go index 048466393c66a..24fe7777a26e0 100644 --- a/api/client/webclient/webclient.go +++ b/api/client/webclient/webclient.go @@ -45,6 +45,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" ) const ( @@ -544,7 +545,9 @@ type AuthenticationSettings struct { // PrivateKeyPolicy contains the cluster-wide private key policy. PrivateKeyPolicy keys.PrivateKeyPolicy `json:"private_key_policy"` // PIVSlot specifies a specific PIV slot to use with hardware key support. - PIVSlot keys.PIVSlot `json:"piv_slot"` + PIVSlot hardwarekey.PIVSlotKeyString `json:"piv_slot"` + // PIVPINCacheTTL specifies how long to cache the user's PIV PIN. + PIVPINCacheTTL time.Duration `json:"piv_pin_cache_ttl"` // DeviceTrust holds cluster-wide device trust settings. DeviceTrust DeviceTrustSettings `json:"device_trust,omitempty"` // HasMessageOfTheDay is a flag indicating that the cluster has MOTD diff --git a/api/constants/constants.go b/api/constants/constants.go index e0105386d8324..eaa6848592104 100644 --- a/api/constants/constants.go +++ b/api/constants/constants.go @@ -512,3 +512,6 @@ const ( // specifically using the `terraform` join method. EnvVarTerraformCloudJoinAudienceTag = "TF_TELEPORT_JOIN_AUDIENCE_TAG" ) + +// MaxPIVPINCacheTTL defines the maximum allowed TTL for PIV PIN client caches. +const MaxPIVPINCacheTTL = time.Hour diff --git a/api/gen/proto/go/teleport/hardwarekeyagent/v1/hardwarekeyagent_service.pb.go b/api/gen/proto/go/teleport/hardwarekeyagent/v1/hardwarekeyagent_service.pb.go new file mode 100644 index 0000000000000..a6baffdc66507 --- /dev/null +++ b/api/gen/proto/go/teleport/hardwarekeyagent/v1/hardwarekeyagent_service.pb.go @@ -0,0 +1,709 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.5 +// protoc (unknown) +// source: teleport/hardwarekeyagent/v1/hardwarekeyagent_service.proto + +package hardwarekeyagentv1 + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb "google.golang.org/protobuf/types/known/durationpb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// PIVSlotKey is the key reference for a specific PIV slot. +type PIVSlotKey int32 + +const ( + // PIV slot key not specified. + PIVSlotKey_PIV_SLOT_KEY_UNSPECIFIED PIVSlotKey = 0 + // PIV slot key 9a. This is the default slot for pin_policy=never, touch_policy=never. + PIVSlotKey_PIV_SLOT_KEY_9A PIVSlotKey = 1 + // PIV slot key 9c. This is the default slot for pin_policy=never, touch_policy=cached. + PIVSlotKey_PIV_SLOT_KEY_9C PIVSlotKey = 2 + // PIV slot key 9d. This is the default slot for pin_policy=once, touch_policy=cached. + PIVSlotKey_PIV_SLOT_KEY_9D PIVSlotKey = 3 + // PIV slot key 9e. This is the default slot for pin_policy=once, touch_policy=never. + PIVSlotKey_PIV_SLOT_KEY_9E PIVSlotKey = 4 +) + +// Enum value maps for PIVSlotKey. +var ( + PIVSlotKey_name = map[int32]string{ + 0: "PIV_SLOT_KEY_UNSPECIFIED", + 1: "PIV_SLOT_KEY_9A", + 2: "PIV_SLOT_KEY_9C", + 3: "PIV_SLOT_KEY_9D", + 4: "PIV_SLOT_KEY_9E", + } + PIVSlotKey_value = map[string]int32{ + "PIV_SLOT_KEY_UNSPECIFIED": 0, + "PIV_SLOT_KEY_9A": 1, + "PIV_SLOT_KEY_9C": 2, + "PIV_SLOT_KEY_9D": 3, + "PIV_SLOT_KEY_9E": 4, + } +) + +func (x PIVSlotKey) Enum() *PIVSlotKey { + p := new(PIVSlotKey) + *p = x + return p +} + +func (x PIVSlotKey) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (PIVSlotKey) Descriptor() protoreflect.EnumDescriptor { + return file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_enumTypes[0].Descriptor() +} + +func (PIVSlotKey) Type() protoreflect.EnumType { + return &file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_enumTypes[0] +} + +func (x PIVSlotKey) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use PIVSlotKey.Descriptor instead. +func (PIVSlotKey) EnumDescriptor() ([]byte, []int) { + return file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDescGZIP(), []int{0} +} + +// Hash refers to a specific hash function used during signing. +type Hash int32 + +const ( + Hash_HASH_UNSPECIFIED Hash = 0 + Hash_HASH_NONE Hash = 1 + Hash_HASH_SHA256 Hash = 2 + Hash_HASH_SHA512 Hash = 3 +) + +// Enum value maps for Hash. +var ( + Hash_name = map[int32]string{ + 0: "HASH_UNSPECIFIED", + 1: "HASH_NONE", + 2: "HASH_SHA256", + 3: "HASH_SHA512", + } + Hash_value = map[string]int32{ + "HASH_UNSPECIFIED": 0, + "HASH_NONE": 1, + "HASH_SHA256": 2, + "HASH_SHA512": 3, + } +) + +func (x Hash) Enum() *Hash { + p := new(Hash) + *p = x + return p +} + +func (x Hash) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Hash) Descriptor() protoreflect.EnumDescriptor { + return file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_enumTypes[1].Descriptor() +} + +func (Hash) Type() protoreflect.EnumType { + return &file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_enumTypes[1] +} + +func (x Hash) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Hash.Descriptor instead. +func (Hash) EnumDescriptor() ([]byte, []int) { + return file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDescGZIP(), []int{1} +} + +// PingRequest is a request to Ping. +type PingRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PingRequest) Reset() { + *x = PingRequest{} + mi := &file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PingRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PingRequest) ProtoMessage() {} + +func (x *PingRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PingRequest.ProtoReflect.Descriptor instead. +func (*PingRequest) Descriptor() ([]byte, []int) { + return file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDescGZIP(), []int{0} +} + +// PingResponse is a response to Ping. +type PingResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // PID is the PID of the client process running the agent. + Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PingResponse) Reset() { + *x = PingResponse{} + mi := &file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PingResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PingResponse) ProtoMessage() {} + +func (x *PingResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PingResponse.ProtoReflect.Descriptor instead. +func (*PingResponse) Descriptor() ([]byte, []int) { + return file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDescGZIP(), []int{1} +} + +func (x *PingResponse) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +// SignRequest is a request to perform a signature with a specific hardware private key. +type SignRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Digest is a hashed message to sign. + Digest []byte `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"` + // Hash is the hash function used to prepare the digest. + Hash Hash `protobuf:"varint,2,opt,name=hash,proto3,enum=teleport.hardwarekeyagent.v1.Hash" json:"hash,omitempty"` + // SaltLength specifies the length of the salt added to the digest before a signature. + // This salt length is precomputed by the client, following the crypto/rsa implementation. + // Only used, and required, for PSS RSA signatures. + SaltLength uint32 `protobuf:"varint,3,opt,name=salt_length,json=saltLength,proto3" json:"salt_length,omitempty"` + // KeyRef references a specific hardware private key. + KeyRef *KeyRef `protobuf:"bytes,4,opt,name=key_ref,json=keyRef,proto3" json:"key_ref,omitempty"` + // KeyInfo contains additional, optional key info which generally will improve UX by + // giving the agent context about the key, such as whether PIN/touch prompts are + // expected, or what cluster login is trying to interface with the key. + KeyInfo *KeyInfo `protobuf:"bytes,5,opt,name=key_info,json=keyInfo,proto3" json:"key_info,omitempty"` + // Command is the client command or action requiring a signature, e.g. "tsh ssh server01". + // The agent can include this detail in PIN/touch prompts to show the origin of the + // signature request to the user. + Command string `protobuf:"bytes,6,opt,name=command,proto3" json:"command,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SignRequest) Reset() { + *x = SignRequest{} + mi := &file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SignRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SignRequest) ProtoMessage() {} + +func (x *SignRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SignRequest.ProtoReflect.Descriptor instead. +func (*SignRequest) Descriptor() ([]byte, []int) { + return file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDescGZIP(), []int{2} +} + +func (x *SignRequest) GetDigest() []byte { + if x != nil { + return x.Digest + } + return nil +} + +func (x *SignRequest) GetHash() Hash { + if x != nil { + return x.Hash + } + return Hash_HASH_UNSPECIFIED +} + +func (x *SignRequest) GetSaltLength() uint32 { + if x != nil { + return x.SaltLength + } + return 0 +} + +func (x *SignRequest) GetKeyRef() *KeyRef { + if x != nil { + return x.KeyRef + } + return nil +} + +func (x *SignRequest) GetKeyInfo() *KeyInfo { + if x != nil { + return x.KeyInfo + } + return nil +} + +func (x *SignRequest) GetCommand() string { + if x != nil { + return x.Command + } + return "" +} + +// Signature is a private key signature. +type Signature struct { + state protoimpl.MessageState `protogen:"open.v1"` + // For an RSA key, signature should be either a PKCS #1 v1.5 or PSS signature, + // depending on the hash and salt chosen. For an (EC)DSA key, it should be a + // DER-serialised, ASN.1 signature structure. + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Signature) Reset() { + *x = Signature{} + mi := &file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Signature) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Signature) ProtoMessage() {} + +func (x *Signature) ProtoReflect() protoreflect.Message { + mi := &file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Signature.ProtoReflect.Descriptor instead. +func (*Signature) Descriptor() ([]byte, []int) { + return file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDescGZIP(), []int{3} +} + +func (x *Signature) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +// KeyRef references a specific hardware private key. +type KeyRef struct { + state protoimpl.MessageState `protogen:"open.v1"` + // SerialNumber is the serial number of the hardware key. + SerialNumber uint32 `protobuf:"varint,1,opt,name=serial_number,json=serialNumber,proto3" json:"serial_number,omitempty"` + // SlotKey is a PIV slot key reference. + SlotKey PIVSlotKey `protobuf:"varint,2,opt,name=slot_key,json=slotKey,proto3,enum=teleport.hardwarekeyagent.v1.PIVSlotKey" json:"slot_key,omitempty"` + // PublicKey is the public key encoded in PKIX, ASN.1 DER form. If the public key does + // not match the private key currently in the hardware key's PIV slot, the signature + // will fail early. + PublicKeyDer []byte `protobuf:"bytes,3,opt,name=public_key_der,json=publicKeyDer,proto3" json:"public_key_der,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *KeyRef) Reset() { + *x = KeyRef{} + mi := &file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *KeyRef) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*KeyRef) ProtoMessage() {} + +func (x *KeyRef) ProtoReflect() protoreflect.Message { + mi := &file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use KeyRef.ProtoReflect.Descriptor instead. +func (*KeyRef) Descriptor() ([]byte, []int) { + return file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDescGZIP(), []int{4} +} + +func (x *KeyRef) GetSerialNumber() uint32 { + if x != nil { + return x.SerialNumber + } + return 0 +} + +func (x *KeyRef) GetSlotKey() PIVSlotKey { + if x != nil { + return x.SlotKey + } + return PIVSlotKey_PIV_SLOT_KEY_UNSPECIFIED +} + +func (x *KeyRef) GetPublicKeyDer() []byte { + if x != nil { + return x.PublicKeyDer + } + return nil +} + +// KeyInfo contains additional information about a hardware private key. +type KeyInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + // TouchRequired is a client hint as to whether the hardware private key requires touch. + // The agent will use this to provide the ideal UX for the touch prompt. If this client + // hint is incorrect, touch will still be prompted. + TouchRequired bool `protobuf:"varint,1,opt,name=touch_required,json=touchRequired,proto3" json:"touch_required,omitempty"` + // PinRequired is a client hint as to whether the hardware private key requires PIN. + // The agent will use this to provide the ideal UX for the PIN prompt. If this client + // hint is incorrect, PIN will still be prompted for YubiKey versions >= 4.3.0, and + // failing with an auth error otherwise. + PinRequired bool `protobuf:"varint,2,opt,name=pin_required,json=pinRequired,proto3" json:"pin_required,omitempty"` + // ProxyHost is a Teleport proxy hostname that the key is associated with. + // May be used to add context to PIN/touch prompts. + ProxyHost string `protobuf:"bytes,3,opt,name=proxy_host,json=proxyHost,proto3" json:"proxy_host,omitempty"` + // Username is a Teleport username that the key is associated with. + // May be used to add context to PIN/touch prompts. + Username string `protobuf:"bytes,4,opt,name=username,proto3" json:"username,omitempty"` + // ClusterName is a Teleport cluster name that the key is associated with. + // May be used to add context to PIN/touch prompts. + ClusterName string `protobuf:"bytes,5,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"` + // PinCacheTtl is the amount of time that the PIN should be cached for + // PIN prompts associated with this key. A TTL of 0 means no PIN caching. + PinCacheTtl *durationpb.Duration `protobuf:"bytes,6,opt,name=pin_cache_ttl,json=pinCacheTtl,proto3" json:"pin_cache_ttl,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *KeyInfo) Reset() { + *x = KeyInfo{} + mi := &file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *KeyInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*KeyInfo) ProtoMessage() {} + +func (x *KeyInfo) ProtoReflect() protoreflect.Message { + mi := &file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use KeyInfo.ProtoReflect.Descriptor instead. +func (*KeyInfo) Descriptor() ([]byte, []int) { + return file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDescGZIP(), []int{5} +} + +func (x *KeyInfo) GetTouchRequired() bool { + if x != nil { + return x.TouchRequired + } + return false +} + +func (x *KeyInfo) GetPinRequired() bool { + if x != nil { + return x.PinRequired + } + return false +} + +func (x *KeyInfo) GetProxyHost() string { + if x != nil { + return x.ProxyHost + } + return "" +} + +func (x *KeyInfo) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *KeyInfo) GetClusterName() string { + if x != nil { + return x.ClusterName + } + return "" +} + +func (x *KeyInfo) GetPinCacheTtl() *durationpb.Duration { + if x != nil { + return x.PinCacheTtl + } + return nil +} + +var File_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto protoreflect.FileDescriptor + +var file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDesc = string([]byte{ + 0x0a, 0x3b, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x68, 0x61, 0x72, 0x64, 0x77, + 0x61, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x68, + 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, + 0x6b, 0x65, 0x79, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x0d, 0x0a, 0x0b, 0x50, + 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x20, 0x0a, 0x0c, 0x50, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x70, 0x69, 0x64, 0x22, 0x99, 0x02, 0x0a, + 0x0b, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64, 0x69, + 0x67, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x61, + 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, + 0x31, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1f, 0x0a, 0x0b, + 0x73, 0x61, 0x6c, 0x74, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0a, 0x73, 0x61, 0x6c, 0x74, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x3d, 0x0a, + 0x07, 0x6b, 0x65, 0x79, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, + 0x72, 0x65, 0x6b, 0x65, 0x79, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, + 0x79, 0x52, 0x65, 0x66, 0x52, 0x06, 0x6b, 0x65, 0x79, 0x52, 0x65, 0x66, 0x12, 0x40, 0x0a, 0x08, + 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, + 0x72, 0x65, 0x6b, 0x65, 0x79, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, + 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x18, + 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x22, 0x29, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x22, 0x98, 0x01, 0x0a, 0x06, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x66, 0x12, 0x23, + 0x0a, 0x0d, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x12, 0x43, 0x0a, 0x08, 0x73, 0x6c, 0x6f, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x61, 0x67, 0x65, 0x6e, + 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x49, 0x56, 0x53, 0x6c, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x52, + 0x07, 0x73, 0x6c, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x44, 0x65, 0x72, 0x22, 0xf0, + 0x01, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x6f, + 0x75, 0x63, 0x68, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x0d, 0x74, 0x6f, 0x75, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x69, 0x72, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x68, 0x6f, + 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x48, + 0x6f, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x0d, 0x70, 0x69, 0x6e, 0x5f, 0x63, 0x61, 0x63, 0x68, 0x65, 0x5f, + 0x74, 0x74, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x69, 0x6e, 0x43, 0x61, 0x63, 0x68, 0x65, 0x54, 0x74, + 0x6c, 0x2a, 0x7e, 0x0a, 0x0a, 0x50, 0x49, 0x56, 0x53, 0x6c, 0x6f, 0x74, 0x4b, 0x65, 0x79, 0x12, + 0x1c, 0x0a, 0x18, 0x50, 0x49, 0x56, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, + 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x13, 0x0a, + 0x0f, 0x50, 0x49, 0x56, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x39, 0x41, + 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x49, 0x56, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4b, + 0x45, 0x59, 0x5f, 0x39, 0x43, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x50, 0x49, 0x56, 0x5f, 0x53, + 0x4c, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x39, 0x44, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, + 0x50, 0x49, 0x56, 0x5f, 0x53, 0x4c, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x39, 0x45, 0x10, + 0x04, 0x2a, 0x4d, 0x0a, 0x04, 0x48, 0x61, 0x73, 0x68, 0x12, 0x14, 0x0a, 0x10, 0x48, 0x41, 0x53, + 0x48, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, + 0x0d, 0x0a, 0x09, 0x48, 0x41, 0x53, 0x48, 0x5f, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x01, 0x12, 0x0f, + 0x0a, 0x0b, 0x48, 0x41, 0x53, 0x48, 0x5f, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x10, 0x02, 0x12, + 0x0f, 0x0a, 0x0b, 0x48, 0x41, 0x53, 0x48, 0x5f, 0x53, 0x48, 0x41, 0x35, 0x31, 0x32, 0x10, 0x03, + 0x32, 0xd8, 0x01, 0x0a, 0x17, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, + 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5f, 0x0a, 0x04, + 0x50, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x61, 0x72, 0x64, 0x77, + 0x61, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5c, 0x0a, + 0x04, 0x53, 0x69, 0x67, 0x6e, 0x12, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x68, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x61, 0x67, 0x65, 0x6e, + 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x27, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x68, 0x61, 0x72, 0x64, + 0x77, 0x61, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x76, 0x31, 0x2e, + 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x00, 0x42, 0x64, 0x5a, 0x62, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, + 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x68, 0x61, 0x72, 0x64, 0x77, + 0x61, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2f, 0x76, 0x31, 0x3b, 0x68, + 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x6b, 0x65, 0x79, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x76, + 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +}) + +var ( + file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDescOnce sync.Once + file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDescData []byte +) + +func file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDescGZIP() []byte { + file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDescOnce.Do(func() { + file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDesc), len(file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDesc))) + }) + return file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDescData +} + +var file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_goTypes = []any{ + (PIVSlotKey)(0), // 0: teleport.hardwarekeyagent.v1.PIVSlotKey + (Hash)(0), // 1: teleport.hardwarekeyagent.v1.Hash + (*PingRequest)(nil), // 2: teleport.hardwarekeyagent.v1.PingRequest + (*PingResponse)(nil), // 3: teleport.hardwarekeyagent.v1.PingResponse + (*SignRequest)(nil), // 4: teleport.hardwarekeyagent.v1.SignRequest + (*Signature)(nil), // 5: teleport.hardwarekeyagent.v1.Signature + (*KeyRef)(nil), // 6: teleport.hardwarekeyagent.v1.KeyRef + (*KeyInfo)(nil), // 7: teleport.hardwarekeyagent.v1.KeyInfo + (*durationpb.Duration)(nil), // 8: google.protobuf.Duration +} +var file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_depIdxs = []int32{ + 1, // 0: teleport.hardwarekeyagent.v1.SignRequest.hash:type_name -> teleport.hardwarekeyagent.v1.Hash + 6, // 1: teleport.hardwarekeyagent.v1.SignRequest.key_ref:type_name -> teleport.hardwarekeyagent.v1.KeyRef + 7, // 2: teleport.hardwarekeyagent.v1.SignRequest.key_info:type_name -> teleport.hardwarekeyagent.v1.KeyInfo + 0, // 3: teleport.hardwarekeyagent.v1.KeyRef.slot_key:type_name -> teleport.hardwarekeyagent.v1.PIVSlotKey + 8, // 4: teleport.hardwarekeyagent.v1.KeyInfo.pin_cache_ttl:type_name -> google.protobuf.Duration + 2, // 5: teleport.hardwarekeyagent.v1.HardwareKeyAgentService.Ping:input_type -> teleport.hardwarekeyagent.v1.PingRequest + 4, // 6: teleport.hardwarekeyagent.v1.HardwareKeyAgentService.Sign:input_type -> teleport.hardwarekeyagent.v1.SignRequest + 3, // 7: teleport.hardwarekeyagent.v1.HardwareKeyAgentService.Ping:output_type -> teleport.hardwarekeyagent.v1.PingResponse + 5, // 8: teleport.hardwarekeyagent.v1.HardwareKeyAgentService.Sign:output_type -> teleport.hardwarekeyagent.v1.Signature + 7, // [7:9] is the sub-list for method output_type + 5, // [5:7] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_init() } +func file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_init() { + if File_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDesc), len(file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_rawDesc)), + NumEnums: 2, + NumMessages: 6, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_goTypes, + DependencyIndexes: file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_depIdxs, + EnumInfos: file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_enumTypes, + MessageInfos: file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_msgTypes, + }.Build() + File_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto = out.File + file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_goTypes = nil + file_teleport_hardwarekeyagent_v1_hardwarekeyagent_service_proto_depIdxs = nil +} diff --git a/api/gen/proto/go/teleport/hardwarekeyagent/v1/hardwarekeyagent_service_grpc.pb.go b/api/gen/proto/go/teleport/hardwarekeyagent/v1/hardwarekeyagent_service_grpc.pb.go new file mode 100644 index 0000000000000..6010a37dc53ef --- /dev/null +++ b/api/gen/proto/go/teleport/hardwarekeyagent/v1/hardwarekeyagent_service_grpc.pb.go @@ -0,0 +1,171 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc (unknown) +// source: teleport/hardwarekeyagent/v1/hardwarekeyagent_service.proto + +package hardwarekeyagentv1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + HardwareKeyAgentService_Ping_FullMethodName = "/teleport.hardwarekeyagent.v1.HardwareKeyAgentService/Ping" + HardwareKeyAgentService_Sign_FullMethodName = "/teleport.hardwarekeyagent.v1.HardwareKeyAgentService/Sign" +) + +// HardwareKeyAgentServiceClient is the client API for HardwareKeyAgentService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type HardwareKeyAgentServiceClient interface { + // Ping the agent service to check if it is active. + Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) + // Sign produces a signature with the provided options for the specified hardware private key + // + // This rpc implements Go's crypto.Signer interface. + Sign(ctx context.Context, in *SignRequest, opts ...grpc.CallOption) (*Signature, error) +} + +type hardwareKeyAgentServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewHardwareKeyAgentServiceClient(cc grpc.ClientConnInterface) HardwareKeyAgentServiceClient { + return &hardwareKeyAgentServiceClient{cc} +} + +func (c *hardwareKeyAgentServiceClient) Ping(ctx context.Context, in *PingRequest, opts ...grpc.CallOption) (*PingResponse, error) { + out := new(PingResponse) + err := c.cc.Invoke(ctx, HardwareKeyAgentService_Ping_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *hardwareKeyAgentServiceClient) Sign(ctx context.Context, in *SignRequest, opts ...grpc.CallOption) (*Signature, error) { + out := new(Signature) + err := c.cc.Invoke(ctx, HardwareKeyAgentService_Sign_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// HardwareKeyAgentServiceServer is the server API for HardwareKeyAgentService service. +// All implementations must embed UnimplementedHardwareKeyAgentServiceServer +// for forward compatibility +type HardwareKeyAgentServiceServer interface { + // Ping the agent service to check if it is active. + Ping(context.Context, *PingRequest) (*PingResponse, error) + // Sign produces a signature with the provided options for the specified hardware private key + // + // This rpc implements Go's crypto.Signer interface. + Sign(context.Context, *SignRequest) (*Signature, error) + mustEmbedUnimplementedHardwareKeyAgentServiceServer() +} + +// UnimplementedHardwareKeyAgentServiceServer must be embedded to have forward compatible implementations. +type UnimplementedHardwareKeyAgentServiceServer struct { +} + +func (UnimplementedHardwareKeyAgentServiceServer) Ping(context.Context, *PingRequest) (*PingResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented") +} +func (UnimplementedHardwareKeyAgentServiceServer) Sign(context.Context, *SignRequest) (*Signature, error) { + return nil, status.Errorf(codes.Unimplemented, "method Sign not implemented") +} +func (UnimplementedHardwareKeyAgentServiceServer) mustEmbedUnimplementedHardwareKeyAgentServiceServer() { +} + +// UnsafeHardwareKeyAgentServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to HardwareKeyAgentServiceServer will +// result in compilation errors. +type UnsafeHardwareKeyAgentServiceServer interface { + mustEmbedUnimplementedHardwareKeyAgentServiceServer() +} + +func RegisterHardwareKeyAgentServiceServer(s grpc.ServiceRegistrar, srv HardwareKeyAgentServiceServer) { + s.RegisterService(&HardwareKeyAgentService_ServiceDesc, srv) +} + +func _HardwareKeyAgentService_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HardwareKeyAgentServiceServer).Ping(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: HardwareKeyAgentService_Ping_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HardwareKeyAgentServiceServer).Ping(ctx, req.(*PingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _HardwareKeyAgentService_Sign_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SignRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(HardwareKeyAgentServiceServer).Sign(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: HardwareKeyAgentService_Sign_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(HardwareKeyAgentServiceServer).Sign(ctx, req.(*SignRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// HardwareKeyAgentService_ServiceDesc is the grpc.ServiceDesc for HardwareKeyAgentService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var HardwareKeyAgentService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "teleport.hardwarekeyagent.v1.HardwareKeyAgentService", + HandlerType: (*HardwareKeyAgentServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Ping", + Handler: _HardwareKeyAgentService_Ping_Handler, + }, + { + MethodName: "Sign", + Handler: _HardwareKeyAgentService_Sign_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "teleport/hardwarekeyagent/v1/hardwarekeyagent_service.proto", +} diff --git a/api/profile/profile.go b/api/profile/profile.go index 25809600d737e..9c1022b1f572a 100644 --- a/api/profile/profile.go +++ b/api/profile/profile.go @@ -35,6 +35,7 @@ import ( "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/api/utils/keypaths" "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" "github.com/gravitational/teleport/api/utils/sshutils" ) @@ -107,7 +108,10 @@ type Profile struct { PrivateKeyPolicy keys.PrivateKeyPolicy `yaml:"private_key_policy"` // PIVSlot is a specific piv slot that Teleport clients should use for hardware key support. - PIVSlot keys.PIVSlot `yaml:"piv_slot"` + PIVSlot hardwarekey.PIVSlotKeyString `yaml:"piv_slot"` + + // PIVPINCacheTTL specifies how long to cache the user's PIV PIN. + PIVPINCacheTTL time.Duration `yaml:"piv_pin_cache_ttl"` // MissingClusterDetails means this profile was created with limited cluster details. // Missing cluster details should be loaded into the profile by pinging the proxy. @@ -276,16 +280,6 @@ func SetCurrentProfileName(dir string, name string) error { return nil } -// RemoveProfile removes cluster profile file -func RemoveProfile(dir, name string) error { - profilePath := filepath.Join(dir, name+".yaml") - if err := os.Remove(profilePath); err != nil { - return trace.ConvertSystemError(err) - } - - return nil -} - // GetCurrentProfileName attempts to load the current profile name. func GetCurrentProfileName(dir string) (name string, err error) { if dir == "" { @@ -306,33 +300,6 @@ func GetCurrentProfileName(dir string) (name string, err error) { return name, nil } -// ListProfileNames lists all available profiles. -func ListProfileNames(dir string) ([]string, error) { - if dir == "" { - return nil, trace.BadParameter("cannot list profiles: missing dir") - } - files, err := os.ReadDir(dir) - if err != nil { - return nil, trace.Wrap(err) - } - - var names []string - for _, file := range files { - if file.IsDir() { - continue - } - - if file.Type()&os.ModeSymlink != 0 { - continue - } - if !strings.HasSuffix(file.Name(), ".yaml") { - continue - } - names = append(names, strings.TrimSuffix(file.Name(), ".yaml")) - } - return names, nil -} - // FullProfilePath returns the full path to the user profile directory. // If the parameter is empty, it returns expanded "~/.tsh", otherwise // returns its unmodified parameter diff --git a/api/proto/teleport/hardwarekeyagent/v1/hardwarekeyagent_service.proto b/api/proto/teleport/hardwarekeyagent/v1/hardwarekeyagent_service.proto new file mode 100644 index 0000000000000..e29389ee5beb9 --- /dev/null +++ b/api/proto/teleport/hardwarekeyagent/v1/hardwarekeyagent_service.proto @@ -0,0 +1,134 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +syntax = "proto3"; + +package teleport.hardwarekeyagent.v1; + +import "google/protobuf/duration.proto"; + +option go_package = "github.com/gravitational/teleport/api/gen/proto/go/teleport/hardwarekeyagent/v1;hardwarekeyagentv1"; + +// HardwareKeyAgentService provides an agent service for hardware key (PIV) signatures. +// This allows multiple Teleport clients to share a PIV connection rather than blocking +// each other, due to the exclusive nature of PIV connections. This also enabled shared +// hardware key states, such as a custom PIN cache shared across Teleport clients. +service HardwareKeyAgentService { + // Ping the agent service to check if it is active. + rpc Ping(PingRequest) returns (PingResponse) {} + // Sign produces a signature with the provided options for the specified hardware private key + // + // This rpc implements Go's crypto.Signer interface. + rpc Sign(SignRequest) returns (Signature) {} +} + +// PingRequest is a request to Ping. +message PingRequest {} + +// PingResponse is a response to Ping. +message PingResponse { + // PID is the PID of the client process running the agent. + uint32 pid = 1; +} + +// SignRequest is a request to perform a signature with a specific hardware private key. +message SignRequest { + // Digest is a hashed message to sign. + bytes digest = 1; + // Hash is the hash function used to prepare the digest. + Hash hash = 2; + // SaltLength specifies the length of the salt added to the digest before a signature. + // This salt length is precomputed by the client, following the crypto/rsa implementation. + // Only used, and required, for PSS RSA signatures. + uint32 salt_length = 3; + // KeyRef references a specific hardware private key. + KeyRef key_ref = 4; + // KeyInfo contains additional, optional key info which generally will improve UX by + // giving the agent context about the key, such as whether PIN/touch prompts are + // expected, or what cluster login is trying to interface with the key. + KeyInfo key_info = 5; + // Command is the client command or action requiring a signature, e.g. "tsh ssh server01". + // The agent can include this detail in PIN/touch prompts to show the origin of the + // signature request to the user. + string command = 6; +} + +// Signature is a private key signature. +message Signature { + // For an RSA key, signature should be either a PKCS #1 v1.5 or PSS signature, + // depending on the hash and salt chosen. For an (EC)DSA key, it should be a + // DER-serialised, ASN.1 signature structure. + bytes signature = 1; +} + +// KeyRef references a specific hardware private key. +message KeyRef { + // SerialNumber is the serial number of the hardware key. + uint32 serial_number = 1; + // SlotKey is a PIV slot key reference. + PIVSlotKey slot_key = 2; + // PublicKey is the public key encoded in PKIX, ASN.1 DER form. If the public key does + // not match the private key currently in the hardware key's PIV slot, the signature + // will fail early. + bytes public_key_der = 3; +} + +// KeyInfo contains additional information about a hardware private key. +message KeyInfo { + // TouchRequired is a client hint as to whether the hardware private key requires touch. + // The agent will use this to provide the ideal UX for the touch prompt. If this client + // hint is incorrect, touch will still be prompted. + bool touch_required = 1; + // PinRequired is a client hint as to whether the hardware private key requires PIN. + // The agent will use this to provide the ideal UX for the PIN prompt. If this client + // hint is incorrect, PIN will still be prompted for YubiKey versions >= 4.3.0, and + // failing with an auth error otherwise. + bool pin_required = 2; + // ProxyHost is a Teleport proxy hostname that the key is associated with. + // May be used to add context to PIN/touch prompts. + string proxy_host = 3; + // Username is a Teleport username that the key is associated with. + // May be used to add context to PIN/touch prompts. + string username = 4; + // ClusterName is a Teleport cluster name that the key is associated with. + // May be used to add context to PIN/touch prompts. + string cluster_name = 5; + // PinCacheTtl is the amount of time that the PIN should be cached for + // PIN prompts associated with this key. A TTL of 0 means no PIN caching. + google.protobuf.Duration pin_cache_ttl = 6; +} + +// PIVSlotKey is the key reference for a specific PIV slot. +enum PIVSlotKey { + // PIV slot key not specified. + PIV_SLOT_KEY_UNSPECIFIED = 0; + // PIV slot key 9a. This is the default slot for pin_policy=never, touch_policy=never. + PIV_SLOT_KEY_9A = 1; + // PIV slot key 9c. This is the default slot for pin_policy=never, touch_policy=cached. + PIV_SLOT_KEY_9C = 2; + // PIV slot key 9d. This is the default slot for pin_policy=once, touch_policy=cached. + PIV_SLOT_KEY_9D = 3; + // PIV slot key 9e. This is the default slot for pin_policy=once, touch_policy=never. + PIV_SLOT_KEY_9E = 4; +} + +// Hash refers to a specific hash function used during signing. +enum Hash { + HASH_UNSPECIFIED = 0; + HASH_NONE = 1; + HASH_SHA256 = 2; + HASH_SHA512 = 3; +} diff --git a/api/proto/teleport/legacy/types/types.proto b/api/proto/teleport/legacy/types/types.proto index cedd32731ae3d..5b2c384a98c29 100644 --- a/api/proto/teleport/legacy/types/types.proto +++ b/api/proto/teleport/legacy/types/types.proto @@ -2248,6 +2248,13 @@ message HardwareKey { // SerialNumberValidation holds settings for hardware key serial number validation. // By default, serial number validation is disabled. HardwareKeySerialNumberValidation SerialNumberValidation = 2 [(gogoproto.jsontag) = "serial_number_validation,omitempty"]; + + // PinCacheTTL is the amount of time in nanoseconds that Teleport clients + // will cache the user's PIV PIN when hardware key PIN policy is enabled. + int64 PinCacheTTL = 3 [ + (gogoproto.jsontag) = "pin_cache_ttl,omitempty", + (gogoproto.casttype) = "Duration" + ]; } message HardwareKeySerialNumberValidation { diff --git a/api/types/authentication.go b/api/types/authentication.go index 6237b99538484..fd199128301e4 100644 --- a/api/types/authentication.go +++ b/api/types/authentication.go @@ -33,6 +33,7 @@ import ( "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" "github.com/gravitational/teleport/api/utils/tlsutils" ) @@ -125,10 +126,12 @@ type AuthPreference interface { // GetHardwareKey returns the hardware key settings configured for the cluster. GetHardwareKey() (*HardwareKey, error) // GetPIVSlot returns the configured piv slot for the cluster. - GetPIVSlot() keys.PIVSlot + GetPIVSlot() hardwarekey.PIVSlotKeyString // GetHardwareKeySerialNumberValidation returns the cluster's hardware key // serial number validation settings. GetHardwareKeySerialNumberValidation() (*HardwareKeySerialNumberValidation, error) + // GetPIVPINCacheTTL returns the configured piv pin cache duration for the cluster. + GetPIVPINCacheTTL() time.Duration // GetDisconnectExpiredCert returns disconnect expired certificate setting GetDisconnectExpiredCert() bool @@ -437,9 +440,9 @@ func (c *AuthPreferenceV2) GetHardwareKey() (*HardwareKey, error) { } // GetPIVSlot returns the configured piv slot for the cluster. -func (c *AuthPreferenceV2) GetPIVSlot() keys.PIVSlot { +func (c *AuthPreferenceV2) GetPIVSlot() hardwarekey.PIVSlotKeyString { if hk, err := c.GetHardwareKey(); err == nil { - return keys.PIVSlot(hk.PIVSlot) + return hardwarekey.PIVSlotKeyString(hk.PIVSlot) } return "" } @@ -453,6 +456,14 @@ func (c *AuthPreferenceV2) GetHardwareKeySerialNumberValidation() (*HardwareKeyS return c.Spec.HardwareKey.SerialNumberValidation, nil } +// GetPIVPINCacheTTL returns the configured piv pin cache duration for the cluster. +func (c *AuthPreferenceV2) GetPIVPINCacheTTL() time.Duration { + if c.Spec.HardwareKey == nil { + return 0 + } + return time.Duration(c.Spec.HardwareKey.PinCacheTTL) +} + // GetDisconnectExpiredCert returns disconnect expired certificate setting func (c *AuthPreferenceV2) GetDisconnectExpiredCert() bool { return c.Spec.DisconnectExpiredCert.Value @@ -705,7 +716,7 @@ func (c *AuthPreferenceV2) CheckAndSetDefaults() error { c.CheckSetPIVSlot() if hk, err := c.GetHardwareKey(); err == nil && hk.PIVSlot != "" { - if err := keys.PIVSlot(hk.PIVSlot).Validate(); err != nil { + if err := hardwarekey.PIVSlotKeyString(hk.PIVSlot).Validate(); err != nil { return trace.Wrap(err) } } @@ -731,6 +742,10 @@ func (c *AuthPreferenceV2) CheckAndSetDefaults() error { c.Spec.Okta = &OktaOptions{} } + if c.GetPIVPINCacheTTL() > constants.MaxPIVPINCacheTTL { + return trace.BadParameter("piv_pin_cache_ttl cannot be larger than %s", constants.MaxPIVPINCacheTTL) + } + return nil } diff --git a/api/types/types.pb.go b/api/types/types.pb.go index 222ddb30b2242..aee88d02085fe 100644 --- a/api/types/types.pb.go +++ b/api/types/types.pb.go @@ -6419,9 +6419,12 @@ type HardwareKey struct { // SerialNumberValidation holds settings for hardware key serial number validation. // By default, serial number validation is disabled. SerialNumberValidation *HardwareKeySerialNumberValidation `protobuf:"bytes,2,opt,name=SerialNumberValidation,proto3" json:"serial_number_validation,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + // PinCacheTTL is the amount of time in nanoseconds that Teleport clients + // will cache the user's PIV PIN when hardware key PIN policy is enabled. + PinCacheTTL Duration `protobuf:"varint,3,opt,name=PinCacheTTL,proto3,casttype=Duration" json:"pin_cache_ttl,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *HardwareKey) Reset() { *m = HardwareKey{} } @@ -20233,1767 +20236,1769 @@ func init() { func init() { proto.RegisterFile("teleport/legacy/types/types.proto", fileDescriptor_9198ee693835762e) } var fileDescriptor_9198ee693835762e = []byte{ - // 28152 bytes of a gzipped FileDescriptorProto + // 28182 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x6b, 0x90, 0x1c, 0x49, 0x7a, 0x18, 0xb6, 0xdd, 0x3d, 0x8f, 0x9e, 0x6f, 0x5e, 0x3d, 0x39, 0x78, 0x0c, 0xb0, 0x8b, 0x6d, 0x6c, 0xed, 0x2e, 0x16, 0xd8, 0x07, 0x70, 0x18, 0xdc, 0xe2, 0x6e, 0x6f, 0x5f, 0xd7, 0xd3, 0x3d, 0xc0, 0x34, 0x30, 0x98, 0x99, 0xab, 0x1e, 0x00, 0xb7, 0xbc, 0x47, 0x5d, 0x4d, 0x57, 0xce, 0x4c, - 0xed, 0x74, 0x77, 0xf5, 0x55, 0x55, 0x63, 0x30, 0x77, 0x92, 0xc5, 0x87, 0x4e, 0x67, 0x86, 0xcc, - 0x97, 0x45, 0x9a, 0x94, 0x83, 0x62, 0x30, 0x68, 0x4b, 0xa6, 0xe5, 0x10, 0xc3, 0x26, 0xad, 0xb0, - 0x14, 0x0c, 0x53, 0xa4, 0x83, 0x41, 0xd3, 0x54, 0x84, 0xc5, 0x08, 0xcb, 0xaf, 0x33, 0x63, 0x68, - 0x9a, 0xfa, 0xe1, 0x98, 0x08, 0x47, 0x50, 0x56, 0xd8, 0x0e, 0xaf, 0x83, 0x92, 0x23, 0xbf, 0xcc, - 0xac, 0xca, 0xac, 0xaa, 0xee, 0xe9, 0xd9, 0xc5, 0x4a, 0xc2, 0x86, 0xfe, 0x00, 0xd3, 0x5f, 0x7e, - 0xdf, 0x97, 0x95, 0xef, 0x2f, 0xbf, 0xfc, 0x1e, 0xf0, 0x42, 0x48, 0x5b, 0xb4, 0xeb, 0xf9, 0xe1, - 0xb5, 0x16, 0xdd, 0xb1, 0x9b, 0x07, 0xd7, 0xc2, 0x83, 0x2e, 0x0d, 0xf8, 0xbf, 0x57, 0xbb, 0xbe, - 0x17, 0x7a, 0x64, 0x14, 0x7f, 0x9c, 0x3f, 0xb5, 0xe3, 0xed, 0x78, 0x08, 0xb9, 0xc6, 0xfe, 0xe2, - 0x85, 0xe7, 0x9f, 0xdf, 0xf1, 0xbc, 0x9d, 0x16, 0xbd, 0x86, 0xbf, 0xb6, 0x7a, 0xdb, 0xd7, 0x9c, - 0x9e, 0x6f, 0x87, 0xae, 0xd7, 0x11, 0xe5, 0xe5, 0x64, 0x79, 0xe8, 0xb6, 0x69, 0x10, 0xda, 0xed, - 0xae, 0x40, 0xb8, 0x12, 0x7d, 0x80, 0x1d, 0x86, 0xac, 0x84, 0x11, 0x5f, 0x7b, 0x74, 0x5d, 0xfd, - 0x29, 0x50, 0x6f, 0xf6, 0xf9, 0x56, 0xbf, 0x17, 0x84, 0xd4, 0xb1, 0x1c, 0xfa, 0xc8, 0x6d, 0x52, - 0xcb, 0xa7, 0xdf, 0xee, 0xb9, 0x3e, 0x6d, 0xd3, 0x4e, 0x28, 0xe8, 0xde, 0xc8, 0xa6, 0xdb, 0xf7, - 0xed, 0x6e, 0x97, 0xfa, 0xf1, 0x1f, 0x1c, 0xdd, 0xf8, 0xa5, 0x02, 0x4c, 0xdc, 0xa5, 0xb4, 0x5b, - 0x69, 0xb9, 0x8f, 0x28, 0x79, 0x11, 0x46, 0xd6, 0xec, 0x36, 0x5d, 0xc8, 0x5d, 0xcc, 0x5d, 0x9e, - 0x58, 0x9a, 0x3d, 0x3a, 0x2c, 0x4f, 0x06, 0xd4, 0x7f, 0x44, 0x7d, 0xab, 0x63, 0xb7, 0xa9, 0x89, - 0x85, 0xe4, 0x35, 0x98, 0x60, 0xff, 0x07, 0x5d, 0xbb, 0x49, 0x17, 0xf2, 0x88, 0x39, 0x7d, 0x74, - 0x58, 0x9e, 0xe8, 0x48, 0xa0, 0x19, 0x97, 0x93, 0x3a, 0x8c, 0x2f, 0x3f, 0xee, 0xba, 0x3e, 0x0d, - 0x16, 0x46, 0x2e, 0xe6, 0x2e, 0x4f, 0x2e, 0x9e, 0xbf, 0xca, 0x3b, 0xe9, 0xaa, 0xec, 0xa4, 0xab, - 0x9b, 0xb2, 0x93, 0x96, 0xe6, 0x7f, 0xff, 0xb0, 0xfc, 0xcc, 0xd1, 0x61, 0x79, 0x9c, 0x72, 0x92, - 0x9f, 0xfe, 0xe3, 0x72, 0xce, 0x94, 0xf4, 0xe4, 0x1d, 0x18, 0xd9, 0x3c, 0xe8, 0xd2, 0x85, 0x89, - 0x8b, 0xb9, 0xcb, 0x33, 0x8b, 0xcf, 0x5f, 0xe5, 0xc3, 0x16, 0x7d, 0x7c, 0xfc, 0x17, 0xc3, 0x5a, - 0x2a, 0x1e, 0x1d, 0x96, 0x47, 0x18, 0x8a, 0x89, 0x54, 0xe4, 0x0d, 0x18, 0x5b, 0xf1, 0x82, 0xb0, - 0x5e, 0x5b, 0x00, 0xfc, 0xe4, 0xd3, 0x47, 0x87, 0xe5, 0xb9, 0x5d, 0x2f, 0x08, 0x2d, 0xd7, 0x79, - 0xdd, 0x6b, 0xbb, 0x21, 0x6d, 0x77, 0xc3, 0x03, 0x53, 0x20, 0x19, 0x8f, 0x61, 0x5a, 0xe3, 0x47, - 0x26, 0x61, 0xfc, 0xfe, 0xda, 0xdd, 0xb5, 0xf5, 0x87, 0x6b, 0xa5, 0x67, 0x48, 0x11, 0x46, 0xd6, - 0xd6, 0x6b, 0xcb, 0xa5, 0x1c, 0x19, 0x87, 0x42, 0x65, 0x63, 0xa3, 0x94, 0x27, 0x53, 0x50, 0xac, - 0x55, 0x36, 0x2b, 0x4b, 0x95, 0xc6, 0x72, 0xa9, 0x40, 0xe6, 0x61, 0xf6, 0x61, 0x7d, 0xad, 0xb6, - 0xfe, 0xb0, 0x61, 0xd5, 0x96, 0x1b, 0x77, 0x37, 0xd7, 0x37, 0x4a, 0x23, 0x64, 0x06, 0xe0, 0xee, - 0xfd, 0xa5, 0x65, 0x73, 0x6d, 0x79, 0x73, 0xb9, 0x51, 0x1a, 0x25, 0xa7, 0xa0, 0x24, 0x49, 0xac, - 0xc6, 0xb2, 0xf9, 0xa0, 0x5e, 0x5d, 0x2e, 0x8d, 0xdd, 0x19, 0x29, 0x16, 0x4a, 0x23, 0xe6, 0xf8, - 0x2a, 0xb5, 0x03, 0x5a, 0xaf, 0x19, 0xff, 0x41, 0x01, 0x8a, 0xf7, 0x68, 0x68, 0x3b, 0x76, 0x68, - 0x93, 0xe7, 0xb4, 0xf1, 0xc1, 0x26, 0x2a, 0x03, 0xf3, 0x62, 0x7a, 0x60, 0x46, 0x8f, 0x0e, 0xcb, - 0xb9, 0x37, 0xd4, 0x01, 0x79, 0x1b, 0x26, 0x6b, 0x34, 0x68, 0xfa, 0x6e, 0x97, 0x4d, 0xb6, 0x85, - 0x02, 0xa2, 0x9d, 0x3b, 0x3a, 0x2c, 0x9f, 0x76, 0x62, 0xb0, 0xd2, 0x21, 0x2a, 0x36, 0xa9, 0xc3, - 0xd8, 0xaa, 0xbd, 0x45, 0x5b, 0xc1, 0xc2, 0xe8, 0xc5, 0xc2, 0xe5, 0xc9, 0xc5, 0x67, 0xc5, 0x20, - 0xc8, 0x0f, 0xbc, 0xca, 0x4b, 0x97, 0x3b, 0xa1, 0x7f, 0xb0, 0x74, 0xea, 0xe8, 0xb0, 0x5c, 0x6a, - 0x21, 0x40, 0xed, 0x60, 0x8e, 0x42, 0x1a, 0xf1, 0xc4, 0x18, 0x3b, 0x76, 0x62, 0x5c, 0xf8, 0xfd, - 0xc3, 0x72, 0x8e, 0x0d, 0x98, 0x98, 0x18, 0x31, 0x3f, 0x7d, 0x8a, 0x2c, 0x42, 0xd1, 0xa4, 0x8f, - 0xdc, 0x80, 0xb5, 0xac, 0x88, 0x2d, 0x3b, 0x73, 0x74, 0x58, 0x26, 0xbe, 0x80, 0x29, 0x9f, 0x11, - 0xe1, 0x9d, 0x7f, 0x0b, 0x26, 0x95, 0xaf, 0x26, 0x25, 0x28, 0xec, 0xd1, 0x03, 0xde, 0xc3, 0x26, - 0xfb, 0x93, 0x9c, 0x82, 0xd1, 0x47, 0x76, 0xab, 0x27, 0xba, 0xd4, 0xe4, 0x3f, 0xbe, 0x94, 0xff, - 0x62, 0xee, 0xce, 0x48, 0x71, 0xbc, 0x54, 0x34, 0xf3, 0xf5, 0x9a, 0xf1, 0xef, 0x8e, 0x40, 0xd1, - 0xf4, 0xf8, 0x02, 0x26, 0x57, 0x60, 0xb4, 0x11, 0xda, 0xa1, 0x1c, 0xa6, 0xf9, 0xa3, 0xc3, 0xf2, - 0x2c, 0x5b, 0xdc, 0x54, 0xa9, 0x9f, 0x63, 0x30, 0xd4, 0x8d, 0x5d, 0x3b, 0x90, 0xc3, 0x85, 0xa8, - 0x5d, 0x06, 0x50, 0x51, 0x11, 0x83, 0x5c, 0x82, 0x91, 0x7b, 0x9e, 0x43, 0xc5, 0x88, 0x91, 0xa3, - 0xc3, 0xf2, 0x4c, 0xdb, 0x73, 0x54, 0x44, 0x2c, 0x27, 0xaf, 0xc3, 0x44, 0xb5, 0xe7, 0xfb, 0xb4, - 0xc3, 0xe6, 0xfa, 0x08, 0x22, 0xcf, 0x1c, 0x1d, 0x96, 0xa1, 0xc9, 0x81, 0x96, 0xeb, 0x98, 0x31, - 0x02, 0x1b, 0x86, 0x46, 0x68, 0xfb, 0x21, 0x75, 0x16, 0x46, 0x87, 0x1a, 0x06, 0xb6, 0x3e, 0xe7, - 0x02, 0x4e, 0x92, 0x1c, 0x06, 0xc1, 0x89, 0xac, 0xc0, 0xe4, 0x6d, 0xdf, 0x6e, 0xd2, 0x0d, 0xea, - 0xbb, 0x9e, 0x83, 0xe3, 0x5b, 0x58, 0xba, 0x74, 0x74, 0x58, 0x3e, 0xb3, 0xc3, 0xc0, 0x56, 0x17, - 0xe1, 0x31, 0xf5, 0x47, 0x87, 0xe5, 0x62, 0x4d, 0x6c, 0xa5, 0xa6, 0x4a, 0x4a, 0xbe, 0xc5, 0x06, - 0x27, 0x08, 0xb1, 0x6b, 0xa9, 0xb3, 0x30, 0x7e, 0xec, 0x27, 0x1a, 0xe2, 0x13, 0xcf, 0xb4, 0xec, - 0x20, 0xb4, 0x7c, 0x4e, 0x97, 0xf8, 0x4e, 0x95, 0x25, 0x59, 0x87, 0x62, 0xa3, 0xb9, 0x4b, 0x9d, - 0x5e, 0x8b, 0xe2, 0x94, 0x99, 0x5c, 0x3c, 0x2b, 0x26, 0xb5, 0x1c, 0x4f, 0x59, 0xbc, 0x74, 0x5e, - 0xf0, 0x26, 0x81, 0x80, 0xa8, 0xf3, 0x49, 0x62, 0x7d, 0xa9, 0xf8, 0x0b, 0xbf, 0x5c, 0x7e, 0xe6, - 0x87, 0xff, 0xe8, 0xe2, 0x33, 0xc6, 0x7f, 0x91, 0x87, 0x52, 0x92, 0x09, 0xd9, 0x86, 0xe9, 0xfb, - 0x5d, 0xc7, 0x0e, 0x69, 0xb5, 0xe5, 0xd2, 0x4e, 0x18, 0xe0, 0x24, 0x19, 0xdc, 0xa6, 0x97, 0x44, - 0xbd, 0x0b, 0x3d, 0x24, 0xb4, 0x9a, 0x9c, 0x32, 0xd1, 0x2a, 0x9d, 0x6d, 0x5c, 0x4f, 0x03, 0x37, - 0xf0, 0x00, 0x67, 0xd8, 0xc9, 0xea, 0xe1, 0x5b, 0x7f, 0x9f, 0x7a, 0x04, 0x5b, 0x31, 0x81, 0x3a, - 0xce, 0xd6, 0x01, 0xce, 0xcc, 0xe1, 0x27, 0x10, 0x23, 0xc9, 0x98, 0x40, 0x0c, 0x6c, 0xfc, 0x93, - 0x1c, 0xcc, 0x98, 0x34, 0xf0, 0x7a, 0x7e, 0x93, 0xae, 0x50, 0xdb, 0xa1, 0x3e, 0x9b, 0xfe, 0x77, - 0xdd, 0x8e, 0x23, 0xd6, 0x14, 0x4e, 0xff, 0x3d, 0xb7, 0xa3, 0x6e, 0xdd, 0x58, 0x4e, 0x3e, 0x07, - 0xe3, 0x8d, 0xde, 0x16, 0xa2, 0xe6, 0xe3, 0x1d, 0x20, 0xe8, 0x6d, 0x59, 0x09, 0x74, 0x89, 0x46, - 0xae, 0xc1, 0xf8, 0x03, 0xea, 0x07, 0xf1, 0x6e, 0x88, 0x47, 0xc3, 0x23, 0x0e, 0x52, 0x09, 0x04, - 0x16, 0xb9, 0x1d, 0xef, 0xc8, 0xe2, 0x50, 0x9b, 0x4d, 0xec, 0x83, 0xf1, 0x54, 0x69, 0x0b, 0x88, - 0x3a, 0x55, 0x24, 0x96, 0xf1, 0x33, 0x79, 0x28, 0xd5, 0xec, 0xd0, 0xde, 0xb2, 0x03, 0xd1, 0x9f, - 0x0f, 0x6e, 0xb0, 0x3d, 0x5e, 0x69, 0x28, 0xee, 0xf1, 0xec, 0xcb, 0x3f, 0x76, 0xf3, 0x5e, 0x4e, - 0x36, 0x6f, 0x92, 0x9d, 0xb0, 0xa2, 0x79, 0x71, 0xa3, 0xde, 0x3d, 0xbe, 0x51, 0x25, 0xd1, 0xa8, - 0xa2, 0x6c, 0x54, 0xdc, 0x14, 0xf2, 0x2e, 0x8c, 0x34, 0xba, 0xb4, 0x29, 0x36, 0x11, 0x79, 0x2e, - 0xe8, 0x8d, 0x63, 0x08, 0x0f, 0x6e, 0x2c, 0x4d, 0x09, 0x36, 0x23, 0x41, 0x97, 0x36, 0x4d, 0x24, - 0x53, 0x16, 0xcd, 0xdf, 0x2b, 0xc0, 0xa9, 0x2c, 0x32, 0xb5, 0x1d, 0x63, 0x03, 0xda, 0x71, 0x19, - 0x8a, 0xec, 0x08, 0x67, 0xc7, 0x22, 0x6e, 0x17, 0x13, 0x4b, 0x53, 0xec, 0x93, 0x77, 0x05, 0xcc, - 0x8c, 0x4a, 0xc9, 0x8b, 0x91, 0x44, 0x50, 0x8c, 0xf9, 0x09, 0x89, 0x40, 0xca, 0x01, 0x6c, 0xac, - 0xe5, 0x12, 0x46, 0xc1, 0x21, 0xee, 0x16, 0x09, 0x8e, 0xc7, 0xda, 0x17, 0x10, 0xed, 0x98, 0x91, - 0x87, 0xc2, 0x32, 0x14, 0x65, 0xb3, 0x16, 0xa6, 0x90, 0xd1, 0x5c, 0xa2, 0x93, 0x1e, 0xdc, 0xe0, - 0x83, 0xe9, 0x88, 0xdf, 0x2a, 0x1b, 0x89, 0x43, 0x6e, 0x40, 0x71, 0xc3, 0xf7, 0x1e, 0x1f, 0xd4, - 0x6b, 0xc1, 0xc2, 0xf4, 0xc5, 0xc2, 0xe5, 0x89, 0xa5, 0xb3, 0x47, 0x87, 0xe5, 0xf9, 0x2e, 0x83, - 0x59, 0xae, 0xa3, 0x9e, 0xb4, 0x11, 0xe2, 0x9d, 0x91, 0x62, 0xae, 0x94, 0xbf, 0x33, 0x52, 0xcc, - 0x97, 0x0a, 0x5c, 0xbc, 0xb8, 0x33, 0x52, 0x1c, 0x29, 0x8d, 0xde, 0x19, 0x29, 0x8e, 0xa2, 0xc0, - 0x31, 0x51, 0x82, 0x3b, 0x23, 0xc5, 0xc9, 0xd2, 0x94, 0x76, 0xda, 0x23, 0x83, 0xd0, 0x6b, 0x7a, - 0x2d, 0xb3, 0x70, 0xdf, 0xac, 0x9b, 0x63, 0xd5, 0x4a, 0x95, 0xfa, 0xa1, 0x59, 0xa8, 0x3c, 0x6c, - 0x98, 0xd3, 0xb5, 0x83, 0x8e, 0xdd, 0x76, 0x9b, 0xfc, 0xe8, 0x34, 0x0b, 0xb7, 0xab, 0x1b, 0x46, - 0x05, 0x66, 0xe2, 0xb6, 0xac, 0xba, 0x41, 0x48, 0xae, 0xc1, 0x84, 0x84, 0xb0, 0x8d, 0xae, 0x90, - 0xd9, 0x6a, 0x33, 0xc6, 0x31, 0x7e, 0x2f, 0x0f, 0x10, 0x97, 0x3c, 0xa5, 0x6b, 0xe1, 0x0b, 0xda, - 0x5a, 0x38, 0x9d, 0x5c, 0x0b, 0x7d, 0x57, 0x01, 0x79, 0x1f, 0xc6, 0x98, 0x58, 0xd0, 0x93, 0x22, - 0xd1, 0xd9, 0x24, 0x29, 0x16, 0x3e, 0xb8, 0xb1, 0x34, 0x23, 0x88, 0xc7, 0x02, 0x84, 0x98, 0x82, - 0x4c, 0x59, 0x46, 0xbf, 0x34, 0x1e, 0x0f, 0x86, 0x58, 0x40, 0x97, 0x21, 0x1a, 0x50, 0xd1, 0xa1, - 0xb8, 0x32, 0xba, 0x72, 0x90, 0xa3, 0x52, 0x72, 0x0e, 0xd8, 0x80, 0x8b, 0x4e, 0x1d, 0x3f, 0x3a, - 0x2c, 0x17, 0x7a, 0xbe, 0x8b, 0x93, 0x80, 0x5c, 0x03, 0x31, 0x0d, 0x44, 0x07, 0xb2, 0xd9, 0x37, - 0xd7, 0xb4, 0xad, 0x26, 0xf5, 0xc3, 0xb8, 0xc7, 0x17, 0x72, 0x72, 0xb6, 0x90, 0x2e, 0xe8, 0x53, - 0x65, 0x61, 0x04, 0xa7, 0xc1, 0xe5, 0xcc, 0x5e, 0xb9, 0xaa, 0xa1, 0x72, 0x31, 0xf2, 0xa2, 0x3c, - 0x95, 0x1c, 0x5e, 0x66, 0xa5, 0x44, 0x4a, 0xbd, 0x02, 0x72, 0x03, 0xd8, 0x0c, 0x15, 0xbd, 0x0f, - 0xa2, 0x9e, 0xca, 0xc3, 0xc6, 0xd2, 0x69, 0xc1, 0x69, 0xda, 0xde, 0x57, 0xc9, 0x19, 0x36, 0x79, - 0x1b, 0xd8, 0x14, 0x16, 0xfd, 0x4e, 0x04, 0xd1, 0xed, 0xea, 0x46, 0xb5, 0xe5, 0xf5, 0x9c, 0xc6, - 0x57, 0x56, 0x63, 0xe2, 0x9d, 0x66, 0x57, 0x25, 0xbe, 0x5d, 0xdd, 0x20, 0x6f, 0xc3, 0x68, 0xe5, - 0x3b, 0x3d, 0x9f, 0x0a, 0xf9, 0x64, 0x4a, 0xd6, 0xc9, 0x60, 0x4b, 0x67, 0x05, 0xe1, 0xac, 0xcd, - 0x7e, 0xaa, 0x72, 0x1d, 0x96, 0xb3, 0x9a, 0x37, 0x57, 0x1b, 0x42, 0xf6, 0x20, 0x89, 0x6e, 0xd9, - 0x5c, 0x55, 0x3e, 0x3b, 0xd4, 0x5a, 0xcd, 0xa8, 0xc8, 0x35, 0xc8, 0x57, 0x6a, 0x78, 0x23, 0x9a, - 0x5c, 0x9c, 0x90, 0xd5, 0xd6, 0x96, 0x4e, 0x09, 0x92, 0x29, 0x5b, 0x5d, 0x06, 0xf9, 0x4a, 0x8d, - 0x2c, 0xc1, 0xe8, 0xbd, 0x83, 0xc6, 0x57, 0x56, 0xc5, 0x66, 0x36, 0x2f, 0xe7, 0x35, 0x83, 0xad, - 0xe3, 0xb2, 0x0f, 0xe2, 0x2f, 0x6e, 0x1f, 0x04, 0xdf, 0x6e, 0xa9, 0x5f, 0x8c, 0x68, 0x64, 0x03, - 0x26, 0x2a, 0x4e, 0xdb, 0xed, 0xdc, 0x0f, 0xa8, 0xbf, 0x30, 0x89, 0x7c, 0x16, 0x12, 0xdf, 0x1d, - 0x95, 0x2f, 0x2d, 0x1c, 0x1d, 0x96, 0x4f, 0xd9, 0xec, 0xa7, 0xd5, 0x0b, 0xa8, 0xaf, 0x70, 0x8b, - 0x99, 0x90, 0x0d, 0x80, 0x7b, 0x5e, 0x67, 0xc7, 0xab, 0x84, 0x2d, 0x3b, 0x48, 0x6c, 0x8f, 0x71, - 0x41, 0x24, 0x3e, 0x9c, 0x6e, 0x33, 0x98, 0x65, 0x33, 0xa0, 0xc2, 0x50, 0xe1, 0x41, 0x6e, 0xc1, - 0xd8, 0xba, 0x6f, 0x37, 0x5b, 0x74, 0x61, 0x1a, 0xb9, 0x9d, 0x12, 0xdc, 0x38, 0x50, 0xb6, 0x74, - 0x41, 0x30, 0x2c, 0x79, 0x08, 0x56, 0xaf, 0x29, 0x1c, 0xf1, 0xfc, 0x43, 0x20, 0xe9, 0x39, 0x99, - 0x71, 0x49, 0x78, 0x4d, 0xbd, 0x24, 0xc4, 0x8b, 0xbe, 0xea, 0xb5, 0xdb, 0x76, 0xc7, 0x41, 0xda, - 0x07, 0x8b, 0xca, 0xdd, 0xc1, 0xf8, 0x36, 0xcc, 0xa5, 0x3a, 0xeb, 0x98, 0xfb, 0xdd, 0x7b, 0x30, - 0x5b, 0xa3, 0xdb, 0x76, 0xaf, 0x15, 0x46, 0x27, 0x09, 0x5f, 0xa2, 0x78, 0xd3, 0x72, 0x78, 0x91, - 0x25, 0x8f, 0x0f, 0x33, 0x89, 0x6c, 0xbc, 0x0b, 0xd3, 0x5a, 0xf3, 0xd9, 0x55, 0xa1, 0xd2, 0x73, - 0xdc, 0x10, 0x07, 0x32, 0x17, 0x5f, 0x15, 0x6c, 0x06, 0xc4, 0xe1, 0x32, 0x63, 0x04, 0xe3, 0x3f, - 0x54, 0xa5, 0x15, 0xb1, 0x13, 0xb1, 0x6b, 0xb5, 0xd8, 0x0f, 0x72, 0xb1, 0xec, 0x94, 0xda, 0x0f, - 0xa2, 0xdd, 0xe0, 0x0a, 0x5f, 0x9b, 0xf9, 0xd4, 0xda, 0x9c, 0x14, 0x23, 0x51, 0xb0, 0xf7, 0x03, - 0xbe, 0x22, 0xa3, 0x99, 0x5a, 0xf8, 0xf8, 0x33, 0xf5, 0x7d, 0x98, 0xba, 0x67, 0x77, 0xec, 0x1d, - 0xea, 0xb0, 0x16, 0xf0, 0xbd, 0x67, 0x62, 0xe9, 0xd9, 0xa3, 0xc3, 0xf2, 0xd9, 0x36, 0x87, 0x63, - 0x2b, 0xd5, 0x49, 0xa4, 0x11, 0x90, 0xeb, 0x72, 0x65, 0x8f, 0x66, 0xac, 0xec, 0x69, 0x51, 0xfb, - 0x28, 0xae, 0x6c, 0xb1, 0x9e, 0x8d, 0xdf, 0x9e, 0xc0, 0x36, 0x92, 0xd7, 0x61, 0xcc, 0xa4, 0x3b, - 0xec, 0xa8, 0xc9, 0xc5, 0x83, 0xe4, 0x23, 0x44, 0xed, 0x18, 0x8e, 0x83, 0x72, 0x06, 0x75, 0x82, - 0x5d, 0x77, 0x3b, 0x14, 0xbd, 0x13, 0xc9, 0x19, 0x02, 0xac, 0xc8, 0x19, 0x02, 0xa2, 0x5f, 0x67, - 0x39, 0x8c, 0xed, 0x7e, 0x66, 0xad, 0x21, 0x3a, 0x4d, 0xf6, 0xb0, 0x59, 0x53, 0xb6, 0x11, 0x5f, - 0x93, 0x12, 0x18, 0x36, 0xb9, 0x09, 0x13, 0x95, 0x66, 0xd3, 0xeb, 0x29, 0x77, 0x46, 0xbe, 0x6e, - 0x39, 0x50, 0x57, 0x91, 0xc4, 0xa8, 0xa4, 0x01, 0x93, 0xcb, 0xec, 0xa2, 0xe5, 0x56, 0xed, 0xe6, - 0xae, 0xec, 0x24, 0xb9, 0x87, 0x29, 0x25, 0xf1, 0xca, 0xa5, 0x08, 0x6c, 0x32, 0xa0, 0xaa, 0x64, - 0x50, 0x70, 0xc9, 0x26, 0x4c, 0x36, 0x68, 0xd3, 0xa7, 0x61, 0x23, 0xf4, 0x7c, 0x9a, 0xd8, 0x92, - 0x95, 0x92, 0xa5, 0xe7, 0xe5, 0x5d, 0x2f, 0x40, 0xa0, 0x15, 0x30, 0xa8, 0xca, 0x55, 0x41, 0xe6, - 0x42, 0x7b, 0xdb, 0xf3, 0x0f, 0x6a, 0x4b, 0x62, 0x9b, 0x8e, 0xcf, 0x74, 0x0e, 0x56, 0x85, 0x76, - 0x06, 0x71, 0xb6, 0x74, 0xa1, 0x9d, 0x63, 0xe1, 0x48, 0xd5, 0x1a, 0x28, 0x5b, 0x89, 0x4d, 0x7b, - 0x36, 0xee, 0x65, 0x04, 0x2b, 0x23, 0xe5, 0x04, 0x28, 0x99, 0x69, 0x23, 0x25, 0xb0, 0x48, 0x17, - 0x88, 0x1c, 0x35, 0x2e, 0xe8, 0xb6, 0x68, 0x10, 0x88, 0xbd, 0xfc, 0x5c, 0x62, 0xf0, 0x63, 0x84, - 0xa5, 0x97, 0x05, 0xf3, 0x0b, 0x72, 0x1a, 0x88, 0x7b, 0x1a, 0x2b, 0x54, 0xea, 0xc9, 0xe0, 0x4d, - 0xde, 0x02, 0x58, 0x7e, 0x1c, 0x52, 0xbf, 0x63, 0xb7, 0x22, 0x3d, 0x18, 0xaa, 0x7e, 0xa8, 0x80, - 0xea, 0x03, 0xad, 0x20, 0x93, 0x2a, 0x4c, 0x57, 0x82, 0xa0, 0xd7, 0xa6, 0xa6, 0xd7, 0xa2, 0x15, - 0x73, 0x0d, 0xf7, 0xfd, 0x89, 0xa5, 0x0b, 0x47, 0x87, 0xe5, 0x73, 0x36, 0x16, 0x58, 0xbe, 0xd7, - 0xa2, 0x96, 0xed, 0xab, 0xb3, 0x5b, 0xa7, 0x21, 0xeb, 0x00, 0xeb, 0x5d, 0xda, 0x69, 0x50, 0xdb, - 0x6f, 0xee, 0x26, 0xb6, 0xf9, 0xb8, 0x60, 0xe9, 0x39, 0xd1, 0xc2, 0x53, 0x5e, 0x97, 0x76, 0x02, - 0x84, 0xa9, 0x5f, 0x15, 0x63, 0x92, 0x87, 0x30, 0x5b, 0xaf, 0xdc, 0xdb, 0xf0, 0x5a, 0x6e, 0xf3, - 0x40, 0x48, 0x4e, 0x33, 0xa8, 0x1d, 0x3c, 0x23, 0xb8, 0x26, 0x4a, 0xf9, 0xf6, 0xe4, 0xda, 0x6d, - 0xab, 0x8b, 0x50, 0x4b, 0xc8, 0x4f, 0x49, 0x2e, 0xe4, 0x03, 0x36, 0x07, 0x03, 0x26, 0x0c, 0x6e, - 0xda, 0x3b, 0xc1, 0xc2, 0xac, 0xa6, 0xed, 0xaa, 0x3c, 0x6c, 0x5c, 0x55, 0x4a, 0xb9, 0x98, 0x72, - 0x9e, 0x4f, 0x44, 0x84, 0x5a, 0xa1, 0xbd, 0x13, 0xe8, 0x13, 0x31, 0xc2, 0x26, 0x77, 0x00, 0x6a, - 0x5e, 0xb3, 0xd7, 0xa6, 0x9d, 0xb0, 0xb6, 0xb4, 0x50, 0xd2, 0xaf, 0x02, 0x51, 0x41, 0xbc, 0xb5, - 0x39, 0x5e, 0x53, 0x9b, 0x89, 0x0a, 0xf5, 0xf9, 0xf7, 0xa0, 0x94, 0xfc, 0x90, 0x13, 0x2a, 0xb0, - 0xa6, 0x4b, 0x33, 0x4a, 0xeb, 0x97, 0x1f, 0xbb, 0x41, 0x18, 0x18, 0xdf, 0xd5, 0x56, 0x20, 0xdb, - 0x1d, 0xee, 0xd2, 0x83, 0x0d, 0x9f, 0x6e, 0xbb, 0x8f, 0xc5, 0x66, 0x86, 0xbb, 0xc3, 0x1e, 0x3d, - 0xb0, 0xba, 0x08, 0x55, 0x77, 0x87, 0x08, 0x95, 0x7c, 0x1e, 0x8a, 0x77, 0xef, 0x35, 0xee, 0xd2, - 0x83, 0x7a, 0x4d, 0x1c, 0x54, 0x9c, 0xac, 0x1d, 0x58, 0x8c, 0x54, 0x9b, 0x6b, 0x11, 0xa6, 0xb1, - 0x14, 0xef, 0x84, 0xac, 0xe6, 0x6a, 0xab, 0x17, 0x84, 0xd4, 0xaf, 0xd7, 0xd4, 0x9a, 0x9b, 0x1c, - 0x98, 0xd8, 0x97, 0x22, 0x54, 0xe3, 0x5f, 0xe4, 0x71, 0x17, 0x64, 0x13, 0xbe, 0xde, 0x09, 0x42, - 0xbb, 0xd3, 0xa4, 0x11, 0x03, 0x9c, 0xf0, 0xae, 0x80, 0x26, 0x26, 0x7c, 0x8c, 0xac, 0x57, 0x9d, - 0x1f, 0xba, 0x6a, 0x56, 0xa5, 0xd4, 0x5c, 0xd4, 0x6b, 0xaa, 0x7a, 0xd5, 0x17, 0xd0, 0x44, 0x95, - 0x31, 0x32, 0xb9, 0x04, 0xe3, 0xf5, 0xca, 0xbd, 0x4a, 0x2f, 0xdc, 0xc5, 0x3d, 0xb8, 0xc8, 0xe5, - 0x73, 0x36, 0x5b, 0xed, 0x5e, 0xb8, 0x6b, 0xca, 0x42, 0x72, 0x0d, 0xef, 0x3d, 0x1d, 0x1a, 0x72, - 0x35, 0xac, 0x38, 0x74, 0x03, 0x0e, 0x4a, 0x5c, 0x7b, 0x18, 0x88, 0xbc, 0x0a, 0xa3, 0x0f, 0x36, - 0xaa, 0xf5, 0x9a, 0xb8, 0x38, 0xe3, 0x49, 0xf4, 0xa8, 0xdb, 0xd4, 0xbf, 0x84, 0xa3, 0x90, 0x65, - 0x98, 0x69, 0xd0, 0x66, 0xcf, 0x77, 0xc3, 0x83, 0xdb, 0xbe, 0xd7, 0xeb, 0x06, 0x0b, 0xe3, 0x58, - 0x07, 0xae, 0xf4, 0x40, 0x94, 0x58, 0x3b, 0x58, 0xa4, 0x50, 0x27, 0x88, 0x8c, 0xdf, 0xc9, 0xc5, - 0xdb, 0x24, 0xb9, 0xa4, 0x89, 0x35, 0xa8, 0xbb, 0x61, 0x62, 0x8d, 0xaa, 0xbb, 0x41, 0x01, 0xc7, - 0x04, 0x52, 0xed, 0x05, 0xa1, 0xd7, 0x5e, 0xee, 0x38, 0x5d, 0xcf, 0xed, 0x84, 0x48, 0xc5, 0x3b, - 0xdf, 0x38, 0x3a, 0x2c, 0x3f, 0xdf, 0xc4, 0x52, 0x8b, 0x8a, 0x62, 0x2b, 0xc1, 0x25, 0x83, 0xfa, - 0x13, 0x8c, 0x87, 0xf1, 0x07, 0x79, 0xed, 0x78, 0x63, 0x9f, 0x67, 0xd2, 0x6e, 0xcb, 0x6d, 0xe2, - 0x8d, 0x1e, 0x1b, 0x1a, 0xcd, 0x2a, 0xfc, 0x3c, 0x3f, 0x2e, 0xe5, 0x3d, 0xa4, 0xf3, 0xce, 0xa0, - 0x26, 0x5f, 0x86, 0x29, 0x26, 0x69, 0x88, 0x9f, 0xc1, 0x42, 0x1e, 0x3b, 0xfb, 0x39, 0xd4, 0xc2, - 0x05, 0xd4, 0x8f, 0xd8, 0x68, 0x22, 0x8a, 0x4a, 0x41, 0x1c, 0x58, 0xd8, 0xf4, 0xed, 0x4e, 0xe0, - 0x86, 0xcb, 0x9d, 0xa6, 0x7f, 0x80, 0x92, 0xd1, 0x72, 0xc7, 0xde, 0x6a, 0x51, 0x07, 0x9b, 0x5b, - 0x5c, 0xba, 0x7c, 0x74, 0x58, 0x7e, 0x29, 0xe4, 0x38, 0x16, 0x8d, 0x90, 0x2c, 0xca, 0xb1, 0x14, - 0xce, 0x7d, 0x39, 0x31, 0x49, 0x4a, 0x76, 0x2b, 0x3e, 0xc2, 0x70, 0x21, 0x01, 0x25, 0xa9, 0x68, - 0x34, 0xd8, 0x1e, 0xa6, 0x7e, 0xa6, 0x4a, 0x60, 0xfc, 0xdf, 0xb9, 0xf8, 0x00, 0x26, 0xef, 0xc0, - 0xa4, 0x58, 0x31, 0xca, 0xbc, 0xc0, 0x1d, 0x54, 0x2e, 0xaf, 0xc4, 0xc8, 0xaa, 0xe8, 0xec, 0xde, - 0x5f, 0xa9, 0xae, 0x2a, 0x73, 0x03, 0xef, 0xfd, 0x76, 0xb3, 0x95, 0xa4, 0x92, 0x68, 0x6c, 0x12, - 0x6c, 0xae, 0x36, 0xf4, 0x5e, 0xc1, 0x49, 0x10, 0xb6, 0x82, 0x8c, 0x6e, 0x50, 0x90, 0x3f, 0x79, - 0xc3, 0xff, 0xc7, 0x5c, 0xd6, 0x39, 0x4f, 0x96, 0x60, 0xfa, 0xa1, 0xe7, 0xef, 0xe1, 0xf8, 0x2a, - 0x9d, 0x80, 0x23, 0xbf, 0x2f, 0x0b, 0x92, 0x0d, 0xd2, 0x49, 0xd4, 0x6f, 0x53, 0x7a, 0x43, 0xff, - 0xb6, 0x04, 0x07, 0x8d, 0x80, 0x8d, 0x43, 0xc4, 0x31, 0x5a, 0x1d, 0x38, 0x0e, 0xf1, 0x27, 0x68, - 0x53, 0x58, 0x45, 0x37, 0xfe, 0xcb, 0x9c, 0x7a, 0x9e, 0xb3, 0x4e, 0xae, 0x79, 0x6d, 0xdb, 0xed, - 0x28, 0xcd, 0xe1, 0x0f, 0x4b, 0x08, 0x4d, 0x7e, 0x89, 0x82, 0x4c, 0x6e, 0x40, 0x91, 0xff, 0x8a, - 0xf6, 0x5a, 0xd4, 0x6a, 0x09, 0x42, 0xfd, 0xa0, 0x90, 0x88, 0xa9, 0x91, 0x29, 0x9c, 0x74, 0x64, - 0x7e, 0x3b, 0xa7, 0x1e, 0xc5, 0x1f, 0xf7, 0xb0, 0x49, 0x1c, 0x32, 0xf9, 0x93, 0x1c, 0x32, 0x9f, - 0xb8, 0x09, 0x3f, 0x9c, 0x83, 0x49, 0x45, 0x4b, 0xc1, 0xda, 0xb0, 0xe1, 0x7b, 0x1f, 0xd2, 0x66, - 0xa8, 0xb7, 0xa1, 0xcb, 0x81, 0x89, 0x36, 0x44, 0xa8, 0x9f, 0xa0, 0x0d, 0xc6, 0x3f, 0xcd, 0x89, - 0x3b, 0xd2, 0xd0, 0xdb, 0xbc, 0xbe, 0x25, 0xe7, 0x4f, 0x72, 0x44, 0x7e, 0x19, 0x46, 0x4d, 0xea, - 0xb8, 0x81, 0xb8, 0xdf, 0xcc, 0xa9, 0xf7, 0x31, 0x2c, 0x88, 0xe5, 0x26, 0x9f, 0xfd, 0x54, 0xcf, - 0x37, 0x2c, 0x67, 0x82, 0x6c, 0x3d, 0xb8, 0xd5, 0xa2, 0x8f, 0x5d, 0xbe, 0x18, 0xc5, 0x51, 0x8b, - 0xc7, 0x9b, 0x1b, 0x58, 0xdb, 0xac, 0x44, 0x48, 0xd4, 0xea, 0xc2, 0xd3, 0x68, 0x8c, 0x0f, 0x00, - 0xe2, 0x2a, 0xc9, 0x5d, 0x28, 0x89, 0xd9, 0xe0, 0x76, 0x76, 0xb8, 0x20, 0x25, 0xfa, 0xa0, 0x7c, - 0x74, 0x58, 0x7e, 0xb6, 0x19, 0x95, 0x09, 0xa9, 0x53, 0xe1, 0x9b, 0x22, 0x34, 0xfe, 0xa3, 0x3c, - 0xe4, 0x2b, 0x38, 0x20, 0x77, 0xe9, 0x41, 0x68, 0x6f, 0xdd, 0x72, 0x5b, 0xda, 0x62, 0xda, 0x43, - 0xa8, 0xb5, 0xed, 0x6a, 0xea, 0x0a, 0x05, 0x99, 0x2d, 0xa6, 0xbb, 0xfe, 0xd6, 0x9b, 0x48, 0xa8, - 0x2c, 0xa6, 0x3d, 0x7f, 0xeb, 0xcd, 0x24, 0x59, 0x84, 0x48, 0x0c, 0x18, 0xe3, 0x0b, 0x4b, 0xcc, - 0x41, 0x38, 0x3a, 0x2c, 0x8f, 0xf1, 0xf5, 0x67, 0x8a, 0x12, 0x72, 0x0e, 0x0a, 0x8d, 0x8d, 0x35, - 0xb1, 0x03, 0xa2, 0x5a, 0x30, 0xe8, 0x76, 0x4c, 0x06, 0x63, 0x75, 0xae, 0xd6, 0x2a, 0x1b, 0xa8, - 0x08, 0x18, 0x8d, 0xeb, 0x6c, 0x39, 0x76, 0x37, 0xa9, 0x0a, 0x88, 0x10, 0xc9, 0xbb, 0x30, 0x79, - 0xb7, 0x56, 0x5d, 0xf1, 0x02, 0xbe, 0x7b, 0x8d, 0xc5, 0x93, 0x7f, 0xcf, 0x69, 0x5a, 0xa8, 0x89, - 0x4f, 0x1e, 0x03, 0x0a, 0xbe, 0xf1, 0xbd, 0x3c, 0x4c, 0x2a, 0x7a, 0x32, 0xf2, 0x79, 0xf1, 0x40, - 0x9a, 0xd3, 0x6e, 0x00, 0x0a, 0x06, 0x2b, 0xe5, 0x4a, 0x95, 0xb6, 0xe7, 0x50, 0xf1, 0x5c, 0x1a, - 0x2b, 0x30, 0xf2, 0xc3, 0x28, 0x30, 0xde, 0x02, 0xe0, 0x73, 0x00, 0x3f, 0x59, 0x11, 0x27, 0x14, - 0x3b, 0x09, 0x75, 0x5c, 0x62, 0x64, 0xf2, 0x00, 0xe6, 0x37, 0xfd, 0x5e, 0x10, 0x36, 0x0e, 0x82, - 0x90, 0xb6, 0x19, 0xb7, 0x0d, 0xcf, 0x6b, 0x89, 0xf9, 0xf7, 0xd2, 0xd1, 0x61, 0xf9, 0x22, 0x1a, - 0x77, 0x58, 0x01, 0x96, 0xe3, 0x07, 0x58, 0x5d, 0xcf, 0x53, 0xd5, 0x1a, 0x59, 0x0c, 0x0c, 0x13, - 0xa6, 0x54, 0xa5, 0x08, 0x3b, 0x59, 0xc4, 0x63, 0x92, 0x50, 0x75, 0x2b, 0x27, 0x8b, 0xf8, 0xca, - 0xf4, 0xe3, 0x96, 0x4e, 0x62, 0x7c, 0x5e, 0x55, 0xc8, 0x0d, 0xbb, 0xb0, 0x8d, 0x1f, 0xcd, 0xc5, - 0xdb, 0xc8, 0x83, 0xeb, 0xe4, 0x6d, 0x18, 0xe3, 0x8f, 0x77, 0xe2, 0x8d, 0xf3, 0x74, 0x74, 0xa9, - 0x55, 0x5f, 0xf6, 0xb8, 0x26, 0xfc, 0x0f, 0xf9, 0x03, 0xff, 0x33, 0xa6, 0x20, 0x89, 0x94, 0xe8, - 0xba, 0x3e, 0x4d, 0x72, 0x47, 0x75, 0xf1, 0xf5, 0x2c, 0x25, 0xba, 0xf1, 0xbb, 0x23, 0x30, 0xa3, - 0xa3, 0xa9, 0x2f, 0x7c, 0xb9, 0xa1, 0x5e, 0xf8, 0xbe, 0x0c, 0x45, 0xd6, 0x1f, 0x6e, 0x93, 0x4a, - 0x89, 0xec, 0x25, 0x7c, 0x5a, 0x10, 0x30, 0xed, 0xe5, 0x1a, 0xf8, 0x70, 0xb0, 0x3b, 0xae, 0x19, - 0x51, 0x91, 0x45, 0xe5, 0x19, 0xaa, 0x10, 0x0b, 0x29, 0xf2, 0x19, 0x4a, 0x5d, 0x0f, 0xd1, 0x83, - 0xd4, 0x1b, 0x30, 0xc6, 0xe4, 0xfb, 0x48, 0x05, 0x83, 0x5f, 0xc9, 0x44, 0xff, 0x84, 0x89, 0x0a, - 0x47, 0x22, 0x0f, 0xa1, 0xb8, 0x6a, 0x07, 0x61, 0x83, 0xd2, 0xce, 0x10, 0x6f, 0xf7, 0x65, 0xd1, - 0x55, 0xf3, 0xf8, 0x30, 0x1e, 0x50, 0xda, 0x49, 0x3c, 0xbe, 0x46, 0xcc, 0xc8, 0x37, 0x00, 0xaa, - 0x5e, 0x27, 0xf4, 0xbd, 0xd6, 0xaa, 0xb7, 0xb3, 0x30, 0x86, 0x77, 0xdf, 0xe7, 0x13, 0x03, 0x10, - 0x23, 0xf0, 0xeb, 0x6f, 0xa4, 0xe0, 0x69, 0xf2, 0x02, 0xab, 0xe5, 0xed, 0xa8, 0xeb, 0x20, 0xc6, - 0x27, 0xb7, 0xa0, 0x24, 0x15, 0x0b, 0xf7, 0xbb, 0x3b, 0x3e, 0x4e, 0x90, 0xf1, 0x58, 0xf2, 0xa0, - 0x8f, 0x43, 0xab, 0x27, 0xe0, 0xea, 0x4e, 0x99, 0xa4, 0x21, 0x5f, 0x87, 0xb3, 0x49, 0x98, 0x1c, - 0xe5, 0x62, 0x2c, 0x93, 0xab, 0xec, 0x32, 0xe6, 0x7d, 0x3f, 0x16, 0xc6, 0x47, 0x79, 0x38, 0xdb, - 0xa7, 0xb1, 0x6c, 0x3d, 0xe0, 0x71, 0xad, 0xac, 0x87, 0xc4, 0x29, 0xcd, 0x6d, 0x8e, 0x2e, 0x42, - 0x5e, 0x1c, 0x70, 0x23, 0x4b, 0xa5, 0xa3, 0xc3, 0xf2, 0x94, 0x36, 0x8e, 0xf9, 0x7a, 0x8d, 0xdc, - 0x81, 0x11, 0x36, 0x44, 0x43, 0x3c, 0x9d, 0x4b, 0x9d, 0xd2, 0x4c, 0xe8, 0xaa, 0xd3, 0x07, 0x87, - 0x0e, 0x79, 0x90, 0xcf, 0x43, 0x61, 0x73, 0x73, 0x15, 0xe7, 0x4e, 0x01, 0xdb, 0x3e, 0x1d, 0x86, - 0x2d, 0x6d, 0xaa, 0x4e, 0x33, 0xda, 0xab, 0x91, 0xa5, 0x05, 0x43, 0x27, 0x5f, 0x4d, 0x98, 0xf4, - 0xbc, 0x3a, 0x78, 0xa0, 0x87, 0xb7, 0xf0, 0xf9, 0x04, 0x86, 0x35, 0xc6, 0x2f, 0xe6, 0xe3, 0x35, - 0x7c, 0xcb, 0x6d, 0x85, 0xd4, 0x27, 0xe7, 0xf9, 0x92, 0x8c, 0x85, 0x33, 0x33, 0xfa, 0x4d, 0x16, - 0xe2, 0xf5, 0xcd, 0x59, 0x45, 0x0b, 0xf9, 0x55, 0x65, 0x21, 0x17, 0x70, 0x21, 0xcf, 0xf4, 0x5d, - 0xb2, 0xaf, 0x66, 0xcc, 0x4b, 0x5c, 0x88, 0x19, 0x73, 0xef, 0x25, 0x98, 0x5e, 0xf3, 0x96, 0x1f, - 0x87, 0x11, 0x22, 0x5b, 0x80, 0x45, 0x53, 0x07, 0x32, 0x8e, 0xeb, 0x2d, 0x87, 0xfa, 0x9b, 0xbb, - 0x76, 0x47, 0x7b, 0xbb, 0x36, 0x53, 0x70, 0x86, 0xbb, 0x46, 0xf7, 0x75, 0xdc, 0x71, 0x8e, 0x9b, - 0x84, 0x1b, 0x3f, 0x92, 0x97, 0x9d, 0xf1, 0x60, 0xf1, 0x29, 0x7d, 0x23, 0x7d, 0x53, 0x7b, 0x23, - 0x9d, 0x8f, 0xb4, 0xbb, 0xd1, 0x83, 0xff, 0xe2, 0x31, 0x76, 0x02, 0xff, 0xd3, 0x28, 0x4c, 0xa9, - 0xe8, 0xac, 0x1f, 0x2a, 0x8e, 0xe3, 0xab, 0xfd, 0x60, 0x3b, 0x8e, 0x6f, 0x22, 0x54, 0x33, 0x0b, - 0x28, 0x0c, 0x34, 0x0b, 0xf8, 0x26, 0x4c, 0x54, 0xdb, 0x8e, 0xf6, 0x58, 0x69, 0x64, 0x7c, 0xde, - 0xd5, 0x08, 0x89, 0xaf, 0x85, 0x48, 0x69, 0xd9, 0x6c, 0x3b, 0xe9, 0x27, 0xca, 0x98, 0xa5, 0x66, - 0x51, 0x30, 0xfa, 0x49, 0x2c, 0x0a, 0x6e, 0xc2, 0xc4, 0xfd, 0x80, 0x6e, 0xf6, 0x3a, 0x1d, 0xda, - 0xc2, 0x69, 0x55, 0xe4, 0xb2, 0x7e, 0x2f, 0xa0, 0x56, 0x88, 0x50, 0xf5, 0x03, 0x22, 0x54, 0x75, - 0x80, 0xc7, 0x07, 0x0c, 0xf0, 0x0d, 0x28, 0x6e, 0x50, 0xea, 0x63, 0x9f, 0x4e, 0xc6, 0x22, 0x5d, - 0x97, 0x52, 0xdf, 0x62, 0x1d, 0xab, 0x59, 0x1a, 0x08, 0x44, 0xcd, 0x3c, 0x61, 0x6a, 0x48, 0xf3, - 0x04, 0xf2, 0x02, 0x4c, 0x75, 0x7b, 0x5b, 0x2d, 0xb7, 0x89, 0x7c, 0x85, 0x5d, 0x83, 0x39, 0xc9, - 0x61, 0x8c, 0x6d, 0x40, 0xbe, 0x0a, 0xd3, 0x78, 0xc7, 0x89, 0xa6, 0xdc, 0x8c, 0xf6, 0xaa, 0xa7, - 0x95, 0x71, 0x49, 0xa7, 0xc9, 0x40, 0x56, 0x86, 0xf9, 0x8d, 0xce, 0xe8, 0x7c, 0x03, 0x66, 0xf4, - 0x91, 0x7c, 0x02, 0x8f, 0x7b, 0x91, 0xa9, 0x45, 0xb1, 0x34, 0x71, 0x67, 0xa4, 0x08, 0xa5, 0x49, - 0x6e, 0x64, 0x61, 0xc2, 0x46, 0xd4, 0x26, 0x93, 0xdc, 0xed, 0x6d, 0x51, 0xbf, 0x43, 0x43, 0x1a, - 0x88, 0x4b, 0x40, 0x60, 0x8e, 0x54, 0xba, 0xdd, 0xc0, 0xf8, 0xcf, 0xf3, 0x30, 0x5e, 0x79, 0xd8, - 0xa8, 0x77, 0xb6, 0x3d, 0x7c, 0xa2, 0x8b, 0x5e, 0x66, 0xd4, 0x27, 0xba, 0xe8, 0x65, 0x46, 0x7d, - 0x8f, 0xb9, 0x96, 0x71, 0x8d, 0x43, 0x2b, 0x5e, 0xe5, 0x1a, 0xa7, 0x5d, 0x40, 0xe3, 0x47, 0xaa, - 0xc2, 0x10, 0x8f, 0x54, 0x91, 0x1e, 0x71, 0xe4, 0x78, 0x3d, 0xe2, 0xdb, 0x30, 0x59, 0xef, 0x84, - 0x74, 0xc7, 0x8f, 0x67, 0x7a, 0x74, 0xa5, 0x8c, 0xc0, 0xaa, 0x68, 0xaf, 0x60, 0xb3, 0x69, 0xc4, - 0x75, 0x97, 0x91, 0xce, 0x12, 0xa7, 0x11, 0x57, 0x71, 0x26, 0xf4, 0x01, 0x12, 0xd1, 0xa8, 0x25, - 0xe6, 0x88, 0x34, 0x04, 0xe0, 0xc2, 0xe7, 0x4c, 0xac, 0xbc, 0x67, 0x1d, 0xbb, 0x34, 0x97, 0x6d, - 0x08, 0x60, 0x7c, 0x3f, 0x0f, 0x93, 0x95, 0x6e, 0xf7, 0x29, 0x37, 0xc7, 0xfa, 0xa2, 0xb6, 0xbd, - 0xca, 0xbb, 0x50, 0xd4, 0xae, 0xa1, 0x2c, 0xb1, 0x7e, 0x2d, 0x0f, 0xb3, 0x09, 0x0a, 0xf5, 0xeb, - 0x73, 0x43, 0x1a, 0x61, 0xe5, 0x87, 0x34, 0xc2, 0x2a, 0x0c, 0x67, 0x84, 0x35, 0xf2, 0x49, 0xb6, - 0xcc, 0x57, 0xa0, 0x50, 0xe9, 0x76, 0x93, 0x8f, 0xb9, 0xdd, 0xee, 0x83, 0x1b, 0xfc, 0x3e, 0x6b, - 0x77, 0xbb, 0x26, 0xc3, 0xd0, 0xf6, 0xb1, 0xb1, 0x21, 0xf7, 0x31, 0xe3, 0x0d, 0x98, 0x40, 0x5e, - 0x68, 0xfa, 0x74, 0x11, 0x70, 0x31, 0x0b, 0xab, 0x27, 0xad, 0x2e, 0xb1, 0xcc, 0xff, 0xbf, 0x1c, - 0x8c, 0xe2, 0xef, 0xa7, 0x74, 0x8e, 0x2d, 0x6a, 0x73, 0xac, 0xa4, 0xcc, 0xb1, 0x61, 0x66, 0xd7, - 0xdf, 0x2e, 0x00, 0x54, 0xd7, 0xcd, 0x06, 0x57, 0x7b, 0x90, 0x5b, 0x30, 0x6b, 0xb7, 0x5a, 0xde, - 0x3e, 0x75, 0x2c, 0xcf, 0x77, 0x77, 0xdc, 0x0e, 0xef, 0x39, 0xf9, 0xc2, 0xa8, 0x17, 0xa9, 0xef, - 0x0e, 0xa2, 0x68, 0x9d, 0x97, 0xa8, 0x7c, 0xda, 0x34, 0xdc, 0xf5, 0x1c, 0x79, 0x81, 0xd3, 0xf8, - 0x88, 0xa2, 0x0c, 0x3e, 0xf7, 0x78, 0x89, 0xca, 0x67, 0x17, 0x2f, 0xa4, 0x52, 0x7e, 0xd4, 0xf8, - 0x88, 0xa2, 0x0c, 0x3e, 0xfc, 0x16, 0x1b, 0x90, 0x55, 0x98, 0x43, 0x88, 0xd5, 0xf4, 0xa9, 0x43, - 0x3b, 0xa1, 0x6b, 0xb7, 0x02, 0x71, 0xe5, 0x47, 0xe5, 0x50, 0xaa, 0x50, 0xbd, 0xf2, 0x60, 0x61, - 0x35, 0x2e, 0x23, 0x57, 0x61, 0xbc, 0x6d, 0x3f, 0xb6, 0xec, 0x1d, 0xfe, 0xd6, 0x3e, 0xcd, 0xaf, - 0x88, 0x02, 0xa4, 0x6e, 0xd8, 0x6d, 0xfb, 0x71, 0x65, 0x87, 0xb2, 0x56, 0xd0, 0xc7, 0x5d, 0x2f, - 0x50, 0x5a, 0x31, 0x16, 0xb7, 0x22, 0x51, 0xa4, 0xb6, 0x42, 0x14, 0x89, 0x56, 0x18, 0xff, 0xcf, - 0x28, 0x4e, 0x6d, 0xb1, 0x09, 0x08, 0xf3, 0xb0, 0x5c, 0x86, 0x79, 0xd8, 0x5b, 0xa0, 0x1c, 0x71, - 0xaa, 0x6a, 0x4f, 0x39, 0xe0, 0xd5, 0x6b, 0x61, 0x8c, 0x4c, 0xf6, 0x92, 0x86, 0x62, 0x05, 0x5c, - 0x39, 0x2f, 0x26, 0xe7, 0xd5, 0x13, 0xb1, 0x11, 0x5b, 0x01, 0x52, 0xef, 0xe0, 0x6b, 0x16, 0x6d, - 0xec, 0xb9, 0xdd, 0x07, 0xd4, 0x77, 0xb7, 0x0f, 0xc4, 0xb8, 0xa0, 0x10, 0xe5, 0x8a, 0x52, 0x2b, - 0xd8, 0x73, 0xbb, 0xec, 0xde, 0xe8, 0x6e, 0x1f, 0x98, 0x19, 0x34, 0xe4, 0x7d, 0x18, 0x37, 0xe9, - 0xbe, 0xef, 0x86, 0xd2, 0xfc, 0x61, 0x26, 0xd2, 0x72, 0x20, 0x94, 0x0f, 0x91, 0xcf, 0x7f, 0xa8, - 0x8b, 0x55, 0x94, 0x93, 0x45, 0x7e, 0x4a, 0x71, 0x33, 0x87, 0xe9, 0xb8, 0xb5, 0x95, 0x87, 0x8d, - 0x7e, 0x87, 0x14, 0xb9, 0x02, 0xa3, 0x78, 0xd4, 0x09, 0x01, 0x0e, 0xdd, 0x06, 0x50, 0xe0, 0x51, - 0xcf, 0x61, 0xc4, 0x20, 0xcf, 0x03, 0x44, 0xcf, 0x45, 0xc1, 0x42, 0x11, 0x45, 0x2b, 0x05, 0x92, - 0x3c, 0xa7, 0x27, 0x4e, 0x74, 0x4e, 0xaf, 0x42, 0xc9, 0xe4, 0x1e, 0x48, 0x4e, 0xa5, 0x8b, 0x6f, - 0x12, 0xc1, 0x02, 0xe0, 0x04, 0xbb, 0x78, 0x74, 0x58, 0x7e, 0x4e, 0x78, 0x27, 0x39, 0x96, 0xdd, - 0xe5, 0x4f, 0x19, 0xda, 0xec, 0x4e, 0x52, 0x92, 0xb7, 0x60, 0x84, 0xed, 0x08, 0xc2, 0xa4, 0x4c, - 0xea, 0x76, 0xe3, 0x4d, 0x82, 0xdf, 0xb4, 0x9b, 0x9e, 0x36, 0x55, 0x91, 0xe4, 0xd3, 0x33, 0xd3, - 0xfa, 0xb5, 0x3c, 0xbc, 0x18, 0x1d, 0x82, 0xeb, 0x7e, 0xa3, 0x72, 0x6f, 0xb5, 0xee, 0x6c, 0x88, - 0x3b, 0xe3, 0x86, 0xef, 0x3d, 0x72, 0x1d, 0xea, 0x3f, 0xb8, 0x7e, 0xcc, 0x16, 0xbe, 0xca, 0x97, - 0x0f, 0x57, 0x38, 0xe7, 0x35, 0x83, 0x16, 0x45, 0xd6, 0x10, 0x36, 0x37, 0xdd, 0x6e, 0x4a, 0xff, - 0xbc, 0xf2, 0x8c, 0x19, 0x33, 0x20, 0x3f, 0x9a, 0x83, 0x33, 0xd9, 0x1f, 0x22, 0xf4, 0x08, 0x65, - 0x79, 0x5f, 0xe9, 0xf3, 0xb5, 0x4b, 0xaf, 0x1c, 0x1d, 0x96, 0x5f, 0x0c, 0xec, 0x76, 0xcb, 0x72, - 0x1d, 0x5e, 0x9b, 0xdb, 0xa4, 0x56, 0x57, 0x20, 0x68, 0xf5, 0xf6, 0xa9, 0xe9, 0x4b, 0x20, 0x77, - 0xf2, 0x85, 0xdc, 0x12, 0x40, 0x51, 0xea, 0xf4, 0x8c, 0xbf, 0x9b, 0x03, 0x65, 0x6a, 0x17, 0x4d, - 0xea, 0xb8, 0x3e, 0x6d, 0x86, 0x62, 0x37, 0x17, 0xee, 0x38, 0x1c, 0x96, 0xb0, 0x5f, 0x42, 0x18, - 0x79, 0x0f, 0xc6, 0xc5, 0xae, 0x83, 0x1b, 0x77, 0xbc, 0x24, 0x84, 0xb6, 0x90, 0xfb, 0x6d, 0xa5, - 0x76, 0x2c, 0x49, 0xc4, 0x6e, 0x45, 0x77, 0x1e, 0x6e, 0x56, 0x5b, 0xb6, 0xdb, 0x0e, 0xc4, 0xe9, - 0x87, 0xdd, 0xfa, 0xe1, 0x7e, 0x68, 0x35, 0x11, 0xaa, 0xde, 0x8a, 0x22, 0x54, 0xe3, 0xb6, 0x54, - 0x56, 0x1e, 0x63, 0x84, 0x57, 0x86, 0xd1, 0x07, 0xb1, 0xd2, 0x62, 0x69, 0xe2, 0xe8, 0xb0, 0xcc, - 0xa7, 0x8b, 0xc9, 0xe1, 0xc6, 0x5f, 0xcd, 0xc1, 0x8c, 0x3e, 0x9f, 0xc8, 0x55, 0x18, 0x13, 0xae, - 0x30, 0x39, 0x54, 0xce, 0xb0, 0x5e, 0x18, 0xe3, 0x4e, 0x30, 0x9a, 0xeb, 0x8b, 0xc0, 0x62, 0xe7, - 0xb7, 0xe0, 0x20, 0x0e, 0x2f, 0x3c, 0xbf, 0x9b, 0x1c, 0x64, 0xca, 0x32, 0x62, 0x30, 0xe1, 0x3d, - 0xe8, 0xb5, 0x42, 0x55, 0x67, 0xef, 0x23, 0xc4, 0x14, 0x25, 0x46, 0x15, 0xc6, 0xf8, 0x5e, 0x92, - 0x30, 0xfe, 0xc9, 0x9d, 0xc0, 0xf8, 0xc7, 0x38, 0xcc, 0x01, 0x34, 0x1a, 0x2b, 0x77, 0xe9, 0xc1, - 0x86, 0xed, 0xfa, 0xf8, 0xc8, 0x84, 0xfb, 0xf6, 0x5d, 0xb1, 0xb8, 0xa6, 0xc4, 0x23, 0x13, 0xdf, - 0xe3, 0xf7, 0xe8, 0x81, 0xf6, 0xc8, 0x24, 0x51, 0xf1, 0x70, 0xf0, 0xdd, 0x47, 0x76, 0x48, 0x19, - 0x61, 0x1e, 0x09, 0xf9, 0xe1, 0xc0, 0xa1, 0x09, 0x4a, 0x05, 0x99, 0x7c, 0x03, 0x66, 0xe2, 0x5f, - 0xd1, 0x53, 0xd9, 0x4c, 0xb4, 0x80, 0xf5, 0xc2, 0xa5, 0xe7, 0x8f, 0x0e, 0xcb, 0xe7, 0x15, 0xae, - 0xc9, 0x47, 0xb4, 0x04, 0x33, 0xe3, 0x57, 0x72, 0xf8, 0x40, 0x2c, 0x1b, 0x78, 0x09, 0x46, 0x22, - 0x93, 0xc6, 0x29, 0xb1, 0xeb, 0xe8, 0xcf, 0x01, 0x58, 0x4e, 0x5e, 0x84, 0x42, 0xdc, 0x12, 0xdc, - 0xab, 0xf5, 0x16, 0xb0, 0x52, 0x72, 0x1b, 0xc6, 0x87, 0xfa, 0x66, 0x5c, 0x1a, 0x19, 0xdf, 0x2a, - 0xa9, 0x71, 0x14, 0xee, 0x3c, 0xdc, 0xfc, 0xec, 0x8e, 0xc2, 0x4f, 0xe5, 0x61, 0x96, 0xf5, 0x6b, - 0xa5, 0x17, 0xee, 0x7a, 0xbe, 0x1b, 0x1e, 0x3c, 0xb5, 0xda, 0xad, 0x77, 0x34, 0xd1, 0xf8, 0xbc, - 0x3c, 0x65, 0xd4, 0xb6, 0x0d, 0xa5, 0xe4, 0xfa, 0xbd, 0x51, 0x98, 0xcf, 0xa0, 0x22, 0xaf, 0x6b, - 0x0a, 0xe8, 0x05, 0xe9, 0xea, 0xfa, 0xd1, 0x61, 0x79, 0x4a, 0xa2, 0x6f, 0xc6, 0xae, 0xaf, 0x8b, - 0xba, 0xb5, 0x05, 0xef, 0x29, 0xd4, 0x47, 0xab, 0xd6, 0x16, 0xba, 0x8d, 0xc5, 0x15, 0x18, 0x35, - 0xbd, 0x16, 0x95, 0x16, 0x46, 0x28, 0x61, 0xf8, 0x0c, 0xa0, 0xbd, 0xa8, 0x32, 0x00, 0x59, 0x81, - 0x71, 0xf6, 0xc7, 0x3d, 0xbb, 0x2b, 0xde, 0x0a, 0x48, 0x74, 0x39, 0x43, 0x68, 0xd7, 0xed, 0xec, - 0xa8, 0xf7, 0xb3, 0x16, 0xb5, 0xda, 0x76, 0x57, 0x13, 0x85, 0x38, 0xa2, 0x76, 0xcf, 0x2b, 0xf6, - 0xbf, 0xe7, 0xe5, 0x8e, 0xbd, 0xe7, 0x39, 0x00, 0x0d, 0x77, 0xa7, 0xe3, 0x76, 0x76, 0x2a, 0xad, - 0x1d, 0xe1, 0x30, 0x7c, 0xa5, 0xff, 0x28, 0x5c, 0x8d, 0x91, 0x71, 0xe2, 0xf2, 0x07, 0x3d, 0x0e, - 0xb3, 0xec, 0x96, 0xf6, 0x90, 0x11, 0xa3, 0x92, 0x35, 0x80, 0x4a, 0x33, 0x74, 0x1f, 0xb1, 0x09, - 0x1c, 0x08, 0xa9, 0x45, 0x7e, 0x70, 0xb5, 0x72, 0x97, 0x1e, 0x34, 0x68, 0x18, 0x3f, 0x8c, 0xd8, - 0x88, 0xca, 0xd6, 0x81, 0x66, 0xb3, 0x1e, 0x73, 0x20, 0x5d, 0x38, 0x5d, 0x71, 0x1c, 0x97, 0xb5, - 0xc0, 0x6e, 0x6d, 0x72, 0x47, 0x6f, 0x64, 0x3d, 0x95, 0xcd, 0xfa, 0x8a, 0x60, 0xfd, 0x82, 0x1d, - 0x51, 0x59, 0xd2, 0x3f, 0x3c, 0x51, 0x4d, 0x36, 0x63, 0x63, 0x1d, 0x66, 0xf4, 0xa6, 0xeb, 0x6e, - 0xce, 0x53, 0x50, 0x34, 0x1b, 0x15, 0xab, 0xb1, 0x52, 0xb9, 0x5e, 0xca, 0x91, 0x12, 0x4c, 0x89, - 0x5f, 0x8b, 0xd6, 0xe2, 0x9b, 0x37, 0x4b, 0x79, 0x0d, 0xf2, 0xe6, 0xf5, 0xc5, 0x94, 0x77, 0xd1, - 0x78, 0xa9, 0xc8, 0xd5, 0x5f, 0xc6, 0xaf, 0xe7, 0xa0, 0x28, 0xbf, 0x9b, 0xdc, 0x84, 0x42, 0xa3, - 0xb1, 0x92, 0xf0, 0x07, 0x8a, 0xcf, 0x17, 0xbe, 0x93, 0x06, 0x81, 0x6a, 0xf4, 0xc9, 0x08, 0x18, - 0xdd, 0xe6, 0x6a, 0x43, 0x88, 0x05, 0x92, 0x2e, 0xde, 0xb6, 0x39, 0x5d, 0x86, 0x93, 0xc4, 0x4d, - 0x28, 0xdc, 0x79, 0xb8, 0x29, 0xee, 0x13, 0x92, 0x2e, 0xde, 0x49, 0x39, 0xdd, 0x87, 0xfb, 0xea, - 0xfe, 0xce, 0x08, 0x0c, 0x13, 0x26, 0x95, 0x29, 0xcc, 0x8f, 0xdb, 0xb6, 0x17, 0xf9, 0xf5, 0x8a, - 0xe3, 0x96, 0x41, 0x4c, 0x51, 0xc2, 0xa4, 0x83, 0x55, 0xaf, 0x69, 0xb7, 0xc4, 0xb9, 0x8d, 0xd2, - 0x41, 0x8b, 0x01, 0x4c, 0x0e, 0x37, 0x7e, 0x27, 0x07, 0x25, 0x94, 0xa1, 0xd0, 0x68, 0xd3, 0xdb, - 0xa3, 0x9d, 0x07, 0xd7, 0xc9, 0x1b, 0x72, 0xb1, 0xe5, 0x22, 0x55, 0xc3, 0x28, 0x2e, 0xb6, 0xc4, - 0x5b, 0x85, 0x58, 0x70, 0x8a, 0xeb, 0x74, 0x7e, 0x78, 0x97, 0xcb, 0x63, 0x5c, 0xa7, 0xcb, 0x30, - 0x8a, 0x9f, 0x23, 0xb6, 0x45, 0xfc, 0xf2, 0x90, 0x01, 0x4c, 0x0e, 0x57, 0x76, 0xa5, 0x9f, 0xc9, - 0xa7, 0xda, 0xb0, 0xf8, 0x99, 0x72, 0x5b, 0xd4, 0x1b, 0x37, 0xd4, 0x4e, 0xfd, 0x01, 0x9c, 0x4a, - 0x76, 0x09, 0xaa, 0x81, 0x2a, 0x30, 0xab, 0xc3, 0xa5, 0x46, 0xe8, 0x6c, 0x66, 0x5d, 0x0f, 0x16, - 0xcd, 0x24, 0xbe, 0xf1, 0xbf, 0xe5, 0x60, 0x02, 0xff, 0x34, 0x7b, 0x2d, 0x34, 0x9e, 0xa9, 0x3c, - 0x6c, 0x08, 0x95, 0xaf, 0x2a, 0xc6, 0xd9, 0xfb, 0x81, 0x25, 0xb4, 0xc2, 0xda, 0xfe, 0x12, 0x21, - 0x0b, 0x52, 0xae, 0xcb, 0x95, 0x6a, 0x91, 0x88, 0x94, 0x2b, 0x7d, 0x83, 0x04, 0xa9, 0x40, 0x46, - 0x93, 0xbb, 0x87, 0x0d, 0x36, 0xfd, 0xd4, 0xd7, 0x6c, 0xa4, 0xf3, 0x5a, 0xba, 0xc9, 0x1d, 0x47, - 0xc3, 0xc7, 0xec, 0x87, 0x8d, 0x8a, 0xb9, 0xa6, 0x3d, 0x66, 0xb3, 0x6f, 0xd4, 0x2c, 0xc4, 0x05, - 0x92, 0xf1, 0x8b, 0x93, 0xc9, 0x0e, 0x14, 0x47, 0xdd, 0x09, 0xd7, 0xc6, 0xdb, 0x30, 0x5a, 0x69, - 0xb5, 0xbc, 0x7d, 0xb1, 0x4b, 0x48, 0xad, 0x54, 0xd4, 0x7f, 0xfc, 0x24, 0x43, 0xc5, 0x8a, 0xe6, - 0x8a, 0xc5, 0x00, 0xa4, 0x0a, 0x13, 0x95, 0x87, 0x8d, 0x7a, 0xbd, 0xb6, 0xb9, 0xc9, 0xdd, 0x4e, - 0x0a, 0x4b, 0x2f, 0xcb, 0xfe, 0x71, 0x5d, 0xc7, 0x4a, 0xbe, 0xa7, 0xc6, 0x92, 0x7b, 0x4c, 0x47, - 0xde, 0x05, 0xb8, 0xe3, 0xb9, 0x1d, 0xae, 0x48, 0x12, 0x8d, 0xbf, 0x70, 0x74, 0x58, 0x9e, 0xfc, - 0xd0, 0x73, 0x3b, 0x42, 0xf3, 0xc4, 0xbe, 0x3d, 0x46, 0x32, 0x95, 0xbf, 0x59, 0x4f, 0x2f, 0x79, - 0xdc, 0x20, 0x66, 0x34, 0xee, 0xe9, 0x2d, 0x2f, 0x65, 0x0b, 0x23, 0xd1, 0x48, 0x1b, 0x66, 0x1b, - 0xbd, 0x9d, 0x1d, 0xca, 0x76, 0x75, 0xa1, 0x3a, 0x19, 0x13, 0xb7, 0xdb, 0x28, 0xd8, 0x07, 0xbf, - 0x89, 0xb0, 0xfb, 0x49, 0xb0, 0xf4, 0x3a, 0x9b, 0xc8, 0x3f, 0x38, 0x2c, 0x8b, 0x77, 0x5a, 0x26, - 0xa4, 0x05, 0x92, 0x3e, 0xad, 0x38, 0x49, 0xf2, 0x26, 0xeb, 0x30, 0x76, 0xdb, 0x0d, 0x57, 0x7a, - 0x5b, 0xc2, 0x8d, 0xe2, 0x85, 0x01, 0x8b, 0x86, 0x23, 0xf2, 0x87, 0x82, 0x1d, 0x37, 0xdc, 0xed, - 0xa9, 0x86, 0xec, 0x82, 0x0d, 0x79, 0x08, 0xc5, 0xaa, 0xeb, 0x37, 0x5b, 0xb4, 0x5a, 0x17, 0xa7, - 0xfe, 0x8b, 0x03, 0x58, 0x4a, 0x54, 0xde, 0x2f, 0x4d, 0xfc, 0xd5, 0x74, 0x55, 0x29, 0x40, 0x62, - 0x90, 0xbf, 0x96, 0x83, 0x67, 0xa3, 0xaf, 0xaf, 0xec, 0xd0, 0x4e, 0x78, 0xcf, 0x0e, 0x9b, 0xbb, - 0xd4, 0x17, 0xbd, 0x34, 0x31, 0xa8, 0x97, 0xbe, 0x94, 0xea, 0xa5, 0xcb, 0x71, 0x2f, 0xd9, 0x8c, - 0x99, 0xd5, 0xe6, 0xdc, 0xd2, 0x7d, 0x36, 0xa8, 0x56, 0x62, 0x01, 0xc4, 0x2f, 0x3f, 0xc2, 0x0d, - 0xef, 0xe5, 0x01, 0x0d, 0x8e, 0x91, 0x85, 0xf9, 0x7c, 0xf4, 0x5b, 0xb3, 0xff, 0x8a, 0xa0, 0xe4, - 0xae, 0xf4, 0x59, 0xe2, 0x12, 0xc9, 0xc5, 0x01, 0xbc, 0xb9, 0x1f, 0xd3, 0xfc, 0x00, 0xef, 0x44, - 0x3e, 0xda, 0xab, 0xf6, 0x96, 0x10, 0x42, 0x8e, 0x19, 0xed, 0x55, 0x3b, 0x1e, 0xed, 0x96, 0x9d, - 0x1c, 0xed, 0x55, 0x7b, 0x8b, 0x54, 0xb9, 0xa3, 0x25, 0xf7, 0xca, 0x7b, 0x7e, 0x10, 0xb7, 0xea, - 0x06, 0x3f, 0x99, 0x33, 0x1c, 0x2e, 0xbf, 0x06, 0x13, 0x8d, 0xae, 0xdd, 0xa4, 0x2d, 0x77, 0x3b, - 0x14, 0x4f, 0x81, 0x2f, 0x0d, 0x60, 0x15, 0xe1, 0x8a, 0x67, 0x24, 0xf9, 0x53, 0xbd, 0x20, 0x45, - 0x38, 0xec, 0x0b, 0x37, 0x37, 0xee, 0x2d, 0xcc, 0x1e, 0xfb, 0x85, 0x9b, 0x1b, 0xf7, 0x84, 0xcc, - 0xd1, 0x6d, 0x6b, 0x32, 0xc7, 0xc6, 0x3d, 0xd2, 0x85, 0x99, 0x4d, 0xea, 0xfb, 0xf6, 0xb6, 0xe7, - 0xb7, 0xb9, 0xaa, 0x8e, 0x7b, 0x7a, 0x5c, 0x19, 0xc4, 0x4f, 0x23, 0xe0, 0x3a, 0xda, 0x50, 0xc2, - 0xac, 0xa4, 0x7e, 0x2f, 0xc1, 0x9f, 0xf5, 0xc9, 0x92, 0x1b, 0x6e, 0xf5, 0x9a, 0x7b, 0x34, 0x5c, - 0x98, 0x3b, 0xb6, 0x4f, 0x22, 0x5c, 0xde, 0x27, 0x5b, 0xf2, 0xa7, 0xda, 0x27, 0x11, 0x8e, 0xf1, - 0x9b, 0x05, 0x38, 0xdb, 0xa7, 0x0b, 0xc8, 0x9a, 0xdc, 0x72, 0x73, 0x9a, 0xc2, 0xb6, 0x0f, 0xfa, - 0xd5, 0x63, 0x77, 0xe1, 0x55, 0x28, 0x2d, 0xdf, 0x45, 0x29, 0x9d, 0xab, 0xd2, 0xab, 0x15, 0x79, - 0x58, 0xa1, 0x52, 0x91, 0xee, 0xa1, 0x65, 0x9c, 0x54, 0xc1, 0x37, 0x35, 0x17, 0xd0, 0x14, 0xe5, - 0xf9, 0x1f, 0xc9, 0xc3, 0x08, 0x1e, 0x9c, 0x89, 0xc0, 0x37, 0xb9, 0x13, 0x05, 0xbe, 0xf9, 0x32, - 0x4c, 0x2d, 0xdf, 0xe5, 0x77, 0xe8, 0x15, 0x3b, 0xd8, 0x15, 0xdb, 0x3a, 0x3e, 0x34, 0xd3, 0x3d, - 0x4b, 0x5c, 0xb9, 0x77, 0x6d, 0x4d, 0x66, 0xd5, 0x28, 0xc8, 0x7d, 0x98, 0xe7, 0xdf, 0xe6, 0x6e, - 0xbb, 0x4d, 0x1e, 0x3f, 0xc3, 0xb5, 0x5b, 0x62, 0x8f, 0x7f, 0xf1, 0xe8, 0xb0, 0x5c, 0xa6, 0x7b, - 0x68, 0xf3, 0x27, 0xca, 0xad, 0x00, 0x11, 0x54, 0xe3, 0xbf, 0x0c, 0x7a, 0xd5, 0xa9, 0xdf, 0x9c, - 0xc0, 0x0a, 0x59, 0x6d, 0xac, 0x6e, 0x86, 0xcb, 0x91, 0x8c, 0x3f, 0x1b, 0x85, 0xf3, 0xfd, 0xb7, - 0x67, 0xf2, 0x15, 0x7d, 0x00, 0x2f, 0x1d, 0xbb, 0xa1, 0x1f, 0x3f, 0x86, 0x5f, 0x85, 0x53, 0xcb, - 0x9d, 0x90, 0xfa, 0x5d, 0xdf, 0x95, 0x61, 0x1c, 0x56, 0xbc, 0x40, 0xda, 0x58, 0xa2, 0xb1, 0x23, - 0x8d, 0xca, 0x85, 0xba, 0x13, 0x2d, 0x3e, 0x15, 0x56, 0x99, 0x1c, 0xc8, 0x32, 0xcc, 0x28, 0xf0, - 0x56, 0x6f, 0x47, 0x08, 0x24, 0xfc, 0x45, 0x43, 0xe1, 0xd9, 0xea, 0xa9, 0xf7, 0xb6, 0x04, 0x11, - 0xda, 0x71, 0xb2, 0xcb, 0x62, 0xf3, 0xce, 0xc3, 0xbb, 0x0d, 0x31, 0x9c, 0xfc, 0xda, 0x87, 0x50, - 0xeb, 0xc3, 0xfd, 0x3d, 0x6d, 0x7f, 0x8d, 0x91, 0xcf, 0xff, 0x4a, 0x41, 0xcc, 0xa8, 0x17, 0xa1, - 0xd0, 0xe8, 0x6d, 0x89, 0x99, 0xc4, 0x2f, 0x2d, 0xda, 0x01, 0xc7, 0x4a, 0xc9, 0x17, 0x01, 0x4c, - 0xda, 0xf5, 0x02, 0x37, 0xf4, 0xfc, 0x03, 0xd5, 0x91, 0xc8, 0x8f, 0xa0, 0xba, 0xad, 0xb3, 0x84, - 0x92, 0x15, 0x98, 0x8d, 0x7f, 0xad, 0xef, 0x77, 0x84, 0x7a, 0x77, 0x82, 0xeb, 0x55, 0x62, 0x72, - 0xcb, 0x63, 0x65, 0xea, 0x91, 0x9d, 0x20, 0x23, 0x8b, 0x50, 0x7c, 0xe8, 0xf9, 0x7b, 0xdb, 0x6c, - 0x8c, 0x47, 0x62, 0xa1, 0x62, 0x5f, 0xc0, 0xd4, 0xc3, 0x53, 0xe2, 0xb1, 0xe5, 0xb2, 0xdc, 0x79, - 0xe4, 0xfa, 0x5e, 0xa7, 0x4d, 0x3b, 0xa1, 0xfa, 0x7e, 0x4f, 0x63, 0xb0, 0xe6, 0xc2, 0x19, 0x83, - 0xc9, 0x15, 0x18, 0xad, 0x34, 0x43, 0xcf, 0x17, 0x8f, 0xf7, 0x7c, 0xa6, 0x30, 0x80, 0x36, 0x53, - 0x18, 0x80, 0x75, 0xa2, 0x49, 0xb7, 0xc5, 0x43, 0x06, 0x76, 0xa2, 0x4f, 0xb7, 0x35, 0xff, 0x54, - 0xba, 0xcd, 0x84, 0x22, 0x93, 0x6e, 0xa3, 0xca, 0x43, 0x0b, 0xeb, 0xb4, 0x9d, 0x52, 0x96, 0x09, - 0x34, 0xe3, 0xc7, 0xa0, 0xef, 0x94, 0x67, 0xa7, 0xd0, 0xc9, 0xa6, 0xfc, 0xaa, 0x3d, 0xc4, 0x94, - 0x7f, 0x3d, 0xb2, 0xa0, 0x56, 0x9d, 0xb2, 0x11, 0xa2, 0x1e, 0x83, 0xc2, 0x96, 0x5a, 0x9f, 0x7f, - 0x85, 0x93, 0xcc, 0xbf, 0x5f, 0x2d, 0x9e, 0x64, 0xfe, 0x89, 0xfe, 0xcd, 0x0f, 0xdb, 0xbf, 0x85, - 0xa1, 0xfa, 0x97, 0x2c, 0xc1, 0x74, 0x14, 0x53, 0x6c, 0xc3, 0x0e, 0xb5, 0x1d, 0x31, 0x0a, 0x04, - 0x67, 0x75, 0xed, 0x50, 0xdd, 0x11, 0x75, 0x12, 0xf2, 0x0e, 0x4c, 0x0a, 0x0f, 0x04, 0xe4, 0x30, - 0x1a, 0xdb, 0x80, 0x4a, 0x77, 0x85, 0x04, 0xbd, 0x8a, 0xce, 0x36, 0x82, 0x0d, 0xb7, 0x4b, 0x5b, - 0x6e, 0x87, 0x36, 0xf0, 0xe9, 0x41, 0x4c, 0x36, 0xdc, 0x08, 0xba, 0xa2, 0xc4, 0xe2, 0xaf, 0x12, - 0x9a, 0xd2, 0x51, 0x23, 0x4a, 0xce, 0xf3, 0xf1, 0x13, 0xcd, 0x73, 0x6e, 0x82, 0xe5, 0xaf, 0x7a, - 0x3b, 0xae, 0x34, 0x3a, 0x95, 0x26, 0x58, 0xbe, 0xd5, 0x62, 0xd0, 0x84, 0x09, 0x16, 0x47, 0x65, - 0x97, 0x23, 0xf6, 0xa3, 0x5e, 0x13, 0xef, 0x6d, 0x78, 0x39, 0x42, 0x22, 0xdd, 0xd2, 0x97, 0x23, - 0xc9, 0x6a, 0x96, 0xdb, 0xb6, 0xdb, 0x12, 0x6e, 0xbb, 0x71, 0x35, 0x94, 0x41, 0x93, 0xd5, 0x20, - 0x2a, 0x69, 0xc2, 0x94, 0x49, 0xb7, 0x37, 0x7c, 0x2f, 0xa4, 0xcd, 0x90, 0x3a, 0x42, 0x20, 0x94, - 0x77, 0xa2, 0x25, 0xcf, 0xe3, 0xc2, 0xee, 0xd2, 0x1b, 0xbf, 0x7f, 0x58, 0xce, 0xfd, 0xe0, 0xb0, - 0x0c, 0x0c, 0xc4, 0xcd, 0xc8, 0x8f, 0x0e, 0xcb, 0x67, 0xd9, 0xf8, 0x77, 0x25, 0xb1, 0x7a, 0xb0, - 0xa9, 0x4c, 0xc9, 0x77, 0xd9, 0x56, 0x1f, 0x75, 0x49, 0x5c, 0xd9, 0x54, 0x9f, 0xca, 0xde, 0xcc, - 0xac, 0xac, 0xac, 0xf4, 0x76, 0x66, 0xa5, 0x99, 0x95, 0x90, 0x77, 0x61, 0xb2, 0x5a, 0xaf, 0x7a, - 0x9d, 0x6d, 0x77, 0xa7, 0xb1, 0x52, 0x41, 0xa9, 0x52, 0xb8, 0x10, 0x34, 0x5d, 0xab, 0x89, 0x70, - 0x2b, 0xd8, 0xb5, 0x35, 0x4f, 0xb2, 0x18, 0x9f, 0xdc, 0x86, 0x19, 0xf9, 0xd3, 0xa4, 0xdb, 0xf7, - 0xcd, 0x3a, 0x0a, 0x93, 0xd2, 0x6f, 0x23, 0xe2, 0xc0, 0x3a, 0xa2, 0xe7, 0xab, 0x97, 0x8c, 0x04, - 0x19, 0x9b, 0x8c, 0x35, 0xda, 0x6d, 0x79, 0x07, 0xec, 0xf3, 0x36, 0x5d, 0xea, 0xa3, 0xf8, 0x28, - 0x26, 0xa3, 0x13, 0x95, 0x58, 0xa1, 0xab, 0xed, 0xd4, 0x09, 0x22, 0xb2, 0x06, 0x73, 0x62, 0x8a, - 0x3f, 0x70, 0x03, 0x77, 0xcb, 0x6d, 0xb9, 0xe1, 0x01, 0x0a, 0x8e, 0x42, 0xf6, 0x91, 0xeb, 0xe2, - 0x51, 0x54, 0xaa, 0x30, 0x4b, 0x93, 0x1a, 0xbf, 0x9e, 0x87, 0xe7, 0x06, 0x5d, 0xa2, 0x48, 0x43, - 0xdf, 0x07, 0x2f, 0x0f, 0x71, 0xf1, 0x3a, 0x7e, 0x27, 0x5c, 0x86, 0x99, 0x75, 0x7f, 0xc7, 0xee, - 0xb8, 0xdf, 0xc1, 0xcb, 0x71, 0x64, 0x89, 0x86, 0x9d, 0xe1, 0x29, 0x25, 0xfa, 0x6c, 0x4f, 0x10, - 0x9d, 0x7f, 0x24, 0xb6, 0xb9, 0x8f, 0xeb, 0xd3, 0x74, 0x13, 0x26, 0xaa, 0x5e, 0x27, 0xa4, 0x8f, - 0xc3, 0x84, 0x07, 0x2f, 0x07, 0x26, 0xfd, 0xb9, 0x24, 0xaa, 0xf1, 0x2f, 0xf2, 0x70, 0x61, 0xe0, - 0x2d, 0x82, 0x6c, 0xea, 0xbd, 0x76, 0x65, 0x98, 0xab, 0xc7, 0xf1, 0xdd, 0xb6, 0x98, 0x32, 0x9a, - 0x3a, 0xd6, 0x65, 0xe0, 0xfc, 0x7f, 0x97, 0x13, 0x9d, 0xf4, 0x39, 0x18, 0xc7, 0xaa, 0xa2, 0x2e, - 0xe2, 0x0a, 0x36, 0xdc, 0x85, 0x5d, 0x5d, 0xc1, 0xc6, 0xd1, 0xc8, 0x0d, 0x28, 0x56, 0xed, 0x56, - 0x4b, 0xf1, 0x6f, 0xc6, 0x8b, 0x40, 0x13, 0x61, 0x09, 0x1b, 0x3b, 0x89, 0xc8, 0x8e, 0x2d, 0xfe, - 0xb7, 0x72, 0x56, 0xe0, 0x66, 0x29, 0xc8, 0x12, 0xc7, 0x85, 0x82, 0x8c, 0x51, 0x11, 0x9b, 0x5e, - 0xe4, 0x41, 0xc9, 0xa3, 0x22, 0x32, 0x80, 0x16, 0x15, 0x91, 0x01, 0x8c, 0xdf, 0x28, 0xc0, 0xf3, - 0x83, 0xaf, 0xc2, 0xe4, 0xbe, 0x3e, 0x04, 0xaf, 0x0e, 0x75, 0x81, 0x3e, 0x7e, 0x0c, 0x64, 0x8c, - 0x51, 0xde, 0x21, 0x97, 0xd3, 0x96, 0xfd, 0x1f, 0x1d, 0x96, 0x15, 0xc3, 0xcd, 0x3b, 0x9e, 0xdb, - 0x51, 0x1e, 0x5a, 0xbe, 0x9d, 0x3a, 0xd4, 0x27, 0x17, 0x6f, 0x0e, 0xf7, 0x65, 0x31, 0x1d, 0xdf, - 0x57, 0x86, 0x15, 0x06, 0xbe, 0x04, 0xa5, 0x24, 0x29, 0xb9, 0x04, 0x23, 0xf8, 0x01, 0x8a, 0x7b, - 0x42, 0x82, 0x03, 0x96, 0x9f, 0xbf, 0x27, 0xe6, 0x0e, 0xba, 0x7c, 0xe3, 0xeb, 0xbe, 0xae, 0x56, - 0x14, 0x2e, 0xdf, 0xdc, 0x38, 0x20, 0xad, 0x5a, 0x4c, 0x10, 0x19, 0x7f, 0x9e, 0x83, 0x73, 0x7d, - 0x95, 0x0c, 0x64, 0x43, 0x1f, 0xb0, 0x97, 0x8f, 0xd3, 0x4a, 0x1c, 0x3b, 0x56, 0xe7, 0x7f, 0x42, - 0xce, 0xfd, 0xf7, 0x60, 0xaa, 0xd1, 0xdb, 0x4a, 0x5e, 0xed, 0x78, 0x40, 0x06, 0x05, 0xae, 0x9e, - 0x60, 0x2a, 0x3e, 0x6b, 0xbf, 0x34, 0x5f, 0x10, 0x66, 0x32, 0x8a, 0xc9, 0x58, 0xe4, 0x93, 0x98, - 0x76, 0x79, 0xd7, 0x89, 0x8c, 0x5f, 0xcb, 0x67, 0xdf, 0x91, 0x6f, 0x57, 0x37, 0x4e, 0x72, 0x47, - 0xbe, 0x5d, 0xdd, 0x38, 0xbe, 0xed, 0xff, 0x95, 0x6c, 0x3b, 0xbe, 0xe6, 0x8a, 0x1d, 0x4f, 0xea, - 0x48, 0xc5, 0x6b, 0xae, 0xdc, 0x1d, 0x03, 0xfd, 0x35, 0x57, 0x22, 0x93, 0x37, 0x61, 0x62, 0xd5, - 0xe3, 0xde, 0xe8, 0xb2, 0xc5, 0xdc, 0x69, 0x4f, 0x02, 0xd5, 0xed, 0x31, 0xc2, 0x64, 0xd7, 0x12, - 0x7d, 0xe0, 0xa5, 0x65, 0x1c, 0x5e, 0x4b, 0x12, 0xd3, 0x45, 0xd7, 0x24, 0xea, 0x64, 0xc6, 0x7f, - 0x36, 0x0a, 0xc6, 0xf1, 0x7a, 0x10, 0xf2, 0x81, 0xde, 0x77, 0x57, 0x87, 0xd6, 0xa0, 0x0c, 0xb5, - 0xe5, 0x56, 0x7a, 0x8e, 0x4b, 0x3b, 0x4d, 0xdd, 0x95, 0x5c, 0xc0, 0xd4, 0x2d, 0x50, 0xe2, 0x7d, - 0x1c, 0xcf, 0xae, 0xf3, 0xff, 0x75, 0x21, 0x5e, 0x6a, 0x89, 0xa3, 0x31, 0xf7, 0x31, 0x8e, 0x46, - 0x72, 0x17, 0x4a, 0x2a, 0x44, 0x79, 0xd6, 0x45, 0xc9, 0x45, 0x63, 0x94, 0xf8, 0xa8, 0x14, 0xa1, - 0x7e, 0xbe, 0x16, 0x86, 0x3f, 0x5f, 0x63, 0xf1, 0x1d, 0xeb, 0x1f, 0x49, 0x8b, 0xef, 0x49, 0xef, - 0x4d, 0x05, 0x5d, 0xba, 0x9e, 0x07, 0xe2, 0xd0, 0x1a, 0xd5, 0x5d, 0xcf, 0x33, 0x0e, 0x2e, 0x15, - 0x5d, 0x7a, 0xcf, 0xe3, 0x4f, 0xc5, 0x79, 0x34, 0xf2, 0x9e, 0xe7, 0xf4, 0x59, 0xde, 0xf3, 0x11, - 0x09, 0x3b, 0x00, 0xcd, 0x5e, 0x87, 0x87, 0xdf, 0x1d, 0x8f, 0x0f, 0x40, 0xbf, 0xd7, 0xb1, 0x92, - 0x21, 0x78, 0x23, 0x44, 0xe3, 0xef, 0x8d, 0x64, 0x0b, 0x07, 0x91, 0xaa, 0xec, 0x24, 0xc2, 0x41, - 0x44, 0xf4, 0xe9, 0xcc, 0xd4, 0xfb, 0x30, 0x5f, 0x47, 0xd3, 0xd1, 0xf0, 0x40, 0x9a, 0x6d, 0xdd, - 0x37, 0x57, 0xc5, 0x10, 0xa3, 0xca, 0xc9, 0x15, 0xc5, 0x91, 0xe9, 0x97, 0xd5, 0xf3, 0x35, 0x95, - 0x53, 0x06, 0xfd, 0xf9, 0xbf, 0x2b, 0x35, 0x6a, 0xea, 0x20, 0xdc, 0xbf, 0x1f, 0xcd, 0xe5, 0xc4, - 0x20, 0xf4, 0x7a, 0xda, 0x30, 0xea, 0x24, 0x7c, 0xef, 0x95, 0xda, 0x0a, 0x64, 0xa2, 0xc8, 0x8a, - 0x8a, 0x8e, 0x23, 0xc1, 0x25, 0x41, 0x44, 0x76, 0xe0, 0x5c, 0x2c, 0x4a, 0x2b, 0x37, 0x05, 0xe4, - 0xc8, 0x1b, 0x7c, 0xe5, 0xe8, 0xb0, 0xfc, 0xb2, 0x22, 0x8a, 0xab, 0x17, 0x8e, 0x04, 0xf7, 0xfe, - 0xbc, 0xd8, 0x7e, 0xbb, 0xe4, 0xdb, 0x9d, 0xe6, 0xae, 0x32, 0xe7, 0x71, 0xbf, 0xdd, 0x42, 0x68, - 0xca, 0xff, 0x37, 0x46, 0x36, 0x7e, 0x22, 0x0f, 0x33, 0xfc, 0xac, 0xe6, 0x0f, 0x7b, 0x4f, 0xed, - 0xa3, 0xe9, 0xdb, 0xda, 0xa3, 0xa9, 0x0c, 0x55, 0xa5, 0x36, 0x6d, 0xa8, 0x27, 0xd3, 0x5d, 0x20, - 0x69, 0x1a, 0x62, 0xc2, 0x94, 0x0a, 0x1d, 0xfc, 0x5a, 0x7a, 0x3d, 0x8e, 0x6a, 0x26, 0x44, 0x25, - 0x7c, 0xb2, 0x0e, 0x4c, 0x8d, 0x87, 0xf1, 0x57, 0xf3, 0x30, 0xad, 0x18, 0xb7, 0x3c, 0xb5, 0x1d, - 0xff, 0x25, 0xad, 0xe3, 0x17, 0x22, 0xe7, 0xa7, 0xa8, 0x65, 0x43, 0xf5, 0x7b, 0x0f, 0xe6, 0x52, - 0x24, 0x49, 0x1b, 0xa1, 0xdc, 0x30, 0x36, 0x42, 0xaf, 0xa7, 0x43, 0x24, 0xf1, 0x48, 0xe3, 0x51, - 0xc0, 0x0c, 0x35, 0x26, 0xd3, 0x4f, 0xe5, 0xe1, 0x94, 0xf8, 0x85, 0x31, 0x05, 0xb9, 0xb0, 0xfa, - 0xd4, 0x8e, 0x45, 0x45, 0x1b, 0x8b, 0xb2, 0x3e, 0x16, 0x4a, 0x03, 0xfb, 0x0f, 0x89, 0xf1, 0x97, - 0x01, 0x16, 0xfa, 0x11, 0x0c, 0xed, 0x63, 0x1c, 0x7b, 0x70, 0xe5, 0x87, 0xf0, 0xe0, 0x5a, 0x85, - 0x12, 0x56, 0x25, 0xa2, 0x86, 0x05, 0xf7, 0xcd, 0xba, 0xe8, 0x24, 0xd4, 0x2f, 0xf0, 0xc0, 0x8f, - 0x22, 0x8a, 0x59, 0x90, 0xd0, 0x79, 0xa4, 0x28, 0xc9, 0xaf, 0xe4, 0x60, 0x06, 0x81, 0xcb, 0x8f, - 0x68, 0x27, 0x44, 0x66, 0x23, 0xc2, 0xe1, 0x28, 0x7a, 0x53, 0x6d, 0x84, 0xbe, 0xdb, 0xd9, 0x11, - 0x8f, 0xaa, 0x5b, 0xe2, 0x51, 0xf5, 0x1d, 0xfe, 0x18, 0x7c, 0xb5, 0xe9, 0xb5, 0xaf, 0xed, 0xf8, - 0xf6, 0x23, 0x97, 0xdb, 0x6d, 0xd9, 0xad, 0x6b, 0x71, 0x82, 0x8c, 0xae, 0x9b, 0x48, 0x5d, 0x21, - 0x58, 0xe1, 0x83, 0x35, 0xff, 0x50, 0x8a, 0xd5, 0x26, 0x55, 0x33, 0xfa, 0x17, 0x91, 0x1f, 0x82, - 0xb3, 0x3c, 0x96, 0x0f, 0xbb, 0xe1, 0xbb, 0x9d, 0x9e, 0xd7, 0x0b, 0x96, 0xec, 0xe6, 0x1e, 0x13, - 0xf3, 0xb9, 0xd3, 0x24, 0xb6, 0xbc, 0x19, 0x15, 0x5a, 0x5b, 0xbc, 0x54, 0x73, 0x12, 0xcf, 0x66, - 0x40, 0x56, 0x60, 0x8e, 0x17, 0x55, 0x7a, 0xa1, 0xd7, 0x68, 0xda, 0x2d, 0xb7, 0xb3, 0x83, 0xb2, - 0x44, 0x91, 0x8b, 0x32, 0x76, 0x2f, 0xf4, 0xac, 0x80, 0xc3, 0x55, 0x4d, 0x4d, 0x8a, 0x88, 0xd4, - 0x61, 0xd6, 0xa4, 0xb6, 0x73, 0xcf, 0x7e, 0x5c, 0xb5, 0xbb, 0x76, 0xd3, 0x0d, 0x79, 0x70, 0xc1, - 0x02, 0x17, 0xe8, 0x7c, 0x6a, 0x3b, 0x56, 0xdb, 0x7e, 0x6c, 0x35, 0x45, 0xa1, 0xae, 0xed, 0xd7, - 0xe8, 0x22, 0x56, 0x6e, 0x27, 0x62, 0x35, 0x91, 0x64, 0xe5, 0x76, 0xfa, 0xb3, 0x8a, 0xe9, 0x24, - 0xab, 0x4d, 0xdb, 0xdf, 0xa1, 0x21, 0x37, 0x7b, 0x86, 0x8b, 0xb9, 0xcb, 0x39, 0x85, 0x55, 0x88, - 0x65, 0x16, 0x9a, 0x40, 0x27, 0x59, 0x29, 0x74, 0x6c, 0xe6, 0x3d, 0xf4, 0xdd, 0x90, 0xaa, 0x2d, - 0x9c, 0xc4, 0xcf, 0xc2, 0xfe, 0x47, 0x83, 0xf1, 0x7e, 0x4d, 0x4c, 0x51, 0xc6, 0xdc, 0x94, 0x46, - 0x4e, 0xa5, 0xb8, 0x65, 0xb7, 0x32, 0x45, 0x19, 0x71, 0x53, 0xdb, 0x39, 0x8d, 0xed, 0x54, 0xb8, - 0xf5, 0x69, 0x68, 0x8a, 0x92, 0xac, 0xb1, 0x4e, 0x0b, 0x99, 0xdc, 0xe4, 0x75, 0x84, 0xd9, 0xf7, - 0x0c, 0x7e, 0xda, 0x4b, 0xc2, 0x76, 0xb1, 0xe4, 0xcb, 0x62, 0x2b, 0xc3, 0x08, 0x3c, 0x49, 0x4c, - 0xfe, 0x02, 0xcc, 0xde, 0x0f, 0xe8, 0xad, 0xfa, 0x46, 0x43, 0x86, 0xfe, 0x41, 0xe5, 0xe2, 0xcc, - 0xe2, 0xf5, 0x63, 0x36, 0x9d, 0xab, 0x2a, 0x0d, 0xe6, 0x9b, 0xe0, 0xe3, 0xd6, 0x0b, 0xa8, 0xb5, - 0xed, 0x76, 0x83, 0x28, 0x8e, 0x9a, 0x3a, 0x6e, 0x89, 0xaa, 0x8c, 0x15, 0x98, 0x4b, 0xb1, 0x21, - 0x33, 0x00, 0x0c, 0x68, 0xdd, 0x5f, 0x6b, 0x2c, 0x6f, 0x96, 0x9e, 0x21, 0x25, 0x98, 0xc2, 0xdf, - 0xcb, 0x6b, 0x95, 0xa5, 0xd5, 0xe5, 0x5a, 0x29, 0x47, 0xe6, 0x60, 0x1a, 0x21, 0xb5, 0x7a, 0x83, - 0x83, 0xf2, 0x3c, 0xda, 0xb8, 0x59, 0xe2, 0x4b, 0x37, 0x64, 0x0b, 0x00, 0xcf, 0x14, 0xe3, 0xaf, - 0xe7, 0xe1, 0x9c, 0x3c, 0x56, 0x68, 0xc8, 0x04, 0x47, 0xb7, 0xb3, 0xf3, 0x94, 0x9f, 0x0e, 0xb7, - 0xb4, 0xd3, 0xe1, 0xa5, 0xc4, 0x49, 0x9d, 0x68, 0xe5, 0x80, 0x23, 0xe2, 0xb7, 0x27, 0xe0, 0xc2, - 0x40, 0x2a, 0xf2, 0x15, 0x76, 0x9a, 0xbb, 0xb4, 0x13, 0xd6, 0x9d, 0x16, 0xdd, 0x74, 0xdb, 0xd4, - 0xeb, 0x85, 0xc2, 0xcd, 0xe0, 0x45, 0xd4, 0xe7, 0x61, 0xa1, 0xe5, 0x3a, 0x2d, 0x6a, 0x85, 0xbc, - 0x58, 0x9b, 0x6e, 0x69, 0x6a, 0xc6, 0x32, 0xca, 0x7d, 0x53, 0xef, 0x84, 0xd4, 0x7f, 0x84, 0x06, - 0x8d, 0x11, 0xcb, 0x3d, 0x4a, 0xbb, 0x96, 0xcd, 0x4a, 0x2d, 0x57, 0x14, 0xeb, 0x2c, 0x53, 0xd4, - 0xe4, 0x96, 0xc2, 0xb2, 0xca, 0x6e, 0xff, 0xf7, 0xec, 0xc7, 0xc2, 0xc2, 0x4a, 0x84, 0x92, 0x8c, - 0x58, 0x72, 0xbf, 0xe6, 0xb6, 0xfd, 0xd8, 0x4c, 0x93, 0x90, 0x6f, 0xc0, 0x69, 0x71, 0x00, 0x89, - 0xb8, 0x14, 0xb2, 0xc5, 0x3c, 0xea, 0xc5, 0x2b, 0x47, 0x87, 0xe5, 0xb3, 0x32, 0x08, 0xa7, 0x8c, - 0x44, 0x92, 0xd5, 0xea, 0x6c, 0x2e, 0x64, 0x93, 0x1d, 0xc8, 0x89, 0xee, 0xb8, 0x47, 0x83, 0x40, - 0x3a, 0xdc, 0x89, 0x9b, 0xb1, 0xda, 0x99, 0x56, 0x9b, 0x97, 0x9b, 0x7d, 0x29, 0xc9, 0x0a, 0xcc, - 0x3c, 0xa4, 0x5b, 0xea, 0xf8, 0x8c, 0x45, 0x5b, 0x55, 0x69, 0x9f, 0x6e, 0xf5, 0x1f, 0x9c, 0x04, - 0x1d, 0x71, 0xf1, 0x7d, 0xe0, 0xf1, 0xc1, 0xaa, 0x1b, 0x84, 0xb4, 0x43, 0x7d, 0x8c, 0x77, 0x34, - 0x8e, 0x9b, 0xc1, 0x42, 0x2c, 0x21, 0xeb, 0xe5, 0x4b, 0x2f, 0x1c, 0x1d, 0x96, 0x2f, 0x70, 0xcf, - 0xd5, 0x96, 0x80, 0x5b, 0x89, 0xcc, 0x31, 0x69, 0xae, 0xe4, 0x5b, 0x30, 0x6b, 0x7a, 0xbd, 0xd0, - 0xed, 0xec, 0x34, 0x42, 0xdf, 0x0e, 0xe9, 0x0e, 0x3f, 0x90, 0xe2, 0xc0, 0x4a, 0x89, 0x52, 0xf1, - 0x2a, 0xcd, 0x81, 0x56, 0x20, 0xa0, 0xda, 0x89, 0xa0, 0x13, 0x90, 0x6f, 0xc2, 0x0c, 0x8f, 0x48, - 0x10, 0x55, 0x30, 0xa1, 0x45, 0xbd, 0xd7, 0x0b, 0x1f, 0x5c, 0x17, 0x06, 0x31, 0x08, 0xcd, 0xaa, - 0x20, 0xc1, 0x8d, 0x7c, 0x4d, 0x74, 0xd6, 0x86, 0xdb, 0xd9, 0x89, 0xa6, 0x31, 0x60, 0xcf, 0xbf, - 0x11, 0x77, 0x49, 0x97, 0x7d, 0xae, 0x9c, 0xc6, 0x7d, 0xac, 0xfb, 0xd2, 0x7c, 0x48, 0x08, 0x17, - 0x2a, 0x41, 0xe0, 0x06, 0xa1, 0x70, 0xc6, 0x59, 0x7e, 0x4c, 0x9b, 0x3d, 0x86, 0xcc, 0xae, 0xb7, - 0xd4, 0xe7, 0xe6, 0xe0, 0xa3, 0x4b, 0x57, 0x8f, 0x0e, 0xcb, 0xaf, 0xda, 0x88, 0x68, 0x09, 0xff, - 0x1d, 0x8b, 0x4a, 0x54, 0x6b, 0x9f, 0xe3, 0x2a, 0x6d, 0x18, 0xcc, 0x94, 0x7c, 0x13, 0xce, 0x54, - 0xed, 0x80, 0xd6, 0x3b, 0x01, 0xed, 0x04, 0x6e, 0xe8, 0x3e, 0xa2, 0xa2, 0x53, 0xf1, 0xf0, 0x2b, - 0x62, 0x8e, 0x1d, 0xa3, 0x69, 0x07, 0x6c, 0x61, 0x46, 0x28, 0x96, 0x18, 0x14, 0xa5, 0x9a, 0x3e, - 0x5c, 0x88, 0x09, 0x33, 0x8d, 0xc6, 0x4a, 0xcd, 0xb5, 0xa3, 0x75, 0x35, 0x8d, 0xfd, 0xf5, 0x2a, - 0xaa, 0xf6, 0x82, 0x5d, 0xcb, 0x71, 0xed, 0x68, 0x41, 0xf5, 0xe9, 0xac, 0x04, 0x07, 0xe3, 0x30, - 0x07, 0xa5, 0xe4, 0x50, 0x92, 0xaf, 0xc2, 0x04, 0x37, 0x8d, 0xa3, 0xc1, 0xae, 0x70, 0xd0, 0x97, - 0x96, 0x56, 0x11, 0x5c, 0x27, 0x12, 0xce, 0x71, 0xdc, 0xf0, 0x8e, 0xaa, 0x86, 0x36, 0xe8, 0x1c, - 0x27, 0x89, 0x88, 0x03, 0x53, 0x7c, 0xb4, 0x28, 0x46, 0x55, 0x13, 0x16, 0xd2, 0x2f, 0xa8, 0xab, - 0x43, 0x14, 0x25, 0xf8, 0xe3, 0xab, 0xa1, 0x98, 0x13, 0x1c, 0x41, 0xab, 0x42, 0xe3, 0xba, 0x04, - 0x50, 0x94, 0x84, 0xc6, 0x39, 0x38, 0xdb, 0xe7, 0x9b, 0x8d, 0x47, 0x68, 0x84, 0xd0, 0xa7, 0x46, - 0xf2, 0x55, 0x38, 0x85, 0x84, 0x55, 0xaf, 0xd3, 0xa1, 0xcd, 0x10, 0xb7, 0x23, 0xa9, 0x7d, 0x2f, - 0x70, 0x23, 0x19, 0xde, 0xde, 0x66, 0x84, 0x60, 0x25, 0x95, 0xf0, 0x99, 0x1c, 0x8c, 0x9f, 0xcf, - 0xc3, 0x82, 0xd8, 0xe1, 0x4c, 0xda, 0xf4, 0x7c, 0xe7, 0xe9, 0x3f, 0x51, 0x97, 0xb5, 0x13, 0xf5, - 0xc5, 0x28, 0x22, 0x4b, 0x56, 0x23, 0x07, 0x1c, 0xa8, 0xbf, 0x96, 0x83, 0xe7, 0x06, 0x11, 0xb1, - 0xde, 0x89, 0xa2, 0xc8, 0x4d, 0xa4, 0xa2, 0xc5, 0x75, 0x61, 0x1e, 0x07, 0xb4, 0xba, 0x4b, 0x9b, - 0x7b, 0xc1, 0x8a, 0x17, 0x84, 0xe8, 0xa0, 0x91, 0xef, 0xf3, 0xd6, 0xfd, 0x7a, 0xe6, 0x5b, 0xf7, - 0x19, 0x3e, 0xcb, 0x9a, 0xc8, 0x83, 0xc7, 0xb9, 0xdb, 0xa3, 0x07, 0x81, 0x99, 0xc5, 0x1a, 0x8d, - 0xed, 0x2b, 0xbd, 0x70, 0x77, 0xc3, 0xa7, 0xdb, 0xd4, 0xa7, 0x9d, 0x26, 0xfd, 0x8c, 0x19, 0xdb, - 0xeb, 0x8d, 0x1b, 0x4a, 0x83, 0xf1, 0x6b, 0x53, 0x70, 0x2a, 0x8b, 0x8c, 0xf5, 0x8b, 0x72, 0x69, - 0x4e, 0xa6, 0x00, 0xfc, 0xb1, 0x1c, 0x4c, 0x35, 0x68, 0xd3, 0xeb, 0x38, 0xb7, 0xd0, 0x18, 0x49, - 0xf4, 0x8e, 0xc5, 0x85, 0x06, 0x06, 0xb7, 0xb6, 0x13, 0x56, 0x4a, 0x1f, 0x1d, 0x96, 0xbf, 0x3c, - 0xdc, 0x5d, 0xb5, 0xe9, 0x61, 0x54, 0x95, 0x10, 0x43, 0xd4, 0x47, 0x55, 0xe0, 0xe3, 0xa0, 0x56, - 0x29, 0x59, 0x82, 0x69, 0xb1, 0x5c, 0x3d, 0x35, 0x88, 0x20, 0x0f, 0x5a, 0x23, 0x0b, 0x52, 0xaa, - 0x6b, 0x8d, 0x84, 0xdc, 0x80, 0xc2, 0xfd, 0xc5, 0x5b, 0x62, 0x0c, 0x64, 0x90, 0xff, 0xfb, 0x8b, - 0xb7, 0x50, 0x1d, 0xc6, 0xae, 0x18, 0xd3, 0xbd, 0x45, 0xcd, 0xc8, 0xe7, 0xfe, 0xe2, 0x2d, 0xf2, - 0x97, 0xe0, 0x74, 0xcd, 0x0d, 0x44, 0x15, 0xdc, 0xed, 0xc3, 0x41, 0x37, 0xc7, 0xb1, 0x3e, 0xb3, - 0xf7, 0x0b, 0x99, 0xb3, 0xf7, 0x05, 0x27, 0x62, 0x62, 0x71, 0x9f, 0x12, 0x27, 0x19, 0x2c, 0x31, - 0xbb, 0x1e, 0xf2, 0x21, 0xcc, 0xa0, 0x32, 0x1b, 0x3d, 0x61, 0x30, 0xcc, 0xf5, 0x78, 0x9f, 0x9a, - 0x3f, 0x97, 0x59, 0xf3, 0x79, 0x1e, 0x2c, 0x01, 0xfd, 0x69, 0x30, 0x24, 0xb6, 0x76, 0xeb, 0xd7, - 0x38, 0x93, 0x3b, 0x30, 0x2b, 0xc4, 0xaf, 0xf5, 0xed, 0xcd, 0x5d, 0x5a, 0xb3, 0x0f, 0x84, 0x7d, - 0x0e, 0xde, 0xe8, 0x84, 0xcc, 0x66, 0x79, 0xdb, 0x56, 0xb8, 0x4b, 0x2d, 0xc7, 0xd6, 0x04, 0x95, - 0x04, 0x21, 0xf9, 0x2e, 0x4c, 0xae, 0x7a, 0x4d, 0x26, 0x79, 0xe3, 0xce, 0xc0, 0x4d, 0x76, 0x3e, - 0xc0, 0x24, 0x73, 0x1c, 0x9c, 0x10, 0xa7, 0x3e, 0x3a, 0x2c, 0xbf, 0x7d, 0xd2, 0x49, 0xa3, 0x54, - 0x60, 0xaa, 0xb5, 0x91, 0x2a, 0x14, 0x1f, 0xd2, 0x2d, 0xd6, 0xda, 0x64, 0x02, 0x2a, 0x09, 0x16, - 0xc6, 0x7c, 0xe2, 0x97, 0x66, 0xcc, 0x27, 0x60, 0xc4, 0x87, 0x39, 0xec, 0x9f, 0x0d, 0x3b, 0x08, - 0xf6, 0x3d, 0xdf, 0xc1, 0x4c, 0x03, 0xfd, 0xac, 0x81, 0x16, 0x33, 0x3b, 0xff, 0x39, 0xde, 0xf9, - 0x5d, 0x85, 0x83, 0x2a, 0x40, 0xa6, 0xd8, 0x93, 0x6f, 0xc1, 0x8c, 0xf0, 0xf0, 0xbf, 0x77, 0xab, - 0x82, 0xab, 0x72, 0x4a, 0x73, 0x16, 0xd5, 0x0b, 0xb9, 0x94, 0x2a, 0x02, 0x06, 0x48, 0x0d, 0x94, - 0xd5, 0xde, 0xb6, 0x75, 0xa5, 0xbf, 0x4a, 0x42, 0x36, 0x60, 0xb2, 0x86, 0x69, 0x50, 0xd1, 0xa5, - 0x4d, 0x98, 0x94, 0x47, 0x19, 0x74, 0xe2, 0x12, 0xae, 0x8b, 0x11, 0x19, 0x53, 0xd1, 0x41, 0x4e, - 0x37, 0xf3, 0x8d, 0x10, 0xc9, 0x4d, 0x28, 0xd4, 0x6b, 0x1b, 0xc2, 0xa2, 0x5c, 0x7a, 0x8a, 0xd5, - 0x9d, 0x0d, 0x99, 0x6f, 0x04, 0xed, 0xe7, 0x5c, 0x47, 0xb3, 0x47, 0xaf, 0xd7, 0x36, 0xc8, 0x36, - 0x4c, 0x63, 0x07, 0xac, 0x50, 0x9b, 0xf7, 0xed, 0x6c, 0x9f, 0xbe, 0xbd, 0x9a, 0xd9, 0xb7, 0x0b, - 0xbc, 0x6f, 0x77, 0x05, 0xb5, 0x96, 0x40, 0x41, 0x65, 0xcb, 0x44, 0x5a, 0x91, 0xd4, 0x45, 0x86, - 0xfd, 0xdf, 0x5c, 0x45, 0xfb, 0x20, 0x21, 0xd2, 0xca, 0x1c, 0x30, 0x51, 0x1e, 0x82, 0xbe, 0x0e, - 0x2b, 0x69, 0x3e, 0xe4, 0x4b, 0x30, 0xb2, 0xbe, 0x17, 0xda, 0xc2, 0x76, 0x5c, 0xf6, 0x23, 0x03, - 0xc9, 0xe6, 0xa3, 0x16, 0xd2, 0xdb, 0xd3, 0xc2, 0x69, 0x21, 0x0d, 0x59, 0x84, 0xf1, 0x8d, 0xfa, - 0x83, 0x46, 0xcb, 0x0b, 0x17, 0x48, 0x74, 0x4f, 0x22, 0x5d, 0xf7, 0x91, 0x15, 0xb4, 0x3c, 0x3d, - 0x31, 0x94, 0x44, 0x64, 0xc3, 0xb7, 0x62, 0xfb, 0xce, 0xbe, 0xed, 0xa3, 0x27, 0xf2, 0xbc, 0x56, - 0xad, 0x52, 0xc2, 0x87, 0x6f, 0x57, 0x00, 0x12, 0xee, 0xc9, 0x2a, 0x0b, 0xa1, 0x61, 0x98, 0x13, - 0xd3, 0x44, 0x34, 0xed, 0xde, 0xad, 0x8a, 0xf1, 0xf7, 0x73, 0xb8, 0x61, 0x92, 0x57, 0x31, 0xe2, - 0x4e, 0xf4, 0x52, 0x85, 0xba, 0x52, 0xbb, 0x9b, 0x88, 0x71, 0xcd, 0x51, 0xc8, 0xeb, 0x30, 0x76, - 0xcb, 0x6e, 0xd2, 0x50, 0xbe, 0x8d, 0x23, 0xf2, 0x36, 0x42, 0x54, 0xc5, 0x2a, 0xc7, 0x61, 0xb2, - 0x1c, 0x9f, 0x48, 0x95, 0x38, 0x93, 0x6f, 0xb5, 0x22, 0x9f, 0xc6, 0x51, 0x96, 0x13, 0x13, 0x50, - 0x49, 0xf5, 0x9b, 0x30, 0x5e, 0xcf, 0xe4, 0x60, 0xfc, 0x59, 0x2e, 0xde, 0x01, 0xc8, 0x2b, 0x30, - 0x62, 0x6e, 0x44, 0xdf, 0xcf, 0xbd, 0x72, 0x13, 0x9f, 0x8f, 0x08, 0xe4, 0x6b, 0x70, 0x5a, 0xe1, - 0x93, 0xb2, 0xa4, 0x7f, 0x19, 0xdd, 0x46, 0x95, 0x2f, 0xc9, 0x36, 0xa7, 0xcf, 0xe6, 0x81, 0x82, - 0x6b, 0x5c, 0x50, 0xa3, 0x1d, 0x97, 0xf3, 0x56, 0x1a, 0xab, 0xf2, 0x76, 0x10, 0x21, 0xd9, 0xd8, - 0x2c, 0x0e, 0xdc, 0x73, 0xd4, 0xf8, 0xad, 0x9c, 0xb6, 0xb2, 0xa3, 0xd4, 0xa7, 0xb9, 0x63, 0x52, - 0x9f, 0xbe, 0x05, 0x50, 0xe9, 0x85, 0xde, 0x72, 0xc7, 0xf7, 0x5a, 0x5c, 0x63, 0x21, 0xc2, 0xbc, - 0xa3, 0x1e, 0x96, 0x22, 0x58, 0x73, 0x70, 0x8b, 0x90, 0x33, 0x9d, 0x0e, 0x0a, 0x1f, 0xd7, 0xe9, - 0xc0, 0xf8, 0x83, 0x9c, 0x36, 0xb7, 0x99, 0x44, 0x26, 0x97, 0x87, 0x62, 0x9d, 0x95, 0x5e, 0x1e, - 0xf1, 0xe2, 0xf8, 0xb7, 0x73, 0x70, 0x86, 0x5b, 0xef, 0xaf, 0xf5, 0xda, 0x5b, 0xd4, 0x7f, 0x60, - 0xb7, 0x5c, 0x87, 0x7b, 0x46, 0x73, 0x61, 0xf3, 0x72, 0x7a, 0xa1, 0x64, 0xe3, 0xf3, 0x4b, 0x21, - 0xf7, 0x26, 0xb0, 0x3a, 0x58, 0x68, 0x3d, 0x8a, 0x4a, 0xd5, 0x4b, 0x61, 0x36, 0xbd, 0xf1, 0xeb, - 0x39, 0x78, 0xe1, 0xd8, 0x5a, 0xc8, 0x35, 0x18, 0x97, 0xf1, 0xf5, 0x73, 0xd8, 0xf1, 0x68, 0xd3, - 0x9a, 0x8e, 0xad, 0x2f, 0xb1, 0xc8, 0xd7, 0xe1, 0xb4, 0xca, 0x6a, 0xd3, 0xb7, 0x5d, 0x35, 0x8a, - 0x7d, 0xc6, 0x57, 0x87, 0x0c, 0x25, 0x29, 0x19, 0x65, 0x33, 0x31, 0xfe, 0xdf, 0x9c, 0x92, 0x0c, - 0xf9, 0x29, 0x95, 0x97, 0x6f, 0x6a, 0xf2, 0xb2, 0x8c, 0x75, 0x18, 0xb5, 0x8a, 0x95, 0x65, 0xde, - 0x71, 0x66, 0x15, 0xdb, 0x6c, 0x04, 0x7c, 0x3f, 0x0f, 0x93, 0xf7, 0x03, 0xea, 0xf3, 0x47, 0xd3, - 0xcf, 0x56, 0x4c, 0xbb, 0xa8, 0x5d, 0x43, 0x45, 0x1d, 0xfb, 0x93, 0x1c, 0x2a, 0xd3, 0x55, 0x0a, - 0xd6, 0x1b, 0x4a, 0x02, 0x34, 0xec, 0x0d, 0x4c, 0x7d, 0x86, 0x50, 0x1e, 0xec, 0x6a, 0x55, 0xcf, - 0x85, 0x88, 0x09, 0x31, 0x57, 0xc9, 0x97, 0x61, 0xf4, 0x3e, 0xaa, 0x06, 0xf5, 0x28, 0x18, 0x11, - 0x7f, 0x2c, 0xe4, 0x9b, 0x74, 0x2f, 0xd0, 0x03, 0x74, 0x71, 0x42, 0xd2, 0x80, 0xf1, 0xaa, 0x4f, - 0x31, 0xb5, 0xf1, 0xc8, 0xf0, 0x9e, 0xdc, 0x4d, 0x4e, 0x92, 0xf4, 0xe4, 0x16, 0x9c, 0x8c, 0x9f, - 0xcb, 0x03, 0x89, 0xdb, 0x88, 0x79, 0x7c, 0x82, 0xa7, 0x76, 0xd0, 0xdf, 0xd7, 0x06, 0xfd, 0x42, - 0x6a, 0xd0, 0x79, 0xf3, 0x86, 0x1a, 0xfb, 0xdf, 0xc9, 0xc1, 0x99, 0x6c, 0x42, 0xf2, 0x22, 0x8c, - 0xad, 0x6f, 0x6e, 0xc8, 0x40, 0x2a, 0xa2, 0x29, 0x5e, 0x17, 0xef, 0xe5, 0xa6, 0x28, 0x22, 0x6f, - 0xc0, 0xd8, 0x57, 0xcc, 0x2a, 0x3b, 0x87, 0x94, 0x48, 0xf1, 0xdf, 0xf6, 0xad, 0xa6, 0x7e, 0x14, - 0x09, 0x24, 0x75, 0x6c, 0x0b, 0x4f, 0x6c, 0x6c, 0x7f, 0x2a, 0x0f, 0xb3, 0x95, 0x66, 0x93, 0x06, - 0x01, 0x13, 0x72, 0x68, 0x10, 0x3e, 0xb5, 0x03, 0x9b, 0x1d, 0x22, 0x45, 0x6b, 0xdb, 0x50, 0xa3, - 0xfa, 0x7b, 0x39, 0x38, 0x2d, 0xa9, 0x1e, 0xb9, 0x74, 0x7f, 0x73, 0xd7, 0xa7, 0xc1, 0xae, 0xd7, - 0x72, 0x86, 0x4e, 0x47, 0xc1, 0x04, 0x3d, 0x8c, 0x31, 0xad, 0xbe, 0xa0, 0x6f, 0x23, 0x44, 0x13, - 0xf4, 0x78, 0x1c, 0xea, 0x6b, 0x30, 0x5e, 0xe9, 0x76, 0x7d, 0xef, 0x11, 0x5f, 0xf6, 0x22, 0x04, - 0x9f, 0xcd, 0x41, 0x9a, 0x23, 0x3c, 0x07, 0xb1, 0xcf, 0xa8, 0xd1, 0x0e, 0x0f, 0x2e, 0x37, 0xcd, - 0x3f, 0xc3, 0xa1, 0x1d, 0x55, 0x86, 0xc5, 0x72, 0xa3, 0x01, 0x64, 0xc3, 0xf7, 0xda, 0x5e, 0x48, - 0x1d, 0xde, 0x1e, 0x8c, 0x1f, 0x70, 0x6c, 0x30, 0xaa, 0x4d, 0x37, 0x6c, 0x69, 0xc1, 0xa8, 0x42, - 0x06, 0x30, 0x39, 0xdc, 0xf8, 0x3f, 0x47, 0x61, 0x4a, 0xed, 0x1d, 0x62, 0xf0, 0x18, 0xf3, 0x9e, - 0xaf, 0x06, 0xb1, 0xb0, 0x11, 0x62, 0x8a, 0x92, 0x38, 0xf6, 0x4b, 0xfe, 0xd8, 0xd8, 0x2f, 0x0f, - 0x61, 0x7a, 0xc3, 0xf7, 0x30, 0x56, 0x20, 0x4f, 0x79, 0xcf, 0xb7, 0xc2, 0x79, 0xe5, 0x8e, 0xc7, - 0x06, 0x12, 0xdf, 0x1e, 0x51, 0xc3, 0xd1, 0x15, 0xd8, 0x56, 0x32, 0x21, 0xbe, 0xce, 0x87, 0x9b, - 0x35, 0xd8, 0x81, 0x08, 0xf8, 0x19, 0x99, 0x35, 0x30, 0x88, 0x6e, 0xd6, 0xc0, 0x20, 0xea, 0x5a, - 0x1b, 0x7d, 0x52, 0x6b, 0x8d, 0xfc, 0x5c, 0x0e, 0x26, 0x2b, 0x9d, 0x8e, 0x88, 0x29, 0x73, 0x8c, - 0x53, 0xfd, 0xd7, 0x85, 0x65, 0xc3, 0xdb, 0x1f, 0xcb, 0xb2, 0x01, 0xe5, 0x96, 0x00, 0x25, 0xd5, - 0xb8, 0x42, 0xf5, 0x96, 0xa3, 0x7c, 0x07, 0x79, 0x1b, 0x4a, 0xd1, 0x24, 0xaf, 0x77, 0x1c, 0xfa, - 0x98, 0xf2, 0x1c, 0x5d, 0xd3, 0x22, 0xd4, 0xaf, 0x2a, 0x99, 0x26, 0x11, 0xc9, 0x26, 0x80, 0x1d, - 0xcd, 0xae, 0x44, 0xb2, 0xc1, 0xf4, 0xf4, 0x13, 0xd2, 0x33, 0xfe, 0xc6, 0xc7, 0x23, 0x55, 0x7a, - 0x8e, 0xf9, 0x90, 0x36, 0xcc, 0xf2, 0x4c, 0x7f, 0x8d, 0xd0, 0xf6, 0x43, 0x8c, 0x68, 0x0f, 0xc7, - 0x8e, 0xc3, 0x2b, 0x42, 0x57, 0xf5, 0xac, 0xc8, 0x1f, 0x18, 0x30, 0x5a, 0x2b, 0x23, 0xbc, 0x7d, - 0x92, 0x37, 0x0f, 0xac, 0x6c, 0x9e, 0x4d, 0x7f, 0x2f, 0x9f, 0xf4, 0x3f, 0x95, 0x83, 0x33, 0xea, - 0xa4, 0x6f, 0xf4, 0xb6, 0xda, 0x2e, 0xde, 0x05, 0xc9, 0x55, 0x98, 0x10, 0x73, 0x32, 0xba, 0x44, - 0xa5, 0x03, 0xf3, 0xc7, 0x28, 0x64, 0x99, 0x4d, 0x43, 0xc6, 0x43, 0x48, 0xdd, 0xf3, 0x89, 0x7d, - 0x8a, 0x15, 0xc5, 0x59, 0x64, 0x7d, 0xfc, 0xad, 0xcf, 0x4f, 0x06, 0x31, 0xde, 0x83, 0x39, 0x7d, - 0x24, 0x1a, 0x34, 0x24, 0x57, 0x60, 0x5c, 0x0e, 0x5f, 0x2e, 0x7b, 0xf8, 0x64, 0xb9, 0xf1, 0x10, - 0x48, 0x8a, 0x3e, 0x40, 0x13, 0x24, 0x76, 0x3f, 0xe5, 0x26, 0x72, 0xf2, 0x01, 0x30, 0x85, 0xb8, - 0x34, 0x2f, 0xbe, 0x6f, 0x52, 0x73, 0x01, 0x60, 0xa4, 0xc6, 0x9f, 0xcd, 0xc0, 0x7c, 0xc6, 0x9e, - 0x7b, 0x8c, 0x4c, 0x54, 0xd6, 0x37, 0x88, 0x89, 0x28, 0x26, 0x87, 0xdc, 0x16, 0xde, 0x83, 0xd1, - 0x63, 0xb7, 0x03, 0xee, 0x00, 0x92, 0xd8, 0x05, 0x38, 0xd9, 0xa7, 0x22, 0x17, 0xa9, 0x61, 0x73, - 0x46, 0x9f, 0x58, 0xd8, 0x9c, 0x25, 0x98, 0x16, 0xad, 0x12, 0xdb, 0x95, 0x62, 0x88, 0xec, 0xf3, - 0x02, 0x2b, 0xb5, 0x6d, 0xe9, 0x24, 0x9c, 0x47, 0xe0, 0xb5, 0x1e, 0x51, 0xc1, 0x63, 0x5c, 0xe5, - 0x81, 0x05, 0x99, 0x3c, 0x14, 0x12, 0xf2, 0x9f, 0x60, 0x96, 0x31, 0x84, 0xa8, 0x7b, 0x56, 0x71, - 0xd0, 0x9e, 0xe5, 0x3c, 0x99, 0x3d, 0xeb, 0x82, 0xfc, 0xc6, 0xec, 0xbd, 0x2b, 0xe3, 0xb3, 0xc8, - 0xaf, 0xe6, 0x60, 0x8e, 0xc7, 0x6e, 0x51, 0x3f, 0x76, 0x60, 0x3c, 0x8e, 0xe6, 0x93, 0xf9, 0xd8, - 0xe7, 0x44, 0x76, 0x9d, 0xec, 0x6f, 0x4d, 0x7f, 0x14, 0xf9, 0x21, 0x80, 0x68, 0x45, 0xf1, 0xe0, - 0xa6, 0x93, 0x8b, 0xcf, 0x65, 0xec, 0x02, 0x11, 0x52, 0x9c, 0x09, 0x20, 0x8c, 0xe8, 0xb4, 0xdc, - 0x72, 0x11, 0x94, 0xfc, 0x25, 0x38, 0xc5, 0xd6, 0x4b, 0x04, 0x11, 0x91, 0xa6, 0x16, 0x26, 0xb1, - 0x96, 0xcf, 0xf7, 0x97, 0x89, 0xae, 0x66, 0x91, 0xf1, 0x50, 0xb8, 0x71, 0x9a, 0xdf, 0x50, 0x0d, - 0x4a, 0x91, 0x59, 0x11, 0x86, 0x6e, 0xc3, 0xaf, 0xe7, 0xd1, 0xfa, 0xfb, 0xec, 0x6f, 0xe7, 0xe4, - 0x5a, 0xe0, 0xfb, 0x5b, 0xa0, 0xfb, 0x03, 0x23, 0x88, 0x7c, 0x05, 0x48, 0x14, 0xf4, 0x84, 0xc3, - 0xa8, 0x8c, 0xe4, 0xcf, 0x55, 0xbb, 0x71, 0xf0, 0x14, 0x5f, 0x16, 0xab, 0x93, 0x24, 0x4d, 0x4c, - 0x28, 0x9c, 0x12, 0x8d, 0x66, 0x50, 0x99, 0x02, 0x2c, 0x58, 0x98, 0xd1, 0xe2, 0x78, 0xc5, 0x25, - 0x71, 0x3e, 0x60, 0x25, 0x8f, 0x98, 0xa6, 0x72, 0xca, 0x62, 0x47, 0x6e, 0xc2, 0x04, 0x3a, 0xe5, - 0xae, 0x48, 0xc3, 0x2a, 0x61, 0xe4, 0x81, 0xee, 0xbb, 0xd6, 0xae, 0x6e, 0x1e, 0x15, 0xa3, 0xb2, - 0xeb, 0x40, 0xcd, 0x3f, 0x30, 0x7b, 0x1d, 0x54, 0xc0, 0x0a, 0x7d, 0x87, 0xe3, 0x1f, 0x58, 0x7e, - 0x4f, 0x77, 0xf8, 0x46, 0x24, 0xf2, 0x2d, 0x98, 0xbc, 0x67, 0x3f, 0x96, 0xfa, 0x57, 0xa1, 0x64, - 0x1d, 0xb4, 0x03, 0x19, 0xb2, 0x35, 0x6d, 0xfb, 0xb1, 0xe5, 0xf4, 0x92, 0x81, 0x78, 0x71, 0x1b, - 0x52, 0x59, 0x92, 0x6f, 0x00, 0x28, 0x5a, 0x61, 0x72, 0x6c, 0x05, 0x2f, 0xc8, 0xc8, 0x74, 0x99, - 0xda, 0x62, 0xe4, 0xaf, 0x30, 0x4c, 0x48, 0x0e, 0xa7, 0x3e, 0x3d, 0xc9, 0xe1, 0xf4, 0xa7, 0x27, - 0x39, 0x9c, 0xdf, 0x82, 0x73, 0x7d, 0x97, 0x4e, 0x46, 0xb8, 0xe0, 0x6b, 0x7a, 0xb8, 0xe0, 0x73, - 0xfd, 0x8e, 0xd8, 0x40, 0x4f, 0xfe, 0x30, 0x5f, 0x3a, 0xd5, 0x5f, 0x3a, 0xf9, 0x41, 0x3e, 0x71, - 0xe4, 0x8a, 0x8b, 0x05, 0x4f, 0x16, 0xd4, 0x4f, 0x26, 0xc9, 0x63, 0x7e, 0x58, 0x7e, 0x28, 0xe7, - 0xe3, 0x0b, 0x4d, 0x22, 0xa5, 0x3e, 0x3f, 0x9e, 0x3f, 0xe9, 0xe9, 0xfb, 0x0e, 0xcc, 0xf0, 0x94, - 0x8e, 0x77, 0xe9, 0xc1, 0xbe, 0xe7, 0x3b, 0x32, 0x6f, 0x3a, 0xca, 0xe0, 0xa9, 0x7c, 0xcc, 0x09, - 0x5c, 0x52, 0x93, 0x7e, 0x9e, 0xa3, 0x58, 0xfb, 0xb9, 0xcc, 0x5d, 0x8c, 0x21, 0x0c, 0x72, 0x01, - 0x25, 0x6f, 0x46, 0x82, 0x1a, 0xf5, 0xd5, 0x14, 0x10, 0xbe, 0x04, 0x66, 0xc8, 0x6b, 0xd4, 0x37, - 0xfe, 0xa8, 0x00, 0x84, 0xd7, 0x54, 0xb5, 0xbb, 0x36, 0x7a, 0x41, 0xbb, 0x18, 0x12, 0xa9, 0x24, - 0x70, 0xec, 0xad, 0x16, 0x55, 0xe3, 0x89, 0x09, 0x43, 0xd6, 0xa8, 0xcc, 0x4a, 0x5e, 0x74, 0x52, - 0x84, 0x7d, 0xb6, 0xba, 0xfc, 0x27, 0xd9, 0xea, 0xbe, 0x05, 0xcf, 0x56, 0xba, 0x98, 0x1b, 0x56, - 0xd6, 0x72, 0xcb, 0xf3, 0xe5, 0x26, 0xa5, 0xf9, 0xd7, 0xd9, 0x11, 0x5a, 0xea, 0x4b, 0x07, 0xb1, - 0x50, 0xe4, 0x14, 0x36, 0x2f, 0xbb, 0xa1, 0x1a, 0xaf, 0x41, 0xca, 0x29, 0x5d, 0x2c, 0xc9, 0x90, - 0x53, 0x38, 0x89, 0xe4, 0xe1, 0xfa, 0x52, 0x4e, 0xc1, 0xa4, 0x47, 0x31, 0x0f, 0xd7, 0xa7, 0x7d, - 0x64, 0x9d, 0x88, 0x84, 0xbc, 0x03, 0x93, 0x95, 0x5e, 0xe8, 0x09, 0xc6, 0xc2, 0x02, 0x3b, 0xb6, - 0x95, 0x16, 0x9f, 0xa2, 0x5d, 0x7d, 0x62, 0x74, 0xe3, 0x4f, 0x0b, 0x70, 0x2e, 0x3d, 0xbc, 0xa2, - 0x34, 0x5a, 0x1f, 0xb9, 0x63, 0xd6, 0x47, 0xd6, 0x6c, 0xc8, 0xc7, 0x21, 0xf9, 0x9f, 0xc4, 0x6c, - 0xe0, 0x29, 0x66, 0x3f, 0xe6, 0x6c, 0x68, 0xc0, 0xa4, 0x7a, 0xde, 0x8d, 0x7c, 0xdc, 0xf3, 0x4e, - 0xe5, 0xc2, 0x2e, 0xf5, 0x3c, 0x4c, 0xc5, 0x68, 0xfc, 0x74, 0x94, 0x8c, 0x50, 0xc1, 0x31, 0xc8, - 0xbf, 0x05, 0x17, 0xf9, 0x9e, 0x94, 0x6c, 0xec, 0xd2, 0x81, 0xe4, 0x28, 0x06, 0x6e, 0xf1, 0xe8, - 0xb0, 0x7c, 0x95, 0xab, 0x4a, 0xac, 0x54, 0xb7, 0x59, 0x5b, 0x07, 0x96, 0xfc, 0x32, 0xa5, 0x92, - 0x63, 0x79, 0x1b, 0x55, 0x38, 0x27, 0x4a, 0x63, 0x07, 0x69, 0x59, 0xc8, 0x06, 0x79, 0x2f, 0xd6, - 0x76, 0xe1, 0x20, 0x27, 0x14, 0x59, 0x58, 0x8e, 0xc9, 0x69, 0x95, 0xc4, 0xa1, 0x6f, 0x64, 0xf9, - 0xb7, 0xf0, 0xb0, 0xda, 0x1c, 0xac, 0xbb, 0xb6, 0x48, 0x9d, 0x5a, 0x3e, 0x53, 0xa7, 0x26, 0x95, - 0x32, 0x85, 0x4c, 0xa5, 0x4c, 0x0d, 0x66, 0x1b, 0xbd, 0x2d, 0x59, 0x77, 0xd2, 0x37, 0x32, 0xe8, - 0x6d, 0x65, 0xf5, 0x4a, 0x92, 0xc4, 0xf8, 0xf1, 0x3c, 0x4c, 0x6d, 0xb4, 0x7a, 0x3b, 0x6e, 0xa7, - 0x66, 0x87, 0xf6, 0x53, 0xab, 0xe6, 0x7b, 0x4b, 0x53, 0xf3, 0x45, 0x6e, 0x5c, 0x51, 0xc3, 0x86, - 0xd2, 0xf1, 0xfd, 0x6c, 0x0e, 0x66, 0x63, 0x12, 0x7e, 0x58, 0xaf, 0xc0, 0x08, 0xfb, 0x21, 0x2e, - 0xbf, 0x17, 0x53, 0x8c, 0x79, 0xb6, 0xba, 0xe8, 0x2f, 0xa1, 0x78, 0xd3, 0x53, 0x41, 0x21, 0x87, - 0xf3, 0x5f, 0x80, 0x89, 0x98, 0xed, 0x49, 0xb2, 0xd4, 0xfd, 0x46, 0x0e, 0x4a, 0xc9, 0x96, 0x90, - 0xbb, 0x30, 0xce, 0x38, 0xb9, 0x54, 0xde, 0xcb, 0x5f, 0xea, 0xd3, 0xe6, 0xab, 0x02, 0x8d, 0x7f, - 0x1e, 0x76, 0x3e, 0xe5, 0x10, 0x53, 0x72, 0x38, 0x6f, 0xc2, 0x94, 0x8a, 0x95, 0xf1, 0x75, 0xaf, - 0xeb, 0x12, 0xca, 0x99, 0xec, 0x7e, 0xd0, 0x72, 0xeb, 0x69, 0x5f, 0x2d, 0x84, 0x8f, 0x4b, 0xda, - 0xe4, 0xca, 0x5c, 0x55, 0x38, 0x69, 0x16, 0xe3, 0x48, 0xff, 0xea, 0x3c, 0xcb, 0x98, 0xd0, 0x11, - 0x1e, 0x79, 0x1d, 0xc6, 0x78, 0x7d, 0x6a, 0x8e, 0xa9, 0x2e, 0x42, 0x54, 0x39, 0x99, 0xe3, 0x18, - 0x7f, 0xa3, 0x00, 0x67, 0xe2, 0xcf, 0xbb, 0xdf, 0x75, 0xec, 0x90, 0x6e, 0xd8, 0xbe, 0xdd, 0x0e, - 0x8e, 0x59, 0x01, 0x97, 0x53, 0x9f, 0x86, 0x39, 0x87, 0xe4, 0xa7, 0x29, 0x1f, 0x64, 0x24, 0x3e, - 0x08, 0x75, 0xa0, 0xfc, 0x83, 0xe4, 0x67, 0x90, 0xbb, 0x50, 0x68, 0xd0, 0x50, 0xec, 0xbd, 0x97, - 0x52, 0xbd, 0xaa, 0x7e, 0xd7, 0xd5, 0x06, 0x0d, 0xf9, 0x20, 0xf2, 0x18, 0x4c, 0x5a, 0x0c, 0x3d, - 0xc6, 0x85, 0x3c, 0x84, 0xb1, 0xe5, 0xc7, 0x5d, 0xda, 0x0c, 0x45, 0x8e, 0xc5, 0x2b, 0x83, 0xf9, - 0x71, 0x5c, 0x25, 0xc5, 0x22, 0x45, 0x80, 0xda, 0x59, 0x1c, 0xe5, 0xfc, 0x4d, 0x28, 0xca, 0xca, - 0x4f, 0x32, 0x73, 0xcf, 0xbf, 0x05, 0x93, 0x4a, 0x25, 0x27, 0x9a, 0xf4, 0xbf, 0xc4, 0xf6, 0x55, - 0xaf, 0x25, 0xd3, 0x32, 0x2e, 0xa7, 0x64, 0x45, 0x25, 0x6d, 0x0f, 0x97, 0x15, 0xad, 0x3d, 0x51, - 0x34, 0x40, 0x68, 0xac, 0xc3, 0x6c, 0x63, 0xcf, 0xed, 0xc6, 0xf1, 0x5c, 0xb5, 0x13, 0x19, 0x73, - 0xb0, 0x88, 0x8b, 0x7b, 0xf2, 0x44, 0x4e, 0xd2, 0x19, 0x7f, 0x9e, 0x83, 0x31, 0xf6, 0xd7, 0x83, - 0x9b, 0x4f, 0xe9, 0x96, 0x79, 0x43, 0xdb, 0x32, 0xe7, 0x94, 0x60, 0xea, 0xb8, 0x71, 0xdc, 0x3c, - 0x66, 0xb3, 0x3c, 0x14, 0x03, 0xc4, 0x91, 0xc9, 0x6d, 0x18, 0x17, 0xe6, 0x3b, 0xc2, 0xce, 0x5a, - 0x8d, 0xce, 0x2e, 0x0d, 0x7b, 0xa2, 0x1b, 0xbe, 0xd7, 0x4d, 0xaa, 0x44, 0x24, 0x35, 0x93, 0xeb, - 0x65, 0x64, 0x5d, 0x2d, 0x99, 0xaf, 0x87, 0x8e, 0x71, 0x3c, 0xba, 0xb8, 0x92, 0x7e, 0xbb, 0x8f, - 0x1f, 0x7b, 0x45, 0xbc, 0x86, 0x14, 0x06, 0x31, 0x39, 0x23, 0x73, 0x9d, 0x66, 0x3e, 0x94, 0xfc, - 0xe3, 0xd3, 0x3c, 0x2e, 0xb7, 0xfc, 0xb0, 0x77, 0x61, 0xea, 0x96, 0xe7, 0xef, 0xdb, 0x3e, 0x8f, - 0xb6, 0x2a, 0xcc, 0x0f, 0xd8, 0xfd, 0x73, 0x7a, 0x9b, 0xc3, 0x79, 0xbc, 0xd6, 0x8f, 0x0e, 0xcb, - 0x23, 0x4b, 0x9e, 0xd7, 0x32, 0x35, 0x74, 0xb2, 0x0e, 0xd3, 0xf7, 0xec, 0xc7, 0xca, 0xcd, 0x99, - 0x7b, 0xba, 0x5c, 0x61, 0x13, 0x98, 0x5d, 0xbd, 0x8f, 0xb7, 0xa5, 0xd2, 0xe9, 0x89, 0x0b, 0x33, - 0x1b, 0x9e, 0x1f, 0x8a, 0x4a, 0xdc, 0xce, 0x8e, 0x68, 0x6c, 0xda, 0x1a, 0xec, 0x5a, 0xa6, 0x35, - 0xd8, 0xb9, 0xae, 0xe7, 0x87, 0xd6, 0x76, 0x44, 0xae, 0x05, 0x2f, 0xd3, 0x18, 0x93, 0x77, 0x61, - 0x4e, 0x09, 0x09, 0x79, 0xcb, 0xf3, 0xdb, 0xb6, 0x94, 0xec, 0x51, 0x99, 0x8c, 0x46, 0x2b, 0xdb, - 0x08, 0x36, 0xd3, 0x98, 0xe4, 0x6b, 0x59, 0xbe, 0x43, 0xa3, 0xb1, 0x39, 0x59, 0x86, 0xef, 0x50, - 0x3f, 0x73, 0xb2, 0xb4, 0x17, 0xd1, 0xce, 0x20, 0x73, 0xd3, 0xe2, 0xd2, 0x75, 0x71, 0x87, 0x3f, - 0xde, 0x9c, 0x34, 0x1a, 0xb7, 0x3e, 0x66, 0xa5, 0x8b, 0x50, 0x58, 0xda, 0xb8, 0x85, 0x4f, 0x20, - 0xd2, 0x5a, 0xa7, 0xb3, 0x6b, 0x77, 0x9a, 0x28, 0x71, 0x0b, 0x13, 0x6f, 0x75, 0x47, 0x5e, 0xda, - 0xb8, 0x45, 0x6c, 0x98, 0xdf, 0xa0, 0x7e, 0xdb, 0x0d, 0xbf, 0x7a, 0xfd, 0xba, 0x32, 0x50, 0x45, - 0xfc, 0xb4, 0x6b, 0xe2, 0xd3, 0xca, 0x5d, 0x44, 0xb1, 0x1e, 0x5f, 0xbf, 0x9e, 0x39, 0x1c, 0xd1, - 0x87, 0x65, 0xf1, 0x62, 0x3b, 0xe3, 0x3d, 0xfb, 0x71, 0x6c, 0x99, 0x1f, 0x08, 0x2f, 0xcc, 0x0b, - 0x72, 0x62, 0xc5, 0x56, 0xfd, 0xda, 0xce, 0xa8, 0x13, 0xb1, 0x0b, 0x53, 0x3c, 0xbd, 0x02, 0xe1, - 0xbf, 0x72, 0x5e, 0xea, 0x85, 0xa4, 0xab, 0xae, 0x2a, 0xf5, 0x2b, 0xe8, 0xe4, 0x7e, 0x74, 0xed, - 0xe3, 0xd7, 0x26, 0x91, 0xc9, 0xf3, 0x9a, 0x7a, 0xed, 0xe3, 0xda, 0x18, 0xad, 0x59, 0xb3, 0x91, - 0xae, 0x80, 0xbb, 0x2a, 0x98, 0x3a, 0x97, 0xf4, 0x6d, 0x72, 0xea, 0xe4, 0xb7, 0x49, 0x0a, 0x23, - 0xab, 0x5e, 0x73, 0x4f, 0x44, 0x5c, 0xfb, 0x0a, 0x5b, 0xee, 0x2d, 0xaf, 0xb9, 0xf7, 0xe4, 0xcc, - 0x68, 0x91, 0x3d, 0x59, 0x63, 0x9f, 0xca, 0x66, 0x81, 0xe8, 0x13, 0x61, 0x9a, 0x79, 0x2a, 0xba, - 0x4e, 0x29, 0x65, 0x5c, 0xf0, 0xe1, 0x93, 0x46, 0x76, 0xad, 0xa9, 0x93, 0x13, 0x0a, 0xa5, 0x1a, - 0x0d, 0xf6, 0x42, 0xaf, 0x5b, 0x6d, 0xb9, 0xdd, 0x2d, 0xcf, 0xf6, 0x65, 0x68, 0xdf, 0xf4, 0xfa, - 0x7e, 0x25, 0x73, 0x7d, 0xcf, 0x39, 0x9c, 0xde, 0x6a, 0x4a, 0x06, 0x66, 0x8a, 0x25, 0xf9, 0x1a, - 0xcc, 0xb0, 0xc9, 0xbd, 0xfc, 0x38, 0xa4, 0x1d, 0x3e, 0xf2, 0x73, 0x28, 0x3a, 0x9c, 0x52, 0xb2, - 0x58, 0x44, 0x85, 0x7c, 0x4e, 0xe1, 0x62, 0xa7, 0x11, 0x81, 0x16, 0xad, 0x4e, 0x63, 0x45, 0x1c, - 0x58, 0xb8, 0x67, 0x3f, 0x56, 0xf2, 0x8f, 0x2a, 0x93, 0x94, 0xe0, 0x04, 0xbb, 0x7c, 0x74, 0x58, - 0x7e, 0x89, 0x4d, 0xb0, 0x38, 0xda, 0x74, 0x9f, 0xf9, 0xda, 0x97, 0x13, 0xf9, 0x2e, 0x9c, 0x15, - 0xcd, 0xaa, 0x61, 0x6a, 0x27, 0xcf, 0x3f, 0x68, 0xec, 0xda, 0xe8, 0x94, 0x33, 0x7f, 0xb2, 0x0d, - 0x51, 0x76, 0x98, 0x23, 0xf9, 0x58, 0x01, 0x67, 0x64, 0xf6, 0xab, 0x81, 0x7c, 0x08, 0x33, 0xfc, - 0xdd, 0x67, 0xc5, 0x0b, 0x42, 0xd4, 0x0a, 0x9c, 0x3a, 0x99, 0xad, 0x39, 0x7f, 0x4c, 0xe2, 0xde, - 0x19, 0x09, 0x2d, 0x42, 0x82, 0x33, 0x79, 0x1b, 0x26, 0x37, 0xdc, 0x0e, 0x8f, 0x27, 0x59, 0xdf, - 0x40, 0xfd, 0xa5, 0x38, 0x7f, 0xba, 0x6e, 0xc7, 0x92, 0x57, 0xf3, 0x6e, 0xb4, 0x5d, 0xa8, 0xd8, - 0xe4, 0x21, 0x4c, 0x36, 0x1a, 0x2b, 0xb7, 0x5c, 0x76, 0x00, 0x76, 0x0f, 0x16, 0xce, 0xf4, 0xf9, - 0xca, 0x17, 0x33, 0xbf, 0x72, 0x3a, 0x08, 0x76, 0xad, 0x6d, 0xb7, 0x45, 0xad, 0xa6, 0xd7, 0x3d, - 0x30, 0x55, 0x4e, 0x19, 0xf6, 0xd7, 0x67, 0x9f, 0xb0, 0xfd, 0x75, 0x1d, 0x66, 0x15, 0x2b, 0x4d, - 0xb4, 0xd0, 0x5c, 0x88, 0x83, 0x10, 0xa9, 0xf6, 0xd6, 0x49, 0x7f, 0xc3, 0x24, 0x9d, 0x34, 0xbc, - 0x3e, 0x77, 0x52, 0xc3, 0x6b, 0x17, 0xe6, 0xf8, 0x60, 0x88, 0x79, 0x80, 0x23, 0x7d, 0xbe, 0x4f, - 0x1f, 0x5e, 0xc9, 0xec, 0xc3, 0x79, 0x31, 0xd2, 0x72, 0x92, 0xe1, 0x3b, 0x67, 0x9a, 0x2b, 0xd9, - 0x06, 0x22, 0x80, 0x76, 0x68, 0x6f, 0xd9, 0x01, 0xc5, 0xba, 0x9e, 0xed, 0x53, 0xd7, 0x4b, 0x99, - 0x75, 0xcd, 0xc8, 0xba, 0xb6, 0x78, 0x35, 0x19, 0x1c, 0x49, 0x47, 0xd6, 0x23, 0xe7, 0x17, 0x76, - 0xec, 0x73, 0x9a, 0x32, 0x35, 0x8d, 0xc0, 0xe3, 0xf9, 0x24, 0x27, 0x6d, 0xb2, 0xdf, 0x33, 0x38, - 0x93, 0xc7, 0x70, 0x26, 0xfd, 0x15, 0x58, 0xe7, 0x05, 0xac, 0xf3, 0x82, 0x56, 0x67, 0x12, 0x89, - 0xcf, 0x1b, 0xbd, 0x59, 0xc9, 0x5a, 0xfb, 0xf0, 0x27, 0x7f, 0x39, 0x07, 0x67, 0xef, 0xdd, 0xaa, - 0x60, 0x22, 0x45, 0x97, 0x87, 0x17, 0x8b, 0xfc, 0x34, 0x9f, 0x17, 0x0a, 0xf7, 0xe4, 0x23, 0x80, - 0x94, 0x38, 0x70, 0xab, 0x60, 0x32, 0xe2, 0x8b, 0xed, 0x6d, 0x9b, 0xe7, 0x67, 0x14, 0x2c, 0x32, - 0x9c, 0x39, 0x7f, 0xe1, 0x8f, 0xcb, 0x39, 0xb3, 0x5f, 0x55, 0xa4, 0x05, 0xe7, 0xf5, 0x6e, 0x91, - 0xa6, 0xf1, 0xbb, 0xb4, 0xd5, 0x5a, 0x28, 0xe3, 0x8c, 0x7e, 0xfd, 0xe8, 0xb0, 0x7c, 0x39, 0xd5, - 0xbb, 0x91, 0xb9, 0x3d, 0xc3, 0x54, 0x1a, 0x3c, 0x80, 0xdf, 0x9d, 0x91, 0xe2, 0x74, 0x69, 0x26, - 0xcb, 0x46, 0xfd, 0xb7, 0xf3, 0x89, 0x93, 0x8a, 0xd4, 0x61, 0x5c, 0x4c, 0x40, 0x21, 0xba, 0xa7, - 0xa7, 0xd9, 0x85, 0xcc, 0x69, 0x36, 0x2e, 0xe6, 0xb2, 0x29, 0xe9, 0xc9, 0x3e, 0x63, 0x85, 0x5f, - 0x21, 0xee, 0x3a, 0xdf, 0xe0, 0x07, 0x11, 0x82, 0xb4, 0x23, 0xb7, 0x76, 0x72, 0x77, 0x27, 0xdd, - 0x9b, 0x0e, 0xcf, 0x5e, 0x59, 0x1b, 0xd9, 0xe3, 0xb9, 0x7e, 0x0a, 0x91, 0xcf, 0x8c, 0x9e, 0xd8, - 0xe7, 0x89, 0x55, 0xc8, 0x6a, 0x31, 0x7e, 0x2b, 0x07, 0xd3, 0xda, 0x51, 0x47, 0x6e, 0x2a, 0x0e, - 0x61, 0xb1, 0x8f, 0xb4, 0x86, 0x83, 0xbb, 0x5f, 0xd2, 0x55, 0xec, 0xa6, 0xb0, 0x38, 0xcf, 0xf7, - 0xa7, 0xc3, 0xd9, 0x9f, 0xf4, 0x0f, 0x1c, 0xac, 0x19, 0x8c, 0x72, 0x07, 0x8e, 0xf4, 0xc9, 0x1d, - 0xf8, 0xf7, 0x2f, 0xc0, 0x8c, 0x7e, 0x17, 0x22, 0xaf, 0xc3, 0x18, 0x6a, 0x65, 0xe5, 0xc5, 0x1a, - 0x15, 0x02, 0xa8, 0xb8, 0xd5, 0xdc, 0x10, 0x38, 0x0e, 0x79, 0x19, 0x20, 0x32, 0xfd, 0x95, 0x6f, - 0x12, 0xa3, 0x47, 0x87, 0xe5, 0xdc, 0x1b, 0xa6, 0x52, 0x40, 0xbe, 0x09, 0xb0, 0xe6, 0x39, 0x34, - 0x4a, 0xb4, 0x3a, 0xe0, 0xdd, 0xfd, 0x95, 0x54, 0x1e, 0x8c, 0xd3, 0x1d, 0xcf, 0xa1, 0xe9, 0xa4, - 0x17, 0x0a, 0x47, 0xf2, 0x25, 0x18, 0x35, 0x7b, 0xec, 0x12, 0xcf, 0xf5, 0x27, 0x93, 0xf2, 0xc8, - 0xe9, 0xb5, 0x68, 0x7c, 0x43, 0xf4, 0x7b, 0x49, 0x93, 0x32, 0x06, 0x20, 0xef, 0xf3, 0xfc, 0x18, - 0x22, 0x12, 0xe3, 0x68, 0xfc, 0x4a, 0xa3, 0x88, 0x22, 0xa9, 0x58, 0x8c, 0x0a, 0x09, 0x59, 0x87, - 0x71, 0xf5, 0x79, 0x41, 0xf1, 0x2c, 0x56, 0x9f, 0xa0, 0x94, 0xeb, 0xa6, 0xc8, 0xd0, 0x9a, 0x7c, - 0x79, 0x90, 0x5c, 0xc8, 0x3b, 0x30, 0xc1, 0xd8, 0xb3, 0xa5, 0x1c, 0x88, 0x6b, 0x06, 0xbe, 0xc5, - 0x28, 0x1f, 0xc4, 0xb6, 0x03, 0x2d, 0x5e, 0x62, 0x44, 0x40, 0xbe, 0x86, 0xb9, 0x3f, 0x45, 0x57, - 0x0f, 0xb4, 0xc7, 0xb8, 0x94, 0xea, 0x6a, 0x4c, 0x06, 0x9a, 0xea, 0xe9, 0x98, 0x1f, 0xd9, 0x89, - 0x02, 0x5b, 0x0d, 0x93, 0xd3, 0xe4, 0xd5, 0x54, 0x05, 0x0b, 0x32, 0x56, 0x53, 0x3a, 0x61, 0xae, - 0xc6, 0x97, 0x74, 0xa1, 0x14, 0x4b, 0x79, 0xa2, 0x2e, 0x18, 0x54, 0xd7, 0x1b, 0xa9, 0xba, 0xd4, - 0x01, 0x4c, 0x55, 0x97, 0xe2, 0x4e, 0x1c, 0x98, 0x91, 0x27, 0x86, 0xa8, 0x6f, 0x72, 0x50, 0x7d, - 0x2f, 0xa7, 0xea, 0x9b, 0x77, 0xb6, 0xd2, 0xf5, 0x24, 0x78, 0x92, 0x77, 0x60, 0x5a, 0x42, 0x78, - 0xfa, 0xda, 0xa9, 0x38, 0x4f, 0xa9, 0xb3, 0x95, 0x4a, 0x5a, 0xab, 0x23, 0xab, 0xd4, 0x7c, 0x76, - 0x4c, 0x6b, 0xd4, 0xc9, 0x59, 0xa1, 0x23, 0x93, 0x0f, 0x60, 0xb2, 0xde, 0x66, 0x0d, 0xf1, 0x3a, - 0x76, 0x48, 0x85, 0xd7, 0x99, 0xb4, 0x2d, 0x51, 0x4a, 0x94, 0xa9, 0xca, 0x13, 0xf3, 0xc6, 0x45, - 0x5a, 0x62, 0xde, 0x18, 0xcc, 0x3a, 0x8f, 0xbf, 0x27, 0x89, 0x39, 0x2c, 0x3d, 0xd2, 0x2e, 0x64, - 0xd8, 0x77, 0x28, 0xec, 0x45, 0xd4, 0x3d, 0x06, 0x95, 0xef, 0x39, 0x89, 0x88, 0xa7, 0x2a, 0x4f, - 0xf2, 0x2e, 0x4c, 0x8a, 0x74, 0x4f, 0x15, 0x73, 0x2d, 0x58, 0x28, 0x61, 0xe3, 0xd1, 0x8f, 0x5e, - 0x66, 0x86, 0xb2, 0x6c, 0x3f, 0x61, 0xc8, 0x18, 0xe3, 0x93, 0xaf, 0xc2, 0xa9, 0x87, 0x6e, 0xc7, - 0xf1, 0xf6, 0x03, 0x71, 0x4c, 0x89, 0x8d, 0x6e, 0x2e, 0x76, 0x23, 0xda, 0xe7, 0xe5, 0x91, 0x70, - 0x96, 0xda, 0xf8, 0x32, 0x39, 0x90, 0xbf, 0x98, 0xe2, 0xcc, 0x67, 0x10, 0x19, 0x34, 0x83, 0x16, - 0x53, 0x33, 0x28, 0x5d, 0x7d, 0x72, 0x3a, 0x65, 0x56, 0x43, 0x3c, 0x20, 0xfa, 0xf9, 0x7e, 0xc7, - 0x73, 0x3b, 0x0b, 0xf3, 0xb8, 0x17, 0x3e, 0x9b, 0xf4, 0x5c, 0x47, 0x3c, 0x91, 0xe0, 0xd8, 0x38, - 0x3a, 0x2c, 0x3f, 0x9f, 0x14, 0xc2, 0x3f, 0xf4, 0x34, 0x45, 0x79, 0x06, 0x6b, 0xf2, 0x01, 0x4c, - 0xb1, 0xff, 0x23, 0x2d, 0xc1, 0x29, 0xcd, 0x22, 0x50, 0xc1, 0x14, 0xf5, 0xe0, 0x18, 0x61, 0x3e, - 0xaa, 0x0c, 0x05, 0x82, 0xc6, 0x8a, 0xbc, 0x05, 0xc0, 0xe4, 0x18, 0xb1, 0x1d, 0x9f, 0x8e, 0x03, - 0xcc, 0xa2, 0x18, 0x94, 0xde, 0x88, 0x63, 0x64, 0xf2, 0x0e, 0x4c, 0xb2, 0x5f, 0x8d, 0x9e, 0xe3, - 0xb1, 0xb5, 0x71, 0x06, 0x69, 0xb9, 0x33, 0x1f, 0xa3, 0x0d, 0x38, 0x5c, 0x73, 0xe6, 0x8b, 0xd1, - 0xc9, 0x0a, 0xcc, 0x62, 0x20, 0x60, 0x11, 0x82, 0xd2, 0xa5, 0xc1, 0xc2, 0x59, 0xe5, 0x1d, 0x9c, - 0x15, 0x59, 0x6e, 0x54, 0xa6, 0x5e, 0x2e, 0x12, 0x64, 0x24, 0x80, 0xf9, 0xf4, 0x43, 0x62, 0xb0, - 0xb0, 0x80, 0x9d, 0x24, 0x45, 0xea, 0x34, 0x06, 0xdf, 0x8f, 0xd9, 0x88, 0x28, 0x1b, 0x97, 0x7c, - 0x4e, 0x50, 0x2b, 0xcc, 0xe2, 0x4e, 0x4c, 0x20, 0xb7, 0xab, 0x1b, 0xc9, 0x48, 0xb9, 0xe7, 0xb0, - 0x05, 0x38, 0xcc, 0x3b, 0xcd, 0x38, 0xf3, 0x72, 0x46, 0xb4, 0xdc, 0x0c, 0x6a, 0xf2, 0x1d, 0x38, - 0x2d, 0x77, 0x10, 0x51, 0x24, 0xe6, 0xf5, 0xf9, 0x13, 0xee, 0xc4, 0xce, 0x56, 0x54, 0x75, 0x6a, - 0x4a, 0x67, 0x57, 0x41, 0x6c, 0x98, 0xc4, 0x61, 0x15, 0x35, 0x3e, 0x3b, 0xa8, 0xc6, 0xcb, 0xa9, - 0x1a, 0xcf, 0xe0, 0x44, 0x49, 0x57, 0xa6, 0xf2, 0x24, 0x4b, 0x30, 0x2d, 0xd6, 0x91, 0x98, 0x6d, - 0xcf, 0x61, 0x6f, 0xa1, 0x56, 0x49, 0xae, 0xc0, 0xd4, 0x84, 0xd3, 0x49, 0xd4, 0x1d, 0x99, 0x3f, - 0x23, 0x5c, 0xd0, 0x76, 0xe4, 0xe4, 0xeb, 0x81, 0x8e, 0xcc, 0x76, 0xa4, 0x58, 0x8a, 0x59, 0x7e, - 0xdc, 0xf5, 0x85, 0xce, 0xe8, 0xf9, 0x38, 0x6d, 0x8d, 0x22, 0xfc, 0x58, 0x34, 0xc2, 0x50, 0xb7, - 0x84, 0x2c, 0x0e, 0xe4, 0x3e, 0xcc, 0x47, 0xa7, 0xb6, 0xc2, 0xb8, 0x1c, 0xc7, 0x62, 0x8d, 0x8f, - 0xfa, 0x6c, 0xbe, 0x59, 0xf4, 0xc4, 0x86, 0xb3, 0xda, 0x39, 0xad, 0xb0, 0xbe, 0x88, 0xac, 0x31, - 0xd3, 0xb7, 0x7e, 0xc8, 0x67, 0xb3, 0xef, 0xc7, 0x87, 0x7c, 0x08, 0xe7, 0x93, 0x67, 0xb3, 0x52, - 0xcb, 0x0b, 0x58, 0xcb, 0xab, 0x47, 0x87, 0xe5, 0x4b, 0xa9, 0xe3, 0x3d, 0xbb, 0xa2, 0x01, 0xdc, - 0xc8, 0x37, 0x61, 0x41, 0x3f, 0x9f, 0x95, 0x9a, 0x0c, 0xac, 0x09, 0x97, 0x4e, 0x74, 0xb0, 0x67, - 0xd7, 0xd0, 0x97, 0x07, 0x09, 0xa1, 0x9c, 0x39, 0xbb, 0x95, 0x6a, 0x5e, 0x8c, 0x1b, 0x94, 0x5a, - 0x25, 0xd9, 0xd5, 0x1d, 0xc7, 0x92, 0xec, 0xc3, 0xf3, 0x59, 0xc7, 0x84, 0x52, 0xe9, 0x4b, 0x91, - 0x56, 0xf6, 0xb5, 0xec, 0x23, 0x27, 0xbb, 0xe6, 0x63, 0xd8, 0x92, 0xaf, 0xc1, 0x69, 0x65, 0x7d, - 0x29, 0xf5, 0xbd, 0x8c, 0xf5, 0xa1, 0x13, 0xb0, 0xba, 0x30, 0xb3, 0x6b, 0xc9, 0xe6, 0x41, 0xda, - 0x30, 0x2f, 0x1b, 0x8e, 0xea, 0x6f, 0x71, 0xf4, 0x5c, 0xd2, 0x76, 0xd5, 0x34, 0xc6, 0xd2, 0x45, - 0xb1, 0xab, 0x2e, 0x38, 0x5b, 0x56, 0x37, 0x26, 0x54, 0x67, 0x7a, 0x06, 0x5f, 0xb2, 0x02, 0x63, - 0x8d, 0x8d, 0xfa, 0xad, 0x5b, 0xcb, 0x0b, 0xaf, 0x60, 0x0d, 0xd2, 0x63, 0x88, 0x03, 0xb5, 0x4b, - 0x93, 0x30, 0x54, 0xeb, 0xba, 0xdb, 0xdb, 0x9a, 0x63, 0x16, 0x47, 0x25, 0x3f, 0x96, 0x83, 0x33, - 0x0f, 0x3d, 0x7f, 0xaf, 0xe5, 0xd9, 0x8e, 0x8c, 0x6f, 0x2c, 0x76, 0xb5, 0xd7, 0x07, 0xed, 0x6a, - 0x9f, 0x4f, 0xed, 0x6a, 0xc6, 0xbe, 0x60, 0x63, 0x45, 0x81, 0x94, 0x53, 0x3b, 0x5c, 0x9f, 0xaa, - 0xc8, 0x5f, 0x84, 0x8b, 0xd9, 0x25, 0xca, 0x30, 0xbd, 0x81, 0xc3, 0x74, 0xfd, 0xe8, 0xb0, 0xfc, - 0x46, 0xbf, 0x9a, 0xb2, 0x87, 0xec, 0x58, 0xd6, 0x77, 0x46, 0x8a, 0x97, 0x4b, 0x57, 0xee, 0x8c, - 0x14, 0xaf, 0x94, 0x5e, 0x35, 0x9f, 0xcb, 0x4e, 0xf1, 0xcf, 0x69, 0xcc, 0x4b, 0x83, 0x4a, 0x63, - 0x8e, 0xc6, 0x2f, 0xe5, 0x60, 0x3e, 0x63, 0x30, 0xc8, 0x25, 0x18, 0xc1, 0xf4, 0x3e, 0x8a, 0x69, - 0x41, 0x22, 0xad, 0x0f, 0x96, 0x93, 0xcf, 0xc1, 0x78, 0x6d, 0xad, 0xd1, 0xa8, 0xac, 0xc9, 0x7b, - 0x2b, 0xdf, 0xb3, 0x3b, 0x81, 0x15, 0xd8, 0xfa, 0x8b, 0xa4, 0x40, 0x23, 0x6f, 0xc0, 0x58, 0x7d, - 0x03, 0x09, 0xb8, 0x81, 0x1c, 0xde, 0xe3, 0xdc, 0x6e, 0x12, 0x5f, 0x20, 0x19, 0x3f, 0x91, 0x03, - 0x92, 0x9e, 0x59, 0xe4, 0x3a, 0x4c, 0xaa, 0xf3, 0x97, 0xdf, 0xb2, 0xf1, 0xf5, 0x4c, 0x99, 0x9d, - 0xa6, 0x8a, 0x43, 0x6a, 0x30, 0x8a, 0x39, 0x1d, 0xa3, 0xa7, 0xd0, 0xcc, 0xf9, 0x72, 0x36, 0x35, - 0x5f, 0x46, 0x31, 0x63, 0xa4, 0xc9, 0x89, 0x8d, 0xdf, 0xcb, 0x01, 0xc9, 0x36, 0x70, 0x1a, 0xca, - 0x14, 0xe3, 0x4d, 0xc5, 0xc1, 0x59, 0x4d, 0xe0, 0x11, 0x65, 0x5f, 0x52, 0x6f, 0x8c, 0xb1, 0x2b, - 0xf4, 0x25, 0x4d, 0x43, 0xd1, 0xdf, 0x2b, 0xee, 0x0a, 0x8c, 0x3e, 0xa0, 0xfe, 0x96, 0xb4, 0xfd, - 0x44, 0x7b, 0xb1, 0x47, 0x0c, 0xa0, 0xde, 0xd8, 0x11, 0xc3, 0xf8, 0xd3, 0x1c, 0x9c, 0xca, 0x12, - 0x67, 0x8f, 0x71, 0x5e, 0x33, 0x12, 0x7e, 0x77, 0x68, 0x86, 0xc1, 0x8d, 0xc9, 0x22, 0x6f, 0xbb, - 0x32, 0x8c, 0xb2, 0xc6, 0xca, 0x11, 0x46, 0x8d, 0x09, 0xeb, 0x8d, 0xc0, 0xe4, 0x70, 0x86, 0xc0, - 0x83, 0x66, 0x8d, 0x60, 0xbc, 0x35, 0x44, 0x40, 0x69, 0xc9, 0xe4, 0x70, 0x86, 0x70, 0xcf, 0x73, - 0xa2, 0x44, 0xe6, 0x88, 0xd0, 0x66, 0x00, 0x93, 0xc3, 0xc9, 0x25, 0x18, 0x5f, 0xef, 0xac, 0x52, - 0xfb, 0x91, 0x0c, 0x22, 0x8f, 0x66, 0x23, 0x5e, 0xc7, 0x6a, 0x31, 0x98, 0x29, 0x0b, 0x8d, 0x9f, - 0xcd, 0xc1, 0x5c, 0x4a, 0x92, 0x3e, 0xde, 0x3f, 0x6f, 0xb0, 0xa3, 0xcc, 0x30, 0xed, 0xe3, 0x9f, - 0x3f, 0x92, 0xfd, 0xf9, 0xc6, 0xff, 0x35, 0x0a, 0x67, 0xfb, 0x28, 0x36, 0x62, 0x47, 0xbe, 0xdc, - 0xb1, 0x8e, 0x7c, 0x5f, 0x87, 0xe9, 0x6a, 0xcb, 0x76, 0xdb, 0xc1, 0xa6, 0x17, 0x7f, 0x71, 0xec, - 0x0f, 0x80, 0x65, 0x32, 0x97, 0xbb, 0x34, 0x1c, 0x3f, 0xd7, 0x44, 0x0a, 0x2b, 0xf4, 0xd2, 0x72, - 0x95, 0xc6, 0x2c, 0xe5, 0x4a, 0x57, 0xf8, 0xd7, 0xc4, 0x95, 0x4e, 0x77, 0xee, 0x18, 0x79, 0xa2, - 0xce, 0x1d, 0xd9, 0x86, 0xa1, 0xa3, 0x9f, 0xc4, 0x4c, 0xb8, 0x0a, 0xd3, 0xdc, 0x6e, 0xa6, 0x12, - 0xf0, 0x41, 0x1a, 0x4b, 0xd9, 0xda, 0xd8, 0x41, 0x7a, 0x2c, 0x34, 0x1a, 0xb2, 0xa2, 0x3b, 0x22, - 0x8c, 0xe3, 0x7b, 0xdf, 0xa5, 0xfe, 0x8e, 0x06, 0xda, 0x3b, 0xbf, 0xe6, 0x70, 0xf0, 0x5d, 0x38, - 0x95, 0x75, 0x33, 0x5a, 0x28, 0x6a, 0x26, 0x79, 0x7d, 0x4d, 0x39, 0x87, 0xbf, 0x5f, 0xed, 0xa5, - 0xef, 0x57, 0xc6, 0xcf, 0xe6, 0x75, 0x27, 0xbf, 0x7f, 0x1d, 0xa7, 0xfd, 0x15, 0x18, 0x7d, 0xb8, - 0x4b, 0x7d, 0xb9, 0xd9, 0xe2, 0x87, 0xec, 0x33, 0x80, 0xfa, 0x21, 0x88, 0x41, 0x6e, 0xc1, 0xcc, - 0x06, 0x9f, 0x06, 0x72, 0x6c, 0x47, 0xe2, 0xcb, 0x6e, 0x57, 0xa8, 0x64, 0x32, 0x06, 0x37, 0x41, - 0x65, 0xdc, 0x86, 0x0b, 0xda, 0x6e, 0x20, 0x82, 0x92, 0x70, 0x67, 0x04, 0x7e, 0x1c, 0xcf, 0xc4, - 0xee, 0x17, 0xf1, 0xd6, 0x65, 0x26, 0xa0, 0xc6, 0x36, 0x3c, 0x3f, 0x90, 0x11, 0x3b, 0x05, 0xa1, - 0x1b, 0xfd, 0x4a, 0x18, 0x3b, 0x0e, 0x24, 0x35, 0x15, 0x3a, 0xe3, 0xbb, 0x30, 0xa5, 0xf6, 0x32, - 0x6e, 0xe8, 0xec, 0xb7, 0xd8, 0x51, 0xf9, 0x86, 0xce, 0x00, 0x26, 0x87, 0xc7, 0x4a, 0xf4, 0x7c, - 0xb6, 0x12, 0x3d, 0x1e, 0xfe, 0xc2, 0x71, 0xc3, 0xcf, 0x2a, 0xc7, 0xfd, 0x42, 0xa9, 0x1c, 0x7f, - 0xab, 0x95, 0x63, 0xd4, 0x11, 0x93, 0xc3, 0x9f, 0x68, 0xe5, 0xbf, 0x2b, 0x73, 0xf8, 0xa0, 0xaf, - 0x83, 0x5c, 0x3c, 0x71, 0x9a, 0xf3, 0xf9, 0xac, 0xb5, 0x10, 0x63, 0xc6, 0x27, 0x74, 0xfe, 0xb8, - 0x13, 0xfa, 0x24, 0x13, 0xf1, 0x1a, 0x8c, 0x57, 0x84, 0x29, 0xc0, 0x48, 0x2c, 0x55, 0xd9, 0xa9, - 0x77, 0x7f, 0x89, 0x65, 0xfc, 0x42, 0x0e, 0x4e, 0x67, 0x2a, 0x2b, 0x59, 0xad, 0x5c, 0x2b, 0xaa, - 0xac, 0xc3, 0xa4, 0x4a, 0x94, 0x63, 0x9c, 0xc4, 0xe5, 0x7c, 0xf8, 0xb6, 0x18, 0x2f, 0xc0, 0x44, - 0xf4, 0x54, 0x46, 0x4e, 0xc9, 0xa1, 0x43, 0xfb, 0x30, 0xf9, 0xe2, 0xd2, 0x00, 0x60, 0x5f, 0xf0, - 0x44, 0xad, 0x19, 0x8d, 0xdf, 0xcd, 0xf3, 0xfc, 0x8e, 0x4f, 0x6d, 0xa4, 0xc6, 0x6c, 0x13, 0x44, - 0xd6, 0xa4, 0xfe, 0xf1, 0x19, 0xc9, 0x32, 0x8c, 0x35, 0x42, 0x3b, 0xec, 0x49, 0x4f, 0xf9, 0x79, - 0x95, 0x0c, 0x0b, 0x1e, 0x2c, 0xc6, 0xbe, 0xd2, 0x01, 0x42, 0xb4, 0xeb, 0x19, 0x42, 0x14, 0x4b, - 0xc6, 0x7f, 0x94, 0x83, 0x29, 0x95, 0x98, 0x7c, 0x00, 0x33, 0x32, 0xfe, 0x1c, 0x8f, 0x1f, 0x20, - 0xde, 0xf5, 0xa4, 0x51, 0x8c, 0x8c, 0x3f, 0xa7, 0xc6, 0x1b, 0xd0, 0xf0, 0xd5, 0xad, 0xba, 0xab, - 0x22, 0x13, 0x07, 0x48, 0x7b, 0xdb, 0xb6, 0xf6, 0xa9, 0xbd, 0x47, 0x83, 0xd0, 0xe2, 0xc6, 0x0b, - 0xe2, 0xf9, 0x4f, 0xb2, 0xbf, 0x77, 0xab, 0xc2, 0xed, 0x16, 0xd8, 0x48, 0x88, 0x40, 0x82, 0x29, - 0x1a, 0xf5, 0x4d, 0xa3, 0xbd, 0x6d, 0x3f, 0xe4, 0x85, 0x9c, 0xce, 0xf8, 0xb3, 0x31, 0x3e, 0xdd, - 0x44, 0xb8, 0xca, 0x2d, 0x98, 0x59, 0xaf, 0xd7, 0xaa, 0x8a, 0x86, 0x53, 0xcf, 0x76, 0xb2, 0xfc, - 0x38, 0xa4, 0x7e, 0xc7, 0x6e, 0xc9, 0x5b, 0x5c, 0x7c, 0x04, 0x79, 0xae, 0xd3, 0xcc, 0xd6, 0x7e, - 0x26, 0x38, 0xb2, 0x3a, 0xf8, 0x05, 0x2e, 0xaa, 0x23, 0x3f, 0x64, 0x1d, 0x81, 0xdd, 0x6e, 0xf5, - 0xa9, 0x43, 0xe7, 0x48, 0x76, 0xa1, 0x74, 0x1b, 0x65, 0x35, 0xa5, 0x96, 0xc2, 0xe0, 0x5a, 0x5e, - 0x14, 0xb5, 0x3c, 0xcb, 0x85, 0xbc, 0xec, 0x7a, 0x52, 0x5c, 0xe3, 0x7d, 0x62, 0xe4, 0xd8, 0x7d, - 0xe2, 0xaf, 0xe4, 0x60, 0x8c, 0x0b, 0x83, 0x62, 0x1a, 0xf7, 0x11, 0x37, 0x1f, 0x3e, 0x19, 0x71, - 0xb3, 0x84, 0xe7, 0x84, 0x36, 0xa1, 0x79, 0x19, 0xa9, 0x25, 0xd6, 0x85, 0x34, 0xc0, 0xc5, 0xb7, - 0x0a, 0x5e, 0x72, 0xfc, 0xb2, 0x20, 0xf5, 0xd8, 0x7b, 0x7d, 0xfc, 0x58, 0x07, 0x49, 0xe9, 0xf1, - 0x3f, 0x2e, 0xbc, 0xd7, 0x75, 0x9f, 0xf5, 0x55, 0x98, 0x10, 0x3e, 0xf1, 0x4b, 0x07, 0xe2, 0x45, - 0xb2, 0xa4, 0x19, 0x79, 0x38, 0x4b, 0x07, 0xb1, 0xa0, 0x2b, 0xbc, 0xea, 0xad, 0xad, 0x03, 0x2d, - 0x5d, 0xa6, 0x44, 0x24, 0xeb, 0x3c, 0x8d, 0x1c, 0x0f, 0xe8, 0xa9, 0x47, 0xf0, 0x8e, 0xe0, 0x22, - 0xda, 0x8e, 0x74, 0xac, 0xcd, 0x88, 0xdf, 0x19, 0xf3, 0x20, 0xab, 0x50, 0x42, 0xc3, 0x20, 0xea, - 0xf0, 0x55, 0x53, 0xaf, 0x71, 0xbf, 0x6b, 0x61, 0xdc, 0x19, 0xf2, 0x32, 0xb1, 0xdc, 0x12, 0x2e, - 0x4f, 0x29, 0x4a, 0x76, 0x39, 0x2d, 0x25, 0x67, 0x1f, 0x79, 0x07, 0x26, 0xa3, 0x80, 0xaa, 0x91, - 0xd3, 0x25, 0xbe, 0x4c, 0xc4, 0x11, 0x58, 0xf5, 0xe4, 0x63, 0x0a, 0x3a, 0x59, 0x84, 0x22, 0x5b, - 0xc4, 0xc9, 0x44, 0x9d, 0x3d, 0x01, 0x53, 0x9d, 0x20, 0x24, 0x1e, 0x69, 0xc0, 0x3c, 0x5b, 0x34, - 0x0d, 0xb7, 0xb3, 0xd3, 0xa2, 0xab, 0xde, 0x8e, 0xd7, 0x0b, 0xe3, 0x5c, 0x5c, 0xfc, 0x3a, 0x60, - 0xb7, 0x5b, 0x5a, 0xb1, 0x9e, 0x89, 0x2b, 0x83, 0x5a, 0xd9, 0x2a, 0xff, 0x38, 0x0f, 0x93, 0xca, - 0x7c, 0x22, 0x57, 0xa0, 0x58, 0x0f, 0x56, 0xbd, 0xe6, 0x5e, 0x14, 0x8e, 0x6d, 0xfa, 0xe8, 0xb0, - 0x3c, 0xe1, 0x06, 0x56, 0x0b, 0x81, 0x66, 0x54, 0x4c, 0x96, 0x60, 0x9a, 0xff, 0x25, 0x03, 0xdd, - 0xe7, 0x63, 0x5b, 0x4e, 0x8e, 0x2c, 0x43, 0xdc, 0xab, 0xbb, 0xa7, 0x46, 0x42, 0xbe, 0x01, 0xc0, - 0x01, 0xe8, 0xc0, 0x5b, 0x18, 0xde, 0xf5, 0x58, 0x54, 0x90, 0xe1, 0xba, 0xab, 0x30, 0x24, 0xdf, - 0xe2, 0x01, 0x58, 0xe5, 0xfc, 0x1f, 0x19, 0xde, 0x77, 0x9a, 0xf1, 0xb7, 0xb2, 0x43, 0x38, 0xa8, - 0x2c, 0x45, 0xe4, 0xc8, 0xf3, 0x26, 0x6d, 0x7a, 0x8f, 0xa8, 0x7f, 0x50, 0x09, 0x11, 0x51, 0xc1, - 0x30, 0xfe, 0xe7, 0x9c, 0xb2, 0x6a, 0xc8, 0x1a, 0xe6, 0x96, 0xe5, 0x33, 0x42, 0x18, 0xe7, 0x44, - 0x77, 0x06, 0x09, 0x37, 0xe9, 0xf6, 0xd2, 0xb3, 0xc2, 0x94, 0x78, 0x3e, 0x9a, 0x57, 0x89, 0x9c, - 0xb3, 0x1c, 0x48, 0xbe, 0x0c, 0x23, 0xd8, 0x75, 0xf9, 0x63, 0x9b, 0x26, 0x8f, 0xed, 0x11, 0xd6, - 0x67, 0xd8, 0x10, 0xa4, 0x24, 0x9f, 0x13, 0xce, 0x8f, 0xbc, 0xf3, 0x67, 0x94, 0xb3, 0x97, 0x7d, - 0x47, 0x74, 0x5e, 0xc7, 0x51, 0x3c, 0x94, 0xd9, 0xf3, 0xef, 0xe5, 0xa1, 0x94, 0x5c, 0xab, 0xe4, - 0x7d, 0x98, 0x92, 0xe7, 0xe9, 0x8a, 0x2d, 0xa2, 0xb4, 0x4f, 0x89, 0x28, 0xe9, 0xf2, 0x50, 0xdd, - 0xb5, 0x55, 0x63, 0x1e, 0x53, 0x23, 0x60, 0xc2, 0xcd, 0xa6, 0x88, 0xaa, 0xa5, 0xac, 0x92, 0xd0, - 0x0b, 0xbb, 0x89, 0x18, 0x9e, 0x12, 0x8d, 0xbc, 0x09, 0x85, 0x7b, 0xb7, 0x2a, 0xc2, 0x49, 0xa6, - 0x94, 0x3c, 0x75, 0xb9, 0x11, 0xa0, 0x6e, 0x92, 0xc8, 0xf0, 0xc9, 0xaa, 0x12, 0x22, 0x77, 0x4c, - 0x4b, 0x8d, 0x25, 0xc1, 0x51, 0xe3, 0x8e, 0x8f, 0x95, 0x7b, 0x67, 0xa4, 0x58, 0x28, 0x8d, 0x88, - 0x40, 0x94, 0xff, 0x71, 0x01, 0x26, 0xa2, 0xfa, 0x09, 0x51, 0x5d, 0x0f, 0xb9, 0x9b, 0x21, 0x39, - 0x07, 0x45, 0x29, 0xae, 0x09, 0x5f, 0x99, 0xf1, 0x40, 0x88, 0x6a, 0x0b, 0x20, 0xe5, 0x32, 0xbe, - 0xcc, 0x4d, 0xf9, 0x93, 0x5c, 0x87, 0x48, 0xe8, 0xea, 0x27, 0x9d, 0x8d, 0xb0, 0x01, 0x33, 0x23, - 0x34, 0x32, 0x03, 0x79, 0x97, 0x07, 0x37, 0x9a, 0x30, 0xf3, 0xae, 0x43, 0xde, 0x87, 0xa2, 0xed, - 0x38, 0xd4, 0xb1, 0x6c, 0x69, 0xe5, 0x32, 0x68, 0xd2, 0x14, 0x19, 0x37, 0x7e, 0x08, 0x20, 0x55, - 0x25, 0x24, 0x15, 0x98, 0x68, 0xd9, 0xdc, 0x90, 0xcd, 0x19, 0xe2, 0x44, 0x89, 0x39, 0x14, 0x19, - 0xd9, 0xfd, 0x80, 0x3a, 0xe4, 0x15, 0x18, 0x61, 0xa3, 0x29, 0x8e, 0x10, 0x29, 0x25, 0xb2, 0xc1, - 0xe4, 0x1d, 0xb6, 0xf2, 0x8c, 0x89, 0x08, 0xe4, 0x25, 0x28, 0xf4, 0x16, 0xb7, 0xc5, 0xe1, 0x50, - 0x8a, 0xc3, 0x55, 0x47, 0x68, 0xac, 0x98, 0xdc, 0x80, 0xe2, 0xbe, 0x1e, 0xe9, 0xf8, 0x74, 0x62, - 0x18, 0x23, 0xfc, 0x08, 0x71, 0xa9, 0x08, 0x63, 0xfc, 0x20, 0x30, 0x9e, 0x07, 0x88, 0xab, 0x4e, - 0xbb, 0x34, 0x19, 0xdf, 0x80, 0x89, 0xa8, 0x4a, 0x72, 0x01, 0x60, 0x8f, 0x1e, 0x58, 0xbb, 0x76, - 0xc7, 0x69, 0x71, 0x29, 0x72, 0xca, 0x9c, 0xd8, 0xa3, 0x07, 0x2b, 0x08, 0x20, 0x67, 0x61, 0xbc, - 0xcb, 0x46, 0x55, 0x4c, 0xdd, 0x29, 0x73, 0xac, 0xdb, 0xdb, 0x62, 0x33, 0x74, 0x01, 0xc6, 0x51, - 0xbb, 0x28, 0x16, 0xda, 0xb4, 0x29, 0x7f, 0x1a, 0xff, 0x4d, 0x1e, 0xb3, 0x7c, 0x28, 0xdf, 0x49, - 0x5e, 0x84, 0xe9, 0xa6, 0x4f, 0xf1, 0xcc, 0xb1, 0x99, 0x24, 0x25, 0xea, 0x99, 0x8a, 0x81, 0x75, - 0x87, 0x5c, 0x82, 0xd9, 0x6e, 0x6f, 0xab, 0xe5, 0x36, 0x59, 0x6d, 0x56, 0x73, 0x4b, 0x84, 0x25, - 0x9f, 0x32, 0xa7, 0x39, 0xf8, 0x2e, 0x3d, 0xa8, 0x6e, 0x61, 0x50, 0xae, 0x92, 0x1a, 0x53, 0x35, - 0x8c, 0xf2, 0x2f, 0x9b, 0xb3, 0x0a, 0x1c, 0x8d, 0xd7, 0xce, 0xc0, 0x98, 0x6d, 0xef, 0xf4, 0x5c, - 0x1e, 0x3c, 0x67, 0xca, 0x14, 0xbf, 0xc8, 0x6b, 0x30, 0x17, 0xb8, 0x3b, 0x1d, 0x3b, 0xec, 0xf9, - 0x22, 0xcd, 0x0a, 0xf5, 0x71, 0x4a, 0x4d, 0x9b, 0xa5, 0xa8, 0xa0, 0xca, 0xe1, 0xe4, 0x0d, 0x20, - 0x6a, 0x7d, 0xde, 0xd6, 0x87, 0xb4, 0xc9, 0xa7, 0xda, 0x94, 0x39, 0xa7, 0x94, 0xac, 0x63, 0x01, - 0x79, 0x01, 0xa6, 0x7c, 0x1a, 0xa0, 0x14, 0x87, 0xdd, 0x86, 0x49, 0xb0, 0xcc, 0x49, 0x09, 0x63, - 0x7d, 0x77, 0x19, 0x4a, 0x4a, 0x77, 0x60, 0xd8, 0x5a, 0x1e, 0x93, 0xdb, 0x9c, 0x89, 0xe1, 0x66, - 0xb7, 0xee, 0x18, 0x4b, 0x30, 0x97, 0x5a, 0xb9, 0x4a, 0xce, 0x7c, 0xbe, 0x13, 0x0d, 0xce, 0x99, - 0x6f, 0x74, 0x60, 0x4a, 0xdd, 0x89, 0x8f, 0x09, 0x0d, 0x7f, 0x06, 0x83, 0x2f, 0xf0, 0x6d, 0x6a, - 0xec, 0xe8, 0xb0, 0x9c, 0x77, 0x1d, 0x0c, 0xb9, 0x70, 0x19, 0x8a, 0x52, 0x68, 0x10, 0x67, 0x35, - 0x6a, 0x87, 0xe5, 0x23, 0x8c, 0x19, 0x95, 0x1a, 0xaf, 0xc0, 0xb8, 0xd8, 0x6c, 0x07, 0xeb, 0x84, - 0x8d, 0xef, 0xe5, 0x61, 0xd6, 0xa4, 0x6c, 0x2b, 0xa0, 0x3c, 0x1f, 0xc4, 0x53, 0x7b, 0x4b, 0xcc, - 0x0e, 0xe1, 0xa7, 0xb5, 0x6d, 0x40, 0x26, 0x86, 0xbf, 0x93, 0x83, 0xf9, 0x0c, 0xdc, 0x8f, 0x95, - 0x89, 0xf0, 0x26, 0x4c, 0xd4, 0x5c, 0xbb, 0x55, 0x71, 0x9c, 0x28, 0x12, 0x03, 0x8a, 0x9a, 0x98, - 0xae, 0xc4, 0x66, 0x50, 0xf5, 0xd8, 0x8d, 0x50, 0xc9, 0xab, 0x62, 0x52, 0xc4, 0x79, 0x7c, 0x71, - 0x52, 0x7c, 0x74, 0x58, 0x06, 0xfe, 0x4d, 0x71, 0x72, 0x6f, 0x0c, 0xab, 0xc9, 0x81, 0xb1, 0x93, - 0xc3, 0x53, 0x3b, 0x74, 0xd9, 0x61, 0x35, 0x93, 0xcd, 0x1b, 0x2a, 0x19, 0xc3, 0x4f, 0xe6, 0xe1, - 0x4c, 0x36, 0xe1, 0xc7, 0x4d, 0x2a, 0x89, 0x69, 0x30, 0x94, 0x50, 0xc0, 0x98, 0x54, 0x92, 0xe7, - 0xcc, 0x40, 0xfc, 0x18, 0x81, 0x6c, 0xc3, 0xf4, 0xaa, 0x1d, 0x84, 0x2b, 0xd4, 0xf6, 0xc3, 0x2d, - 0x6a, 0x87, 0x43, 0xc8, 0x9e, 0x2f, 0xc9, 0xf7, 0x67, 0x3c, 0xfe, 0x76, 0x25, 0x65, 0x42, 0x3a, - 0xd4, 0xd9, 0x46, 0x13, 0x65, 0x64, 0x88, 0x89, 0xf2, 0x6d, 0x98, 0x6d, 0xd0, 0xb6, 0xdd, 0xdd, - 0xf5, 0x7c, 0xe9, 0x25, 0x7b, 0x15, 0xa6, 0x23, 0x50, 0xe6, 0x6c, 0xd1, 0x8b, 0x35, 0x7c, 0xa5, - 0x23, 0xe2, 0xad, 0x44, 0x2f, 0x36, 0xfe, 0x7a, 0x1e, 0xce, 0x56, 0x9a, 0xc2, 0x2c, 0x4c, 0x14, - 0x48, 0xeb, 0xd5, 0x4f, 0xb9, 0x6e, 0x72, 0x0d, 0x26, 0xee, 0xd9, 0x8f, 0x57, 0xa9, 0x1d, 0xd0, - 0x40, 0xa4, 0xf4, 0xe2, 0x82, 0x9a, 0xfd, 0x38, 0xd6, 0xe6, 0x9b, 0x31, 0x8e, 0x7a, 0x93, 0x1d, - 0xf9, 0x84, 0x37, 0x59, 0x03, 0xc6, 0x56, 0xbc, 0x96, 0x23, 0x8e, 0x31, 0xf1, 0x84, 0xb8, 0x8b, - 0x10, 0x53, 0x94, 0xb0, 0x0b, 0xe0, 0x4c, 0xf4, 0xc5, 0xf8, 0x09, 0x9f, 0x7a, 0x97, 0x5c, 0x82, - 0x71, 0xac, 0x28, 0xca, 0x3d, 0x8c, 0x87, 0x46, 0x8b, 0x62, 0x62, 0x26, 0xc7, 0x94, 0x85, 0x6a, - 0x4f, 0x8c, 0x7e, 0xb2, 0x9e, 0x30, 0xfe, 0x36, 0xbe, 0x4e, 0xaa, 0xad, 0x64, 0x27, 0x91, 0xf2, - 0x21, 0xb9, 0x21, 0x3f, 0x24, 0xff, 0xc4, 0x86, 0xa4, 0xd0, 0x77, 0x48, 0xbe, 0x9f, 0x87, 0xc9, - 0xe8, 0x63, 0x3f, 0x63, 0xf1, 0xa8, 0xa3, 0x76, 0x0d, 0x15, 0xd9, 0xa2, 0xa1, 0xec, 0x15, 0x22, - 0x80, 0xc4, 0x97, 0x61, 0x4c, 0x2c, 0xa6, 0x5c, 0xc2, 0x8a, 0x33, 0x31, 0xba, 0x4b, 0x33, 0x82, - 0xf5, 0x18, 0x0e, 0x68, 0x60, 0x0a, 0x3a, 0x0c, 0x1d, 0xf2, 0x90, 0x6e, 0x89, 0xc7, 0xea, 0xa7, - 0xf6, 0x8c, 0xca, 0x0e, 0x1d, 0x12, 0x37, 0x6c, 0xa8, 0xd3, 0xe9, 0x17, 0x8b, 0x50, 0x4a, 0x92, - 0x1c, 0x1f, 0xf1, 0x7b, 0xa3, 0xb7, 0xc5, 0xa5, 0x70, 0x1e, 0xf1, 0xbb, 0xdb, 0xdb, 0x32, 0x19, - 0x0c, 0x6d, 0x59, 0x7c, 0xf7, 0x11, 0xb6, 0x7a, 0x4a, 0xd8, 0xb2, 0xf8, 0xee, 0x23, 0xcd, 0x96, - 0xc5, 0x77, 0x1f, 0xe1, 0xd5, 0x77, 0xb5, 0x81, 0xde, 0xce, 0x28, 0x82, 0x8b, 0xab, 0x6f, 0x2b, - 0x48, 0x66, 0xca, 0x91, 0x68, 0xec, 0xa8, 0x5c, 0xa2, 0xb6, 0x2f, 0xa2, 0x53, 0x8b, 0xed, 0x0c, - 0x8f, 0xca, 0x2d, 0x04, 0xf3, 0xc4, 0xd6, 0xa6, 0x8a, 0x44, 0x5a, 0x40, 0x94, 0x9f, 0x72, 0x01, - 0x1f, 0x7f, 0x1b, 0x94, 0x16, 0x58, 0xa7, 0x54, 0xd6, 0x96, 0xba, 0x9a, 0x33, 0xf8, 0x3e, 0x49, - 0x05, 0xe4, 0x86, 0x08, 0xb9, 0x87, 0x2a, 0x8f, 0xe2, 0xb1, 0xcc, 0x64, 0x38, 0x00, 0xe0, 0x21, - 0xf9, 0x22, 0xc5, 0x47, 0xcc, 0x84, 0xbc, 0x07, 0x93, 0xaa, 0x0f, 0x3b, 0xf7, 0xb4, 0x7e, 0x8e, - 0x47, 0x51, 0xeb, 0x93, 0x5b, 0x51, 0x25, 0x20, 0x5b, 0x70, 0xb6, 0xea, 0x75, 0x82, 0x5e, 0x5b, - 0xc6, 0x6b, 0x8b, 0xa3, 0xc4, 0x02, 0x0e, 0x05, 0x3a, 0xc4, 0x36, 0x05, 0x8a, 0x70, 0x99, 0x96, - 0x26, 0xf2, 0xfa, 0x05, 0xa4, 0x1f, 0x23, 0xb2, 0x09, 0x93, 0xa8, 0xc4, 0x13, 0xc6, 0x5d, 0x93, - 0xfa, 0xb6, 0x11, 0x97, 0xd4, 0xd8, 0xc2, 0xe0, 0xb1, 0x82, 0xec, 0x76, 0x4b, 0x5a, 0x68, 0xab, - 0xca, 0x48, 0x05, 0x99, 0x7c, 0x03, 0x66, 0xf8, 0x75, 0xf3, 0x21, 0xdd, 0xe2, 0x73, 0x67, 0x4a, - 0xbb, 0x3b, 0xeb, 0x85, 0xfc, 0x3d, 0x59, 0xa8, 0x4e, 0xf7, 0xe9, 0x16, 0x1f, 0x7b, 0xcd, 0x3f, - 0x42, 0xc3, 0x27, 0xf7, 0x61, 0x7e, 0xc5, 0x0e, 0x38, 0x50, 0x71, 0x46, 0x9e, 0x46, 0x9d, 0x22, - 0xda, 0xad, 0xee, 0xda, 0x81, 0xd4, 0xc5, 0x66, 0x3a, 0x1f, 0x67, 0xd1, 0x93, 0xef, 0xe5, 0x60, - 0x41, 0x53, 0xd5, 0x0a, 0xc3, 0xa1, 0x36, 0xed, 0x84, 0xe8, 0x08, 0x31, 0x13, 0xa5, 0xd4, 0xee, - 0x87, 0xc6, 0x87, 0x24, 0xa1, 0x0d, 0xf6, 0xe3, 0x72, 0xd5, 0x20, 0xb4, 0x1f, 0x0f, 0xe3, 0x66, - 0xb2, 0xf7, 0x84, 0xa2, 0x25, 0x17, 0x29, 0x5a, 0x4e, 0xc1, 0x28, 0xf6, 0x91, 0x8c, 0x84, 0x82, - 0x3f, 0x8c, 0xcf, 0xa9, 0xbb, 0x8a, 0x10, 0xf2, 0x06, 0xee, 0x2a, 0xc6, 0x7f, 0x3f, 0x06, 0xb3, - 0x89, 0x41, 0x16, 0xb7, 0xce, 0x5c, 0xea, 0xd6, 0xd9, 0x00, 0xe0, 0xaa, 0xc6, 0x21, 0x75, 0x82, - 0xd2, 0xa5, 0x6a, 0x52, 0x78, 0x28, 0x46, 0x2b, 0x44, 0x61, 0xc3, 0x98, 0xf2, 0xf5, 0x37, 0xa4, - 0x8e, 0x36, 0x62, 0xca, 0x97, 0xb0, 0xc2, 0x34, 0x66, 0x43, 0xca, 0x30, 0x8a, 0x31, 0x10, 0x55, - 0x8f, 0x36, 0x97, 0x01, 0x4c, 0x0e, 0x27, 0x2f, 0xc2, 0x18, 0x13, 0x89, 0xea, 0x35, 0xb1, 0xa5, - 0xe1, 0x49, 0xc1, 0x64, 0x26, 0x26, 0x7f, 0x88, 0x22, 0x72, 0x13, 0xa6, 0xf8, 0x5f, 0x22, 0x82, - 0xc5, 0x98, 0x6e, 0x9b, 0x66, 0xb9, 0x8e, 0x0c, 0x62, 0xa1, 0xe1, 0xb1, 0xbb, 0x42, 0xa3, 0x87, - 0xfa, 0x87, 0x7a, 0x4d, 0x04, 0xcd, 0xc5, 0xbb, 0x42, 0xc0, 0x81, 0x98, 0x80, 0x3e, 0x42, 0x60, - 0x92, 0x89, 0xb0, 0x2b, 0x2f, 0xe2, 0x0d, 0x11, 0x25, 0x13, 0x6e, 0x4f, 0x6e, 0x8a, 0x12, 0x72, - 0x85, 0xab, 0xf6, 0x51, 0xc8, 0xe3, 0x79, 0xbe, 0x50, 0x6f, 0x8e, 0x6a, 0x06, 0x94, 0xf4, 0xa2, - 0x62, 0x56, 0x39, 0xfb, 0x7b, 0xb9, 0x6d, 0xbb, 0x2d, 0xb1, 0x49, 0x60, 0xe5, 0x88, 0x4b, 0x19, - 0xd4, 0x8c, 0x11, 0xc8, 0x3b, 0x30, 0xc3, 0x7e, 0x54, 0xbd, 0x76, 0xdb, 0xeb, 0x20, 0xfb, 0xc9, - 0x38, 0x18, 0x12, 0x92, 0x34, 0xb1, 0x88, 0xd7, 0x92, 0xc0, 0x65, 0xa7, 0x03, 0x3e, 0x1b, 0xf6, - 0xf8, 0xa3, 0xc3, 0x54, 0x7c, 0x3a, 0x20, 0x69, 0xc0, 0xe1, 0xa6, 0x8a, 0x44, 0xde, 0x82, 0x69, - 0xf6, 0xf3, 0xb6, 0xfb, 0x88, 0xf2, 0x0a, 0xa7, 0xe3, 0xf7, 0x72, 0xa4, 0xda, 0x61, 0x25, 0xbc, - 0x3e, 0x1d, 0x93, 0x7c, 0x05, 0x4e, 0x23, 0xa7, 0xa6, 0xd7, 0xa5, 0x4e, 0x65, 0x7b, 0xdb, 0x6d, - 0xb9, 0xdc, 0x58, 0x88, 0xc7, 0x6a, 0x40, 0x1d, 0x30, 0xaf, 0x18, 0x31, 0x2c, 0x3b, 0x46, 0x31, - 0xb3, 0x29, 0xc9, 0x43, 0x28, 0x55, 0x7b, 0x41, 0xe8, 0xb5, 0x2b, 0x61, 0xe8, 0xbb, 0x5b, 0xbd, - 0x90, 0x06, 0x0b, 0xb3, 0x5a, 0x44, 0x03, 0xb6, 0x38, 0xa2, 0x42, 0xae, 0xdd, 0x69, 0x22, 0x85, - 0x65, 0x47, 0x24, 0x66, 0x8a, 0x89, 0xf1, 0x8f, 0x73, 0x30, 0xad, 0x91, 0x92, 0x37, 0x61, 0xea, - 0x96, 0xef, 0xd2, 0x8e, 0xd3, 0x3a, 0x50, 0xae, 0x9d, 0x78, 0x27, 0xd9, 0x16, 0x70, 0xde, 0x6a, - 0x0d, 0x2d, 0xd2, 0xda, 0xe4, 0x33, 0x2d, 0xf9, 0xae, 0x71, 0xc7, 0x4a, 0x31, 0x41, 0x0b, 0x71, - 0x88, 0x15, 0x9c, 0xa0, 0x62, 0x76, 0x2a, 0x28, 0xe4, 0x5d, 0x18, 0xe3, 0x0f, 0x8c, 0xc2, 0xac, - 0xec, 0x5c, 0x56, 0x33, 0xb9, 0x13, 0x2f, 0x4e, 0x44, 0xb4, 0x22, 0x09, 0x4c, 0x41, 0x64, 0xfc, - 0x5c, 0x0e, 0x48, 0x1a, 0xf5, 0x18, 0x2d, 0xd6, 0xb1, 0xd6, 0x29, 0x5f, 0x8e, 0x56, 0x63, 0x41, - 0xd3, 0xd9, 0xb2, 0x9a, 0x78, 0x01, 0xef, 0x78, 0xb1, 0xea, 0x54, 0xb5, 0x1a, 0x2f, 0x36, 0xfe, - 0x72, 0x1e, 0x20, 0xc6, 0x26, 0x5f, 0xe4, 0xa9, 0x66, 0xbe, 0xd2, 0xb3, 0x5b, 0xee, 0xb6, 0xab, - 0xc7, 0x5e, 0x44, 0x26, 0xdf, 0x96, 0x25, 0xa6, 0x8e, 0x48, 0xde, 0x87, 0xd9, 0xc6, 0x86, 0x4e, - 0xab, 0xa4, 0xd5, 0x08, 0xba, 0x56, 0x82, 0x3c, 0x89, 0x8d, 0xe6, 0xa3, 0xea, 0x68, 0x70, 0xf3, - 0x51, 0x3e, 0x10, 0xa2, 0x84, 0x6d, 0x2c, 0x8d, 0x0d, 0x61, 0xb1, 0xec, 0xd4, 0x6b, 0x62, 0x97, - 0xc2, 0xaf, 0x0b, 0xba, 0x56, 0x57, 0x98, 0x32, 0xb3, 0x7d, 0x42, 0xc3, 0x8b, 0x3b, 0x72, 0xb4, - 0x8f, 0xa3, 0xee, 0xcf, 0xa3, 0x12, 0xaf, 0xed, 0x85, 0x54, 0xe8, 0x2e, 0x9e, 0xda, 0x5b, 0x4c, - 0xfc, 0x3a, 0x3d, 0xaa, 0xf9, 0x1f, 0x6a, 0xad, 0x13, 0x16, 0x18, 0x37, 0xe2, 0x2b, 0x07, 0x7f, - 0xa7, 0xce, 0x30, 0xda, 0xf8, 0x9b, 0x39, 0x38, 0x9d, 0x49, 0x4b, 0xae, 0x02, 0xc4, 0x1a, 0x22, - 0xd1, 0x4b, 0xb8, 0x63, 0xc6, 0x81, 0x45, 0x4c, 0x05, 0x83, 0x7c, 0x3d, 0xa9, 0xdb, 0x39, 0xfe, - 0x20, 0x3c, 0x2f, 0x03, 0x47, 0xe9, 0xba, 0x9d, 0x0c, 0x8d, 0x8e, 0xf1, 0x77, 0x0a, 0x30, 0xa7, - 0xc4, 0x2d, 0xe1, 0xdf, 0x7a, 0x8c, 0x39, 0xef, 0x1e, 0x4c, 0xb1, 0xd6, 0xb8, 0x4d, 0xe1, 0x2e, - 0xc0, 0x2d, 0x29, 0x5e, 0x4d, 0x79, 0x90, 0x09, 0x6e, 0x57, 0x55, 0x64, 0x1e, 0xce, 0x0d, 0xb7, - 0x4e, 0xd4, 0x9c, 0x37, 0xd3, 0xae, 0x02, 0x1a, 0x73, 0x12, 0xc0, 0x74, 0xed, 0xa0, 0x63, 0xb7, - 0xa3, 0xda, 0xb8, 0x45, 0xc5, 0x6b, 0x7d, 0x6b, 0xd3, 0xb0, 0x79, 0x75, 0xb1, 0xaf, 0x05, 0x2f, - 0xcb, 0x70, 0xf3, 0xd5, 0xa8, 0xce, 0xbf, 0x0f, 0x73, 0xa9, 0x8f, 0x3e, 0x51, 0x64, 0xb9, 0x87, - 0x40, 0xd2, 0xdf, 0x91, 0xc1, 0xe1, 0x35, 0x3d, 0x6e, 0xe1, 0xe9, 0xe8, 0xf1, 0x14, 0x13, 0x5f, - 0x73, 0xfb, 0x8c, 0x45, 0x35, 0xee, 0xdc, 0xcf, 0xe7, 0x55, 0x2f, 0xbe, 0xa7, 0x7d, 0xd5, 0x7d, - 0x59, 0xbb, 0xdb, 0x3e, 0xdf, 0x6f, 0x4c, 0x87, 0xd2, 0x21, 0xfc, 0xa0, 0x00, 0x67, 0xfb, 0x50, - 0x92, 0x83, 0xe4, 0x24, 0xe2, 0x3a, 0x85, 0xeb, 0x83, 0x2b, 0x7c, 0x12, 0x53, 0x89, 0x7c, 0x91, - 0xfb, 0xf1, 0x37, 0x31, 0xb9, 0xb2, 0xb8, 0x4d, 0xf3, 0x5c, 0xff, 0x11, 0x34, 0xe9, 0xc0, 0xcf, - 0xa1, 0xe4, 0x7d, 0x18, 0x45, 0x17, 0xce, 0x44, 0xdc, 0x34, 0x86, 0x81, 0x70, 0x25, 0xc8, 0x1c, - 0xfb, 0xa9, 0x05, 0x99, 0x63, 0x00, 0xf2, 0x05, 0x28, 0x54, 0x1e, 0x36, 0xc4, 0xb8, 0xcc, 0xa8, - 0xe4, 0x0f, 0x1b, 0x71, 0x80, 0x7c, 0x5b, 0x8b, 0x64, 0xcf, 0x28, 0x18, 0xe1, 0xed, 0xea, 0x86, - 0x18, 0x15, 0x95, 0xf0, 0x76, 0x75, 0x23, 0x26, 0xdc, 0x69, 0x6a, 0x71, 0x68, 0x6e, 0x57, 0x37, - 0x3e, 0xbd, 0x69, 0xff, 0xef, 0xe4, 0x79, 0xf0, 0x01, 0xde, 0xb0, 0xf7, 0x61, 0x4a, 0x8b, 0x2b, - 0x9b, 0x8b, 0xe5, 0xb1, 0x28, 0x06, 0x70, 0xc2, 0x04, 0x45, 0x23, 0x90, 0xa9, 0x26, 0xd8, 0x6f, - 0x94, 0x78, 0x55, 0x63, 0x8f, 0x88, 0x03, 0xca, 0xc4, 0xc9, 0x54, 0x13, 0x11, 0x09, 0xb9, 0x01, - 0xc5, 0x4d, 0xda, 0xb1, 0x3b, 0x61, 0xa4, 0xde, 0x44, 0x6b, 0xd5, 0x10, 0x61, 0xba, 0xd4, 0x10, - 0x21, 0xa2, 0x65, 0x65, 0x6f, 0x2b, 0x68, 0xfa, 0x2e, 0x06, 0x29, 0x89, 0xce, 0x62, 0x6e, 0x59, - 0xa9, 0x94, 0xe8, 0x0c, 0x12, 0x44, 0xc6, 0xcf, 0xe7, 0x60, 0x5c, 0x0c, 0x24, 0x4f, 0x11, 0xb4, - 0x13, 0x9f, 0x25, 0x22, 0x45, 0xd0, 0x8e, 0x9b, 0x4c, 0x11, 0xb4, 0xc3, 0x23, 0x81, 0x4c, 0x08, - 0x3f, 0xda, 0xe8, 0xa1, 0x8f, 0x67, 0x94, 0xe7, 0x40, 0xbd, 0xda, 0x18, 0x75, 0x58, 0x7f, 0x19, - 0xe3, 0x6f, 0x88, 0x2f, 0xbb, 0x5d, 0xdd, 0x20, 0x8b, 0x50, 0x5c, 0xf5, 0x78, 0x94, 0x19, 0x35, - 0xdf, 0x63, 0x4b, 0xc0, 0xd4, 0x0e, 0x92, 0x78, 0xec, 0xfb, 0x36, 0x7c, 0x4f, 0xdc, 0x65, 0x94, - 0xef, 0xeb, 0x72, 0x60, 0xe2, 0xfb, 0x22, 0xd4, 0xa1, 0xbf, 0x8f, 0x66, 0x6c, 0x12, 0x0f, 0x6e, - 0x60, 0x0c, 0xfe, 0x3b, 0xaa, 0x1f, 0x92, 0x28, 0x92, 0x3b, 0xc5, 0xf9, 0x7e, 0x3b, 0xc5, 0x83, - 0x1b, 0x66, 0x06, 0x15, 0xbe, 0x92, 0xc5, 0xe0, 0x06, 0xf5, 0x1f, 0x3d, 0xc5, 0xbb, 0x74, 0xf6, - 0x2b, 0x59, 0xb2, 0x79, 0x43, 0x6d, 0xd2, 0xff, 0x28, 0x0f, 0x67, 0xb2, 0x09, 0xd5, 0xb6, 0xe4, - 0x06, 0xb4, 0xe5, 0x32, 0x14, 0x57, 0xbc, 0x20, 0x54, 0xac, 0xce, 0x50, 0x99, 0xbf, 0x2b, 0x60, - 0x66, 0x54, 0xca, 0xee, 0xdc, 0xec, 0xef, 0x68, 0x79, 0x22, 0x3f, 0x74, 0xb9, 0x67, 0x77, 0x6e, - 0x5e, 0x44, 0x6e, 0x43, 0xd1, 0x14, 0x7e, 0x30, 0x89, 0xae, 0x91, 0xe0, 0x48, 0x9a, 0x22, 0xbe, - 0x80, 0x68, 0xe1, 0x7d, 0x05, 0x8c, 0x54, 0x60, 0x5c, 0x8c, 0x7e, 0xe2, 0x21, 0x38, 0x63, 0xca, - 0xe8, 0x11, 0xb7, 0x25, 0x1d, 0xdb, 0x51, 0xf0, 0x49, 0xaf, 0x5e, 0x93, 0x2e, 0x2d, 0xb8, 0xa3, - 0xf0, 0x27, 0x3f, 0xdd, 0xc0, 0x2f, 0x42, 0x34, 0xbe, 0x97, 0x07, 0x90, 0x5a, 0x9b, 0xa7, 0x76, - 0x86, 0x7d, 0x41, 0x9b, 0x61, 0x8a, 0xbd, 0xcb, 0xf0, 0x29, 0x2d, 0xd7, 0xd1, 0xee, 0x64, 0xf8, - 0x84, 0x96, 0x65, 0x18, 0xdd, 0x8c, 0x15, 0x5a, 0xc2, 0xc7, 0x01, 0x95, 0xcb, 0x1c, 0x6e, 0x6c, - 0xc1, 0xa9, 0xdb, 0x34, 0x8c, 0xd5, 0x5b, 0xf2, 0x21, 0x71, 0x30, 0xdb, 0xd7, 0x61, 0x42, 0xe0, - 0x47, 0xfb, 0x17, 0xd7, 0xc5, 0x88, 0x28, 0x16, 0xa8, 0x8b, 0x91, 0x08, 0x6c, 0x37, 0xaa, 0xd1, - 0x16, 0x0d, 0xe9, 0xa7, 0x5b, 0x4d, 0x03, 0x08, 0x6f, 0x0a, 0xb6, 0x6c, 0xb8, 0x1a, 0x8e, 0xed, - 0x9f, 0x07, 0x70, 0x3a, 0xfa, 0xf6, 0x27, 0xc9, 0xf7, 0x1a, 0xbb, 0x52, 0x8a, 0x60, 0xd5, 0x31, - 0xc7, 0x01, 0x96, 0x24, 0x7f, 0x94, 0x83, 0xf3, 0x92, 0xe2, 0xa1, 0x1b, 0x59, 0xee, 0x0d, 0x45, - 0x4c, 0xde, 0x81, 0x49, 0x85, 0x46, 0x44, 0x5b, 0x46, 0xad, 0xf3, 0xbe, 0x1b, 0xee, 0x5a, 0x01, - 0x87, 0xab, 0x5a, 0x67, 0x05, 0x9d, 0x6c, 0xc1, 0xf9, 0x46, 0xe5, 0xde, 0x6a, 0x9c, 0xff, 0x77, - 0xcd, 0xbb, 0xe5, 0xb5, 0x5a, 0xde, 0xfe, 0x7d, 0x73, 0x55, 0x66, 0x3f, 0x40, 0x57, 0x7d, 0x54, - 0x61, 0xc7, 0xe9, 0x86, 0xad, 0x8e, 0x67, 0x6d, 0x23, 0xa2, 0xd5, 0xf3, 0x5b, 0x81, 0x39, 0x80, - 0x8b, 0xf1, 0x0f, 0x72, 0xf0, 0x6c, 0xe4, 0xed, 0x92, 0xd1, 0xbe, 0x44, 0x0b, 0x72, 0x4f, 0xb2, - 0x05, 0xf9, 0x27, 0xd2, 0x82, 0xb5, 0x78, 0x7c, 0xea, 0x9d, 0xc8, 0x05, 0x57, 0x7e, 0x3f, 0x51, - 0xc7, 0x47, 0x8c, 0xca, 0x73, 0x29, 0xa7, 0x5e, 0xc5, 0x77, 0xd7, 0x78, 0x5b, 0xe9, 0x90, 0x0c, - 0x86, 0x1a, 0x71, 0x2e, 0x49, 0xfc, 0xbd, 0x3c, 0xcc, 0xae, 0xd7, 0x6b, 0xd5, 0xc8, 0x2a, 0xea, - 0x33, 0x96, 0x3a, 0x54, 0x6b, 0x5b, 0xff, 0x9d, 0xd3, 0xb8, 0x0f, 0xf3, 0x89, 0x6e, 0x40, 0x21, - 0xe8, 0x3d, 0xee, 0x8b, 0x11, 0x81, 0xa5, 0x00, 0x74, 0x26, 0x8b, 0xfd, 0x83, 0x1b, 0x66, 0x02, - 0xdb, 0xf8, 0x27, 0x13, 0x09, 0xbe, 0x62, 0x33, 0x7e, 0x1d, 0x26, 0xea, 0x41, 0xd0, 0xa3, 0xfe, - 0x7d, 0x73, 0x55, 0x55, 0x7a, 0xb8, 0x08, 0x64, 0x73, 0xc8, 0x8c, 0x11, 0xc8, 0x15, 0x28, 0x8a, - 0x48, 0xca, 0x72, 0x77, 0x43, 0xfd, 0x73, 0x14, 0x88, 0xd9, 0x8c, 0x8a, 0xc9, 0x9b, 0x30, 0xc5, - 0xff, 0xe6, 0x33, 0x5a, 0x74, 0x38, 0xaa, 0x39, 0x05, 0x3a, 0x5f, 0x01, 0xa6, 0x86, 0x46, 0x5e, - 0x85, 0x42, 0xa5, 0x6a, 0x0a, 0xc5, 0x96, 0x90, 0x80, 0x31, 0x21, 0x78, 0x8f, 0xea, 0xd7, 0xa1, - 0xaa, 0xc9, 0xe4, 0x58, 0xe9, 0xee, 0x2f, 0x74, 0xf2, 0x3c, 0x6f, 0xb9, 0x80, 0x25, 0x8e, 0x65, - 0x84, 0x91, 0x6b, 0x30, 0x5e, 0x73, 0x83, 0x6e, 0xcb, 0x3e, 0x10, 0x1a, 0x79, 0x9e, 0x17, 0x8b, - 0x83, 0x34, 0x2f, 0x7e, 0x0e, 0x22, 0x57, 0x64, 0xbe, 0xa0, 0x62, 0xec, 0xd2, 0xd1, 0x27, 0x29, - 0xd0, 0xeb, 0x30, 0x26, 0xe2, 0x0d, 0x4f, 0x28, 0x99, 0x04, 0x92, 0x71, 0x86, 0x05, 0x4e, 0xda, - 0xb7, 0x13, 0x9e, 0xa4, 0x6f, 0xe7, 0x16, 0x9c, 0xbd, 0x8d, 0x7a, 0x28, 0x3d, 0x48, 0xcf, 0x7d, - 0xb3, 0x2e, 0x34, 0xfb, 0xf8, 0x3c, 0xc5, 0x55, 0x55, 0xc9, 0x38, 0x3f, 0x56, 0xcf, 0x57, 0xd3, - 0x3c, 0xf6, 0x63, 0x44, 0xbe, 0x0a, 0xa7, 0xb2, 0x8a, 0x84, 0xfe, 0x1f, 0xc3, 0xd1, 0x64, 0x57, - 0xa0, 0x86, 0xa3, 0xc9, 0xe2, 0x40, 0x56, 0xa1, 0xc4, 0xe1, 0x15, 0xa7, 0xed, 0x76, 0xf8, 0x1b, - 0x06, 0x7f, 0x1f, 0x40, 0x1f, 0x0b, 0xc1, 0xd5, 0x66, 0x85, 0xfc, 0x2d, 0x43, 0xf3, 0xca, 0x49, - 0x50, 0x92, 0x9f, 0xce, 0xb1, 0x7b, 0x29, 0x8f, 0xce, 0x8b, 0xdb, 0xe7, 0x8c, 0x78, 0xdb, 0x8c, - 0xdc, 0x64, 0x1a, 0xa1, 0xef, 0x76, 0x76, 0x84, 0xc7, 0xcd, 0xa6, 0xf0, 0xb8, 0x79, 0xe7, 0x63, - 0x79, 0xdc, 0x70, 0x56, 0xc1, 0xd1, 0x61, 0x79, 0xca, 0x17, 0x75, 0xe2, 0x2a, 0xd2, 0xbe, 0x80, - 0x75, 0x1d, 0xba, 0x9d, 0xde, 0xef, 0xf0, 0xd8, 0xa0, 0xd4, 0xe1, 0x8d, 0x9c, 0xc5, 0x8d, 0x1d, - 0xbb, 0xce, 0xe6, 0x9b, 0x78, 0x84, 0x90, 0x6a, 0x68, 0x26, 0x07, 0x76, 0x85, 0x96, 0x5e, 0x1d, - 0xdc, 0x51, 0xb5, 0x14, 0x5f, 0xa1, 0xa5, 0x0b, 0x88, 0x85, 0xd3, 0x48, 0x9d, 0x3c, 0x1a, 0x09, - 0xb9, 0x06, 0x63, 0xf7, 0xec, 0xc7, 0x95, 0x1d, 0x2a, 0xf2, 0xc0, 0x4d, 0xcb, 0xed, 0x0f, 0x81, - 0x4b, 0xc5, 0x3f, 0xe4, 0x5e, 0x03, 0xcf, 0x98, 0x02, 0x8d, 0xfc, 0x70, 0x0e, 0xce, 0xf0, 0x65, - 0x2c, 0x5b, 0xd9, 0xa0, 0x61, 0xc8, 0xfa, 0x41, 0xc4, 0x34, 0x93, 0xde, 0xd6, 0x8d, 0xc6, 0x7a, - 0x36, 0x1e, 0x4f, 0xa8, 0x2f, 0x76, 0x86, 0xa8, 0xe3, 0x02, 0x51, 0xaa, 0x45, 0x6b, 0xcd, 0xa4, - 0x17, 0x16, 0xf1, 0x5f, 0x90, 0x5f, 0x4e, 0xde, 0x50, 0xfd, 0x29, 0x0b, 0x28, 0xb1, 0x8f, 0xb7, - 0xed, 0xc7, 0x96, 0xbd, 0x43, 0xb5, 0x77, 0x76, 0xa1, 0x31, 0xff, 0xd9, 0x1c, 0x9c, 0xeb, 0xfb, - 0x71, 0xe4, 0x26, 0x9c, 0xb5, 0xb9, 0x97, 0xb0, 0xb5, 0x1b, 0x86, 0xdd, 0xc0, 0x92, 0xd7, 0x1a, - 0xe1, 0x81, 0x69, 0x9e, 0x16, 0xc5, 0x2b, 0xac, 0x54, 0xde, 0x74, 0x02, 0xf2, 0x3e, 0x3c, 0xe7, - 0x76, 0x02, 0xda, 0xec, 0xf9, 0xd4, 0x92, 0x0c, 0x9a, 0xae, 0xe3, 0x5b, 0xbe, 0xdd, 0xd9, 0x91, - 0xee, 0xa4, 0xe6, 0x39, 0x89, 0x23, 0x3c, 0x91, 0xab, 0xae, 0xe3, 0x9b, 0x88, 0x60, 0xfc, 0xc9, - 0x04, 0x3f, 0x15, 0x2b, 0xbd, 0x70, 0x57, 0x9e, 0xa3, 0x8b, 0x59, 0xde, 0x41, 0xdc, 0x6c, 0x51, - 0xf1, 0x0e, 0xd2, 0x7d, 0x82, 0xe4, 0xc3, 0x4c, 0x3e, 0xf3, 0x61, 0xe6, 0x75, 0x98, 0xa8, 0xee, - 0xd2, 0xe6, 0x5e, 0xe4, 0xa1, 0x51, 0x14, 0x9a, 0x6f, 0x06, 0xe4, 0xa1, 0x80, 0x63, 0x04, 0x72, - 0x0d, 0x00, 0x9d, 0x12, 0xb9, 0xb8, 0xa8, 0x84, 0xf3, 0x47, 0x1f, 0x46, 0x61, 0x09, 0xa2, 0xa0, - 0x20, 0xfb, 0x86, 0x79, 0x4b, 0x35, 0x1d, 0xe1, 0xec, 0x03, 0x7f, 0x5b, 0xa0, 0xc7, 0x08, 0xac, - 0x79, 0xca, 0x52, 0x11, 0x1b, 0x7b, 0x29, 0xb5, 0x9e, 0x54, 0x24, 0xb4, 0xca, 0x94, 0xe6, 0xe8, - 0xb8, 0xaf, 0x4f, 0x09, 0xab, 0xcc, 0xc8, 0x74, 0xdd, 0x8c, 0x11, 0xc8, 0x17, 0x60, 0xbc, 0x4a, - 0xfd, 0x70, 0x73, 0x73, 0x15, 0xad, 0x3b, 0x78, 0xcc, 0xfb, 0x22, 0xc6, 0x27, 0x0f, 0xc3, 0xd6, - 0x47, 0x87, 0xe5, 0xe9, 0xd0, 0x6d, 0xd3, 0x28, 0x96, 0xaf, 0x29, 0xb1, 0xc9, 0x12, 0x94, 0xf8, - 0x8b, 0x75, 0x7c, 0x2d, 0xc0, 0xad, 0xbe, 0xc8, 0x0f, 0x1e, 0xf1, 0xbc, 0xbd, 0x4f, 0xb7, 0xa2, - 0xe8, 0xec, 0x29, 0x7c, 0xb2, 0x2c, 0x93, 0x1a, 0xa8, 0x8d, 0x84, 0x58, 0x4f, 0x95, 0x5c, 0x02, - 0xac, 0xad, 0x69, 0x0a, 0x52, 0x81, 0xe9, 0xaa, 0xd7, 0xee, 0xda, 0xa1, 0x8b, 0x69, 0xc6, 0x0e, - 0xc4, 0xae, 0x8e, 0xba, 0xb6, 0xa6, 0x5a, 0xa0, 0x1d, 0x11, 0x6a, 0x01, 0xb9, 0x05, 0x33, 0xa6, - 0xd7, 0x63, 0x83, 0x24, 0x2f, 0xc8, 0x7c, 0xe3, 0x46, 0x1b, 0x0c, 0x9f, 0x95, 0xb0, 0x73, 0x46, - 0xdc, 0x86, 0xb5, 0x30, 0x8b, 0x1a, 0x15, 0x59, 0xcb, 0x78, 0xa9, 0x50, 0x77, 0x6b, 0x35, 0x46, - 0x7b, 0x8a, 0x59, 0xc6, 0x23, 0xc7, 0x0d, 0x98, 0x6c, 0x34, 0xd6, 0x37, 0x69, 0x10, 0xde, 0x6a, - 0x79, 0xfb, 0xb8, 0x59, 0x17, 0x45, 0xee, 0x9a, 0xc0, 0xb3, 0x42, 0x1a, 0x84, 0xd6, 0x76, 0xcb, - 0xdb, 0x37, 0x55, 0x2c, 0xf2, 0x4d, 0xd6, 0x1f, 0x8a, 0x68, 0x23, 0x02, 0x4a, 0x0e, 0x92, 0xbe, - 0x70, 0x4b, 0x8c, 0x97, 0x0c, 0x93, 0xc1, 0xf4, 0xce, 0x52, 0xd0, 0xd1, 0xdd, 0x88, 0x5d, 0xed, - 0x2b, 0x8e, 0xe3, 0xd3, 0x20, 0x10, 0xbb, 0x2a, 0x77, 0x37, 0x42, 0x3d, 0x80, 0xcd, 0x0b, 0x34, - 0x77, 0x23, 0x85, 0x80, 0x7c, 0x3f, 0x07, 0xa7, 0x55, 0x8f, 0x05, 0x5c, 0x2c, 0x68, 0x4f, 0xc2, - 0xf7, 0xd8, 0x37, 0xae, 0xca, 0x53, 0xe5, 0xaa, 0x82, 0x76, 0xf5, 0xd1, 0xf5, 0xab, 0x95, 0xf8, - 0x67, 0x43, 0x12, 0x89, 0x98, 0x6c, 0x59, 0xfc, 0xd4, 0x13, 0xc2, 0xce, 0x20, 0x25, 0x55, 0x26, - 0x78, 0xb0, 0xf9, 0x84, 0xf6, 0x49, 0xf5, 0x0d, 0xdc, 0xa2, 0x85, 0xaa, 0x53, 0xcc, 0x3e, 0x6e, - 0xc9, 0xe4, 0x76, 0x75, 0xf9, 0x42, 0xa1, 0x21, 0x75, 0x98, 0xe5, 0x00, 0xb6, 0x25, 0xf0, 0xc4, - 0x26, 0xf3, 0x71, 0x70, 0x75, 0xc1, 0x06, 0x1f, 0xe1, 0x31, 0xb9, 0x89, 0x1a, 0xff, 0x30, 0x41, - 0x87, 0x92, 0x3f, 0xbb, 0xa5, 0xc4, 0xe2, 0xeb, 0x67, 0xcb, 0xe3, 0x40, 0x6b, 0xdb, 0x00, 0x8f, - 0x83, 0xfb, 0xdc, 0x07, 0x53, 0xe9, 0x06, 0x29, 0xf9, 0x6b, 0xe0, 0xa4, 0xe4, 0x9f, 0xa0, 0x31, - 0x13, 0xd8, 0xc6, 0x47, 0xc5, 0x04, 0x5f, 0x61, 0x65, 0x68, 0xc0, 0x18, 0x17, 0xec, 0xd5, 0x54, - 0xfb, 0x5c, 0xec, 0x37, 0x45, 0x09, 0x39, 0x07, 0x85, 0x46, 0x63, 0x5d, 0x74, 0x32, 0xda, 0x1a, - 0x06, 0x81, 0x67, 0x32, 0x18, 0x1b, 0x21, 0x34, 0x20, 0x54, 0x42, 0x49, 0xb3, 0x1d, 0xd4, 0x44, - 0x28, 0xeb, 0x6f, 0x29, 0x66, 0x8f, 0xc4, 0xfd, 0x2d, 0xc4, 0xec, 0x58, 0xb8, 0xae, 0xc2, 0x42, - 0x25, 0x08, 0xa8, 0xcf, 0x26, 0xa8, 0xb0, 0x4b, 0xf3, 0x85, 0x28, 0x28, 0x0e, 0x0a, 0xac, 0xd4, - 0x6e, 0x06, 0x66, 0x5f, 0x44, 0x72, 0x19, 0x8a, 0x95, 0x9e, 0xe3, 0xd2, 0x4e, 0x53, 0x0b, 0x91, - 0x64, 0x0b, 0x98, 0x19, 0x95, 0x92, 0xaf, 0xc0, 0xe9, 0x44, 0x98, 0x30, 0xd1, 0x03, 0xe3, 0xf1, - 0x6a, 0x96, 0xa2, 0x6a, 0xfc, 0xfa, 0xce, 0xbb, 0x24, 0x9b, 0x92, 0x54, 0xa0, 0xb4, 0x8c, 0x1e, - 0x36, 0x35, 0xca, 0x1f, 0x02, 0x3c, 0x9f, 0x7b, 0x0d, 0xf1, 0x8b, 0x85, 0x08, 0x86, 0xe6, 0x44, - 0x85, 0x66, 0x0a, 0x9d, 0xdc, 0x85, 0xf9, 0x24, 0x8c, 0x9d, 0x09, 0xfc, 0x0e, 0x81, 0xb1, 0x4c, - 0x53, 0x5c, 0xf0, 0x54, 0xc8, 0xa2, 0x22, 0x5b, 0x30, 0x17, 0x5b, 0x9f, 0xe8, 0x37, 0x0b, 0x69, - 0xa2, 0x1a, 0x95, 0xcb, 0xdb, 0xc5, 0xb3, 0x62, 0x32, 0xce, 0xc7, 0x96, 0x2c, 0xd1, 0x0d, 0xc3, - 0x4c, 0xb3, 0x23, 0x0e, 0xcc, 0x34, 0xdc, 0x9d, 0x8e, 0xdb, 0xd9, 0xb9, 0x4b, 0x0f, 0x36, 0x6c, - 0xd7, 0x17, 0xc6, 0x82, 0xd2, 0x14, 0xb8, 0x12, 0x1c, 0xb4, 0xdb, 0x34, 0xf4, 0xf1, 0xb4, 0x65, - 0xe5, 0xe8, 0xf0, 0xca, 0x24, 0xc6, 0xf3, 0x01, 0xa7, 0x43, 0x67, 0xb2, 0xae, 0xed, 0x6a, 0xc7, - 0x8a, 0xce, 0x53, 0xbb, 0xdd, 0x4d, 0x0d, 0x79, 0xbb, 0x6b, 0xc1, 0xdc, 0x72, 0xa7, 0xe9, 0x1f, - 0xe0, 0x7b, 0x8c, 0xfc, 0xb8, 0xe9, 0x63, 0x3e, 0xee, 0x25, 0xf1, 0x71, 0xcf, 0xd9, 0x72, 0x86, - 0x65, 0x7d, 0x5e, 0x9a, 0x31, 0x69, 0xc0, 0x1c, 0x4a, 0x6c, 0xf5, 0xda, 0x46, 0xbd, 0xe3, 0x86, - 0x2e, 0x26, 0x84, 0xe7, 0xc7, 0xd5, 0xcb, 0x82, 0xe7, 0x05, 0x2e, 0xc5, 0xbb, 0x4e, 0xd7, 0x72, - 0x25, 0x8a, 0xca, 0x34, 0x45, 0x3f, 0x48, 0x94, 0x9e, 0xfd, 0x97, 0x23, 0x4a, 0x63, 0xb6, 0xb3, - 0x84, 0x23, 0x78, 0x29, 0xde, 0xdb, 0x03, 0x2c, 0x62, 0x47, 0x84, 0xd7, 0x43, 0xf1, 0x44, 0xcb, - 0x76, 0xa6, 0xd3, 0x19, 0xdf, 0x9f, 0xe0, 0x7b, 0xbb, 0x2a, 0xbf, 0xf6, 0x33, 0x2b, 0x4c, 0xc8, - 0xb5, 0xf9, 0x93, 0xc8, 0xb5, 0x85, 0xe3, 0xe5, 0xda, 0x91, 0xe3, 0xe4, 0xda, 0x84, 0xe0, 0x39, - 0x7a, 0x62, 0xc1, 0x73, 0xec, 0x04, 0x82, 0xe7, 0xf8, 0x89, 0x04, 0x4f, 0x4d, 0x82, 0x2e, 0x1e, - 0x27, 0x41, 0xff, 0x1b, 0x31, 0xf5, 0x69, 0x15, 0x53, 0xb3, 0x44, 0x85, 0x13, 0x89, 0xa9, 0xfd, - 0xa5, 0xcc, 0xd2, 0xbf, 0x6a, 0x29, 0x73, 0xee, 0xc9, 0x48, 0x99, 0xe4, 0x63, 0x4a, 0x99, 0x7f, - 0x01, 0x4a, 0xc9, 0x83, 0xef, 0xf8, 0xe8, 0x88, 0x4f, 0x2c, 0x98, 0x16, 0x3b, 0x96, 0x93, 0x07, - 0x0f, 0xbb, 0x48, 0x6f, 0xf8, 0xee, 0x23, 0x3b, 0xa4, 0x77, 0xa5, 0x19, 0x86, 0x88, 0xec, 0xc9, - 0xa1, 0xb8, 0x7d, 0x28, 0x28, 0x91, 0xcc, 0x95, 0xcf, 0x92, 0xb9, 0x8c, 0x1f, 0xcf, 0xc3, 0x1c, - 0x8f, 0x48, 0xf3, 0xf4, 0xeb, 0xd0, 0xdf, 0xd3, 0x24, 0x69, 0x69, 0xf4, 0x97, 0x68, 0xdd, 0x00, - 0x2d, 0xfa, 0x37, 0xe0, 0x74, 0xaa, 0x2b, 0x50, 0x9a, 0xae, 0xc9, 0x58, 0x40, 0x29, 0x79, 0x7a, - 0x21, 0xbb, 0x92, 0x07, 0x37, 0xcc, 0x14, 0x85, 0xf1, 0x4f, 0x47, 0x52, 0xfc, 0x85, 0x3e, 0x5d, - 0xd5, 0x90, 0xe7, 0x4e, 0xa6, 0x21, 0xcf, 0x0f, 0xa7, 0x21, 0x4f, 0x1c, 0x53, 0x85, 0x61, 0x8e, - 0xa9, 0xaf, 0xc0, 0xf4, 0x26, 0xb5, 0xdb, 0xc1, 0xa6, 0x27, 0x72, 0x18, 0x70, 0xa3, 0x5f, 0x19, - 0xea, 0x87, 0x95, 0x49, 0x61, 0x30, 0x32, 0x5e, 0x0a, 0x19, 0x01, 0xdb, 0x5a, 0x79, 0x52, 0x03, - 0x53, 0xe7, 0xa0, 0x4a, 0xf8, 0xa3, 0x03, 0x24, 0xfc, 0x06, 0x4c, 0x09, 0xba, 0x38, 0x24, 0x64, - 0x2c, 0x8a, 0xb2, 0x22, 0x84, 0xcb, 0xda, 0xa3, 0xd4, 0x9a, 0x51, 0xed, 0x5c, 0x0a, 0xd5, 0x98, - 0xb0, 0x2e, 0x58, 0xee, 0x38, 0x5d, 0xcf, 0xed, 0x60, 0x17, 0x8c, 0xc7, 0x5d, 0x40, 0x05, 0x98, - 0x77, 0x81, 0x82, 0x44, 0xde, 0x81, 0x99, 0xca, 0x46, 0x5d, 0x25, 0x2b, 0xc6, 0x4a, 0x7a, 0xbb, - 0xeb, 0x5a, 0x1a, 0x69, 0x02, 0x77, 0x90, 0x54, 0x36, 0xf1, 0x2f, 0x47, 0x2a, 0x33, 0xfe, 0xe1, - 0x84, 0x5c, 0xde, 0x9f, 0xae, 0x32, 0x50, 0x57, 0xef, 0x15, 0x4e, 0xa8, 0xde, 0x1b, 0x39, 0x4e, - 0x38, 0xd1, 0x24, 0xa6, 0xd1, 0x13, 0x48, 0x4c, 0x63, 0x9f, 0x58, 0x55, 0x37, 0x7e, 0x42, 0x19, - 0x28, 0xb1, 0xd2, 0x8a, 0xc3, 0xac, 0xb4, 0x4c, 0xb9, 0x69, 0xe2, 0x93, 0xcb, 0x4d, 0x70, 0x62, - 0xb9, 0xa9, 0x11, 0x3b, 0xc4, 0x4d, 0x1e, 0x6b, 0x99, 0x7c, 0x41, 0xdc, 0x57, 0xe6, 0xb2, 0x83, - 0x11, 0x45, 0xae, 0x71, 0x9f, 0x29, 0x61, 0xec, 0x5b, 0xd9, 0xc2, 0xd8, 0xe0, 0xd3, 0xe6, 0xdf, - 0x88, 0x63, 0x4f, 0x44, 0x1c, 0xf3, 0x71, 0xc0, 0x1e, 0xda, 0x7e, 0x07, 0xaf, 0x9c, 0xd7, 0x60, - 0x5c, 0xc6, 0xf7, 0xca, 0xc5, 0xda, 0x93, 0x74, 0x60, 0x2f, 0x89, 0x45, 0x16, 0xa1, 0x28, 0x89, - 0xd5, 0x78, 0xec, 0xfb, 0x02, 0xa6, 0x85, 0x4e, 0x12, 0x30, 0xe3, 0x6f, 0x8d, 0xc8, 0x4d, 0x81, - 0x7d, 0x87, 0xc8, 0xda, 0xbe, 0xa4, 0x4c, 0x02, 0x45, 0x18, 0x4c, 0x0c, 0x73, 0xc2, 0x66, 0x51, - 0x27, 0xf9, 0x58, 0x11, 0xd7, 0xe2, 0xc4, 0x69, 0x85, 0x21, 0x12, 0xa7, 0xbd, 0xa5, 0x65, 0x1d, - 0x1b, 0x89, 0xd3, 0xdc, 0xb0, 0x85, 0x32, 0x38, 0xdf, 0xd8, 0x4d, 0x35, 0x3d, 0xd8, 0x68, 0x1c, - 0x7c, 0x04, 0x29, 0x07, 0x24, 0x06, 0x8b, 0xa4, 0xdb, 0xb1, 0x93, 0xc4, 0x32, 0x1c, 0xff, 0x57, - 0x1a, 0xcb, 0x70, 0x19, 0x40, 0xc9, 0x94, 0xcd, 0x1f, 0x77, 0x5e, 0x66, 0xdd, 0x74, 0x7c, 0x96, - 0x6c, 0x85, 0xd0, 0xf8, 0x03, 0x02, 0x73, 0x8d, 0xc6, 0x7a, 0xcd, 0xb5, 0x77, 0x3a, 0x5e, 0x10, - 0xba, 0xcd, 0x7a, 0x67, 0xdb, 0x63, 0xa2, 0x5d, 0xb4, 0xc1, 0x28, 0x41, 0xeb, 0xe2, 0xcd, 0x25, - 0x2a, 0x66, 0x57, 0x87, 0x65, 0xdf, 0xf7, 0x7c, 0xf5, 0xea, 0x40, 0x19, 0xc0, 0xe4, 0x70, 0x26, - 0x3d, 0x35, 0x7a, 0x3c, 0xe5, 0x31, 0x7f, 0x6f, 0x43, 0xe9, 0x29, 0xe0, 0x20, 0x53, 0x96, 0x11, - 0x9a, 0x9e, 0xb0, 0x42, 0x9a, 0x3e, 0xab, 0x45, 0x44, 0x8c, 0x8b, 0xf9, 0xf6, 0x29, 0x8e, 0x37, - 0x5c, 0x8a, 0x5d, 0x84, 0xab, 0x0f, 0xe4, 0xa9, 0x35, 0x70, 0x00, 0xa7, 0x35, 0x67, 0xae, 0x61, - 0x15, 0x87, 0xaf, 0x0a, 0x69, 0xcd, 0x40, 0x23, 0xa4, 0x0c, 0xed, 0xa1, 0x9a, 0xa6, 0x23, 0xb3, - 0x06, 0xf2, 0xe3, 0x39, 0xb8, 0x90, 0x59, 0x12, 0xad, 0xee, 0x49, 0x2d, 0x2a, 0xa5, 0xb2, 0x69, - 0xf0, 0x84, 0x24, 0xfd, 0xaa, 0xb6, 0x32, 0xb6, 0x82, 0xc1, 0x35, 0x91, 0xdf, 0xcc, 0xc1, 0x59, - 0x0d, 0x23, 0xda, 0x3e, 0x83, 0xc8, 0x6b, 0x39, 0x73, 0x5e, 0x7f, 0xf8, 0x64, 0xe6, 0xf5, 0x8b, - 0x7a, 0x5b, 0xe2, 0xdd, 0x5d, 0x6d, 0x43, 0xbf, 0x2f, 0x24, 0x8f, 0x60, 0x0e, 0x8b, 0xa4, 0x12, - 0x93, 0xcd, 0x59, 0xa1, 0xfb, 0x3c, 0x15, 0x7f, 0x36, 0x77, 0x50, 0xc4, 0xa4, 0x92, 0x8b, 0x3f, - 0x38, 0x2c, 0x4f, 0x6b, 0xe8, 0x32, 0xce, 0xa3, 0x15, 0x6b, 0x42, 0xdd, 0xce, 0xb6, 0xa7, 0x1e, - 0xbd, 0xa9, 0x2a, 0xc8, 0x3f, 0xc8, 0xc1, 0x02, 0x83, 0xf2, 0x66, 0xdc, 0xf2, 0xbd, 0x76, 0x54, - 0x2e, 0x2d, 0x2d, 0xfa, 0x74, 0x5b, 0xeb, 0xc9, 0x74, 0xdb, 0xcb, 0xf8, 0xc9, 0x7c, 0x4f, 0xb0, - 0xb6, 0x7d, 0xaf, 0x1d, 0x7f, 0xbe, 0x96, 0x09, 0xba, 0xdf, 0x47, 0x92, 0x1f, 0xc9, 0xc1, 0x39, - 0x4d, 0xf3, 0xa2, 0x06, 0xdd, 0x16, 0x6e, 0xa0, 0xf3, 0x91, 0xbb, 0x77, 0x5c, 0xb4, 0x74, 0x55, - 0xcc, 0xff, 0x4b, 0xf8, 0x05, 0xf1, 0x69, 0x81, 0xdf, 0x62, 0xb5, 0x39, 0x96, 0xf2, 0x09, 0xfd, - 0x6b, 0x21, 0x2e, 0xcc, 0xe1, 0x1b, 0xa5, 0x66, 0x11, 0x74, 0xaa, 0xbf, 0x45, 0x50, 0x14, 0x9c, - 0x1e, 0x43, 0xed, 0xf6, 0x37, 0x0b, 0x4a, 0x73, 0x25, 0x7f, 0x11, 0xce, 0xa5, 0x80, 0xd1, 0x6a, - 0x3b, 0xdd, 0x77, 0xb5, 0xbd, 0x76, 0x74, 0x58, 0x7e, 0x25, 0xab, 0xb6, 0xac, 0x95, 0xd6, 0xbf, - 0x06, 0x62, 0x03, 0xc4, 0x85, 0x22, 0xa1, 0x74, 0xf6, 0x04, 0x7d, 0x4d, 0xcc, 0x0f, 0x05, 0x9f, - 0xed, 0xe5, 0xca, 0x37, 0xa8, 0x47, 0x5e, 0x8c, 0x44, 0x28, 0x4c, 0x29, 0x61, 0x86, 0x0f, 0x30, - 0xb3, 0x74, 0xdf, 0x4a, 0x7e, 0x70, 0x58, 0xd6, 0xb0, 0x99, 0x88, 0xad, 0xc6, 0x2f, 0x56, 0x45, - 0x6c, 0x0d, 0x91, 0xfc, 0x46, 0x0e, 0x4e, 0x31, 0x40, 0x3c, 0xa9, 0x44, 0xa3, 0x16, 0x06, 0xcd, - 0xfa, 0xdd, 0x27, 0x33, 0xeb, 0x5f, 0xc0, 0x6f, 0x54, 0x67, 0x7d, 0xaa, 0x4b, 0x32, 0x3f, 0x0e, - 0x67, 0xbb, 0xf6, 0x1c, 0xae, 0xcd, 0xf6, 0x73, 0x43, 0xcc, 0x76, 0x3e, 0x00, 0xc7, 0xcf, 0xf6, - 0xbe, 0xb5, 0x90, 0x4d, 0x98, 0x12, 0xd2, 0x35, 0xef, 0xb0, 0xe7, 0xb5, 0x10, 0xa5, 0x6a, 0x11, - 0xbf, 0xf2, 0x88, 0x28, 0xcc, 0xa9, 0x16, 0x6a, 0x5c, 0x48, 0x07, 0xe6, 0xf9, 0x6f, 0x5d, 0xd7, - 0x51, 0xee, 0xab, 0xeb, 0xb8, 0x2c, 0x5a, 0x74, 0x51, 0xf0, 0x4f, 0xa8, 0x3c, 0xd4, 0x28, 0x11, - 0x19, 0x8c, 0x49, 0x17, 0x88, 0x06, 0xe6, 0x8b, 0xf6, 0xe2, 0x60, 0x0d, 0xc7, 0x2b, 0xa2, 0xce, - 0x72, 0xb2, 0xce, 0xe4, 0xca, 0xcd, 0xe0, 0x4d, 0x6c, 0x98, 0x15, 0x50, 0x76, 0x97, 0xc6, 0x1d, - 0xfe, 0x05, 0x2d, 0x4e, 0x47, 0xa2, 0x94, 0x0b, 0xe6, 0xb2, 0x26, 0x8c, 0xa3, 0x92, 0xd8, 0xd0, - 0x93, 0xfc, 0xc8, 0x3a, 0xcc, 0x55, 0xba, 0xdd, 0x96, 0x4b, 0x1d, 0x6c, 0x25, 0xcf, 0x93, 0x6b, - 0xc4, 0x69, 0x41, 0x6c, 0x5e, 0x28, 0x6e, 0x0b, 0xc9, 0x24, 0xb9, 0x69, 0x5a, 0xe3, 0xfb, 0xb9, - 0xd4, 0x47, 0x93, 0xd7, 0x61, 0x02, 0x7f, 0x28, 0xce, 0xe2, 0xa8, 0x04, 0xe0, 0x9f, 0x88, 0xca, - 0x88, 0x18, 0x81, 0x09, 0x4b, 0x6a, 0xf8, 0xa7, 0x02, 0x17, 0x96, 0xc4, 0x4d, 0x35, 0xbe, 0x9b, - 0x96, 0xa5, 0xa5, 0x66, 0x21, 0x16, 0xba, 0xd0, 0x52, 0x53, 0xd8, 0x67, 0x1a, 0x3f, 0x92, 0xd7, - 0xa7, 0x1d, 0xb9, 0xac, 0xc8, 0xed, 0x4a, 0x00, 0x2a, 0x29, 0xb7, 0x2b, 0xd2, 0xfa, 0xdf, 0xcc, - 0xc1, 0xfc, 0xba, 0xbf, 0x63, 0x77, 0xdc, 0xef, 0xf0, 0x40, 0x96, 0x1e, 0x8e, 0xcb, 0xe0, 0x0c, - 0x4b, 0x4f, 0x2a, 0x53, 0x8c, 0xa7, 0x54, 0xcc, 0x66, 0x0a, 0x4e, 0x19, 0x33, 0xeb, 0x7b, 0xd0, - 0x8a, 0x1f, 0x3f, 0x4c, 0x49, 0xd8, 0xc3, 0xd1, 0x39, 0xdc, 0xf8, 0xc9, 0x3c, 0x4c, 0x2a, 0x4b, - 0x80, 0x7c, 0x1e, 0xa6, 0x54, 0x3e, 0xaa, 0x02, 0x49, 0xad, 0xd6, 0xd4, 0xb0, 0x50, 0x83, 0x44, - 0xed, 0xb6, 0xa6, 0x41, 0x62, 0x13, 0x1d, 0xa1, 0x27, 0xbc, 0xda, 0xbc, 0x9f, 0x71, 0xb5, 0x39, - 0x51, 0x42, 0xe5, 0x77, 0xd2, 0x17, 0x9c, 0xe1, 0xf3, 0x1f, 0x1b, 0x3f, 0x93, 0x83, 0x52, 0x72, - 0x91, 0x7e, 0x2a, 0xbd, 0x72, 0x82, 0xd7, 0x82, 0x9f, 0xc8, 0x47, 0x31, 0xc6, 0xa5, 0x6f, 0xd2, - 0xd3, 0x6a, 0x12, 0xf3, 0xae, 0xa6, 0xc8, 0x7f, 0x56, 0x0f, 0x9a, 0xa3, 0x7a, 0xf5, 0x66, 0x47, - 0xca, 0x1a, 0xf9, 0x85, 0x5f, 0x2e, 0x3f, 0x63, 0x7c, 0x00, 0xa7, 0x92, 0xdd, 0x81, 0xca, 0xfc, - 0x0a, 0xcc, 0xea, 0xf0, 0x64, 0x86, 0x82, 0x24, 0x95, 0x99, 0xc4, 0x37, 0xfe, 0x30, 0x9f, 0xe4, - 0x2d, 0xcc, 0x63, 0xd8, 0xa6, 0xd3, 0xb1, 0xb7, 0x5a, 0x51, 0x84, 0x72, 0xbe, 0xe9, 0x70, 0x90, - 0x29, 0xcb, 0x4e, 0x92, 0x19, 0x24, 0xf2, 0xb0, 0x29, 0x64, 0x7b, 0xd8, 0x90, 0x9b, 0x09, 0x1b, - 0x33, 0x25, 0x1c, 0xc4, 0x3e, 0xdd, 0xb2, 0x62, 0x3b, 0xb3, 0x84, 0x69, 0x59, 0x15, 0x4e, 0x69, - 0x91, 0x4a, 0x25, 0xfd, 0x68, 0xac, 0xbb, 0x0d, 0xb1, 0x80, 0x13, 0x67, 0x22, 0x93, 0x15, 0x18, - 0x67, 0x9f, 0x79, 0xcf, 0xee, 0x0a, 0x1d, 0x3d, 0x89, 0xfc, 0xed, 0x5a, 0xd1, 0x85, 0x4f, 0x71, - 0xb9, 0x6b, 0x51, 0x76, 0xe4, 0x6b, 0xf9, 0xc8, 0x39, 0xa2, 0xf1, 0xcf, 0x72, 0x6c, 0xfd, 0x37, - 0xf7, 0x3e, 0x63, 0xe9, 0x45, 0x58, 0x93, 0x06, 0x58, 0x6f, 0xfd, 0x49, 0x9e, 0x07, 0x99, 0x17, - 0xd3, 0xe7, 0x2d, 0x18, 0xdb, 0xb4, 0xfd, 0x1d, 0x1a, 0x8a, 0xf0, 0xeb, 0x2a, 0x17, 0x5e, 0x10, - 0x07, 0xab, 0x08, 0xf1, 0xb7, 0x29, 0x08, 0x54, 0x5d, 0x58, 0x7e, 0x28, 0x5d, 0x98, 0xa2, 0xe9, - 0x2d, 0x3c, 0x31, 0x4d, 0xef, 0x0f, 0x45, 0xf1, 0xe4, 0x2b, 0xe1, 0x10, 0x81, 0x30, 0x2f, 0x26, - 0xf3, 0x31, 0xa4, 0x42, 0x96, 0xc6, 0xec, 0xc8, 0x4d, 0x35, 0xc3, 0x83, 0xe2, 0xea, 0x71, 0x4c, - 0x2e, 0x07, 0xe3, 0x4f, 0x0a, 0xbc, 0x8f, 0x45, 0x47, 0x5d, 0xd2, 0x1c, 0xda, 0x70, 0x9d, 0xb0, - 0x8d, 0x5e, 0xf5, 0x2d, 0x46, 0xc3, 0x8e, 0x4b, 0x30, 0xc2, 0xe6, 0xa6, 0xe8, 0x4d, 0xc4, 0x63, - 0xf3, 0x57, 0xc5, 0x63, 0xe5, 0x6c, 0x2d, 0xe3, 0x99, 0xa4, 0xa6, 0xee, 0xc1, 0x63, 0x4b, 0x5d, - 0xcb, 0x88, 0x41, 0x2e, 0xc3, 0xc8, 0x9a, 0xe7, 0xc8, 0x80, 0xab, 0xa7, 0xd0, 0xad, 0xd9, 0x73, - 0x14, 0x96, 0x0b, 0x39, 0x13, 0x31, 0x58, 0x5b, 0xa3, 0x10, 0xed, 0x6a, 0x5b, 0xdb, 0xdb, 0x76, - 0x3a, 0x25, 0x8b, 0x12, 0xcd, 0x7d, 0x19, 0x66, 0xf4, 0xf4, 0xa9, 0xc2, 0xb6, 0x0d, 0x35, 0xb6, - 0x89, 0x2c, 0xac, 0xaa, 0xa2, 0x5d, 0x27, 0x22, 0x4b, 0x30, 0xad, 0x05, 0x7a, 0x13, 0x8f, 0x65, - 0xa8, 0xde, 0xd4, 0xc3, 0xc4, 0xa9, 0xea, 0x4d, 0x8d, 0x84, 0x9d, 0xe7, 0xe2, 0xfb, 0x95, 0x27, - 0xb3, 0xd4, 0xb7, 0x0b, 0x1c, 0x72, 0x03, 0x8a, 0xdc, 0x7f, 0xb8, 0x5e, 0x53, 0x1f, 0x3e, 0x02, - 0x84, 0x25, 0xfc, 0xef, 0x25, 0xa2, 0xe2, 0x2f, 0xfa, 0x39, 0x28, 0x89, 0x2d, 0x29, 0xce, 0xd1, - 0xf9, 0x1c, 0x8c, 0x54, 0xeb, 0x35, 0x53, 0xdd, 0x46, 0x9a, 0xae, 0xe3, 0x9b, 0x08, 0x45, 0xd3, - 0xfd, 0x35, 0x1a, 0xee, 0x7b, 0xfe, 0x9e, 0x49, 0x83, 0xd0, 0x77, 0x79, 0xe6, 0x27, 0x5c, 0x88, - 0x9f, 0x27, 0xef, 0xc0, 0x28, 0x1a, 0x59, 0x25, 0x4e, 0x86, 0x64, 0x1d, 0x4b, 0xd3, 0x62, 0x02, - 0x8f, 0xa2, 0xc5, 0x96, 0xc9, 0x89, 0xc8, 0x5b, 0x30, 0x52, 0xa3, 0x9d, 0x83, 0x44, 0x52, 0x9a, - 0x14, 0x71, 0xb4, 0x21, 0x38, 0xb4, 0x73, 0x60, 0x22, 0x89, 0xf1, 0x33, 0x79, 0x38, 0x9d, 0xf1, - 0x59, 0x0f, 0x3e, 0xff, 0x94, 0xee, 0x8a, 0x4b, 0xda, 0xae, 0x28, 0xdf, 0x3b, 0xfb, 0x76, 0x7c, - 0xe6, 0x26, 0xf9, 0x8b, 0x39, 0x38, 0xab, 0x4f, 0x50, 0x61, 0x55, 0xf9, 0xe0, 0x06, 0x79, 0x1b, - 0xc6, 0x56, 0xa8, 0xed, 0x50, 0x99, 0xb0, 0xe2, 0x74, 0x14, 0xe9, 0x87, 0xbb, 0x14, 0xf2, 0x42, - 0xce, 0x36, 0x76, 0x40, 0xe1, 0x50, 0x52, 0x13, 0x1f, 0xc7, 0xe5, 0x71, 0x43, 0x3a, 0x2a, 0x67, - 0x55, 0x35, 0xc0, 0x6a, 0xe0, 0x07, 0x39, 0x78, 0x76, 0x00, 0x0d, 0x1b, 0x38, 0x36, 0xf4, 0xea, - 0xc0, 0xe1, 0x89, 0x8a, 0x50, 0xf2, 0x1e, 0xcc, 0x6e, 0x0a, 0x79, 0x5e, 0x0e, 0x47, 0x3e, 0x5e, - 0x2f, 0x52, 0xd4, 0xb7, 0xe4, 0xb8, 0x24, 0x91, 0x35, 0x0f, 0xfa, 0xc2, 0x40, 0x0f, 0x7a, 0xd5, - 0x21, 0x7d, 0x64, 0x58, 0x87, 0xf4, 0x0f, 0xe0, 0x94, 0xde, 0x36, 0x11, 0x17, 0x30, 0x76, 0xc7, - 0xcf, 0xf5, 0x77, 0xc7, 0x1f, 0x18, 0x7d, 0xcc, 0xf8, 0xc9, 0x1c, 0x94, 0x74, 0xde, 0x9f, 0x74, - 0x3c, 0xdf, 0xd5, 0xc6, 0xf3, 0xd9, 0xec, 0xf1, 0xec, 0x3f, 0x90, 0xff, 0x47, 0x2e, 0xd9, 0xd8, - 0xa1, 0x46, 0xd0, 0x80, 0xb1, 0x9a, 0xd7, 0xb6, 0xdd, 0x8e, 0x9a, 0xf2, 0xd5, 0x41, 0x88, 0x29, - 0x4a, 0x86, 0x8b, 0x5e, 0x70, 0x11, 0x46, 0xd7, 0xbc, 0x4e, 0xa5, 0x26, 0x8c, 0x0e, 0x91, 0x4f, - 0xc7, 0xeb, 0x58, 0xb6, 0x63, 0xf2, 0x02, 0xb2, 0x0a, 0xd0, 0x68, 0xfa, 0x94, 0x76, 0x1a, 0xee, - 0x77, 0x68, 0x42, 0xd2, 0x60, 0x3d, 0xd4, 0xea, 0xe1, 0xc6, 0x82, 0x6f, 0x3c, 0x01, 0x22, 0x5a, - 0x81, 0xfb, 0x1d, 0x75, 0xbf, 0x55, 0xe8, 0x0d, 0x0a, 0x10, 0x13, 0x61, 0x0a, 0x3a, 0xd7, 0x11, - 0x39, 0x8d, 0xa7, 0x45, 0x0a, 0x3a, 0x06, 0xd0, 0x52, 0xd0, 0x31, 0x00, 0xdb, 0xda, 0x57, 0xa8, - 0xbb, 0xb3, 0xcb, 0xad, 0x4f, 0xa6, 0xf9, 0x54, 0xdd, 0x45, 0x88, 0xba, 0xb5, 0x73, 0x1c, 0xe3, - 0x9f, 0x8f, 0xc2, 0x39, 0x93, 0xee, 0xb8, 0x4c, 0x4c, 0xbe, 0x1f, 0xb8, 0x9d, 0x1d, 0xcd, 0xbf, - 0xdc, 0x48, 0x4c, 0x24, 0x11, 0x5a, 0x99, 0x41, 0xa2, 0x8e, 0xb9, 0x02, 0x45, 0x76, 0x2a, 0x2a, - 0x73, 0x09, 0xdf, 0x50, 0x30, 0x6b, 0x3d, 0x9f, 0xe4, 0xb2, 0x98, 0xbc, 0x2a, 0x4e, 0x6d, 0x25, - 0xf8, 0x3d, 0x3b, 0xb5, 0x3f, 0x3a, 0x2c, 0x43, 0xe3, 0x20, 0x08, 0x29, 0xde, 0xd8, 0xc4, 0xc9, - 0x1d, 0x89, 0xd6, 0x23, 0x7d, 0x44, 0xeb, 0x7b, 0x70, 0xaa, 0xe2, 0xf0, 0xcd, 0xda, 0x6e, 0x6d, - 0xf8, 0x6e, 0xa7, 0xe9, 0x76, 0xed, 0x96, 0xbc, 0x2e, 0x62, 0x2f, 0xdb, 0x51, 0xb9, 0xd5, 0x8d, - 0x10, 0xcc, 0x4c, 0x32, 0xd6, 0x8c, 0xda, 0x5a, 0x03, 0x9d, 0x97, 0xc5, 0xf3, 0x18, 0x36, 0xc3, - 0xe9, 0x04, 0xd8, 0x8a, 0xc0, 0x8c, 0x8a, 0x51, 0xa8, 0x47, 0x73, 0x86, 0xcd, 0xd5, 0x46, 0xec, - 0x9d, 0xc4, 0x63, 0xf3, 0x72, 0x93, 0x87, 0xb0, 0x15, 0xa0, 0xd9, 0x83, 0x86, 0x17, 0xd3, 0x35, - 0x1a, 0x2b, 0x8c, 0xae, 0x98, 0xa2, 0x0b, 0x82, 0x5d, 0x95, 0x8e, 0xe3, 0x91, 0x6b, 0x6c, 0x2a, - 0xb4, 0xbd, 0x90, 0xe2, 0x3c, 0x9f, 0x88, 0xaf, 0x00, 0x3e, 0x42, 0xf9, 0x15, 0x40, 0x41, 0x21, - 0xef, 0xc0, 0xfc, 0x72, 0x75, 0x51, 0x2a, 0x35, 0x6b, 0x5e, 0xb3, 0x87, 0x0f, 0xd4, 0x80, 0xf5, - 0xe1, 0x18, 0xd2, 0xe6, 0x22, 0x9b, 0xdc, 0x59, 0x68, 0xe4, 0x12, 0x8c, 0xd7, 0x6b, 0xbc, 0xef, - 0x27, 0xd5, 0x04, 0x14, 0xc2, 0xf0, 0x43, 0x16, 0x92, 0xf5, 0x58, 0x46, 0x9d, 0x3a, 0x56, 0x98, - 0x3c, 0x37, 0x84, 0x7c, 0xfa, 0x16, 0x4c, 0x2f, 0x79, 0x61, 0xbd, 0x13, 0x84, 0x76, 0xa7, 0x49, - 0xeb, 0x35, 0x35, 0x7e, 0xe4, 0x96, 0x17, 0x5a, 0xae, 0x28, 0x61, 0x5f, 0xae, 0x63, 0x92, 0x2f, - 0x22, 0xe9, 0x6d, 0xda, 0xa1, 0x7e, 0x1c, 0x37, 0x72, 0x94, 0xf7, 0x2d, 0x23, 0xdd, 0x89, 0x4a, - 0x4c, 0x1d, 0x51, 0x24, 0xc7, 0xe0, 0x49, 0x98, 0xaa, 0x9e, 0x43, 0x83, 0x07, 0xd7, 0x3f, 0x63, - 0xc9, 0x31, 0x94, 0xb6, 0xe1, 0x96, 0x79, 0x3d, 0x73, 0x7f, 0xfd, 0xf7, 0x31, 0x39, 0x46, 0x0a, - 0x97, 0x7c, 0x11, 0x46, 0xf1, 0xa7, 0x10, 0xb6, 0xe6, 0x33, 0xd8, 0xc6, 0x82, 0x56, 0x93, 0xe7, - 0x89, 0x46, 0x02, 0x52, 0x87, 0x71, 0x21, 0xe7, 0x9f, 0x24, 0xc4, 0xbb, 0xb8, 0x30, 0xf0, 0x99, - 0x21, 0xe8, 0x0d, 0x07, 0xa6, 0xd4, 0x0a, 0xd9, 0x8a, 0x58, 0xb1, 0x83, 0x5d, 0xea, 0xb0, 0x5f, - 0x22, 0x3b, 0x0b, 0xae, 0x88, 0x5d, 0x84, 0x5a, 0xec, 0x3b, 0x4c, 0x05, 0x85, 0x6d, 0xf1, 0xf5, - 0xe0, 0x7e, 0x20, 0x3e, 0x45, 0xdc, 0xfc, 0x5d, 0xd4, 0x22, 0x39, 0xa6, 0x28, 0x32, 0x7e, 0x08, - 0x4e, 0xad, 0xf5, 0x5a, 0x2d, 0x7b, 0xab, 0x45, 0x65, 0xf4, 0x6e, 0xcc, 0xd4, 0xb8, 0x04, 0xa3, - 0x0d, 0x25, 0xf7, 0xe3, 0x7c, 0x14, 0x1e, 0x3d, 0xc6, 0x41, 0x1b, 0xbb, 0x1c, 0x3a, 0x92, 0x27, - 0xb2, 0x3e, 0x72, 0x52, 0xe3, 0xf7, 0xe3, 0x8c, 0xe5, 0x9b, 0xbe, 0xdd, 0xdc, 0x8b, 0x12, 0x80, - 0x0e, 0x9b, 0x7c, 0xfd, 0x8e, 0xfc, 0x08, 0xfd, 0xfc, 0xcc, 0xfa, 0xe0, 0xe3, 0x3e, 0x86, 0xbc, - 0x03, 0x93, 0xe2, 0x0c, 0x55, 0x02, 0x39, 0x61, 0x1c, 0x0b, 0x71, 0xfb, 0x48, 0x1a, 0x47, 0xa8, - 0xe8, 0x28, 0x1a, 0xe8, 0x4d, 0x79, 0x70, 0xfd, 0xd3, 0x10, 0x0d, 0xf4, 0x3a, 0x06, 0x4c, 0xdd, - 0xdf, 0x9e, 0x4c, 0xf6, 0xad, 0x98, 0xbb, 0x37, 0xd5, 0xd0, 0x2d, 0xb9, 0xf8, 0xa2, 0x16, 0x87, - 0x6e, 0x51, 0x2f, 0x6a, 0x11, 0x6a, 0x34, 0x26, 0xf9, 0x63, 0xc6, 0xe4, 0x3d, 0x39, 0x26, 0x85, - 0xfe, 0x13, 0x63, 0x7e, 0xc0, 0x38, 0x34, 0xe2, 0x15, 0x32, 0x32, 0xd4, 0x2d, 0xff, 0x19, 0x8c, - 0x51, 0xcb, 0x49, 0x92, 0xbb, 0xa8, 0xe0, 0xa4, 0xaa, 0x0e, 0x46, 0x87, 0x67, 0x7a, 0xcc, 0xd6, - 0xfc, 0x25, 0x98, 0xaa, 0x84, 0xa1, 0xdd, 0xdc, 0xa5, 0x4e, 0x8d, 0x6d, 0x4f, 0x4a, 0x6c, 0x06, - 0x5b, 0xc0, 0xd5, 0x37, 0x1c, 0x15, 0x97, 0x47, 0x4d, 0xb3, 0x03, 0x61, 0xad, 0x17, 0x45, 0x4d, - 0x63, 0x10, 0x3d, 0x6a, 0x1a, 0x83, 0x90, 0x6b, 0x30, 0x5e, 0xef, 0x3c, 0x72, 0x59, 0x9f, 0x14, - 0xe3, 0x6c, 0xc0, 0x2e, 0x07, 0xa9, 0x9b, 0xab, 0xc0, 0x22, 0x6f, 0x29, 0x32, 0xf6, 0x44, 0x7c, - 0x9f, 0xe6, 0x1a, 0x98, 0xc8, 0xab, 0x5b, 0x95, 0x9f, 0x23, 0xa1, 0xfb, 0x26, 0x8c, 0x4b, 0xc5, - 0x1a, 0xc4, 0x77, 0x68, 0x41, 0x99, 0xf6, 0xfe, 0x94, 0xc8, 0x98, 0xcc, 0x51, 0xc9, 0x32, 0x33, - 0xa9, 0x24, 0x73, 0x54, 0xb2, 0xcc, 0x68, 0xc9, 0x1c, 0x95, 0x7c, 0x33, 0x91, 0x4e, 0x62, 0xea, - 0x58, 0x9d, 0xc4, 0x03, 0x98, 0xda, 0xb0, 0xfd, 0xd0, 0x65, 0x32, 0x4a, 0x27, 0x0c, 0x16, 0xa6, - 0x35, 0x35, 0x9e, 0x52, 0xb4, 0xf4, 0xbc, 0xcc, 0x3f, 0xd8, 0x55, 0xf0, 0xf5, 0x44, 0x79, 0x31, - 0x3c, 0xdb, 0x56, 0x6f, 0xe6, 0x93, 0xd8, 0xea, 0x61, 0xa7, 0xa2, 0xea, 0x66, 0x36, 0x56, 0x10, - 0xa0, 0x0c, 0x9d, 0xd0, 0xdf, 0x44, 0x88, 0xe4, 0xeb, 0x30, 0xc5, 0xfe, 0xde, 0xf0, 0x5a, 0x6e, - 0xd3, 0xa5, 0xc1, 0x42, 0x09, 0x1b, 0xf7, 0x7c, 0xe6, 0xea, 0x47, 0xa4, 0x83, 0x06, 0x0d, 0xf9, - 0x02, 0x46, 0xc6, 0x49, 0x9d, 0xac, 0xc6, 0x8d, 0xbc, 0x0f, 0x53, 0x6c, 0xf6, 0x6d, 0xd9, 0x01, - 0x17, 0x4d, 0xe7, 0x62, 0x6b, 0x4b, 0x47, 0xc0, 0x53, 0x81, 0x0b, 0x55, 0x02, 0x76, 0xcc, 0x57, - 0xba, 0x7c, 0x83, 0x24, 0xca, 0x6c, 0xef, 0xa6, 0x36, 0x47, 0x89, 0x46, 0xbe, 0x0c, 0x53, 0x95, - 0x6e, 0x37, 0xde, 0x71, 0xe6, 0x15, 0xbd, 0x4c, 0xb7, 0x6b, 0x65, 0xee, 0x3a, 0x1a, 0x45, 0x72, - 0x63, 0x3e, 0x75, 0xa2, 0x8d, 0x99, 0xbc, 0x11, 0x49, 0xeb, 0xa7, 0x63, 0x25, 0xa3, 0xb8, 0xc7, - 0x68, 0xa2, 0x3f, 0x17, 0xdc, 0xab, 0x30, 0xcd, 0xb5, 0x6e, 0x52, 0x9a, 0x39, 0x93, 0x5a, 0x3d, - 0x19, 0x42, 0x8d, 0x4e, 0x43, 0x96, 0x61, 0x86, 0x3b, 0xba, 0xb5, 0x44, 0x44, 0xc9, 0x85, 0xb3, - 0x71, 0xfe, 0x6a, 0xee, 0x1f, 0xd7, 0xc2, 0x40, 0xe3, 0xb6, 0xc6, 0x25, 0x41, 0x64, 0xfc, 0x69, - 0x0e, 0xce, 0xf6, 0x19, 0xf1, 0x28, 0xde, 0x60, 0x6e, 0x70, 0xbc, 0x41, 0xb6, 0x73, 0xe8, 0x97, - 0x74, 0x6c, 0xbf, 0x90, 0xb2, 0xd4, 0xf1, 0x92, 0xf2, 0x96, 0x07, 0x44, 0x44, 0xe6, 0x17, 0x55, - 0xdf, 0xf1, 0x50, 0x53, 0x58, 0x48, 0x1f, 0x42, 0x02, 0x8f, 0x7f, 0x14, 0x8f, 0xd2, 0x24, 0x02, - 0xff, 0x47, 0xc3, 0xfa, 0xa1, 0xa7, 0xad, 0xe0, 0x0c, 0xd6, 0xc6, 0x61, 0x0e, 0x26, 0x95, 0x75, - 0x48, 0x2e, 0x2a, 0x6e, 0x73, 0x25, 0x9e, 0x3a, 0x42, 0xe1, 0x90, 0xe7, 0x27, 0x11, 0x2e, 0xaa, - 0xfc, 0xf1, 0xfa, 0xd0, 0x7b, 0x4c, 0x14, 0x52, 0x62, 0x32, 0xb6, 0x35, 0xe5, 0xa5, 0x89, 0xe5, - 0x98, 0x36, 0xd5, 0x0e, 0xc2, 0x4a, 0x33, 0x74, 0x1f, 0xd1, 0x21, 0x0e, 0x9d, 0x38, 0x6d, 0xaa, - 0x1d, 0x84, 0x96, 0x8d, 0x64, 0xa9, 0xb4, 0xa9, 0x11, 0x43, 0xe3, 0x47, 0x73, 0x00, 0xf7, 0xeb, - 0x55, 0x0c, 0xaa, 0xfa, 0x49, 0x85, 0x82, 0xec, 0x40, 0x75, 0x92, 0xfb, 0x00, 0x71, 0xe0, 0x7f, - 0xc8, 0xc1, 0x8c, 0x8e, 0x46, 0xde, 0x83, 0xd9, 0x46, 0xd3, 0xf7, 0x5a, 0xad, 0x2d, 0xbb, 0xb9, - 0xb7, 0xea, 0x76, 0x28, 0x0f, 0xde, 0x35, 0xca, 0xcf, 0xa2, 0x20, 0x2a, 0xb2, 0x5a, 0xac, 0xcc, - 0x4c, 0x22, 0x93, 0x1f, 0xcb, 0xc1, 0x74, 0x63, 0xd7, 0xdb, 0x8f, 0x93, 0xe6, 0xf3, 0x01, 0xf9, - 0x06, 0x5b, 0xdb, 0xc1, 0xae, 0xb7, 0x6f, 0x65, 0x64, 0xce, 0xff, 0xe8, 0xb0, 0xfc, 0xee, 0x70, - 0xcf, 0xc4, 0x4d, 0x0f, 0x6f, 0x32, 0x61, 0x70, 0x55, 0xab, 0xc4, 0xd4, 0xeb, 0x34, 0xfe, 0x3c, - 0x07, 0x93, 0x78, 0xe7, 0x69, 0xb5, 0x50, 0xe6, 0xfa, 0x2c, 0x25, 0x31, 0x8a, 0xda, 0x35, 0x60, - 0x60, 0xdf, 0x84, 0xd9, 0x04, 0x1a, 0x31, 0x60, 0xac, 0x81, 0xae, 0xd2, 0xaa, 0x82, 0x82, 0x3b, - 0x4f, 0x9b, 0xa2, 0xc4, 0x58, 0x56, 0xc8, 0x1e, 0x5c, 0xc7, 0x57, 0xc6, 0x45, 0x00, 0x57, 0x82, - 0xe4, 0xcd, 0x86, 0x24, 0xbf, 0xe4, 0xc1, 0x75, 0x53, 0xc1, 0x32, 0xd6, 0x60, 0xac, 0xe1, 0xf9, - 0xe1, 0xd2, 0x01, 0xbf, 0x4c, 0xd4, 0x68, 0xd0, 0x54, 0x9f, 0x11, 0x5d, 0x54, 0xdd, 0x37, 0x4d, - 0x51, 0x44, 0xca, 0x30, 0x7a, 0xcb, 0xa5, 0x2d, 0x47, 0xb5, 0x17, 0xdd, 0x66, 0x00, 0x93, 0xc3, - 0xd9, 0x85, 0xeb, 0x4c, 0x1c, 0x7b, 0x3c, 0x36, 0x4c, 0xfd, 0xa4, 0xeb, 0xa6, 0xaa, 0xf5, 0xef, - 0x0b, 0x7a, 0x8e, 0x60, 0xad, 0xa6, 0x01, 0x5d, 0xfd, 0x9f, 0xe6, 0xe0, 0x7c, 0x7f, 0x12, 0xd5, - 0xd6, 0x35, 0x37, 0xc0, 0xd6, 0xf5, 0xe5, 0xe4, 0xb3, 0x17, 0xa2, 0x89, 0x67, 0xaf, 0xf8, 0xb1, - 0xab, 0x86, 0xa6, 0xc6, 0xcd, 0x28, 0x85, 0xfb, 0xc5, 0x01, 0xdf, 0x8c, 0x88, 0x7c, 0x98, 0x43, - 0xa4, 0x31, 0x05, 0xad, 0xf1, 0x5b, 0x23, 0x70, 0xae, 0x2f, 0x05, 0x59, 0x51, 0xd2, 0x18, 0xcc, - 0x44, 0x01, 0xd4, 0xfb, 0xe2, 0x5f, 0xc5, 0x7f, 0xd1, 0x9a, 0x2c, 0xe9, 0x4c, 0xb3, 0x1e, 0x85, - 0xaf, 0xe7, 0xb9, 0xfa, 0x5f, 0x3b, 0x96, 0x17, 0x47, 0x47, 0x66, 0x90, 0x8e, 0x64, 0x8f, 0x6e, - 0x57, 0x34, 0xb4, 0xdd, 0x56, 0xa0, 0x2e, 0x3b, 0x87, 0x83, 0x4c, 0x59, 0x16, 0x1b, 0x20, 0x8f, - 0x64, 0x1b, 0x20, 0x1b, 0xff, 0x3c, 0x07, 0x13, 0xd1, 0x67, 0x93, 0xf3, 0x70, 0x66, 0xd3, 0xac, - 0x54, 0x97, 0xad, 0xcd, 0x0f, 0x36, 0x96, 0xad, 0xfb, 0x6b, 0x8d, 0x8d, 0xe5, 0x6a, 0xfd, 0x56, - 0x7d, 0xb9, 0x56, 0x7a, 0x86, 0xcc, 0xc1, 0xf4, 0xfd, 0xb5, 0xbb, 0x6b, 0xeb, 0x0f, 0xd7, 0xac, - 0x65, 0xd3, 0x5c, 0x37, 0x4b, 0x39, 0x32, 0x0d, 0x13, 0xe6, 0x52, 0xa5, 0x6a, 0xad, 0xad, 0xd7, - 0x96, 0x4b, 0x79, 0x52, 0x82, 0xa9, 0xea, 0xfa, 0xda, 0xda, 0x72, 0x75, 0xb3, 0xfe, 0xa0, 0xbe, - 0xf9, 0x41, 0xa9, 0x40, 0x08, 0xcc, 0x20, 0xc2, 0x86, 0x59, 0x5f, 0xab, 0xd6, 0x37, 0x2a, 0xab, - 0xa5, 0x11, 0x06, 0x63, 0xf8, 0x0a, 0x6c, 0x34, 0x62, 0x74, 0xf7, 0xfe, 0xd2, 0x72, 0x69, 0x8c, - 0xa1, 0xb0, 0xbf, 0x14, 0x94, 0x71, 0x56, 0x3d, 0xa2, 0xd4, 0x2a, 0x9b, 0x95, 0xa5, 0x4a, 0x63, - 0xb9, 0x54, 0x24, 0x67, 0x61, 0x5e, 0x03, 0x59, 0xab, 0xeb, 0xb7, 0xeb, 0x6b, 0xa5, 0x09, 0x72, - 0x0a, 0x4a, 0x11, 0xac, 0xb6, 0x64, 0xdd, 0x6f, 0x2c, 0x9b, 0x25, 0x48, 0x42, 0xd7, 0x2a, 0xf7, - 0x96, 0x4b, 0x93, 0xc6, 0xbb, 0xdc, 0xcd, 0x89, 0x77, 0x35, 0x39, 0x03, 0xa4, 0xb1, 0x59, 0xd9, - 0xbc, 0xdf, 0x48, 0x34, 0x7e, 0x12, 0xc6, 0x1b, 0xf7, 0xab, 0xd5, 0xe5, 0x46, 0xa3, 0x94, 0x23, - 0x00, 0x63, 0xb7, 0x2a, 0xf5, 0xd5, 0xe5, 0x5a, 0x29, 0x6f, 0xfc, 0x74, 0x0e, 0xe6, 0xa4, 0x04, - 0x28, 0xdf, 0x30, 0x3e, 0xe1, 0x5a, 0x7c, 0x4f, 0xbb, 0xd8, 0x4a, 0x2f, 0x94, 0x44, 0x25, 0x03, - 0x96, 0xe1, 0x2f, 0xe6, 0xe0, 0x74, 0x26, 0x36, 0xf9, 0x00, 0x4a, 0xf2, 0x0b, 0xee, 0xd9, 0x61, - 0x73, 0x37, 0xde, 0xc7, 0x9e, 0x4f, 0xd4, 0x92, 0x40, 0xe3, 0x6a, 0xcd, 0x38, 0x4d, 0x62, 0x8a, - 0xcd, 0xf0, 0x61, 0x7f, 0x8d, 0x5f, 0xc8, 0xc1, 0xd9, 0x3e, 0xd5, 0x90, 0x2a, 0x8c, 0x45, 0x01, - 0xe0, 0x07, 0x18, 0x54, 0x9d, 0xfa, 0xc1, 0x61, 0x59, 0x20, 0x62, 0x5e, 0x39, 0xfc, 0xcb, 0x1c, - 0x8b, 0x22, 0xba, 0x63, 0x58, 0x75, 0xde, 0x7d, 0xe7, 0x12, 0x3d, 0x2f, 0x6a, 0xaa, 0x3c, 0x6c, - 0x2c, 0x4d, 0x8a, 0xbe, 0x2b, 0xd8, 0xfb, 0x01, 0xc6, 0x55, 0x37, 0x7e, 0x36, 0xc7, 0x84, 0xbb, - 0x24, 0x22, 0x93, 0x79, 0x2b, 0x41, 0xd0, 0x6b, 0x53, 0xd3, 0x6b, 0xd1, 0x8a, 0xb9, 0x26, 0x8e, - 0x0d, 0x94, 0x56, 0x6d, 0x2c, 0xc0, 0x6b, 0x85, 0x65, 0xfb, 0x1d, 0xed, 0xf1, 0x54, 0xa5, 0x21, - 0x6f, 0x01, 0x44, 0xf9, 0xfd, 0x65, 0x50, 0x03, 0x1e, 0xd4, 0x43, 0x40, 0x75, 0x79, 0x5b, 0x41, - 0x36, 0xfe, 0x4a, 0x0e, 0xa6, 0xc4, 0xa5, 0xa9, 0xd2, 0xa2, 0x7e, 0xf8, 0xc9, 0xa6, 0xd7, 0x5b, - 0xda, 0xf4, 0x8a, 0xfc, 0x07, 0x14, 0xfe, 0xac, 0x38, 0x73, 0x66, 0xfd, 0xb7, 0x39, 0x28, 0x25, - 0x11, 0xc9, 0x7b, 0x50, 0x6c, 0xd0, 0x47, 0xd4, 0x77, 0xc3, 0x03, 0xb1, 0x51, 0xca, 0x54, 0x39, - 0x1c, 0x47, 0x94, 0xf1, 0xf9, 0x10, 0x88, 0x5f, 0x66, 0x44, 0x33, 0xec, 0x7e, 0xaf, 0xa8, 0x3d, - 0x0a, 0x4f, 0x4a, 0xed, 0x61, 0xfc, 0xaf, 0x79, 0x38, 0x7b, 0x9b, 0x86, 0x6a, 0x9b, 0xa2, 0xd7, - 0xee, 0xcf, 0x0d, 0xd7, 0x2e, 0xa5, 0x25, 0x0b, 0x30, 0x8e, 0x45, 0x72, 0x7c, 0x4d, 0xf9, 0x93, - 0x2c, 0x45, 0xf3, 0xba, 0xa0, 0xe5, 0xe2, 0xe8, 0x53, 0xf7, 0x55, 0x25, 0x3a, 0x7f, 0x34, 0xad, - 0x2f, 0xc1, 0x0c, 0x06, 0x86, 0xed, 0xb1, 0xe5, 0x40, 0x1d, 0xa1, 0xfe, 0x29, 0x9a, 0x09, 0x28, - 0x79, 0x15, 0x4a, 0x0c, 0x52, 0x69, 0xee, 0x75, 0xbc, 0xfd, 0x16, 0x75, 0x76, 0x28, 0x4f, 0xc8, - 0x5e, 0x34, 0x53, 0x70, 0xc9, 0xf3, 0x7e, 0x87, 0x5f, 0xdd, 0xa8, 0x83, 0x3a, 0x1a, 0xc1, 0x33, - 0x86, 0x9e, 0x7f, 0x0b, 0x26, 0x3f, 0x66, 0xa6, 0x0d, 0xe3, 0x7f, 0xc9, 0xc1, 0x29, 0x6c, 0x9c, - 0x52, 0x31, 0xbe, 0x18, 0x7c, 0x2e, 0xee, 0x2d, 0x25, 0xf8, 0xbc, 0xcd, 0x40, 0xfa, 0x52, 0x88, - 0x7a, 0x31, 0xd6, 0x09, 0xe5, 0x87, 0xd0, 0x09, 0x35, 0x4e, 0x92, 0xbf, 0x75, 0x48, 0x95, 0x16, - 0xcf, 0xba, 0x1f, 0x0f, 0xb9, 0xf1, 0x63, 0x79, 0x18, 0x37, 0x29, 0x26, 0xb6, 0x24, 0x97, 0x60, - 0x7c, 0xcd, 0x0b, 0x69, 0x70, 0x4f, 0xcb, 0x62, 0xda, 0x61, 0x20, 0xab, 0xed, 0x98, 0xb2, 0x90, - 0x4d, 0xf8, 0x0d, 0xdf, 0x73, 0x7a, 0xcd, 0x50, 0x9d, 0xf0, 0x5d, 0x0e, 0x32, 0x65, 0x19, 0x79, - 0x1d, 0x26, 0x04, 0xe7, 0xe8, 0x8d, 0x11, 0x6d, 0x63, 0x7d, 0x1a, 0x25, 0x46, 0x8d, 0x11, 0x50, - 0xa6, 0xe5, 0x02, 0xc6, 0x88, 0x22, 0xd3, 0xa6, 0x64, 0x06, 0x29, 0xaa, 0x8f, 0x0e, 0x10, 0xd5, - 0x3f, 0x07, 0x63, 0x95, 0x20, 0xa0, 0xa1, 0x74, 0xd2, 0x9e, 0x8a, 0x22, 0xe6, 0x04, 0x34, 0xe4, - 0x8c, 0x6d, 0x2c, 0x37, 0x05, 0x9e, 0xf1, 0xcf, 0xf2, 0x30, 0x8a, 0x7f, 0xe2, 0xbb, 0xaa, 0xdf, - 0xdc, 0xd5, 0xde, 0x55, 0xfd, 0xe6, 0xae, 0x89, 0x50, 0x72, 0x1d, 0x35, 0x15, 0x32, 0x4f, 0x82, - 0x68, 0x3d, 0xaa, 0xe0, 0x9d, 0x18, 0x6c, 0xaa, 0x38, 0xd1, 0x83, 0x73, 0x21, 0x33, 0x34, 0xc3, - 0x19, 0xc8, 0xaf, 0x37, 0x44, 0x8b, 0x31, 0x84, 0x8c, 0x17, 0x98, 0xf9, 0xf5, 0x06, 0xf6, 0xc6, - 0x4a, 0x65, 0xf1, 0xcd, 0x9b, 0x6a, 0xc2, 0xdd, 0x60, 0xd7, 0x5e, 0x7c, 0xf3, 0xa6, 0x29, 0x4a, - 0x58, 0xff, 0xe2, 0x37, 0xe3, 0xc3, 0x2b, 0x77, 0x2a, 0xc6, 0xfe, 0xc5, 0xb6, 0xe1, 0x23, 0xab, - 0x19, 0x23, 0x90, 0x45, 0x98, 0x14, 0xae, 0xec, 0x88, 0xaf, 0xb8, 0x9a, 0x0b, 0x57, 0x77, 0x4e, - 0xa1, 0x22, 0xf1, 0x27, 0x38, 0x31, 0x40, 0x32, 0x9b, 0x9b, 0x78, 0x82, 0x93, 0x43, 0x18, 0x98, - 0x0a, 0x4a, 0xec, 0x13, 0x1d, 0x3b, 0x0b, 0xab, 0x3e, 0xd1, 0x18, 0x84, 0x37, 0x42, 0x30, 0x7e, - 0x35, 0x0f, 0xc5, 0x8d, 0x56, 0x6f, 0xc7, 0xed, 0x3c, 0xb8, 0x4e, 0x08, 0xe0, 0x35, 0x4e, 0x46, - 0x69, 0x66, 0x7f, 0x93, 0x73, 0x50, 0x94, 0x37, 0x37, 0xb9, 0x21, 0x05, 0xe2, 0xd6, 0xb6, 0x00, - 0x72, 0xdc, 0x45, 0x76, 0x7e, 0xf9, 0x93, 0x5c, 0x87, 0xe8, 0xfe, 0xd5, 0xef, 0xa2, 0x36, 0xc2, - 0x16, 0x8b, 0x19, 0xa1, 0x91, 0x37, 0x00, 0x0f, 0x09, 0x71, 0x79, 0x90, 0x0a, 0x6d, 0xfe, 0x69, - 0x42, 0x4e, 0xe1, 0x24, 0x88, 0x46, 0x6e, 0x80, 0x98, 0x98, 0x22, 0x07, 0xe8, 0x69, 0x9d, 0x80, - 0xe7, 0x61, 0x92, 0x24, 0x02, 0x95, 0xbc, 0x03, 0x93, 0x71, 0xf6, 0xfd, 0x38, 0xb5, 0xa7, 0x4a, - 0x59, 0x8d, 0xcb, 0x1f, 0x5c, 0x37, 0x55, 0x74, 0xe3, 0x7b, 0xe3, 0x30, 0xa5, 0x7e, 0x0f, 0x31, - 0x61, 0x3e, 0x68, 0xb1, 0xbb, 0xbb, 0xb0, 0x7d, 0xea, 0x62, 0xa1, 0x38, 0x4e, 0x2f, 0xea, 0x1f, - 0xc4, 0xf0, 0xb8, 0x21, 0x94, 0xf4, 0xc1, 0x5f, 0x79, 0xc6, 0x9c, 0x0b, 0x62, 0x30, 0xc7, 0x23, - 0x15, 0x28, 0x7a, 0xdd, 0x60, 0x87, 0x76, 0x5c, 0xf9, 0xde, 0xf2, 0xa2, 0xc6, 0x68, 0x5d, 0x14, - 0xa6, 0x78, 0x45, 0x64, 0xe4, 0x4d, 0x18, 0xf3, 0xba, 0xb4, 0x63, 0xbb, 0xe2, 0x8c, 0x7b, 0x36, - 0xc1, 0x80, 0x76, 0x2a, 0x75, 0x85, 0x50, 0x20, 0x93, 0x6b, 0x30, 0xe2, 0xed, 0x45, 0xe3, 0x75, - 0x4e, 0x27, 0xda, 0x0b, 0x6d, 0x85, 0x04, 0x11, 0x19, 0xc1, 0x87, 0x76, 0x7b, 0x5b, 0x8c, 0x98, - 0x4e, 0x70, 0xc7, 0x6e, 0x6f, 0xab, 0x04, 0x0c, 0x91, 0xbc, 0x0f, 0xd0, 0xb5, 0x77, 0xa8, 0x6f, - 0x39, 0xbd, 0xf0, 0x40, 0x8c, 0xdb, 0xf3, 0x1a, 0xd9, 0x06, 0x2b, 0xae, 0xf5, 0xc2, 0x03, 0x85, - 0x76, 0xa2, 0x2b, 0x81, 0xa4, 0x02, 0xd0, 0xb6, 0xc3, 0x90, 0xfa, 0x6d, 0x4f, 0x18, 0x9f, 0x4d, - 0x46, 0xa9, 0x33, 0x39, 0x83, 0x7b, 0x51, 0xb1, 0xc2, 0x41, 0x21, 0xc2, 0x8f, 0x76, 0x7d, 0x5b, - 0x64, 0x62, 0x4d, 0x7c, 0xb4, 0xeb, 0x6b, 0xad, 0x64, 0x88, 0xe4, 0x8b, 0x30, 0xee, 0xb8, 0x41, - 0xd3, 0xf3, 0x1d, 0x11, 0x9c, 0xe1, 0x39, 0x8d, 0xa6, 0xc6, 0xcb, 0x14, 0x32, 0x89, 0xce, 0xbe, - 0x56, 0xc4, 0x7f, 0x5b, 0xf3, 0xf6, 0x51, 0xcd, 0x9f, 0xfc, 0xda, 0x46, 0x54, 0xac, 0x7e, 0x6d, - 0x4c, 0xc4, 0x86, 0x72, 0xc7, 0x0d, 0x5b, 0xf6, 0x96, 0x78, 0xe7, 0xd6, 0x87, 0xf2, 0x36, 0x16, - 0xa9, 0x43, 0xc9, 0x91, 0xc9, 0x5b, 0x50, 0xa4, 0x9d, 0xd0, 0xb7, 0x2d, 0xd7, 0x11, 0x4e, 0x7b, - 0xfa, 0x47, 0xb3, 0x03, 0xd8, 0xae, 0xd7, 0xd4, 0x8f, 0x46, 0xfc, 0xba, 0xc3, 0xfa, 0x27, 0x68, - 0xba, 0x6d, 0xe1, 0x6b, 0xa7, 0xf7, 0x4f, 0xa3, 0x5a, 0xbf, 0xa7, 0xf6, 0x0f, 0x43, 0x24, 0xef, - 0xc1, 0x38, 0x5b, 0xbf, 0x8e, 0xb7, 0x23, 0x3c, 0xde, 0x0d, 0xbd, 0x7f, 0x78, 0x59, 0x6a, 0xba, - 0x4a, 0x22, 0xf2, 0x3c, 0x40, 0xfc, 0x44, 0xce, 0x1f, 0x34, 0x4c, 0x05, 0xf2, 0xa5, 0x91, 0xff, - 0xfd, 0x97, 0xcb, 0xb9, 0x25, 0x80, 0xa2, 0x8c, 0x6e, 0x61, 0xac, 0xc2, 0xb9, 0xbe, 0x8b, 0x8a, - 0x5c, 0x81, 0xd2, 0xb6, 0x2d, 0x54, 0x6a, 0xcd, 0x5d, 0xbb, 0xd3, 0xa1, 0x2d, 0xb1, 0x9d, 0xcd, - 0x4a, 0x78, 0x95, 0x83, 0x39, 0x67, 0xe3, 0x7d, 0x38, 0x95, 0xd5, 0x9b, 0xe4, 0x05, 0x98, 0x52, - 0x03, 0x79, 0x08, 0x26, 0x93, 0x76, 0xd7, 0x95, 0xa1, 0x3c, 0x04, 0x83, 0xdf, 0xcc, 0xc1, 0x73, - 0x83, 0xd6, 0x26, 0x39, 0x0f, 0xc5, 0xae, 0xef, 0x7a, 0x28, 0x03, 0xf2, 0x1d, 0x34, 0xfa, 0x4d, - 0x2e, 0x00, 0x70, 0x61, 0x25, 0xb4, 0x77, 0x84, 0x31, 0xbf, 0x39, 0x81, 0x90, 0x4d, 0x7b, 0x27, - 0x20, 0xaf, 0xc1, 0x9c, 0x43, 0xb7, 0xed, 0x5e, 0x2b, 0xb4, 0x82, 0xe6, 0x2e, 0x75, 0xd0, 0x7f, - 0x06, 0x8d, 0xb4, 0xcc, 0x92, 0x28, 0x68, 0x48, 0x78, 0xea, 0x8b, 0x47, 0xfb, 0x7c, 0xf1, 0x9d, - 0x91, 0x62, 0xae, 0x94, 0x37, 0xd1, 0x56, 0xc9, 0xf8, 0xe1, 0x3c, 0x2c, 0xf4, 0x9b, 0x8c, 0xe4, - 0xdd, 0xac, 0x3e, 0xe0, 0xaf, 0x02, 0x2a, 0x5c, 0x7d, 0x15, 0x50, 0x6a, 0x23, 0x8b, 0x10, 0x79, - 0xbf, 0x1c, 0xe7, 0xc9, 0x2e, 0x61, 0x8c, 0xa6, 0x6b, 0x07, 0xc1, 0x3e, 0x5b, 0x6f, 0x05, 0x25, - 0x50, 0x9f, 0x80, 0xa9, 0x34, 0x12, 0x46, 0xbe, 0x00, 0xd0, 0x6c, 0x79, 0x01, 0xc5, 0xc7, 0x77, - 0x71, 0x90, 0x73, 0x13, 0xe0, 0x08, 0xaa, 0xbe, 0xb6, 0x22, 0xb4, 0xea, 0x39, 0x54, 0x0c, 0xa0, - 0x0d, 0x67, 0xfb, 0xec, 0x3e, 0x6c, 0x78, 0xe2, 0x14, 0xa7, 0x32, 0xcd, 0x40, 0x2f, 0x4a, 0x74, - 0x9a, 0xec, 0xf1, 0x7c, 0xbf, 0x39, 0x72, 0x00, 0x24, 0xbd, 0xc5, 0x30, 0xee, 0xc2, 0x90, 0xb5, - 0xe7, 0x47, 0xdc, 0x39, 0xe4, 0xbe, 0xdf, 0x22, 0x65, 0x98, 0x94, 0x09, 0x91, 0x98, 0xa0, 0xcc, - 0x99, 0x83, 0x00, 0xdd, 0xa5, 0x38, 0x79, 0x30, 0x9e, 0x24, 0xfa, 0x38, 0x89, 0x23, 0x78, 0x02, - 0x21, 0x9b, 0x07, 0x5d, 0xd9, 0xba, 0xe7, 0xe4, 0xfc, 0xd6, 0x37, 0x7e, 0x51, 0xfa, 0x73, 0x39, - 0x39, 0xfc, 0xe9, 0x9d, 0xf3, 0xb8, 0xef, 0x23, 0x80, 0x1e, 0x29, 0xe2, 0xc3, 0xf0, 0x6f, 0x26, - 0x12, 0xc8, 0x55, 0x27, 0x44, 0x02, 0xf1, 0x93, 0x5c, 0x82, 0x59, 0x9f, 0xdb, 0x2c, 0x86, 0x9e, - 0xe8, 0x4f, 0x1c, 0x29, 0x73, 0x9a, 0x83, 0x37, 0x3d, 0xec, 0x53, 0xf1, 0x5d, 0x77, 0xa2, 0x0e, - 0x53, 0x0e, 0x12, 0x72, 0x15, 0x26, 0xd8, 0x41, 0x82, 0x71, 0x32, 0x12, 0xa6, 0xf0, 0x88, 0x87, - 0xc7, 0xb2, 0x59, 0xfc, 0x50, 0xfc, 0x2d, 0x78, 0xfd, 0xc3, 0x9c, 0x64, 0xa6, 0x1e, 0x63, 0xe4, - 0x2c, 0x8c, 0x7b, 0xfe, 0x8e, 0xd2, 0xb4, 0x31, 0xcf, 0xdf, 0x61, 0xed, 0xba, 0x0c, 0x25, 0xee, - 0x99, 0xc1, 0x5d, 0xde, 0x83, 0x83, 0x0e, 0xbf, 0xe7, 0x16, 0xcd, 0x19, 0x0e, 0xc7, 0xac, 0xaf, - 0x07, 0x9d, 0x26, 0xc3, 0x0c, 0x02, 0xcf, 0x52, 0x83, 0xe3, 0x88, 0x66, 0xcf, 0x04, 0x81, 0x17, - 0x47, 0xc9, 0x71, 0xc8, 0x12, 0x4c, 0x33, 0x3e, 0x51, 0x88, 0x1e, 0x71, 0xca, 0x5e, 0x48, 0x9f, - 0xb2, 0x07, 0x9d, 0xa6, 0xfc, 0x44, 0x73, 0x2a, 0x50, 0x7e, 0x89, 0xd6, 0xfc, 0x7a, 0x1e, 0xce, - 0x64, 0xa3, 0xe3, 0x78, 0xb1, 0x4a, 0xd0, 0x41, 0x89, 0xab, 0x47, 0xcd, 0x09, 0x06, 0xe1, 0x31, - 0x18, 0xb2, 0xbe, 0x36, 0x9f, 0xf9, 0xb5, 0xaf, 0xc2, 0x1c, 0x32, 0x12, 0x72, 0x4d, 0xcb, 0x0d, - 0x42, 0x11, 0x5a, 0xc0, 0x9c, 0x65, 0x05, 0x7c, 0x83, 0x5b, 0x65, 0x60, 0xf2, 0x32, 0xcc, 0xc8, - 0x2d, 0xca, 0xdb, 0xef, 0xb0, 0x8a, 0xf9, 0xfe, 0x34, 0x2d, 0xa0, 0xeb, 0x08, 0x24, 0xa7, 0x61, - 0xcc, 0xee, 0x76, 0x59, 0x95, 0x7c, 0x5b, 0x1a, 0xb5, 0xbb, 0xdd, 0xba, 0x43, 0x5e, 0x84, 0x69, - 0x74, 0xc7, 0xb2, 0xb6, 0xd1, 0x26, 0x45, 0x18, 0xc0, 0x99, 0x53, 0x08, 0xe4, 0x76, 0x2a, 0x01, - 0x5b, 0x08, 0x8c, 0x56, 0xa2, 0x8c, 0x23, 0x0a, 0xd8, 0xdd, 0x08, 0xe1, 0x1c, 0x14, 0xe5, 0xeb, - 0x28, 0xb7, 0x2a, 0x37, 0xc7, 0x6d, 0xfe, 0x32, 0x2a, 0x3a, 0xed, 0x8b, 0x30, 0x2b, 0x0e, 0x6a, - 0xb1, 0xf9, 0x23, 0x53, 0x31, 0x35, 0x99, 0x04, 0x2d, 0x22, 0xaf, 0x83, 0x00, 0xd5, 0x1d, 0xd9, - 0xdd, 0x7f, 0x94, 0x83, 0xd3, 0x99, 0x27, 0x3d, 0xf9, 0x16, 0x70, 0xc7, 0x95, 0xd0, 0xb3, 0x7c, - 0xda, 0x74, 0xbb, 0x2e, 0xba, 0xf6, 0x73, 0x4d, 0xd8, 0xe2, 0x20, 0x19, 0x01, 0x9d, 0x60, 0x36, - 0x3d, 0x33, 0x22, 0xe2, 0x57, 0xf4, 0x92, 0x9f, 0x00, 0x9f, 0xff, 0x1a, 0x9c, 0xce, 0x44, 0xcd, - 0xb8, 0x3a, 0xbf, 0xae, 0xe7, 0xda, 0x93, 0x6f, 0x1b, 0x89, 0x46, 0x2b, 0x57, 0x6a, 0xd1, 0xbc, - 0xdf, 0x89, 0x9a, 0x97, 0x90, 0x09, 0xc8, 0x72, 0x72, 0xc6, 0x66, 0x89, 0xb5, 0x92, 0xa8, 0xef, - 0xa4, 0x25, 0x5f, 0x83, 0xd3, 0x62, 0x16, 0xed, 0xf8, 0x76, 0x77, 0x37, 0x66, 0xc7, 0x3f, 0xf4, - 0x95, 0x2c, 0x76, 0x7c, 0x7a, 0xdd, 0x66, 0xf8, 0x11, 0xd7, 0x79, 0x3b, 0x0d, 0x14, 0x6d, 0xf8, - 0x91, 0xbc, 0x14, 0x08, 0x32, 0x3e, 0x27, 0x63, 0x7e, 0xe6, 0xb2, 0xe6, 0xe7, 0xf0, 0x8b, 0x63, - 0x0d, 0x88, 0x72, 0x2b, 0xb0, 0xb8, 0xb2, 0x4c, 0xd8, 0xe1, 0x48, 0xf1, 0x4e, 0x7c, 0x88, 0x72, - 0x99, 0x68, 0xf0, 0x5c, 0x47, 0x73, 0xcd, 0x24, 0x88, 0x3c, 0x0b, 0x13, 0x51, 0x3a, 0x41, 0xb1, - 0x25, 0x16, 0x39, 0xa0, 0xee, 0x90, 0x8b, 0x30, 0xc5, 0x25, 0x39, 0x6d, 0xf1, 0x00, 0xc2, 0x2a, - 0x6c, 0x05, 0xc9, 0x3e, 0xc8, 0xc1, 0xc5, 0xe3, 0xfa, 0x90, 0x3c, 0x84, 0x33, 0x68, 0x0d, 0x10, - 0x78, 0xd1, 0x30, 0x58, 0x4d, 0xbb, 0xb9, 0x4b, 0xc5, 0xac, 0x35, 0x32, 0x07, 0xa3, 0xdb, 0x6d, - 0x34, 0xd6, 0x95, 0x71, 0xe8, 0x76, 0x1b, 0x81, 0x27, 0x7f, 0x57, 0x19, 0xb9, 0xf8, 0x06, 0x07, - 0x9e, 0x1d, 0x40, 0xa9, 0xec, 0x00, 0x39, 0x75, 0x07, 0xb8, 0x0c, 0xa5, 0x6d, 0xea, 0x30, 0x69, - 0x8f, 0x3a, 0xf8, 0x69, 0x8f, 0x16, 0x79, 0x02, 0x4d, 0x73, 0x26, 0x82, 0x37, 0x02, 0xef, 0xc1, - 0xa2, 0xa8, 0xa5, 0x2d, 0x37, 0x73, 0x55, 0x1a, 0x25, 0x57, 0x61, 0x3e, 0x11, 0x36, 0x21, 0xf6, - 0xc3, 0x35, 0xe7, 0x58, 0x91, 0x1e, 0x64, 0xe7, 0x05, 0x98, 0x92, 0xb3, 0xc2, 0x8f, 0xbc, 0x79, - 0xcc, 0x49, 0x01, 0x63, 0xab, 0x4e, 0x54, 0xd7, 0x93, 0x8d, 0xca, 0x14, 0x64, 0x87, 0x90, 0x12, - 0xc9, 0x1b, 0x40, 0x22, 0x89, 0x34, 0xda, 0x28, 0x44, 0x85, 0x73, 0xb2, 0x24, 0x5a, 0xe1, 0xa2, - 0xda, 0xbf, 0x95, 0x97, 0x42, 0xe5, 0x92, 0xe7, 0x85, 0x41, 0xe8, 0xdb, 0x5d, 0xed, 0x66, 0x4a, - 0xda, 0x70, 0xce, 0xb3, 0x7b, 0xe1, 0xee, 0xa2, 0xc5, 0xfe, 0xf5, 0x7c, 0xe9, 0xd1, 0xdb, 0x94, - 0x66, 0x89, 0x93, 0x8b, 0xd7, 0xf4, 0xc3, 0xa5, 0xc2, 0xb0, 0x2b, 0x2a, 0x32, 0x93, 0x81, 0x14, - 0xae, 0x2b, 0xcf, 0x98, 0x67, 0x39, 0xcf, 0x14, 0x16, 0x59, 0x81, 0xa9, 0x2d, 0x6a, 0xfb, 0xd4, - 0xe7, 0xa6, 0xb8, 0x99, 0x57, 0xd3, 0x25, 0x44, 0x40, 0x0b, 0x5d, 0x9d, 0xeb, 0xe4, 0x56, 0x5c, - 0x42, 0xde, 0x83, 0x09, 0xd7, 0x11, 0xc1, 0x08, 0xc5, 0x05, 0x55, 0xbf, 0x14, 0xd5, 0x1d, 0x1e, - 0x9b, 0x30, 0xe6, 0xc1, 0x6e, 0xb7, 0xae, 0x80, 0x2e, 0x4d, 0x6b, 0x77, 0x78, 0x63, 0x49, 0xca, - 0x2f, 0x69, 0x32, 0x32, 0x03, 0xf9, 0x68, 0xb6, 0xe5, 0x5d, 0x87, 0x9c, 0x81, 0xb1, 0x40, 0x89, - 0x8e, 0x68, 0x8a, 0x5f, 0xc6, 0x5f, 0x80, 0xcb, 0xc3, 0xf6, 0x11, 0x1b, 0xcd, 0x3e, 0x1d, 0x3e, - 0x61, 0xce, 0xd9, 0xa9, 0x7e, 0x7b, 0x01, 0xd4, 0xf0, 0x6e, 0xae, 0x9c, 0x67, 0x12, 0x76, 0xdf, - 0x77, 0x8d, 0x1f, 0x2d, 0xc0, 0x8c, 0xae, 0xb5, 0x20, 0xaf, 0xc1, 0x48, 0xc4, 0x76, 0x26, 0xd2, - 0xae, 0xab, 0x48, 0x8c, 0xb9, 0x89, 0x48, 0xec, 0x08, 0xc5, 0xc7, 0x38, 0xab, 0xad, 0x2a, 0xc0, - 0xcd, 0x29, 0x04, 0x4a, 0xc5, 0xf7, 0x1d, 0xe0, 0x69, 0xa2, 0x71, 0x4b, 0x0f, 0xdd, 0x36, 0x1d, - 0x42, 0xff, 0x5d, 0xfc, 0xfd, 0xc3, 0xf2, 0x33, 0xa8, 0xb9, 0x9c, 0x62, 0xb4, 0x6c, 0x57, 0x65, - 0x85, 0xca, 0xa5, 0x74, 0xa4, 0xff, 0xa5, 0x54, 0x34, 0xa5, 0xcf, 0xa5, 0x74, 0x74, 0xc0, 0xa5, - 0x34, 0xa6, 0x54, 0x2f, 0xa5, 0xa8, 0x9a, 0x18, 0xef, 0xa7, 0x9a, 0x88, 0x69, 0xb8, 0x6a, 0xe2, - 0x25, 0xd1, 0x5c, 0xdf, 0xde, 0xb7, 0xb0, 0x1f, 0xb8, 0xd9, 0x20, 0x6f, 0x88, 0x69, 0xef, 0xe3, - 0xb3, 0xe5, 0xd2, 0x04, 0xc8, 0xb7, 0x4e, 0xe3, 0xaf, 0xe5, 0x12, 0xd7, 0x40, 0x39, 0x14, 0x2f, - 0xc3, 0x8c, 0xdb, 0x66, 0xf2, 0x29, 0x75, 0x14, 0xb9, 0x6a, 0xda, 0x9c, 0x96, 0x50, 0x2e, 0x5b, - 0xbd, 0x02, 0xb3, 0x11, 0x1a, 0xf7, 0x2f, 0xe7, 0x3e, 0x10, 0x66, 0x44, 0x2d, 0xfc, 0xcb, 0x5f, - 0x83, 0xb9, 0x08, 0x51, 0x88, 0xf2, 0x5c, 0xb4, 0x9a, 0x36, 0x4b, 0xb2, 0x40, 0x24, 0x3c, 0x0d, - 0x8c, 0x9d, 0xe4, 0xe1, 0xfc, 0x29, 0x7d, 0x95, 0xf1, 0x3b, 0x05, 0x4d, 0x44, 0x96, 0xd5, 0x2c, - 0xc1, 0x24, 0xdb, 0x91, 0x45, 0x27, 0x89, 0x6d, 0xe5, 0x85, 0x3e, 0xdd, 0x2f, 0x5e, 0x8b, 0x1b, - 0x8d, 0xf5, 0xff, 0x9f, 0xbd, 0xab, 0xf9, 0x71, 0xe4, 0xb8, 0xee, 0xdb, 0x24, 0x67, 0x76, 0xe6, - 0x71, 0x3e, 0x7a, 0x4a, 0xa3, 0xdd, 0xd1, 0xec, 0xec, 0xac, 0xdc, 0xbb, 0x5e, 0xec, 0xd2, 0x96, - 0xec, 0xd5, 0x46, 0x96, 0x56, 0x89, 0x22, 0xf5, 0x90, 0xcd, 0x61, 0x6b, 0xf8, 0xe5, 0xee, 0xe6, - 0x8c, 0x57, 0xb2, 0xdd, 0xa1, 0xc8, 0x9e, 0x19, 0xc6, 0x9c, 0x26, 0xcd, 0x8f, 0x95, 0x57, 0x97, - 0x24, 0x08, 0xe0, 0x00, 0x49, 0x9c, 0x0f, 0x23, 0x07, 0x21, 0x39, 0x04, 0x41, 0x74, 0xc8, 0x21, - 0xc7, 0xe4, 0x94, 0x7f, 0xc0, 0x80, 0x61, 0xc0, 0x87, 0x9c, 0x12, 0x40, 0x48, 0x04, 0xc4, 0x87, - 0x20, 0xb7, 0x20, 0x3e, 0xf8, 0x14, 0xd4, 0xab, 0xaa, 0xee, 0xea, 0x0f, 0x72, 0x67, 0x76, 0xe5, - 0xc4, 0x06, 0x7c, 0x22, 0xbb, 0xea, 0x55, 0x75, 0x75, 0x7d, 0xbe, 0x57, 0xef, 0xbd, 0xdf, 0xb3, - 0x60, 0x3c, 0x1e, 0x08, 0xe5, 0xb1, 0xcb, 0x0e, 0x1d, 0xc6, 0xe5, 0xe1, 0xf4, 0x13, 0xd5, 0xb1, - 0x3d, 0xa4, 0x30, 0xbf, 0x3a, 0x7d, 0x38, 0xc4, 0x36, 0xd2, 0xd9, 0x87, 0x87, 0x4f, 0xf0, 0x24, - 0x5e, 0xd0, 0x02, 0x94, 0x28, 0xc7, 0xd1, 0xca, 0xb3, 0x29, 0xec, 0x45, 0xa2, 0x72, 0xec, 0x25, - 0xac, 0x59, 0x9d, 0x8a, 0xbf, 0xa2, 0x5a, 0x03, 0x56, 0xc6, 0x9d, 0xde, 0x59, 0x50, 0x61, 0x2e, - 0xe5, 0x72, 0x23, 0xf9, 0xf1, 0x45, 0xb3, 0x66, 0xe5, 0x69, 0x39, 0x51, 0xcd, 0x29, 0xbc, 0x20, - 0x73, 0xd1, 0xd1, 0x46, 0x2e, 0x08, 0xdc, 0xbe, 0xb9, 0x3d, 0x10, 0x32, 0xdb, 0xd8, 0xd4, 0x2b, - 0xed, 0x68, 0x02, 0x27, 0xd3, 0x4e, 0x61, 0x7b, 0xf6, 0x90, 0x50, 0xc1, 0xcc, 0x93, 0x3d, 0xd0, - 0x2d, 0xf1, 0x28, 0x9d, 0xcb, 0x19, 0xf9, 0x5c, 0x96, 0x79, 0xea, 0x6c, 0x84, 0xa7, 0xd6, 0xfe, - 0x36, 0x0b, 0x37, 0xcf, 0x31, 0x5c, 0x73, 0xde, 0xf9, 0x36, 0xe4, 0xd9, 0x95, 0x2c, 0xdb, 0x3e, - 0x33, 0x11, 0xe6, 0x89, 0x56, 0xca, 0xf7, 0x3a, 0xca, 0xc8, 0x85, 0xfb, 0x1d, 0x8c, 0x83, 0xff, - 0xe4, 0xb7, 0x60, 0x9d, 0x6d, 0x68, 0xcc, 0xe0, 0xe3, 0x78, 0xda, 0x3f, 0xc7, 0x8e, 0x76, 0x4d, - 0x58, 0xa7, 0xc7, 0x8a, 0xe2, 0x26, 0x87, 0x3b, 0x86, 0x1d, 0xa4, 0x11, 0x07, 0xf2, 0x48, 0x76, - 0xdc, 0xee, 0xf5, 0xcf, 0x65, 0x26, 0x2d, 0x6c, 0xdf, 0xe5, 0x62, 0xcc, 0x4e, 0x8d, 0x26, 0x94, - 0xf1, 0x99, 0x8a, 0xc1, 0xfe, 0xf4, 0x8c, 0xb2, 0x73, 0x6c, 0x2e, 0x70, 0xbd, 0xda, 0x82, 0xb5, - 0xea, 0x4f, 0xcf, 0xf4, 0xe1, 0x10, 0x87, 0x14, 0x15, 0x70, 0x1b, 0x94, 0x8e, 0xad, 0x5a, 0x41, - 0xb9, 0x88, 0x94, 0xb4, 0x02, 0xb6, 0x6e, 0x39, 0xed, 0x26, 0x30, 0x73, 0x0c, 0xa6, 0x50, 0xb0, - 0xd8, 0x83, 0xf6, 0xd3, 0x8c, 0x60, 0x09, 0x67, 0xcf, 0xfb, 0x5f, 0x0d, 0x51, 0xca, 0x10, 0xdd, - 0x01, 0x95, 0x76, 0x7d, 0xb8, 0xa9, 0x04, 0x63, 0xb4, 0xe6, 0x4f, 0xcf, 0x82, 0xbe, 0x93, 0x3b, - 0x7e, 0x51, 0xee, 0xf8, 0xd7, 0x04, 0xcb, 0x98, 0xba, 0x3d, 0xcc, 0xee, 0x72, 0xed, 0xbf, 0xb2, - 0x70, 0xfb, 0x7c, 0x9b, 0xc0, 0xaf, 0xc6, 0x2d, 0x65, 0xdc, 0x62, 0xd7, 0x04, 0x0b, 0x89, 0x6b, - 0x82, 0x94, 0xb5, 0xb7, 0x98, 0xb6, 0xf6, 0x12, 0x97, 0x12, 0x97, 0x53, 0x2e, 0x25, 0x52, 0x17, - 0xe8, 0xd2, 0x13, 0x16, 0xe8, 0xb2, 0x3c, 0x4f, 0x7e, 0x92, 0x81, 0xe7, 0x52, 0x94, 0x4e, 0xe4, - 0x3d, 0x78, 0x4e, 0xb0, 0xf6, 0xec, 0xe4, 0x60, 0x2c, 0x37, 0x3b, 0x7d, 0xef, 0xa6, 0x31, 0xf5, - 0x48, 0x96, 0xc2, 0x78, 0x6f, 0x70, 0x76, 0x3e, 0xcc, 0xff, 0xc5, 0x61, 0xe4, 0xc9, 0x43, 0xb8, - 0x82, 0xc0, 0xb2, 0x1d, 0x57, 0x16, 0xa7, 0x47, 0xde, 0x31, 0x9f, 0x0f, 0x9f, 0x4b, 0xb0, 0xbd, - 0xbd, 0x8e, 0xd4, 0x1c, 0xcb, 0x3b, 0xae, 0x5c, 0xb2, 0x36, 0xc7, 0x29, 0xe9, 0x71, 0x19, 0xe1, - 0xef, 0x15, 0xd0, 0x9e, 0xdc, 0x5f, 0x28, 0xcb, 0xc5, 0x3b, 0x9c, 0xca, 0x72, 0x52, 0xef, 0xdd, - 0x84, 0xd5, 0x91, 0x77, 0x3c, 0xf2, 0xc6, 0xa7, 0x52, 0xf7, 0x2d, 0x5b, 0x2b, 0x3c, 0x51, 0x74, - 0x8c, 0x80, 0x93, 0xba, 0x10, 0x93, 0x2d, 0x0a, 0x69, 0xe5, 0x40, 0xf4, 0x4b, 0x1d, 0x07, 0x3a, - 0x9b, 0xe4, 0x06, 0xb2, 0x87, 0x77, 0x72, 0x4b, 0x19, 0x35, 0x6b, 0x71, 0xd0, 0xab, 0xe3, 0x5e, - 0xdf, 0xd3, 0xfe, 0x41, 0x11, 0x1c, 0x41, 0x5a, 0xe7, 0x91, 0xf7, 0x24, 0x33, 0xa9, 0x6c, 0x82, - 0x0d, 0x49, 0x2b, 0x22, 0x5b, 0x94, 0x70, 0x1c, 0x26, 0x4c, 0x88, 0xe0, 0x30, 0x61, 0xca, 0xb3, - 0xd8, 0x7a, 0x3c, 0x10, 0x5a, 0x56, 0xba, 0xdb, 0x1d, 0xde, 0x23, 0x77, 0xe1, 0x32, 0x53, 0xac, - 0x8a, 0x86, 0xae, 0x47, 0x1a, 0x7a, 0x78, 0xcf, 0x12, 0xf9, 0xda, 0x47, 0x4a, 0xa0, 0x1a, 0x8a, - 0x37, 0xff, 0xf0, 0x1e, 0x79, 0xed, 0x7c, 0x06, 0x4f, 0x4b, 0xc2, 0xe0, 0x29, 0x30, 0x76, 0x7a, - 0x3d, 0x62, 0xec, 0x74, 0x6b, 0x7e, 0x3f, 0xf1, 0x4b, 0xe8, 0x78, 0xe4, 0xf2, 0x9f, 0x2a, 0x70, - 0x7d, 0x6e, 0x09, 0xb2, 0x03, 0x4b, 0x7a, 0xd3, 0x74, 0xc2, 0x91, 0xa5, 0xab, 0x45, 0xa4, 0x90, - 0x7d, 0x58, 0xde, 0x6b, 0x8f, 0x7b, 0x1d, 0x3a, 0x81, 0x53, 0xef, 0xce, 0x12, 0xd5, 0x06, 0xe4, - 0x95, 0x4b, 0x56, 0x58, 0x96, 0xb8, 0xb0, 0x81, 0xab, 0x20, 0x11, 0x4f, 0x37, 0x7e, 0x61, 0x90, - 0xa8, 0x30, 0x51, 0x8c, 0xee, 0x30, 0x89, 0xc4, 0xf8, 0xe2, 0x7b, 0x24, 0xb8, 0x90, 0xd9, 0x0d, - 0xbc, 0x00, 0x74, 0xda, 0x1d, 0x58, 0x6a, 0x0a, 0xf5, 0x90, 0x64, 0x21, 0x28, 0x54, 0x41, 0x56, - 0x90, 0xab, 0xfd, 0xb1, 0x22, 0xa4, 0xfa, 0x27, 0x7f, 0x88, 0x14, 0x68, 0xa1, 0x3b, 0x3f, 0xd0, - 0x42, 0xf7, 0x29, 0x03, 0x2d, 0x68, 0x7f, 0xc7, 0x81, 0x4d, 0xcd, 0x6e, 0x33, 0x16, 0xfb, 0xeb, - 0x59, 0x2d, 0x3d, 0x8d, 0xc8, 0xec, 0xbc, 0x29, 0x05, 0x7f, 0x49, 0xbe, 0x6b, 0xb6, 0xc1, 0xa7, - 0x34, 0x55, 0x7f, 0x92, 0x81, 0x9d, 0x79, 0xc5, 0x53, 0xc3, 0x94, 0x29, 0x17, 0x0b, 0x53, 0x76, - 0x17, 0x96, 0x58, 0x5a, 0x34, 0xf6, 0x33, 0x2f, 0x4a, 0x3b, 0x5c, 0x64, 0x93, 0x9b, 0xb0, 0xa8, - 0x17, 0xed, 0x30, 0x3a, 0x05, 0xda, 0x1b, 0xb5, 0x3b, 0x63, 0xb4, 0x64, 0xe1, 0x59, 0xe4, 0x9b, - 0xc9, 0x80, 0x2c, 0x3c, 0x2c, 0xc5, 0x35, 0xa9, 0x43, 0x12, 0x98, 0xc3, 0xd8, 0xde, 0x10, 0x23, - 0x97, 0xc3, 0x4e, 0x5a, 0xc9, 0xe0, 0x2e, 0x1a, 0x2c, 0x36, 0x47, 0xde, 0xd8, 0x9b, 0xc8, 0xb6, - 0x40, 0x43, 0x4c, 0xb1, 0x78, 0x0e, 0xb7, 0xd4, 0x69, 0x3f, 0x66, 0x8e, 0x99, 0x8b, 0xb2, 0xb3, - 0x3c, 0x9a, 0xf6, 0xd0, 0x64, 0x4b, 0x22, 0xd1, 0xfe, 0x40, 0x81, 0xcd, 0xb4, 0x66, 0x91, 0x1d, - 0xc8, 0xf9, 0xa9, 0xa1, 0x64, 0x7c, 0xe6, 0x1e, 0x96, 0xc7, 0xb8, 0xbb, 0xc7, 0x83, 0xd1, 0x59, - 0x7b, 0x22, 0x1b, 0x40, 0x49, 0xc9, 0x16, 0xd0, 0x87, 0x32, 0xfe, 0x27, 0x37, 0xc4, 0x66, 0x9b, - 0x4d, 0x04, 0x9f, 0xc1, 0x1f, 0x4d, 0x07, 0x30, 0xbb, 0xcd, 0xc6, 0x90, 0x41, 0xd8, 0xde, 0x87, - 0x1c, 0x6d, 0x56, 0x6c, 0x32, 0xd2, 0xe9, 0xa0, 0xd7, 0xaa, 0x9c, 0x88, 0xb5, 0x6a, 0xdc, 0x3e, - 0xeb, 0x5b, 0x48, 0xac, 0x1d, 0xc1, 0x5a, 0x94, 0x82, 0x18, 0x51, 0xd0, 0xb3, 0xfc, 0x2b, 0x2a, - 0xaf, 0x69, 0x6f, 0x30, 0x60, 0x46, 0xb8, 0x7b, 0x2f, 0xfc, 0xcb, 0x27, 0x37, 0x80, 0x3e, 0xb2, - 0x32, 0x69, 0xa0, 0x68, 0xda, 0x9f, 0x66, 0x60, 0x33, 0xf4, 0xfb, 0x13, 0x4b, 0xe2, 0x97, 0xd6, - 0x09, 0x45, 0x8f, 0x38, 0x49, 0x08, 0x8e, 0x29, 0xf9, 0x81, 0x73, 0x6c, 0xb3, 0xf7, 0x61, 0x6b, - 0x16, 0x3d, 0xf9, 0x42, 0x22, 0x2e, 0x3e, 0x07, 0xc5, 0x08, 0x02, 0xe8, 0x4b, 0x61, 0xf2, 0x7f, - 0xa4, 0xc0, 0x36, 0x37, 0x1d, 0xad, 0xb5, 0x7b, 0x3e, 0x6a, 0x1d, 0x3a, 0xde, 0x67, 0xe3, 0x44, - 0xb5, 0x1f, 0xd9, 0x96, 0x3e, 0x1f, 0xb5, 0x10, 0x4e, 0xbc, 0x6d, 0xf6, 0xd7, 0x92, 0xbb, 0x08, - 0x7f, 0xc2, 0x55, 0x2c, 0x39, 0xe6, 0xb4, 0xea, 0xd3, 0x04, 0xd9, 0x69, 0x15, 0x29, 0xb4, 0xdf, - 0x81, 0xdd, 0xf9, 0x2f, 0x20, 0xdf, 0x80, 0x55, 0x0c, 0x19, 0xd0, 0x1a, 0x9e, 0x8c, 0xda, 0x5d, - 0x4f, 0xdc, 0x69, 0x89, 0x2b, 0x45, 0x39, 0x8f, 0x41, 0xbe, 0x70, 0x27, 0xca, 0x13, 0x0c, 0x46, - 0xc0, 0x0b, 0x45, 0xec, 0xb3, 0xe5, 0xda, 0xb4, 0xdf, 0x55, 0x80, 0x24, 0xeb, 0x20, 0x5f, 0x81, - 0x95, 0x96, 0x53, 0xb4, 0x27, 0xed, 0xd1, 0xa4, 0x32, 0x98, 0x8e, 0x38, 0x94, 0x0a, 0xf3, 0xa9, - 0x9b, 0x74, 0xe8, 0xce, 0x30, 0x9a, 0xb8, 0xa7, 0x83, 0xe9, 0xc8, 0x8a, 0xd0, 0x61, 0x5c, 0x02, - 0xcf, 0xfb, 0x56, 0xb7, 0xfd, 0x38, 0x1a, 0x97, 0x80, 0xa7, 0x45, 0xe2, 0x12, 0xf0, 0x34, 0xed, - 0x63, 0x05, 0xae, 0x09, 0x9b, 0x90, 0x6e, 0x4a, 0x5b, 0x8a, 0xe8, 0x39, 0x3e, 0x12, 0x50, 0x72, - 0xf3, 0x78, 0xd3, 0x0d, 0x01, 0xae, 0x80, 0x0d, 0x44, 0x26, 0x95, 0x95, 0x25, 0x6f, 0x43, 0xce, - 0x9e, 0x0c, 0x86, 0xe7, 0x40, 0x57, 0x50, 0x83, 0x11, 0x9d, 0x0c, 0x86, 0x58, 0x05, 0x96, 0xd4, - 0x3c, 0xd8, 0x94, 0x1b, 0x27, 0x5a, 0x4c, 0x6a, 0x70, 0x99, 0x63, 0xed, 0xc4, 0x94, 0x52, 0x73, - 0xbe, 0x69, 0x6f, 0x5d, 0x40, 0x38, 0x70, 0x28, 0x33, 0x4b, 0xd4, 0xa1, 0xfd, 0x89, 0x02, 0x79, - 0xca, 0x3c, 0xa0, 0x38, 0xf6, 0xac, 0x53, 0x3a, 0xca, 0x07, 0x0a, 0x1d, 0x6b, 0x50, 0xfd, 0xb9, - 0x0e, 0xd7, 0x57, 0x61, 0x3d, 0x56, 0x80, 0x68, 0xe8, 0xbc, 0xdb, 0xef, 0x75, 0xda, 0x0c, 0xe6, - 0x9c, 0xe9, 0x27, 0x23, 0x69, 0xda, 0x1f, 0x2a, 0xb0, 0x49, 0x85, 0x77, 0x13, 0xef, 0x6d, 0xad, - 0x69, 0x5f, 0xac, 0x77, 0xca, 0x10, 0x09, 0xe3, 0x22, 0xe6, 0x58, 0xc8, 0x18, 0x22, 0x9e, 0x66, - 0x05, 0xb9, 0xa4, 0x02, 0x4b, 0xfc, 0x7c, 0x19, 0x73, 0x04, 0xb2, 0x5d, 0xe9, 0x56, 0x20, 0xac, - 0x98, 0x13, 0xd1, 0x2f, 0xc1, 0x2d, 0x8c, 0x97, 0xb1, 0x82, 0xd2, 0xda, 0x7f, 0x2b, 0x70, 0x75, - 0x46, 0x19, 0xf2, 0x26, 0x2c, 0xa0, 0xd3, 0x03, 0x1f, 0xbd, 0x9d, 0x19, 0xaf, 0x98, 0x74, 0x4e, - 0x0f, 0xef, 0xb1, 0x83, 0xe8, 0x8c, 0x3e, 0x58, 0xac, 0x14, 0x79, 0x0f, 0x96, 0xf5, 0x6e, 0x97, - 0xcb, 0x25, 0x99, 0x88, 0x5c, 0x32, 0xe3, 0x8d, 0x2f, 0x07, 0xf4, 0x4c, 0x2e, 0x61, 0xe6, 0xb7, - 0xdd, 0xae, 0xcb, 0x1d, 0x3a, 0xc2, 0xfa, 0xb6, 0x7f, 0x03, 0xd6, 0xa2, 0xc4, 0x17, 0x92, 0x4b, - 0x3e, 0x52, 0x40, 0x8d, 0xb6, 0xe1, 0xe7, 0x03, 0x3e, 0x91, 0x36, 0xcc, 0x4f, 0x98, 0x54, 0x7f, - 0x9e, 0x81, 0xe7, 0x53, 0x7b, 0x98, 0xbc, 0x04, 0x8b, 0xfa, 0x70, 0x68, 0x96, 0xf8, 0xac, 0xe2, - 0x0c, 0x0f, 0x5e, 0xf7, 0x46, 0xc4, 0x36, 0x46, 0x44, 0xee, 0xc3, 0x12, 0xce, 0x4c, 0x5a, 0x20, - 0x13, 0x62, 0x81, 0xb1, 0xdb, 0x90, 0x18, 0x16, 0x98, 0x20, 0x24, 0x65, 0x58, 0xe3, 0x7e, 0xe8, - 0x96, 0x77, 0xe2, 0x7d, 0x27, 0x00, 0xa5, 0x45, 0xdc, 0x5c, 0x71, 0x87, 0xec, 0x8e, 0x58, 0x9e, - 0xec, 0x89, 0x1d, 0x2d, 0x45, 0xaa, 0xa0, 0x62, 0x9d, 0x72, 0x4d, 0x0c, 0x90, 0x0c, 0x91, 0x01, - 0x58, 0x23, 0x66, 0xd4, 0x95, 0x28, 0x19, 0x0c, 0x97, 0x3e, 0x1e, 0xf7, 0x4e, 0xfc, 0x33, 0xcf, - 0x9f, 0xfc, 0xfc, 0x86, 0x2b, 0x7c, 0xc7, 0xb9, 0x86, 0xeb, 0x2f, 0x72, 0x6c, 0x31, 0xc7, 0x8b, - 0x51, 0x8e, 0x46, 0xc2, 0xa0, 0x44, 0x8e, 0x06, 0xe3, 0x8a, 0x32, 0x4f, 0xeb, 0x12, 0x5c, 0x66, - 0x1e, 0xf0, 0x62, 0x65, 0x5c, 0x4f, 0x6d, 0x02, 0xa3, 0x39, 0xbc, 0xc7, 0xd8, 0x17, 0xe6, 0x7d, - 0x31, 0xb6, 0x44, 0x51, 0x72, 0x08, 0xf9, 0x62, 0xdf, 0x6b, 0xfb, 0xd3, 0xa1, 0x73, 0x3e, 0x35, - 0xe0, 0x16, 0xff, 0x96, 0x95, 0x0e, 0x2b, 0x86, 0xea, 0x43, 0xdc, 0xc9, 0xe5, 0x8a, 0x88, 0x13, - 0x18, 0x64, 0xe7, 0xf0, 0xca, 0xf1, 0xcb, 0x73, 0xfa, 0x27, 0x9e, 0x88, 0xe5, 0xa2, 0xde, 0x06, - 0xdc, 0x62, 0xdb, 0x85, 0xb5, 0x6a, 0x7b, 0x3c, 0x71, 0x46, 0x6d, 0x7f, 0x8c, 0xc8, 0x59, 0xe7, - 0x40, 0x16, 0x11, 0x11, 0x88, 0xd9, 0x55, 0xe4, 0x24, 0x28, 0xca, 0xae, 0x22, 0xa3, 0xd5, 0x51, - 0x7e, 0xa9, 0xdc, 0xf3, 0xdb, 0xfd, 0xde, 0x87, 0xc2, 0x6f, 0x85, 0xf1, 0x4b, 0xc7, 0x22, 0xd1, - 0x0a, 0xf3, 0xb5, 0xaf, 0x27, 0xc6, 0x8d, 0xb5, 0x32, 0x0f, 0x97, 0xb9, 0x57, 0x23, 0xf3, 0xf2, - 0x6b, 0x1a, 0xf5, 0x92, 0x59, 0xdf, 0x57, 0x15, 0xb2, 0x06, 0xd0, 0xb4, 0x1a, 0x45, 0xc3, 0xb6, - 0xe9, 0x73, 0x86, 0x3e, 0x73, 0x17, 0xc0, 0x72, 0xab, 0xaa, 0x66, 0x25, 0x2f, 0xc0, 0x9c, 0xf6, - 0x43, 0x05, 0xae, 0xa4, 0x0f, 0x25, 0x71, 0x00, 0xfd, 0x40, 0xb9, 0x42, 0xf8, 0x2b, 0x73, 0xc7, - 0x3d, 0x35, 0x39, 0xee, 0x4f, 0x3a, 0x61, 0x7e, 0x8a, 0x19, 0xa1, 0xf5, 0x09, 0x63, 0xe7, 0xf6, - 0xba, 0x5a, 0x11, 0xb6, 0x66, 0xd5, 0x11, 0xfd, 0xd4, 0x75, 0xc8, 0xeb, 0xcd, 0x66, 0xd5, 0x2c, - 0xea, 0x8e, 0xd9, 0xa8, 0xab, 0x0a, 0x59, 0x86, 0x85, 0x7d, 0xab, 0xd1, 0x6a, 0xaa, 0x19, 0xed, - 0xfb, 0x0a, 0xac, 0x9a, 0xfe, 0xc4, 0x3b, 0x61, 0x76, 0xbc, 0xcf, 0xba, 0xf8, 0xde, 0x88, 0x2c, - 0xbe, 0xad, 0xc0, 0x63, 0x3a, 0x78, 0xc1, 0xb9, 0x56, 0xde, 0x3f, 0x2b, 0xb0, 0x91, 0x28, 0x43, - 0x6c, 0xb8, 0xac, 0x1f, 0xd9, 0x0d, 0xb3, 0x54, 0xe4, 0x2d, 0x13, 0x5c, 0x39, 0x4f, 0x4d, 0xbe, - 0x85, 0x79, 0x19, 0x7d, 0x30, 0x76, 0x07, 0xbd, 0xae, 0x14, 0xb1, 0xab, 0x72, 0xc9, 0x12, 0x35, - 0xe1, 0x49, 0xf6, 0xe1, 0x74, 0xe4, 0x61, 0xb5, 0x99, 0xc8, 0x8d, 0x66, 0x90, 0x9e, 0xac, 0x18, - 0xcd, 0x56, 0xdb, 0x34, 0x3f, 0x59, 0x75, 0x58, 0xdf, 0xde, 0x2a, 0xe4, 0xb9, 0xd4, 0x82, 0x02, - 0xc1, 0xf7, 0x14, 0xd8, 0x9a, 0xd5, 0x56, 0x2a, 0x08, 0x45, 0x5d, 0x0e, 0xaf, 0x04, 0x98, 0xcb, - 0x51, 0x5f, 0x43, 0x41, 0x46, 0xde, 0x82, 0x3c, 0x0b, 0x48, 0x6e, 0xdf, 0x6f, 0x59, 0x26, 0x9f, - 0x20, 0xd7, 0xff, 0xf3, 0x93, 0x1b, 0x57, 0x59, 0xf8, 0x72, 0x77, 0x7c, 0xdf, 0x9d, 0x8e, 0x7a, - 0x11, 0x7c, 0x5a, 0xb9, 0x84, 0xf6, 0x5d, 0x05, 0xb6, 0x67, 0x7f, 0x24, 0x3d, 0x65, 0x1c, 0x66, - 0xe0, 0x24, 0x9c, 0x9c, 0xf0, 0x94, 0x09, 0xac, 0xa0, 0xe4, 0x53, 0x46, 0x10, 0xd2, 0x42, 0x41, - 0x2c, 0xcc, 0x4c, 0x22, 0x04, 0x5e, 0xb4, 0x90, 0x20, 0xd4, 0xfe, 0x2a, 0x03, 0x57, 0xe8, 0x04, - 0xea, 0x7b, 0xe3, 0xb1, 0x3e, 0x9d, 0x9c, 0x7a, 0xfe, 0x84, 0xb3, 0x54, 0xe4, 0x35, 0x58, 0x3c, - 0xbd, 0xd8, 0x6d, 0x20, 0x23, 0x27, 0x04, 0x70, 0x53, 0x16, 0x66, 0xaf, 0xf4, 0x3f, 0xb9, 0x0e, - 0x52, 0xc8, 0x41, 0xdc, 0x53, 0x57, 0xac, 0xe5, 0x61, 0x10, 0x78, 0xf0, 0x75, 0x58, 0x40, 0xe9, - 0x9f, 0x6f, 0x8d, 0x82, 0xa5, 0x4d, 0x6f, 0x19, 0xde, 0x0d, 0x58, 0xac, 0x00, 0xf9, 0x12, 0x40, - 0x88, 0xed, 0xcb, 0xf7, 0x3e, 0x21, 0x46, 0x07, 0xf0, 0xbe, 0xd6, 0xf2, 0xd9, 0x71, 0x9b, 0x03, - 0xe6, 0x16, 0x60, 0x43, 0x74, 0xc9, 0x50, 0x00, 0x09, 0x71, 0xf5, 0xd4, 0x3a, 0xcb, 0x30, 0x87, - 0x1c, 0x4c, 0x48, 0xfb, 0x8f, 0x0c, 0x2c, 0x1f, 0x51, 0x46, 0x01, 0xc5, 0xdf, 0xf9, 0xe2, 0xf4, - 0x2b, 0x90, 0xaf, 0x0e, 0xda, 0xfc, 0xee, 0x7e, 0xcc, 0xb1, 0xcc, 0xd0, 0x75, 0xa9, 0x3f, 0x68, - 0x0b, 0x35, 0xc0, 0xd8, 0x92, 0x89, 0x9e, 0xe0, 0x76, 0xf5, 0x0e, 0x2c, 0x32, 0x5d, 0x0a, 0xbf, - 0xa8, 0x11, 0xac, 0x62, 0xd0, 0xa2, 0x97, 0x59, 0xb6, 0x74, 0xdd, 0xcc, 0xf4, 0x31, 0x32, 0xdf, - 0xc2, 0xa1, 0xcc, 0x24, 0x61, 0x7f, 0xe1, 0x7c, 0xc2, 0xbe, 0x04, 0xd9, 0xb2, 0x78, 0x1e, 0xc8, - 0x96, 0xed, 0x07, 0x90, 0x97, 0xda, 0x73, 0x21, 0xce, 0xf1, 0xf7, 0x32, 0xb0, 0x8a, 0x5f, 0x15, - 0x18, 0x56, 0xfc, 0x72, 0x5e, 0x5d, 0xbc, 0x11, 0xb9, 0xba, 0xd8, 0x92, 0xc7, 0x8b, 0x7d, 0xd9, - 0x9c, 0x3b, 0x8b, 0x77, 0x60, 0x23, 0x41, 0x48, 0x5e, 0x85, 0x05, 0xda, 0x7c, 0x21, 0xea, 0xa9, - 0xf1, 0x19, 0x10, 0xc2, 0xfb, 0xd1, 0x0f, 0x1f, 0x5b, 0x8c, 0x5a, 0xfb, 0x1f, 0x05, 0x56, 0x38, - 0xd8, 0xb3, 0x7f, 0x3c, 0x78, 0x62, 0x77, 0xde, 0x8e, 0x77, 0x27, 0x73, 0x22, 0xe6, 0xdd, 0xf9, - 0x7f, 0xdd, 0x89, 0x0f, 0x22, 0x9d, 0x78, 0x35, 0x00, 0xfb, 0x11, 0x9f, 0x33, 0xa7, 0x0f, 0xff, - 0x09, 0xe1, 0xef, 0xa2, 0x84, 0xe4, 0x9b, 0xb0, 0x5c, 0xf7, 0x3e, 0x88, 0x48, 0x4c, 0xb7, 0x67, - 0x54, 0xfa, 0x72, 0x40, 0xc8, 0xd6, 0x14, 0x1e, 0x36, 0xbe, 0xf7, 0x81, 0x9b, 0x50, 0xe3, 0x84, - 0x55, 0x52, 0xa1, 0x29, 0x5a, 0xec, 0x22, 0x53, 0x9f, 0xbb, 0x9a, 0xa0, 0x5f, 0xfc, 0xdf, 0xe4, - 0x00, 0x42, 0x2b, 0x7d, 0xba, 0x00, 0x23, 0x1a, 0x6c, 0x71, 0x77, 0x8c, 0x49, 0xf2, 0x1c, 0x17, - 0x8a, 0xed, 0xdb, 0xfc, 0x52, 0x34, 0x33, 0x1b, 0x8c, 0x09, 0xaf, 0x47, 0x8b, 0xdc, 0x0a, 0xbe, - 0xeb, 0xf5, 0xdb, 0x6c, 0x2f, 0xce, 0xee, 0xdd, 0x42, 0xec, 0xbd, 0x20, 0x75, 0x46, 0xd4, 0x3e, - 0xb4, 0x95, 0x2f, 0x51, 0x82, 0x84, 0xe7, 0x4b, 0xee, 0xe9, 0x3d, 0x5f, 0x16, 0x9e, 0xc2, 0xf3, - 0x65, 0xf1, 0x9c, 0x9e, 0x2f, 0x4d, 0x58, 0xee, 0xf9, 0x8f, 0x3c, 0x7f, 0x32, 0x18, 0x3d, 0x46, - 0x2d, 0x75, 0x78, 0x95, 0x45, 0xbb, 0xda, 0x14, 0x79, 0x6c, 0xbc, 0xf1, 0xc0, 0x0c, 0xe8, 0xe5, - 0xe1, 0x0e, 0x12, 0xc9, 0xaf, 0x41, 0xa8, 0xf5, 0xe0, 0x08, 0xed, 0xb3, 0xcf, 0xd9, 0x8e, 0x50, - 0x8a, 0xbc, 0x0d, 0x51, 0xe5, 0x07, 0xf7, 0x3b, 0x65, 0x11, 0x66, 0xe5, 0x0c, 0x19, 0x7f, 0xac, - 0x23, 0xe9, 0x47, 0xb8, 0xd9, 0xeb, 0xcf, 0x32, 0x40, 0x92, 0x0d, 0x27, 0x6f, 0x40, 0x9e, 0x6d, - 0xfd, 0xee, 0x68, 0xfc, 0x6d, 0xee, 0xae, 0xc1, 0x70, 0x0f, 0xa4, 0x64, 0x19, 0xf7, 0x80, 0x25, - 0x5b, 0xe3, 0x6f, 0xf7, 0xc9, 0x37, 0xe0, 0x39, 0x1c, 0xf8, 0xa1, 0x37, 0xea, 0x0d, 0xba, 0x2e, - 0x82, 0xd4, 0xb5, 0xfb, 0x3c, 0xf6, 0xcf, 0x4b, 0x18, 0xa4, 0x2e, 0x99, 0x3d, 0x63, 0x82, 0xa0, - 0x57, 0x44, 0x13, 0x29, 0x9b, 0x8c, 0x90, 0x38, 0xa0, 0xca, 0xe5, 0x8f, 0xa7, 0xfd, 0x3e, 0x9f, - 0x73, 0x05, 0x2a, 0xfe, 0xc6, 0xf3, 0x66, 0x54, 0xbc, 0x16, 0x56, 0x5c, 0x9e, 0xf6, 0xfb, 0xe4, - 0x35, 0x80, 0x81, 0xef, 0x9e, 0xf5, 0xc6, 0x63, 0xa6, 0xc8, 0x08, 0x3c, 0x9a, 0xc2, 0x54, 0x79, - 0xf8, 0x06, 0x7e, 0x8d, 0x25, 0xd2, 0xe1, 0x1b, 0xb6, 0x4f, 0x3c, 0xf4, 0x23, 0x66, 0x46, 0x2b, - 0x1c, 0xcd, 0x5b, 0x24, 0x46, 0xa7, 0xd1, 0x89, 0x67, 0xf7, 0x3e, 0x14, 0xa6, 0xce, 0xef, 0xc2, - 0x06, 0x37, 0x17, 0x3d, 0xea, 0x4d, 0x4e, 0x39, 0xdf, 0xfd, 0x2c, 0x4c, 0xbb, 0xc4, 0x78, 0xff, - 0x6b, 0x0e, 0x40, 0x3f, 0xb2, 0x05, 0x44, 0xc7, 0x5d, 0x58, 0xa0, 0xd2, 0x84, 0xb8, 0x95, 0xc0, - 0x3b, 0x5d, 0xac, 0x57, 0xbe, 0xd3, 0x45, 0x0a, 0xba, 0x4f, 0x58, 0xde, 0x09, 0x5e, 0x8c, 0x65, - 0xc2, 0x2b, 0x8c, 0x11, 0x4b, 0x8a, 0x70, 0xaf, 0x2c, 0x89, 0x54, 0x01, 0x42, 0xd0, 0x0c, 0x2e, - 0xdf, 0x6e, 0x84, 0xde, 0xe7, 0x3c, 0x83, 0xc3, 0x34, 0x87, 0xc0, 0x1b, 0xf2, 0xf4, 0x09, 0xc9, - 0xc8, 0x01, 0xe4, 0x9c, 0x76, 0xe0, 0xaf, 0x33, 0x03, 0x4a, 0xe4, 0x45, 0x1e, 0x9b, 0x29, 0x84, - 0x13, 0x59, 0x9b, 0xb4, 0x23, 0x21, 0xec, 0xb0, 0x12, 0x62, 0xc0, 0x22, 0x8f, 0xbb, 0x39, 0x03, - 0x82, 0x8a, 0x87, 0xdd, 0xe4, 0xc0, 0x93, 0x98, 0x28, 0x73, 0x3b, 0x3c, 0xc2, 0xe6, 0x2b, 0x90, - 0xb5, 0xed, 0x1a, 0x77, 0xa0, 0x5d, 0x0d, 0x65, 0x15, 0xdb, 0xae, 0x89, 0x30, 0xc5, 0x67, 0x52, - 0x31, 0x4a, 0x4c, 0x7e, 0x1d, 0xf2, 0x12, 0x23, 0xce, 0x5d, 0xcf, 0xb1, 0x0f, 0x7a, 0x61, 0xb2, - 0xbc, 0x9d, 0x49, 0xd4, 0xa4, 0x0a, 0xea, 0xc1, 0xf4, 0x7d, 0x4f, 0x1f, 0x0e, 0xd1, 0xa1, 0xe4, - 0x91, 0x37, 0x62, 0x10, 0xd2, 0x4b, 0x21, 0x66, 0x23, 0x3a, 0x28, 0x74, 0x45, 0xae, 0x7c, 0x33, - 0x13, 0x2f, 0x49, 0x9a, 0xb0, 0x61, 0x7b, 0x93, 0xe9, 0x90, 0x99, 0x61, 0x94, 0x07, 0x23, 0x2a, - 0x9a, 0xb0, 0x0d, 0x03, 0xe1, 0xed, 0xc6, 0x34, 0x53, 0xd8, 0xbe, 0x1c, 0x0f, 0x46, 0x31, 0x31, - 0x25, 0x59, 0x58, 0xf3, 0xe4, 0x21, 0xa7, 0xe7, 0x7d, 0x54, 0xe0, 0xc1, 0xf3, 0x5e, 0x08, 0x3c, - 0xa1, 0x98, 0xf3, 0xa5, 0x14, 0x30, 0x15, 0x54, 0xa3, 0x49, 0x60, 0x2a, 0x11, 0x08, 0x95, 0x8f, - 0x73, 0x12, 0x9e, 0x17, 0x1f, 0x8b, 0x37, 0x01, 0xde, 0x19, 0xf4, 0xfc, 0x9a, 0x37, 0x39, 0x1d, - 0x74, 0x25, 0x4c, 0x97, 0xfc, 0x6f, 0x0f, 0x7a, 0xbe, 0x7b, 0x86, 0xc9, 0x3f, 0xfb, 0xe4, 0x86, - 0x44, 0x64, 0x49, 0xff, 0xc9, 0x17, 0x61, 0x99, 0x3e, 0x39, 0xa1, 0x31, 0x09, 0xbb, 0xc0, 0xc4, - 0xd2, 0x3c, 0xdc, 0x79, 0x40, 0x40, 0x1e, 0x20, 0xce, 0x7b, 0x6f, 0x38, 0x91, 0xd8, 0x6a, 0x01, - 0xea, 0xde, 0x1b, 0x4e, 0xe2, 0x10, 0x8d, 0x12, 0x31, 0xa9, 0x04, 0x4d, 0x17, 0x91, 0x02, 0x38, - 0x9c, 0x3c, 0xde, 0xd2, 0xf1, 0xb9, 0xe6, 0x0a, 0x6c, 0x38, 0x39, 0xa6, 0x5b, 0xac, 0x18, 0x36, - 0xc2, 0xae, 0x94, 0x98, 0x5a, 0x85, 0x9f, 0x6e, 0xac, 0x11, 0xe3, 0xd3, 0xae, 0xdb, 0xc1, 0xe4, - 0x48, 0x23, 0x02, 0x62, 0xb2, 0x07, 0xeb, 0x0c, 0x79, 0x20, 0x88, 0x38, 0xc4, 0x4f, 0x3a, 0xdc, - 0xdb, 0xc2, 0x90, 0x44, 0xf2, 0xeb, 0x63, 0x05, 0x48, 0x19, 0x16, 0x50, 0xb4, 0xe4, 0xc6, 0xe0, - 0xd7, 0x64, 0x99, 0x3a, 0xbe, 0x8e, 0x70, 0x5f, 0x41, 0x69, 0x5a, 0xde, 0x57, 0x90, 0x94, 0x7c, - 0x0d, 0xc0, 0xf0, 0x47, 0x83, 0x7e, 0x1f, 0xd1, 0x0b, 0x97, 0x50, 0x30, 0xbb, 0x1e, 0x5d, 0x8f, - 0x58, 0x4b, 0x48, 0xc4, 0x91, 0x76, 0xf0, 0xd9, 0x8d, 0x61, 0x1c, 0x4a, 0x75, 0x69, 0x26, 0x2c, - 0xb2, 0xc5, 0x88, 0x48, 0xa0, 0x1c, 0xdb, 0x5c, 0xc2, 0x91, 0x64, 0x48, 0xa0, 0x3c, 0x3d, 0x89, - 0x04, 0x2a, 0x15, 0xd0, 0x0e, 0x60, 0x33, 0xed, 0xc3, 0x22, 0xc2, 0xb0, 0x72, 0x5e, 0x61, 0xf8, - 0xaf, 0xb3, 0xb0, 0x82, 0xb5, 0x89, 0x5d, 0x58, 0x87, 0x55, 0x7b, 0xfa, 0x7e, 0x00, 0x93, 0x21, - 0x76, 0x63, 0x6c, 0xdf, 0x58, 0xce, 0x90, 0x15, 0x5e, 0x91, 0x12, 0xc4, 0x80, 0x35, 0x71, 0x12, - 0xec, 0x0b, 0x03, 0xf3, 0x00, 0x84, 0x53, 0x40, 0x3d, 0x25, 0x23, 0xae, 0xc5, 0x0a, 0x85, 0xe7, - 0x41, 0xf6, 0x22, 0xe7, 0x41, 0xee, 0x5c, 0xe7, 0xc1, 0x7b, 0xb0, 0x22, 0xde, 0x86, 0x3b, 0xf9, - 0xc2, 0xb3, 0xed, 0xe4, 0x91, 0xca, 0x48, 0x35, 0xd8, 0xd1, 0x17, 0xe7, 0xee, 0xe8, 0xa8, 0x45, - 0x14, 0xab, 0x2c, 0x11, 0x44, 0x99, 0xd7, 0x81, 0x21, 0x89, 0xf6, 0x8b, 0xcd, 0xa7, 0x38, 0x25, - 0x5f, 0x85, 0xe5, 0xea, 0x40, 0x28, 0x90, 0xa4, 0x9b, 0xfb, 0xbe, 0x48, 0x94, 0xd9, 0x85, 0x80, - 0x32, 0x38, 0xdd, 0xb2, 0x9f, 0xc5, 0xe9, 0xf6, 0x00, 0x80, 0x7b, 0x2e, 0x84, 0xa1, 0x44, 0x70, - 0xc9, 0x08, 0x2f, 0xe6, 0xa8, 0x02, 0x41, 0x22, 0xa6, 0xbb, 0x13, 0x37, 0x35, 0xd1, 0x3b, 0x9d, - 0xc1, 0xd4, 0x9f, 0x44, 0x62, 0xef, 0x71, 0x40, 0x04, 0x7a, 0x24, 0x60, 0x9e, 0xbc, 0x3d, 0xc4, - 0x8a, 0x7d, 0xb6, 0x03, 0x42, 0xbe, 0x1a, 0xd8, 0xc8, 0xcd, 0x0d, 0x45, 0xae, 0x25, 0x7a, 0x68, - 0xa6, 0x65, 0x9c, 0xf6, 0x43, 0x45, 0x46, 0x40, 0x7e, 0x8a, 0xa1, 0x7e, 0x1d, 0x20, 0xd0, 0xe0, - 0x8b, 0xb1, 0x66, 0x92, 0x5c, 0x90, 0x2a, 0xf7, 0x72, 0x48, 0x2b, 0x7d, 0x4d, 0xf6, 0xb3, 0xfa, - 0x1a, 0x07, 0xf2, 0x8d, 0x6f, 0x4d, 0xda, 0xa1, 0xc9, 0x07, 0xd8, 0x01, 0x27, 0x8b, 0x3b, 0x93, - 0x08, 0x99, 0x1e, 0xf2, 0xc1, 0x33, 0x43, 0xa6, 0x07, 0x05, 0xb5, 0x7f, 0x54, 0x60, 0x5d, 0x76, - 0x60, 0x7c, 0xec, 0x77, 0xc8, 0x6f, 0x32, 0x40, 0x36, 0x25, 0x22, 0xe4, 0x48, 0x44, 0x74, 0xcb, - 0x7d, 0xec, 0x77, 0x18, 0x03, 0xd4, 0xfe, 0x40, 0x6e, 0x2c, 0x2d, 0x48, 0xde, 0x87, 0x95, 0xe6, - 0xa0, 0xdf, 0xa7, 0x6c, 0xcd, 0xe8, 0x11, 0x17, 0x00, 0x68, 0x45, 0x71, 0x3d, 0x82, 0x68, 0xd0, - 0xde, 0x4d, 0x2e, 0x81, 0x5f, 0x1d, 0xd2, 0xfd, 0xbe, 0xc7, 0xcb, 0x85, 0xd5, 0x7e, 0x84, 0x9e, - 0x51, 0x72, 0x9d, 0xda, 0x8f, 0x15, 0x20, 0xc9, 0x26, 0xc9, 0x5b, 0x96, 0xf2, 0xff, 0xc0, 0xc2, - 0xc6, 0x58, 0xbf, 0xdc, 0x45, 0x58, 0xbf, 0xc2, 0x9f, 0x29, 0xb0, 0x6e, 0xea, 0x35, 0x8e, 0x89, - 0xcc, 0xd4, 0x1d, 0x9f, 0x83, 0xeb, 0xa6, 0x5e, 0x73, 0x9b, 0x8d, 0xaa, 0x59, 0x7c, 0xe8, 0xa6, - 0x42, 0x1d, 0x5e, 0x87, 0x17, 0x92, 0x24, 0xa1, 0x5a, 0x64, 0x07, 0xb6, 0x92, 0xd9, 0x02, 0x0e, - 0x31, 0xbd, 0xb0, 0x40, 0x4e, 0xcc, 0x16, 0xde, 0x82, 0x75, 0x01, 0xfd, 0xe7, 0x54, 0x6d, 0x04, - 0x17, 0x5e, 0x87, 0xfc, 0xa1, 0x61, 0x99, 0xe5, 0x87, 0x6e, 0xb9, 0x55, 0xad, 0xaa, 0x97, 0xc8, - 0x2a, 0x2c, 0xf3, 0x84, 0xa2, 0xae, 0x2a, 0x64, 0x05, 0x96, 0xcc, 0xba, 0x6d, 0x14, 0x5b, 0x96, - 0xa1, 0x66, 0x0a, 0x6f, 0xc1, 0x5a, 0x73, 0xd4, 0x7b, 0xd4, 0x9e, 0x78, 0x07, 0xde, 0x63, 0xd4, - 0x6a, 0x5c, 0x86, 0xac, 0xa5, 0x1f, 0xa9, 0x97, 0x08, 0xc0, 0x62, 0xf3, 0xa0, 0x68, 0xdf, 0xbb, - 0xa7, 0x2a, 0x24, 0x0f, 0x97, 0xf7, 0x8b, 0x4d, 0xf7, 0xa0, 0x66, 0xab, 0x19, 0xfa, 0xa0, 0x1f, - 0xd9, 0xf8, 0x90, 0x2d, 0x7c, 0x19, 0x36, 0x90, 0x21, 0xa9, 0xf6, 0xc6, 0x13, 0xcf, 0xf7, 0x46, - 0xd8, 0x86, 0x15, 0x58, 0xb2, 0x3d, 0xba, 0x93, 0x4c, 0x3c, 0xd6, 0x80, 0xda, 0xb4, 0x3f, 0xe9, - 0x0d, 0xfb, 0xde, 0x77, 0x54, 0xa5, 0xf0, 0x00, 0xd6, 0xad, 0xc1, 0x74, 0xd2, 0xf3, 0x4f, 0xec, - 0x09, 0xa5, 0x38, 0x79, 0x4c, 0x9e, 0x87, 0x8d, 0x56, 0x5d, 0xaf, 0xed, 0x99, 0xfb, 0xad, 0x46, - 0xcb, 0x76, 0x6b, 0xba, 0x53, 0xac, 0x30, 0x9d, 0x4a, 0xad, 0x61, 0x3b, 0xae, 0x65, 0x14, 0x8d, - 0xba, 0xa3, 0x2a, 0x85, 0x3f, 0x52, 0x60, 0xad, 0x35, 0xe6, 0x76, 0xc0, 0x2d, 0xf4, 0xe6, 0x7b, - 0x11, 0x76, 0x5a, 0xb6, 0x61, 0xb9, 0x4e, 0xe3, 0xc0, 0xa8, 0xbb, 0x2d, 0x5b, 0xdf, 0x8f, 0xe3, - 0x6c, 0xde, 0x80, 0x6b, 0x12, 0x85, 0x65, 0x14, 0x1b, 0x87, 0x86, 0xe5, 0x36, 0x75, 0xdb, 0x3e, - 0x6a, 0x58, 0x25, 0x55, 0x21, 0xdb, 0x70, 0x25, 0x85, 0xa0, 0x56, 0xd6, 0xd5, 0x4c, 0x22, 0xaf, - 0x6e, 0x1c, 0xe9, 0x55, 0x77, 0xaf, 0xe1, 0xa8, 0xd9, 0x42, 0x8d, 0x9e, 0xa6, 0x88, 0x2f, 0xc7, - 0x02, 0x09, 0x2c, 0x41, 0xae, 0xde, 0xa8, 0x1b, 0x71, 0xbd, 0xd7, 0x0a, 0x2c, 0xe9, 0xcd, 0xa6, - 0xd5, 0x38, 0xc4, 0x01, 0x05, 0x58, 0x2c, 0x19, 0x75, 0xda, 0xb2, 0x2c, 0xcd, 0x69, 0x5a, 0x8d, - 0x5a, 0xc3, 0x31, 0x4a, 0x6a, 0xae, 0x60, 0x89, 0x05, 0x23, 0x2a, 0xed, 0x0c, 0x98, 0x92, 0xa9, - 0x64, 0x94, 0xf5, 0x56, 0xd5, 0xe1, 0x1d, 0xf2, 0xd0, 0xb5, 0x8c, 0xaf, 0xb6, 0x0c, 0xdb, 0xb1, - 0x55, 0x85, 0xa8, 0xb0, 0x52, 0x37, 0x8c, 0x92, 0xed, 0x5a, 0xc6, 0xa1, 0x69, 0x1c, 0xa9, 0x19, - 0x5a, 0x27, 0xfb, 0x4f, 0xdf, 0x50, 0xf8, 0x58, 0x01, 0xc2, 0xb0, 0xf9, 0x04, 0xe0, 0x3b, 0x8e, - 0xcf, 0x2e, 0x6c, 0x57, 0x68, 0xc7, 0xe2, 0xa7, 0xd5, 0x1a, 0xa5, 0x78, 0x97, 0x5d, 0x01, 0x12, - 0xcb, 0x6f, 0x94, 0xcb, 0xaa, 0x42, 0xae, 0xc1, 0x73, 0xb1, 0xf4, 0x92, 0xd5, 0x68, 0xaa, 0x99, - 0xed, 0xcc, 0x92, 0x42, 0xae, 0x26, 0x32, 0x0f, 0x0c, 0xa3, 0xa9, 0x66, 0xe9, 0x10, 0xc5, 0x32, - 0xc4, 0x04, 0x64, 0xc5, 0x73, 0x85, 0xef, 0x2a, 0x70, 0x85, 0x35, 0x53, 0xcc, 0xe6, 0xa0, 0xa9, - 0x3b, 0xb0, 0xc5, 0x11, 0x47, 0xd3, 0x1a, 0xba, 0x09, 0x6a, 0x24, 0x97, 0x35, 0xf3, 0x79, 0xd8, - 0x88, 0xa4, 0x62, 0x3b, 0x32, 0x74, 0xad, 0x46, 0x92, 0xf7, 0x0c, 0xdb, 0x71, 0x8d, 0x72, 0xb9, - 0x61, 0x39, 0xac, 0x21, 0xd9, 0x82, 0x06, 0x1b, 0x45, 0x6f, 0x34, 0xa1, 0x82, 0x8e, 0x3f, 0xee, - 0x0d, 0x7c, 0x6c, 0xc2, 0x2a, 0x2c, 0x1b, 0x5f, 0x73, 0x8c, 0xba, 0x6d, 0x36, 0xea, 0xea, 0xa5, - 0xc2, 0x4e, 0x8c, 0x46, 0xac, 0x1a, 0xdb, 0xae, 0xa8, 0x97, 0x0a, 0x6d, 0x58, 0x15, 0x76, 0xb7, - 0x6c, 0x56, 0xec, 0xc2, 0xb6, 0x98, 0x6b, 0xb8, 0x7e, 0xe3, 0x9f, 0xb0, 0x05, 0x9b, 0xc9, 0x7c, - 0xc3, 0x51, 0x15, 0x3a, 0x0a, 0xb1, 0x1c, 0x9a, 0x9e, 0x29, 0xfc, 0xbe, 0x02, 0xab, 0x81, 0xc2, - 0x03, 0xaf, 0x6b, 0x6f, 0xc0, 0xb5, 0x5a, 0x59, 0x77, 0x4b, 0xc6, 0xa1, 0x59, 0x34, 0xdc, 0x03, - 0xb3, 0x5e, 0x8a, 0xbd, 0xe4, 0x05, 0x78, 0x3e, 0x85, 0x00, 0xdf, 0xb2, 0x05, 0x9b, 0xf1, 0x2c, - 0xa7, 0xe1, 0xd0, 0xfe, 0xda, 0x81, 0xad, 0x78, 0xce, 0x91, 0xb1, 0xa7, 0xb7, 0x9c, 0x4a, 0x5d, - 0xcd, 0x16, 0xbe, 0x0e, 0x2b, 0x91, 0xf8, 0x2c, 0x57, 0xe1, 0x39, 0xf9, 0xb9, 0xe9, 0xf9, 0xdd, - 0x9e, 0x7f, 0xa2, 0x5e, 0x8a, 0x67, 0x58, 0x53, 0xdf, 0xa7, 0x19, 0xb8, 0xee, 0xe4, 0x0c, 0xc7, - 0x1b, 0x9d, 0xf5, 0xfc, 0xf6, 0xc4, 0xeb, 0xaa, 0x99, 0xc2, 0xcb, 0xb0, 0x1a, 0x41, 0x85, 0xa4, - 0x1d, 0x5c, 0x6d, 0xf0, 0x6d, 0xa9, 0x66, 0x94, 0xcc, 0x56, 0x4d, 0x5d, 0xa0, 0x2b, 0xae, 0x62, - 0xee, 0x57, 0x54, 0x28, 0x7c, 0x5f, 0xa1, 0xdc, 0x37, 0x62, 0xbd, 0xd7, 0xca, 0xba, 0x18, 0x12, - 0x3a, 0x1d, 0x18, 0xd6, 0xac, 0x61, 0xdb, 0x4c, 0x2d, 0xbb, 0x03, 0x5b, 0xfc, 0xc1, 0xd5, 0xeb, - 0x25, 0xb7, 0xa2, 0x5b, 0xa5, 0x23, 0xdd, 0xa2, 0x73, 0xe4, 0xa1, 0x9a, 0xc1, 0x89, 0x2f, 0xa5, - 0xb8, 0x4e, 0xa3, 0x55, 0xac, 0xa8, 0x59, 0x3a, 0xcf, 0x22, 0xe9, 0x4d, 0xb3, 0xae, 0xe6, 0x70, - 0x19, 0x25, 0xa8, 0xb1, 0x5a, 0x9a, 0xbf, 0x50, 0xf8, 0x4b, 0x05, 0xb6, 0x66, 0xc1, 0x1f, 0x90, - 0xdb, 0xa0, 0x19, 0x75, 0xc7, 0xd2, 0xcd, 0x92, 0x5b, 0xb4, 0x8c, 0x92, 0x51, 0x77, 0x4c, 0xbd, - 0x6a, 0xbb, 0x76, 0xa3, 0x65, 0x15, 0xe9, 0x1c, 0x10, 0x8a, 0xe5, 0x9b, 0x70, 0x63, 0x0e, 0x5d, - 0xc3, 0x2c, 0x15, 0x55, 0x85, 0xdc, 0x83, 0x97, 0xe6, 0x10, 0xd9, 0x0f, 0x6d, 0xc7, 0xa8, 0xc9, - 0x39, 0x6a, 0xa6, 0xd0, 0x03, 0x35, 0xee, 0x45, 0x9d, 0x50, 0xde, 0x5b, 0xad, 0x7a, 0x9d, 0x6d, - 0x62, 0xeb, 0x90, 0x6f, 0x38, 0x15, 0xc3, 0xe2, 0x50, 0xc5, 0x88, 0x4d, 0xdc, 0xaa, 0xd3, 0xb9, - 0xd0, 0xb0, 0xcc, 0x77, 0x71, 0x37, 0xdb, 0x82, 0x4d, 0xbb, 0xaa, 0x17, 0x0f, 0xdc, 0x7a, 0xc3, - 0x71, 0xcd, 0xba, 0x5b, 0xac, 0xe8, 0xf5, 0xba, 0x51, 0x55, 0x01, 0xfb, 0x61, 0x96, 0xbb, 0x15, - 0xf9, 0x22, 0xdc, 0x69, 0x1c, 0x38, 0xba, 0xdb, 0xac, 0xb6, 0xf6, 0xcd, 0xba, 0x6b, 0x3f, 0xac, - 0x17, 0xc5, 0x39, 0x57, 0x4c, 0x2e, 0xf8, 0x3b, 0x70, 0x6b, 0x2e, 0x75, 0x08, 0x2a, 0x7c, 0x1b, - 0xb4, 0xb9, 0x94, 0xfc, 0x43, 0x0a, 0x3f, 0x52, 0xe0, 0xda, 0x1c, 0xed, 0x23, 0x79, 0x09, 0xee, - 0x56, 0x0c, 0xbd, 0x54, 0x35, 0x6c, 0xdb, 0xa5, 0xdf, 0x4b, 0x7b, 0x90, 0x29, 0xf9, 0x53, 0x97, - 0xf3, 0x5d, 0xf8, 0xfc, 0x7c, 0xf2, 0xf0, 0x60, 0xb8, 0x03, 0xb7, 0xe6, 0x93, 0xf2, 0x83, 0x22, - 0x43, 0x0a, 0x70, 0x7b, 0x3e, 0x65, 0x70, 0xc0, 0x64, 0x0b, 0xdf, 0x53, 0xe0, 0x4a, 0xba, 0xd0, - 0x4e, 0xdb, 0x66, 0xd6, 0x6d, 0x47, 0xaf, 0x56, 0xdd, 0xa6, 0x6e, 0xe9, 0x35, 0xd7, 0xa8, 0x5b, - 0x8d, 0x6a, 0x35, 0x6d, 0x63, 0xbd, 0x05, 0x2f, 0xce, 0x26, 0xb5, 0x8b, 0x96, 0xd9, 0xa4, 0x7b, - 0x87, 0x06, 0xbb, 0xb3, 0xa9, 0x0c, 0xb3, 0x68, 0xa8, 0x99, 0xbd, 0x37, 0x7f, 0xf0, 0xef, 0xbb, - 0x97, 0x7e, 0xf0, 0xe9, 0xae, 0xf2, 0xe3, 0x4f, 0x77, 0x95, 0x7f, 0xfb, 0x74, 0x57, 0x79, 0xf7, - 0x0b, 0x17, 0x08, 0xdb, 0xfe, 0xfe, 0x22, 0x72, 0xa3, 0xf7, 0xff, 0x37, 0x00, 0x00, 0xff, 0xff, - 0x02, 0xfd, 0xc5, 0xd9, 0xc4, 0x9f, 0x01, 0x00, + 0xed, 0x74, 0x77, 0xf5, 0x55, 0x55, 0x63, 0x30, 0x77, 0x92, 0xc5, 0xd7, 0xe9, 0xcc, 0x90, 0xf9, + 0xb2, 0x48, 0x93, 0x72, 0x50, 0x0c, 0x06, 0x6d, 0xc9, 0xb4, 0x1c, 0x62, 0xd8, 0xa4, 0x15, 0x96, + 0x82, 0x61, 0x8a, 0x74, 0x30, 0x68, 0x5a, 0x8a, 0xb0, 0x18, 0x61, 0xf9, 0x75, 0x66, 0x0c, 0x4d, + 0x53, 0x3f, 0x1c, 0x13, 0xe1, 0x08, 0xca, 0x0a, 0xdb, 0xe1, 0x75, 0x50, 0x72, 0xe4, 0x97, 0x99, + 0x55, 0x99, 0x55, 0xd5, 0x3d, 0x3d, 0xbb, 0x58, 0x49, 0xd8, 0xd0, 0x1f, 0x60, 0xfa, 0xcb, 0xef, + 0xfb, 0xb2, 0xf2, 0xfd, 0xe5, 0x97, 0xdf, 0x03, 0x5e, 0x08, 0x69, 0x8b, 0x76, 0x3d, 0x3f, 0xbc, + 0xd6, 0xa2, 0x3b, 0x76, 0xf3, 0xe0, 0x5a, 0x78, 0xd0, 0xa5, 0x01, 0xff, 0xf7, 0x6a, 0xd7, 0xf7, + 0x42, 0x8f, 0x8c, 0xe2, 0x8f, 0xf3, 0xa7, 0x76, 0xbc, 0x1d, 0x0f, 0x21, 0xd7, 0xd8, 0x5f, 0xbc, + 0xf0, 0xfc, 0xf3, 0x3b, 0x9e, 0xb7, 0xd3, 0xa2, 0xd7, 0xf0, 0xd7, 0x56, 0x6f, 0xfb, 0x9a, 0xd3, + 0xf3, 0xed, 0xd0, 0xf5, 0x3a, 0xa2, 0xbc, 0x9c, 0x2c, 0x0f, 0xdd, 0x36, 0x0d, 0x42, 0xbb, 0xdd, + 0x15, 0x08, 0x57, 0xa2, 0x0f, 0xb0, 0xc3, 0x90, 0x95, 0x30, 0xe2, 0x6b, 0x8f, 0xae, 0xab, 0x3f, + 0x05, 0xea, 0xcd, 0x3e, 0xdf, 0xea, 0xf7, 0x82, 0x90, 0x3a, 0x96, 0x43, 0x1f, 0xb9, 0x4d, 0x6a, + 0xf9, 0xf4, 0xdb, 0x3d, 0xd7, 0xa7, 0x6d, 0xda, 0x09, 0x05, 0xdd, 0x1b, 0xd9, 0x74, 0xfb, 0xbe, + 0xdd, 0xed, 0x52, 0x3f, 0xfe, 0x83, 0xa3, 0x1b, 0xbf, 0x5c, 0x80, 0x89, 0xbb, 0x94, 0x76, 0x2b, + 0x2d, 0xf7, 0x11, 0x25, 0x2f, 0xc2, 0xc8, 0x9a, 0xdd, 0xa6, 0x0b, 0xb9, 0x8b, 0xb9, 0xcb, 0x13, + 0x4b, 0xb3, 0x47, 0x87, 0xe5, 0xc9, 0x80, 0xfa, 0x8f, 0xa8, 0x6f, 0x75, 0xec, 0x36, 0x35, 0xb1, + 0x90, 0xbc, 0x06, 0x13, 0xec, 0xff, 0xa0, 0x6b, 0x37, 0xe9, 0x42, 0x1e, 0x31, 0xa7, 0x8f, 0x0e, + 0xcb, 0x13, 0x1d, 0x09, 0x34, 0xe3, 0x72, 0x52, 0x87, 0xf1, 0xe5, 0xc7, 0x5d, 0xd7, 0xa7, 0xc1, + 0xc2, 0xc8, 0xc5, 0xdc, 0xe5, 0xc9, 0xc5, 0xf3, 0x57, 0x79, 0x27, 0x5d, 0x95, 0x9d, 0x74, 0x75, + 0x53, 0x76, 0xd2, 0xd2, 0xfc, 0x1f, 0x1c, 0x96, 0x9f, 0x39, 0x3a, 0x2c, 0x8f, 0x53, 0x4e, 0xf2, + 0x33, 0x7f, 0x5c, 0xce, 0x99, 0x92, 0x9e, 0xbc, 0x03, 0x23, 0x9b, 0x07, 0x5d, 0xba, 0x30, 0x71, + 0x31, 0x77, 0x79, 0x66, 0xf1, 0xf9, 0xab, 0x7c, 0xd8, 0xa2, 0x8f, 0x8f, 0xff, 0x62, 0x58, 0x4b, + 0xc5, 0xa3, 0xc3, 0xf2, 0x08, 0x43, 0x31, 0x91, 0x8a, 0xbc, 0x01, 0x63, 0x2b, 0x5e, 0x10, 0xd6, + 0x6b, 0x0b, 0x80, 0x9f, 0x7c, 0xfa, 0xe8, 0xb0, 0x3c, 0xb7, 0xeb, 0x05, 0xa1, 0xe5, 0x3a, 0xaf, + 0x7b, 0x6d, 0x37, 0xa4, 0xed, 0x6e, 0x78, 0x60, 0x0a, 0x24, 0xe3, 0x31, 0x4c, 0x6b, 0xfc, 0xc8, + 0x24, 0x8c, 0xdf, 0x5f, 0xbb, 0xbb, 0xb6, 0xfe, 0x70, 0xad, 0xf4, 0x0c, 0x29, 0xc2, 0xc8, 0xda, + 0x7a, 0x6d, 0xb9, 0x94, 0x23, 0xe3, 0x50, 0xa8, 0x6c, 0x6c, 0x94, 0xf2, 0x64, 0x0a, 0x8a, 0xb5, + 0xca, 0x66, 0x65, 0xa9, 0xd2, 0x58, 0x2e, 0x15, 0xc8, 0x3c, 0xcc, 0x3e, 0xac, 0xaf, 0xd5, 0xd6, + 0x1f, 0x36, 0xac, 0xda, 0x72, 0xe3, 0xee, 0xe6, 0xfa, 0x46, 0x69, 0x84, 0xcc, 0x00, 0xdc, 0xbd, + 0xbf, 0xb4, 0x6c, 0xae, 0x2d, 0x6f, 0x2e, 0x37, 0x4a, 0xa3, 0xe4, 0x14, 0x94, 0x24, 0x89, 0xd5, + 0x58, 0x36, 0x1f, 0xd4, 0xab, 0xcb, 0xa5, 0xb1, 0x3b, 0x23, 0xc5, 0x42, 0x69, 0xc4, 0x1c, 0x5f, + 0xa5, 0x76, 0x40, 0xeb, 0x35, 0xe3, 0x3f, 0x28, 0x40, 0xf1, 0x1e, 0x0d, 0x6d, 0xc7, 0x0e, 0x6d, + 0xf2, 0x9c, 0x36, 0x3e, 0xd8, 0x44, 0x65, 0x60, 0x5e, 0x4c, 0x0f, 0xcc, 0xe8, 0xd1, 0x61, 0x39, + 0xf7, 0x86, 0x3a, 0x20, 0x6f, 0xc3, 0x64, 0x8d, 0x06, 0x4d, 0xdf, 0xed, 0xb2, 0xc9, 0xb6, 0x50, + 0x40, 0xb4, 0x73, 0x47, 0x87, 0xe5, 0xd3, 0x4e, 0x0c, 0x56, 0x3a, 0x44, 0xc5, 0x26, 0x75, 0x18, + 0x5b, 0xb5, 0xb7, 0x68, 0x2b, 0x58, 0x18, 0xbd, 0x58, 0xb8, 0x3c, 0xb9, 0xf8, 0xac, 0x18, 0x04, + 0xf9, 0x81, 0x57, 0x79, 0xe9, 0x72, 0x27, 0xf4, 0x0f, 0x96, 0x4e, 0x1d, 0x1d, 0x96, 0x4b, 0x2d, + 0x04, 0xa8, 0x1d, 0xcc, 0x51, 0x48, 0x23, 0x9e, 0x18, 0x63, 0xc7, 0x4e, 0x8c, 0x0b, 0x7f, 0x70, + 0x58, 0xce, 0xb1, 0x01, 0x13, 0x13, 0x23, 0xe6, 0xa7, 0x4f, 0x91, 0x45, 0x28, 0x9a, 0xf4, 0x91, + 0x1b, 0xb0, 0x96, 0x15, 0xb1, 0x65, 0x67, 0x8e, 0x0e, 0xcb, 0xc4, 0x17, 0x30, 0xe5, 0x33, 0x22, + 0xbc, 0xf3, 0x6f, 0xc1, 0xa4, 0xf2, 0xd5, 0xa4, 0x04, 0x85, 0x3d, 0x7a, 0xc0, 0x7b, 0xd8, 0x64, + 0x7f, 0x92, 0x53, 0x30, 0xfa, 0xc8, 0x6e, 0xf5, 0x44, 0x97, 0x9a, 0xfc, 0xc7, 0x97, 0xf2, 0x5f, + 0xcc, 0xdd, 0x19, 0x29, 0x8e, 0x97, 0x8a, 0x66, 0xbe, 0x5e, 0x33, 0xfe, 0xdd, 0x11, 0x28, 0x9a, + 0x1e, 0x5f, 0xc0, 0xe4, 0x0a, 0x8c, 0x36, 0x42, 0x3b, 0x94, 0xc3, 0x34, 0x7f, 0x74, 0x58, 0x9e, + 0x65, 0x8b, 0x9b, 0x2a, 0xf5, 0x73, 0x0c, 0x86, 0xba, 0xb1, 0x6b, 0x07, 0x72, 0xb8, 0x10, 0xb5, + 0xcb, 0x00, 0x2a, 0x2a, 0x62, 0x90, 0x4b, 0x30, 0x72, 0xcf, 0x73, 0xa8, 0x18, 0x31, 0x72, 0x74, + 0x58, 0x9e, 0x69, 0x7b, 0x8e, 0x8a, 0x88, 0xe5, 0xe4, 0x75, 0x98, 0xa8, 0xf6, 0x7c, 0x9f, 0x76, + 0xd8, 0x5c, 0x1f, 0x41, 0xe4, 0x99, 0xa3, 0xc3, 0x32, 0x34, 0x39, 0xd0, 0x72, 0x1d, 0x33, 0x46, + 0x60, 0xc3, 0xd0, 0x08, 0x6d, 0x3f, 0xa4, 0xce, 0xc2, 0xe8, 0x50, 0xc3, 0xc0, 0xd6, 0xe7, 0x5c, + 0xc0, 0x49, 0x92, 0xc3, 0x20, 0x38, 0x91, 0x15, 0x98, 0xbc, 0xed, 0xdb, 0x4d, 0xba, 0x41, 0x7d, + 0xd7, 0x73, 0x70, 0x7c, 0x0b, 0x4b, 0x97, 0x8e, 0x0e, 0xcb, 0x67, 0x76, 0x18, 0xd8, 0xea, 0x22, + 0x3c, 0xa6, 0xfe, 0xe8, 0xb0, 0x5c, 0xac, 0x89, 0xad, 0xd4, 0x54, 0x49, 0xc9, 0xb7, 0xd8, 0xe0, + 0x04, 0x21, 0x76, 0x2d, 0x75, 0x16, 0xc6, 0x8f, 0xfd, 0x44, 0x43, 0x7c, 0xe2, 0x99, 0x96, 0x1d, + 0x84, 0x96, 0xcf, 0xe9, 0x12, 0xdf, 0xa9, 0xb2, 0x24, 0xeb, 0x50, 0x6c, 0x34, 0x77, 0xa9, 0xd3, + 0x6b, 0x51, 0x9c, 0x32, 0x93, 0x8b, 0x67, 0xc5, 0xa4, 0x96, 0xe3, 0x29, 0x8b, 0x97, 0xce, 0x0b, + 0xde, 0x24, 0x10, 0x10, 0x75, 0x3e, 0x49, 0xac, 0x2f, 0x15, 0x7f, 0xf1, 0x57, 0xca, 0xcf, 0xfc, + 0xf0, 0x1f, 0x5d, 0x7c, 0xc6, 0xf8, 0x2f, 0xf2, 0x50, 0x4a, 0x32, 0x21, 0xdb, 0x30, 0x7d, 0xbf, + 0xeb, 0xd8, 0x21, 0xad, 0xb6, 0x5c, 0xda, 0x09, 0x03, 0x9c, 0x24, 0x83, 0xdb, 0xf4, 0x92, 0xa8, + 0x77, 0xa1, 0x87, 0x84, 0x56, 0x93, 0x53, 0x26, 0x5a, 0xa5, 0xb3, 0x8d, 0xeb, 0x69, 0xe0, 0x06, + 0x1e, 0xe0, 0x0c, 0x3b, 0x59, 0x3d, 0x7c, 0xeb, 0xef, 0x53, 0x8f, 0x60, 0x2b, 0x26, 0x50, 0xc7, + 0xd9, 0x3a, 0xc0, 0x99, 0x39, 0xfc, 0x04, 0x62, 0x24, 0x19, 0x13, 0x88, 0x81, 0x8d, 0x7f, 0x92, + 0x83, 0x19, 0x93, 0x06, 0x5e, 0xcf, 0x6f, 0xd2, 0x15, 0x6a, 0x3b, 0xd4, 0x67, 0xd3, 0xff, 0xae, + 0xdb, 0x71, 0xc4, 0x9a, 0xc2, 0xe9, 0xbf, 0xe7, 0x76, 0xd4, 0xad, 0x1b, 0xcb, 0xc9, 0xe7, 0x60, + 0xbc, 0xd1, 0xdb, 0x42, 0xd4, 0x7c, 0xbc, 0x03, 0x04, 0xbd, 0x2d, 0x2b, 0x81, 0x2e, 0xd1, 0xc8, + 0x35, 0x18, 0x7f, 0x40, 0xfd, 0x20, 0xde, 0x0d, 0xf1, 0x68, 0x78, 0xc4, 0x41, 0x2a, 0x81, 0xc0, + 0x22, 0xb7, 0xe3, 0x1d, 0x59, 0x1c, 0x6a, 0xb3, 0x89, 0x7d, 0x30, 0x9e, 0x2a, 0x6d, 0x01, 0x51, + 0xa7, 0x8a, 0xc4, 0x32, 0x7e, 0x36, 0x0f, 0xa5, 0x9a, 0x1d, 0xda, 0x5b, 0x76, 0x20, 0xfa, 0xf3, + 0xc1, 0x0d, 0xb6, 0xc7, 0x2b, 0x0d, 0xc5, 0x3d, 0x9e, 0x7d, 0xf9, 0xc7, 0x6e, 0xde, 0xcb, 0xc9, + 0xe6, 0x4d, 0xb2, 0x13, 0x56, 0x34, 0x2f, 0x6e, 0xd4, 0xbb, 0xc7, 0x37, 0xaa, 0x24, 0x1a, 0x55, + 0x94, 0x8d, 0x8a, 0x9b, 0x42, 0xde, 0x85, 0x91, 0x46, 0x97, 0x36, 0xc5, 0x26, 0x22, 0xcf, 0x05, + 0xbd, 0x71, 0x0c, 0xe1, 0xc1, 0x8d, 0xa5, 0x29, 0xc1, 0x66, 0x24, 0xe8, 0xd2, 0xa6, 0x89, 0x64, + 0xca, 0xa2, 0xf9, 0xbb, 0x05, 0x38, 0x95, 0x45, 0xa6, 0xb6, 0x63, 0x6c, 0x40, 0x3b, 0x2e, 0x43, + 0x91, 0x1d, 0xe1, 0xec, 0x58, 0xc4, 0xed, 0x62, 0x62, 0x69, 0x8a, 0x7d, 0xf2, 0xae, 0x80, 0x99, + 0x51, 0x29, 0x79, 0x31, 0x92, 0x08, 0x8a, 0x31, 0x3f, 0x21, 0x11, 0x48, 0x39, 0x80, 0x8d, 0xb5, + 0x5c, 0xc2, 0x28, 0x38, 0xc4, 0xdd, 0x22, 0xc1, 0xf1, 0x58, 0xfb, 0x02, 0xa2, 0x1d, 0x33, 0xf2, + 0x50, 0x58, 0x86, 0xa2, 0x6c, 0xd6, 0xc2, 0x14, 0x32, 0x9a, 0x4b, 0x74, 0xd2, 0x83, 0x1b, 0x7c, + 0x30, 0x1d, 0xf1, 0x5b, 0x65, 0x23, 0x71, 0xc8, 0x0d, 0x28, 0x6e, 0xf8, 0xde, 0xe3, 0x83, 0x7a, + 0x2d, 0x58, 0x98, 0xbe, 0x58, 0xb8, 0x3c, 0xb1, 0x74, 0xf6, 0xe8, 0xb0, 0x3c, 0xdf, 0x65, 0x30, + 0xcb, 0x75, 0xd4, 0x93, 0x36, 0x42, 0xbc, 0x33, 0x52, 0xcc, 0x95, 0xf2, 0x77, 0x46, 0x8a, 0xf9, + 0x52, 0x81, 0x8b, 0x17, 0x77, 0x46, 0x8a, 0x23, 0xa5, 0xd1, 0x3b, 0x23, 0xc5, 0x51, 0x14, 0x38, + 0x26, 0x4a, 0x70, 0x67, 0xa4, 0x38, 0x59, 0x9a, 0xd2, 0x4e, 0x7b, 0x64, 0x10, 0x7a, 0x4d, 0xaf, + 0x65, 0x16, 0xee, 0x9b, 0x75, 0x73, 0xac, 0x5a, 0xa9, 0x52, 0x3f, 0x34, 0x0b, 0x95, 0x87, 0x0d, + 0x73, 0xba, 0x76, 0xd0, 0xb1, 0xdb, 0x6e, 0x93, 0x1f, 0x9d, 0x66, 0xe1, 0x76, 0x75, 0xc3, 0xa8, + 0xc0, 0x4c, 0xdc, 0x96, 0x55, 0x37, 0x08, 0xc9, 0x35, 0x98, 0x90, 0x10, 0xb6, 0xd1, 0x15, 0x32, + 0x5b, 0x6d, 0xc6, 0x38, 0xc6, 0xef, 0xe7, 0x01, 0xe2, 0x92, 0xa7, 0x74, 0x2d, 0x7c, 0x41, 0x5b, + 0x0b, 0xa7, 0x93, 0x6b, 0xa1, 0xef, 0x2a, 0x20, 0xef, 0xc3, 0x18, 0x13, 0x0b, 0x7a, 0x52, 0x24, + 0x3a, 0x9b, 0x24, 0xc5, 0xc2, 0x07, 0x37, 0x96, 0x66, 0x04, 0xf1, 0x58, 0x80, 0x10, 0x53, 0x90, + 0x29, 0xcb, 0xe8, 0x97, 0xc7, 0xe3, 0xc1, 0x10, 0x0b, 0xe8, 0x32, 0x44, 0x03, 0x2a, 0x3a, 0x14, + 0x57, 0x46, 0x57, 0x0e, 0x72, 0x54, 0x4a, 0xce, 0x01, 0x1b, 0x70, 0xd1, 0xa9, 0xe3, 0x47, 0x87, + 0xe5, 0x42, 0xcf, 0x77, 0x71, 0x12, 0x90, 0x6b, 0x20, 0xa6, 0x81, 0xe8, 0x40, 0x36, 0xfb, 0xe6, + 0x9a, 0xb6, 0xd5, 0xa4, 0x7e, 0x18, 0xf7, 0xf8, 0x42, 0x4e, 0xce, 0x16, 0xd2, 0x05, 0x7d, 0xaa, + 0x2c, 0x8c, 0xe0, 0x34, 0xb8, 0x9c, 0xd9, 0x2b, 0x57, 0x35, 0x54, 0x2e, 0x46, 0x5e, 0x94, 0xa7, + 0x92, 0xc3, 0xcb, 0xac, 0x94, 0x48, 0xa9, 0x57, 0x40, 0x6e, 0x00, 0x9b, 0xa1, 0xa2, 0xf7, 0x41, + 0xd4, 0x53, 0x79, 0xd8, 0x58, 0x3a, 0x2d, 0x38, 0x4d, 0xdb, 0xfb, 0x2a, 0x39, 0xc3, 0x26, 0x6f, + 0x03, 0x9b, 0xc2, 0xa2, 0xdf, 0x89, 0x20, 0xba, 0x5d, 0xdd, 0xa8, 0xb6, 0xbc, 0x9e, 0xd3, 0xf8, + 0xca, 0x6a, 0x4c, 0xbc, 0xd3, 0xec, 0xaa, 0xc4, 0xb7, 0xab, 0x1b, 0xe4, 0x6d, 0x18, 0xad, 0x7c, + 0xa7, 0xe7, 0x53, 0x21, 0x9f, 0x4c, 0xc9, 0x3a, 0x19, 0x6c, 0xe9, 0xac, 0x20, 0x9c, 0xb5, 0xd9, + 0x4f, 0x55, 0xae, 0xc3, 0x72, 0x56, 0xf3, 0xe6, 0x6a, 0x43, 0xc8, 0x1e, 0x24, 0xd1, 0x2d, 0x9b, + 0xab, 0xca, 0x67, 0x87, 0x5a, 0xab, 0x19, 0x15, 0xb9, 0x06, 0xf9, 0x4a, 0x0d, 0x6f, 0x44, 0x93, + 0x8b, 0x13, 0xb2, 0xda, 0xda, 0xd2, 0x29, 0x41, 0x32, 0x65, 0xab, 0xcb, 0x20, 0x5f, 0xa9, 0x91, + 0x25, 0x18, 0xbd, 0x77, 0xd0, 0xf8, 0xca, 0xaa, 0xd8, 0xcc, 0xe6, 0xe5, 0xbc, 0x66, 0xb0, 0x75, + 0x5c, 0xf6, 0x41, 0xfc, 0xc5, 0xed, 0x83, 0xe0, 0xdb, 0x2d, 0xf5, 0x8b, 0x11, 0x8d, 0x6c, 0xc0, + 0x44, 0xc5, 0x69, 0xbb, 0x9d, 0xfb, 0x01, 0xf5, 0x17, 0x26, 0x91, 0xcf, 0x42, 0xe2, 0xbb, 0xa3, + 0xf2, 0xa5, 0x85, 0xa3, 0xc3, 0xf2, 0x29, 0x9b, 0xfd, 0xb4, 0x7a, 0x01, 0xf5, 0x15, 0x6e, 0x31, + 0x13, 0xb2, 0x01, 0x70, 0xcf, 0xeb, 0xec, 0x78, 0x95, 0xb0, 0x65, 0x07, 0x89, 0xed, 0x31, 0x2e, + 0x88, 0xc4, 0x87, 0xd3, 0x6d, 0x06, 0xb3, 0x6c, 0x06, 0x54, 0x18, 0x2a, 0x3c, 0xc8, 0x2d, 0x18, + 0x5b, 0xf7, 0xed, 0x66, 0x8b, 0x2e, 0x4c, 0x23, 0xb7, 0x53, 0x82, 0x1b, 0x07, 0xca, 0x96, 0x2e, + 0x08, 0x86, 0x25, 0x0f, 0xc1, 0xea, 0x35, 0x85, 0x23, 0x9e, 0x7f, 0x08, 0x24, 0x3d, 0x27, 0x33, + 0x2e, 0x09, 0xaf, 0xa9, 0x97, 0x84, 0x78, 0xd1, 0x57, 0xbd, 0x76, 0xdb, 0xee, 0x38, 0x48, 0xfb, + 0x60, 0x51, 0xb9, 0x3b, 0x18, 0xdf, 0x86, 0xb9, 0x54, 0x67, 0x1d, 0x73, 0xbf, 0x7b, 0x0f, 0x66, + 0x6b, 0x74, 0xdb, 0xee, 0xb5, 0xc2, 0xe8, 0x24, 0xe1, 0x4b, 0x14, 0x6f, 0x5a, 0x0e, 0x2f, 0xb2, + 0xe4, 0xf1, 0x61, 0x26, 0x91, 0x8d, 0x77, 0x61, 0x5a, 0x6b, 0x3e, 0xbb, 0x2a, 0x54, 0x7a, 0x8e, + 0x1b, 0xe2, 0x40, 0xe6, 0xe2, 0xab, 0x82, 0xcd, 0x80, 0x38, 0x5c, 0x66, 0x8c, 0x60, 0xfc, 0x87, + 0xaa, 0xb4, 0x22, 0x76, 0x22, 0x76, 0xad, 0x16, 0xfb, 0x41, 0x2e, 0x96, 0x9d, 0x52, 0xfb, 0x41, + 0xb4, 0x1b, 0x5c, 0xe1, 0x6b, 0x33, 0x9f, 0x5a, 0x9b, 0x93, 0x62, 0x24, 0x0a, 0xf6, 0x7e, 0xc0, + 0x57, 0x64, 0x34, 0x53, 0x0b, 0x1f, 0x7f, 0xa6, 0xbe, 0x0f, 0x53, 0xf7, 0xec, 0x8e, 0xbd, 0x43, + 0x1d, 0xd6, 0x02, 0xbe, 0xf7, 0x4c, 0x2c, 0x3d, 0x7b, 0x74, 0x58, 0x3e, 0xdb, 0xe6, 0x70, 0x6c, + 0xa5, 0x3a, 0x89, 0x34, 0x02, 0x72, 0x5d, 0xae, 0xec, 0xd1, 0x8c, 0x95, 0x3d, 0x2d, 0x6a, 0x1f, + 0xc5, 0x95, 0x2d, 0xd6, 0xb3, 0xf1, 0x3b, 0x13, 0xd8, 0x46, 0xf2, 0x3a, 0x8c, 0x99, 0x74, 0x87, + 0x1d, 0x35, 0xb9, 0x78, 0x90, 0x7c, 0x84, 0xa8, 0x1d, 0xc3, 0x71, 0x50, 0xce, 0xa0, 0x4e, 0xb0, + 0xeb, 0x6e, 0x87, 0xa2, 0x77, 0x22, 0x39, 0x43, 0x80, 0x15, 0x39, 0x43, 0x40, 0xf4, 0xeb, 0x2c, + 0x87, 0xb1, 0xdd, 0xcf, 0xac, 0x35, 0x44, 0xa7, 0xc9, 0x1e, 0x36, 0x6b, 0xca, 0x36, 0xe2, 0x6b, + 0x52, 0x02, 0xc3, 0x26, 0x37, 0x61, 0xa2, 0xd2, 0x6c, 0x7a, 0x3d, 0xe5, 0xce, 0xc8, 0xd7, 0x2d, + 0x07, 0xea, 0x2a, 0x92, 0x18, 0x95, 0x34, 0x60, 0x72, 0x99, 0x5d, 0xb4, 0xdc, 0xaa, 0xdd, 0xdc, + 0x95, 0x9d, 0x24, 0xf7, 0x30, 0xa5, 0x24, 0x5e, 0xb9, 0x14, 0x81, 0x4d, 0x06, 0x54, 0x95, 0x0c, + 0x0a, 0x2e, 0xd9, 0x84, 0xc9, 0x06, 0x6d, 0xfa, 0x34, 0x6c, 0x84, 0x9e, 0x4f, 0x13, 0x5b, 0xb2, + 0x52, 0xb2, 0xf4, 0xbc, 0xbc, 0xeb, 0x05, 0x08, 0xb4, 0x02, 0x06, 0x55, 0xb9, 0x2a, 0xc8, 0x5c, + 0x68, 0x6f, 0x7b, 0xfe, 0x41, 0x6d, 0x49, 0x6c, 0xd3, 0xf1, 0x99, 0xce, 0xc1, 0xaa, 0xd0, 0xce, + 0x20, 0xce, 0x96, 0x2e, 0xb4, 0x73, 0x2c, 0x1c, 0xa9, 0x5a, 0x03, 0x65, 0x2b, 0xb1, 0x69, 0xcf, + 0xc6, 0xbd, 0x8c, 0x60, 0x65, 0xa4, 0x9c, 0x00, 0x25, 0x33, 0x6d, 0xa4, 0x04, 0x16, 0xe9, 0x02, + 0x91, 0xa3, 0xc6, 0x05, 0xdd, 0x16, 0x0d, 0x02, 0xb1, 0x97, 0x9f, 0x4b, 0x0c, 0x7e, 0x8c, 0xb0, + 0xf4, 0xb2, 0x60, 0x7e, 0x41, 0x4e, 0x03, 0x71, 0x4f, 0x63, 0x85, 0x4a, 0x3d, 0x19, 0xbc, 0xc9, + 0x5b, 0x00, 0xcb, 0x8f, 0x43, 0xea, 0x77, 0xec, 0x56, 0xa4, 0x07, 0x43, 0xd5, 0x0f, 0x15, 0x50, + 0x7d, 0xa0, 0x15, 0x64, 0x52, 0x85, 0xe9, 0x4a, 0x10, 0xf4, 0xda, 0xd4, 0xf4, 0x5a, 0xb4, 0x62, + 0xae, 0xe1, 0xbe, 0x3f, 0xb1, 0x74, 0xe1, 0xe8, 0xb0, 0x7c, 0xce, 0xc6, 0x02, 0xcb, 0xf7, 0x5a, + 0xd4, 0xb2, 0x7d, 0x75, 0x76, 0xeb, 0x34, 0x64, 0x1d, 0x60, 0xbd, 0x4b, 0x3b, 0x0d, 0x6a, 0xfb, + 0xcd, 0xdd, 0xc4, 0x36, 0x1f, 0x17, 0x2c, 0x3d, 0x27, 0x5a, 0x78, 0xca, 0xeb, 0xd2, 0x4e, 0x80, + 0x30, 0xf5, 0xab, 0x62, 0x4c, 0xf2, 0x10, 0x66, 0xeb, 0x95, 0x7b, 0x1b, 0x5e, 0xcb, 0x6d, 0x1e, + 0x08, 0xc9, 0x69, 0x06, 0xb5, 0x83, 0x67, 0x04, 0xd7, 0x44, 0x29, 0xdf, 0x9e, 0x5c, 0xbb, 0x6d, + 0x75, 0x11, 0x6a, 0x09, 0xf9, 0x29, 0xc9, 0x85, 0x7c, 0xc0, 0xe6, 0x60, 0xc0, 0x84, 0xc1, 0x4d, + 0x7b, 0x27, 0x58, 0x98, 0xd5, 0xb4, 0x5d, 0x95, 0x87, 0x8d, 0xab, 0x4a, 0x29, 0x17, 0x53, 0xce, + 0xf3, 0x89, 0x88, 0x50, 0x2b, 0xb4, 0x77, 0x02, 0x7d, 0x22, 0x46, 0xd8, 0xe4, 0x0e, 0x40, 0xcd, + 0x6b, 0xf6, 0xda, 0xb4, 0x13, 0xd6, 0x96, 0x16, 0x4a, 0xfa, 0x55, 0x20, 0x2a, 0x88, 0xb7, 0x36, + 0xc7, 0x6b, 0x6a, 0x33, 0x51, 0xa1, 0x3e, 0xff, 0x1e, 0x94, 0x92, 0x1f, 0x72, 0x42, 0x05, 0xd6, + 0x74, 0x69, 0x46, 0x69, 0xfd, 0xf2, 0x63, 0x37, 0x08, 0x03, 0xe3, 0xbb, 0xda, 0x0a, 0x64, 0xbb, + 0xc3, 0x5d, 0x7a, 0xb0, 0xe1, 0xd3, 0x6d, 0xf7, 0xb1, 0xd8, 0xcc, 0x70, 0x77, 0xd8, 0xa3, 0x07, + 0x56, 0x17, 0xa1, 0xea, 0xee, 0x10, 0xa1, 0x92, 0xcf, 0x43, 0xf1, 0xee, 0xbd, 0xc6, 0x5d, 0x7a, + 0x50, 0xaf, 0x89, 0x83, 0x8a, 0x93, 0xb5, 0x03, 0x8b, 0x91, 0x6a, 0x73, 0x2d, 0xc2, 0x34, 0x96, + 0xe2, 0x9d, 0x90, 0xd5, 0x5c, 0x6d, 0xf5, 0x82, 0x90, 0xfa, 0xf5, 0x9a, 0x5a, 0x73, 0x93, 0x03, + 0x13, 0xfb, 0x52, 0x84, 0x6a, 0xfc, 0x8b, 0x3c, 0xee, 0x82, 0x6c, 0xc2, 0xd7, 0x3b, 0x41, 0x68, + 0x77, 0x9a, 0x34, 0x62, 0x80, 0x13, 0xde, 0x15, 0xd0, 0xc4, 0x84, 0x8f, 0x91, 0xf5, 0xaa, 0xf3, + 0x43, 0x57, 0xcd, 0xaa, 0x94, 0x9a, 0x8b, 0x7a, 0x4d, 0x55, 0xaf, 0xfa, 0x02, 0x9a, 0xa8, 0x32, + 0x46, 0x26, 0x97, 0x60, 0xbc, 0x5e, 0xb9, 0x57, 0xe9, 0x85, 0xbb, 0xb8, 0x07, 0x17, 0xb9, 0x7c, + 0xce, 0x66, 0xab, 0xdd, 0x0b, 0x77, 0x4d, 0x59, 0x48, 0xae, 0xe1, 0xbd, 0xa7, 0x43, 0x43, 0xae, + 0x86, 0x15, 0x87, 0x6e, 0xc0, 0x41, 0x89, 0x6b, 0x0f, 0x03, 0x91, 0x57, 0x61, 0xf4, 0xc1, 0x46, + 0xb5, 0x5e, 0x13, 0x17, 0x67, 0x3c, 0x89, 0x1e, 0x75, 0x9b, 0xfa, 0x97, 0x70, 0x14, 0xb2, 0x0c, + 0x33, 0x0d, 0xda, 0xec, 0xf9, 0x6e, 0x78, 0x70, 0xdb, 0xf7, 0x7a, 0xdd, 0x60, 0x61, 0x1c, 0xeb, + 0xc0, 0x95, 0x1e, 0x88, 0x12, 0x6b, 0x07, 0x8b, 0x14, 0xea, 0x04, 0x91, 0xf1, 0xbb, 0xb9, 0x78, + 0x9b, 0x24, 0x97, 0x34, 0xb1, 0x06, 0x75, 0x37, 0x4c, 0xac, 0x51, 0x75, 0x37, 0x28, 0xe0, 0x98, + 0x40, 0xaa, 0xbd, 0x20, 0xf4, 0xda, 0xcb, 0x1d, 0xa7, 0xeb, 0xb9, 0x9d, 0x10, 0xa9, 0x78, 0xe7, + 0x1b, 0x47, 0x87, 0xe5, 0xe7, 0x9b, 0x58, 0x6a, 0x51, 0x51, 0x6c, 0x25, 0xb8, 0x64, 0x50, 0x7f, + 0x82, 0xf1, 0x30, 0xfe, 0x41, 0x5e, 0x3b, 0xde, 0xd8, 0xe7, 0x99, 0xb4, 0xdb, 0x72, 0x9b, 0x78, + 0xa3, 0xc7, 0x86, 0x46, 0xb3, 0x0a, 0x3f, 0xcf, 0x8f, 0x4b, 0x79, 0x0f, 0xe9, 0xbc, 0x33, 0xa8, + 0xc9, 0x97, 0x61, 0x8a, 0x49, 0x1a, 0xe2, 0x67, 0xb0, 0x90, 0xc7, 0xce, 0x7e, 0x0e, 0xb5, 0x70, + 0x01, 0xf5, 0x23, 0x36, 0x9a, 0x88, 0xa2, 0x52, 0x10, 0x07, 0x16, 0x36, 0x7d, 0xbb, 0x13, 0xb8, + 0xe1, 0x72, 0xa7, 0xe9, 0x1f, 0xa0, 0x64, 0xb4, 0xdc, 0xb1, 0xb7, 0x5a, 0xd4, 0xc1, 0xe6, 0x16, + 0x97, 0x2e, 0x1f, 0x1d, 0x96, 0x5f, 0x0a, 0x39, 0x8e, 0x45, 0x23, 0x24, 0x8b, 0x72, 0x2c, 0x85, + 0x73, 0x5f, 0x4e, 0x4c, 0x92, 0x92, 0xdd, 0x8a, 0x8f, 0x30, 0x5c, 0x48, 0x40, 0x49, 0x2a, 0x1a, + 0x0d, 0xb6, 0x87, 0xa9, 0x9f, 0xa9, 0x12, 0x18, 0xff, 0x77, 0x2e, 0x3e, 0x80, 0xc9, 0x3b, 0x30, + 0x29, 0x56, 0x8c, 0x32, 0x2f, 0x70, 0x07, 0x95, 0xcb, 0x2b, 0x31, 0xb2, 0x2a, 0x3a, 0xbb, 0xf7, + 0x57, 0xaa, 0xab, 0xca, 0xdc, 0xc0, 0x7b, 0xbf, 0xdd, 0x6c, 0x25, 0xa9, 0x24, 0x1a, 0x9b, 0x04, + 0x9b, 0xab, 0x0d, 0xbd, 0x57, 0x70, 0x12, 0x84, 0xad, 0x20, 0xa3, 0x1b, 0x14, 0xe4, 0x4f, 0xde, + 0xf0, 0xff, 0x31, 0x97, 0x75, 0xce, 0x93, 0x25, 0x98, 0x7e, 0xe8, 0xf9, 0x7b, 0x38, 0xbe, 0x4a, + 0x27, 0xe0, 0xc8, 0xef, 0xcb, 0x82, 0x64, 0x83, 0x74, 0x12, 0xf5, 0xdb, 0x94, 0xde, 0xd0, 0xbf, + 0x2d, 0xc1, 0x41, 0x23, 0x60, 0xe3, 0x10, 0x71, 0x8c, 0x56, 0x07, 0x8e, 0x43, 0xfc, 0x09, 0xda, + 0x14, 0x56, 0xd1, 0x8d, 0xff, 0x32, 0xa7, 0x9e, 0xe7, 0xac, 0x93, 0x6b, 0x5e, 0xdb, 0x76, 0x3b, + 0x4a, 0x73, 0xf8, 0xc3, 0x12, 0x42, 0x93, 0x5f, 0xa2, 0x20, 0x93, 0x1b, 0x50, 0xe4, 0xbf, 0xa2, + 0xbd, 0x16, 0xb5, 0x5a, 0x82, 0x50, 0x3f, 0x28, 0x24, 0x62, 0x6a, 0x64, 0x0a, 0x27, 0x1d, 0x99, + 0xdf, 0xc9, 0xa9, 0x47, 0xf1, 0xc7, 0x3d, 0x6c, 0x12, 0x87, 0x4c, 0xfe, 0x24, 0x87, 0xcc, 0x27, + 0x6e, 0xc2, 0x0f, 0xe7, 0x60, 0x52, 0xd1, 0x52, 0xb0, 0x36, 0x6c, 0xf8, 0xde, 0x87, 0xb4, 0x19, + 0xea, 0x6d, 0xe8, 0x72, 0x60, 0xa2, 0x0d, 0x11, 0xea, 0x27, 0x68, 0x83, 0xf1, 0x4f, 0x73, 0xe2, + 0x8e, 0x34, 0xf4, 0x36, 0xaf, 0x6f, 0xc9, 0xf9, 0x93, 0x1c, 0x91, 0x5f, 0x86, 0x51, 0x93, 0x3a, + 0x6e, 0x20, 0xee, 0x37, 0x73, 0xea, 0x7d, 0x0c, 0x0b, 0x62, 0xb9, 0xc9, 0x67, 0x3f, 0xd5, 0xf3, + 0x0d, 0xcb, 0x99, 0x20, 0x5b, 0x0f, 0x6e, 0xb5, 0xe8, 0x63, 0x97, 0x2f, 0x46, 0x71, 0xd4, 0xe2, + 0xf1, 0xe6, 0x06, 0xd6, 0x36, 0x2b, 0x11, 0x12, 0xb5, 0xba, 0xf0, 0x34, 0x1a, 0xe3, 0x03, 0x80, + 0xb8, 0x4a, 0x72, 0x17, 0x4a, 0x62, 0x36, 0xb8, 0x9d, 0x1d, 0x2e, 0x48, 0x89, 0x3e, 0x28, 0x1f, + 0x1d, 0x96, 0x9f, 0x6d, 0x46, 0x65, 0x42, 0xea, 0x54, 0xf8, 0xa6, 0x08, 0x8d, 0xff, 0x28, 0x0f, + 0xf9, 0x0a, 0x0e, 0xc8, 0x5d, 0x7a, 0x10, 0xda, 0x5b, 0xb7, 0xdc, 0x96, 0xb6, 0x98, 0xf6, 0x10, + 0x6a, 0x6d, 0xbb, 0x9a, 0xba, 0x42, 0x41, 0x66, 0x8b, 0xe9, 0xae, 0xbf, 0xf5, 0x26, 0x12, 0x2a, + 0x8b, 0x69, 0xcf, 0xdf, 0x7a, 0x33, 0x49, 0x16, 0x21, 0x12, 0x03, 0xc6, 0xf8, 0xc2, 0x12, 0x73, + 0x10, 0x8e, 0x0e, 0xcb, 0x63, 0x7c, 0xfd, 0x99, 0xa2, 0x84, 0x9c, 0x83, 0x42, 0x63, 0x63, 0x4d, + 0xec, 0x80, 0xa8, 0x16, 0x0c, 0xba, 0x1d, 0x93, 0xc1, 0x58, 0x9d, 0xab, 0xb5, 0xca, 0x06, 0x2a, + 0x02, 0x46, 0xe3, 0x3a, 0x5b, 0x8e, 0xdd, 0x4d, 0xaa, 0x02, 0x22, 0x44, 0xf2, 0x2e, 0x4c, 0xde, + 0xad, 0x55, 0x57, 0xbc, 0x80, 0xef, 0x5e, 0x63, 0xf1, 0xe4, 0xdf, 0x73, 0x9a, 0x16, 0x6a, 0xe2, + 0x93, 0xc7, 0x80, 0x82, 0x6f, 0x7c, 0x2f, 0x0f, 0x93, 0x8a, 0x9e, 0x8c, 0x7c, 0x5e, 0x3c, 0x90, + 0xe6, 0xb4, 0x1b, 0x80, 0x82, 0xc1, 0x4a, 0xb9, 0x52, 0xa5, 0xed, 0x39, 0x54, 0x3c, 0x97, 0xc6, + 0x0a, 0x8c, 0xfc, 0x30, 0x0a, 0x8c, 0xb7, 0x00, 0xf8, 0x1c, 0xc0, 0x4f, 0x56, 0xc4, 0x09, 0xc5, + 0x4e, 0x42, 0x1d, 0x97, 0x18, 0x99, 0x3c, 0x80, 0xf9, 0x4d, 0xbf, 0x17, 0x84, 0x8d, 0x83, 0x20, + 0xa4, 0x6d, 0xc6, 0x6d, 0xc3, 0xf3, 0x5a, 0x62, 0xfe, 0xbd, 0x74, 0x74, 0x58, 0xbe, 0x88, 0xc6, + 0x1d, 0x56, 0x80, 0xe5, 0xf8, 0x01, 0x56, 0xd7, 0xf3, 0x54, 0xb5, 0x46, 0x16, 0x03, 0xc3, 0x84, + 0x29, 0x55, 0x29, 0xc2, 0x4e, 0x16, 0xf1, 0x98, 0x24, 0x54, 0xdd, 0xca, 0xc9, 0x22, 0xbe, 0x32, + 0xfd, 0xb8, 0xa5, 0x93, 0x18, 0x9f, 0x57, 0x15, 0x72, 0xc3, 0x2e, 0x6c, 0xe3, 0x47, 0x73, 0xf1, + 0x36, 0xf2, 0xe0, 0x3a, 0x79, 0x1b, 0xc6, 0xf8, 0xe3, 0x9d, 0x78, 0xe3, 0x3c, 0x1d, 0x5d, 0x6a, + 0xd5, 0x97, 0x3d, 0xae, 0x09, 0xff, 0x43, 0xfe, 0xc0, 0xff, 0x8c, 0x29, 0x48, 0x22, 0x25, 0xba, + 0xae, 0x4f, 0x93, 0xdc, 0x51, 0x5d, 0x7c, 0x3d, 0x4b, 0x89, 0x6e, 0xfc, 0xde, 0x08, 0xcc, 0xe8, + 0x68, 0xea, 0x0b, 0x5f, 0x6e, 0xa8, 0x17, 0xbe, 0x2f, 0x43, 0x91, 0xf5, 0x87, 0xdb, 0xa4, 0x52, + 0x22, 0x7b, 0x09, 0x9f, 0x16, 0x04, 0x4c, 0x7b, 0xb9, 0x06, 0x3e, 0x1c, 0xec, 0x8e, 0x6b, 0x46, + 0x54, 0x64, 0x51, 0x79, 0x86, 0x2a, 0xc4, 0x42, 0x8a, 0x7c, 0x86, 0x52, 0xd7, 0x43, 0xf4, 0x20, + 0xf5, 0x06, 0x8c, 0x31, 0xf9, 0x3e, 0x52, 0xc1, 0xe0, 0x57, 0x32, 0xd1, 0x3f, 0x61, 0xa2, 0xc2, + 0x91, 0xc8, 0x43, 0x28, 0xae, 0xda, 0x41, 0xd8, 0xa0, 0xb4, 0x33, 0xc4, 0xdb, 0x7d, 0x59, 0x74, + 0xd5, 0x3c, 0x3e, 0x8c, 0x07, 0x94, 0x76, 0x12, 0x8f, 0xaf, 0x11, 0x33, 0xf2, 0x0d, 0x80, 0xaa, + 0xd7, 0x09, 0x7d, 0xaf, 0xb5, 0xea, 0xed, 0x2c, 0x8c, 0xe1, 0xdd, 0xf7, 0xf9, 0xc4, 0x00, 0xc4, + 0x08, 0xfc, 0xfa, 0x1b, 0x29, 0x78, 0x9a, 0xbc, 0xc0, 0x6a, 0x79, 0x3b, 0xea, 0x3a, 0x88, 0xf1, + 0xc9, 0x2d, 0x28, 0x49, 0xc5, 0xc2, 0xfd, 0xee, 0x8e, 0x8f, 0x13, 0x64, 0x3c, 0x96, 0x3c, 0xe8, + 0xe3, 0xd0, 0xea, 0x09, 0xb8, 0xba, 0x53, 0x26, 0x69, 0xc8, 0xd7, 0xe1, 0x6c, 0x12, 0x26, 0x47, + 0xb9, 0x18, 0xcb, 0xe4, 0x2a, 0xbb, 0x8c, 0x79, 0xdf, 0x8f, 0x85, 0xf1, 0x51, 0x1e, 0xce, 0xf6, + 0x69, 0x2c, 0x5b, 0x0f, 0x78, 0x5c, 0x2b, 0xeb, 0x21, 0x71, 0x4a, 0x73, 0x9b, 0xa3, 0x8b, 0x90, + 0x17, 0x07, 0xdc, 0xc8, 0x52, 0xe9, 0xe8, 0xb0, 0x3c, 0xa5, 0x8d, 0x63, 0xbe, 0x5e, 0x23, 0x77, + 0x60, 0x84, 0x0d, 0xd1, 0x10, 0x4f, 0xe7, 0x52, 0xa7, 0x34, 0x13, 0xba, 0xea, 0xf4, 0xc1, 0xa1, + 0x43, 0x1e, 0xe4, 0xf3, 0x50, 0xd8, 0xdc, 0x5c, 0xc5, 0xb9, 0x53, 0xc0, 0xb6, 0x4f, 0x87, 0x61, + 0x4b, 0x9b, 0xaa, 0xd3, 0x8c, 0xf6, 0x6a, 0x64, 0x69, 0xc1, 0xd0, 0xc9, 0x57, 0x13, 0x26, 0x3d, + 0xaf, 0x0e, 0x1e, 0xe8, 0xe1, 0x2d, 0x7c, 0x3e, 0x81, 0x61, 0x8d, 0xf1, 0x4b, 0xf9, 0x78, 0x0d, + 0xdf, 0x72, 0x5b, 0x21, 0xf5, 0xc9, 0x79, 0xbe, 0x24, 0x63, 0xe1, 0xcc, 0x8c, 0x7e, 0x93, 0x85, + 0x78, 0x7d, 0x73, 0x56, 0xd1, 0x42, 0x7e, 0x55, 0x59, 0xc8, 0x05, 0x5c, 0xc8, 0x33, 0x7d, 0x97, + 0xec, 0xab, 0x19, 0xf3, 0x12, 0x17, 0x62, 0xc6, 0xdc, 0x7b, 0x09, 0xa6, 0xd7, 0xbc, 0xe5, 0xc7, + 0x61, 0x84, 0xc8, 0x16, 0x60, 0xd1, 0xd4, 0x81, 0x8c, 0xe3, 0x7a, 0xcb, 0xa1, 0xfe, 0xe6, 0xae, + 0xdd, 0xd1, 0xde, 0xae, 0xcd, 0x14, 0x9c, 0xe1, 0xae, 0xd1, 0x7d, 0x1d, 0x77, 0x9c, 0xe3, 0x26, + 0xe1, 0xc6, 0x8f, 0xe4, 0x65, 0x67, 0x3c, 0x58, 0x7c, 0x4a, 0xdf, 0x48, 0xdf, 0xd4, 0xde, 0x48, + 0xe7, 0x23, 0xed, 0x6e, 0xf4, 0xe0, 0xbf, 0x78, 0x8c, 0x9d, 0xc0, 0xff, 0x34, 0x0a, 0x53, 0x2a, + 0x3a, 0xeb, 0x87, 0x8a, 0xe3, 0xf8, 0x6a, 0x3f, 0xd8, 0x8e, 0xe3, 0x9b, 0x08, 0xd5, 0xcc, 0x02, + 0x0a, 0x03, 0xcd, 0x02, 0xbe, 0x09, 0x13, 0xd5, 0xb6, 0xa3, 0x3d, 0x56, 0x1a, 0x19, 0x9f, 0x77, + 0x35, 0x42, 0xe2, 0x6b, 0x21, 0x52, 0x5a, 0x36, 0xdb, 0x4e, 0xfa, 0x89, 0x32, 0x66, 0xa9, 0x59, + 0x14, 0x8c, 0x7e, 0x12, 0x8b, 0x82, 0x9b, 0x30, 0x71, 0x3f, 0xa0, 0x9b, 0xbd, 0x4e, 0x87, 0xb6, + 0x70, 0x5a, 0x15, 0xb9, 0xac, 0xdf, 0x0b, 0xa8, 0x15, 0x22, 0x54, 0xfd, 0x80, 0x08, 0x55, 0x1d, + 0xe0, 0xf1, 0x01, 0x03, 0x7c, 0x03, 0x8a, 0x1b, 0x94, 0xfa, 0xd8, 0xa7, 0x93, 0xb1, 0x48, 0xd7, + 0xa5, 0xd4, 0xb7, 0x58, 0xc7, 0x6a, 0x96, 0x06, 0x02, 0x51, 0x33, 0x4f, 0x98, 0x1a, 0xd2, 0x3c, + 0x81, 0xbc, 0x00, 0x53, 0xdd, 0xde, 0x56, 0xcb, 0x6d, 0x22, 0x5f, 0x61, 0xd7, 0x60, 0x4e, 0x72, + 0x18, 0x63, 0x1b, 0x90, 0xaf, 0xc2, 0x34, 0xde, 0x71, 0xa2, 0x29, 0x37, 0xa3, 0xbd, 0xea, 0x69, + 0x65, 0x5c, 0xd2, 0x69, 0x32, 0x90, 0x95, 0x61, 0x7e, 0xa3, 0x33, 0x3a, 0xdf, 0x80, 0x19, 0x7d, + 0x24, 0x9f, 0xc0, 0xe3, 0x5e, 0x64, 0x6a, 0x51, 0x2c, 0x4d, 0xdc, 0x19, 0x29, 0x42, 0x69, 0x92, + 0x1b, 0x59, 0x98, 0xb0, 0x11, 0xb5, 0xc9, 0x24, 0x77, 0x7b, 0x5b, 0xd4, 0xef, 0xd0, 0x90, 0x06, + 0xe2, 0x12, 0x10, 0x98, 0x23, 0x95, 0x6e, 0x37, 0x30, 0xfe, 0xf3, 0x3c, 0x8c, 0x57, 0x1e, 0x36, + 0xea, 0x9d, 0x6d, 0x0f, 0x9f, 0xe8, 0xa2, 0x97, 0x19, 0xf5, 0x89, 0x2e, 0x7a, 0x99, 0x51, 0xdf, + 0x63, 0xae, 0x65, 0x5c, 0xe3, 0xd0, 0x8a, 0x57, 0xb9, 0xc6, 0x69, 0x17, 0xd0, 0xf8, 0x91, 0xaa, + 0x30, 0xc4, 0x23, 0x55, 0xa4, 0x47, 0x1c, 0x39, 0x5e, 0x8f, 0xf8, 0x36, 0x4c, 0xd6, 0x3b, 0x21, + 0xdd, 0xf1, 0xe3, 0x99, 0x1e, 0x5d, 0x29, 0x23, 0xb0, 0x2a, 0xda, 0x2b, 0xd8, 0x6c, 0x1a, 0x71, + 0xdd, 0x65, 0xa4, 0xb3, 0xc4, 0x69, 0xc4, 0x55, 0x9c, 0x09, 0x7d, 0x80, 0x44, 0x34, 0x6a, 0x89, + 0x39, 0x22, 0x0d, 0x01, 0xb8, 0xf0, 0x39, 0x13, 0x2b, 0xef, 0x59, 0xc7, 0x2e, 0xcd, 0x65, 0x1b, + 0x02, 0x18, 0xdf, 0xcf, 0xc3, 0x64, 0xa5, 0xdb, 0x7d, 0xca, 0xcd, 0xb1, 0xbe, 0xa8, 0x6d, 0xaf, + 0xf2, 0x2e, 0x14, 0xb5, 0x6b, 0x28, 0x4b, 0xac, 0x5f, 0xcf, 0xc3, 0x6c, 0x82, 0x42, 0xfd, 0xfa, + 0xdc, 0x90, 0x46, 0x58, 0xf9, 0x21, 0x8d, 0xb0, 0x0a, 0xc3, 0x19, 0x61, 0x8d, 0x7c, 0x92, 0x2d, + 0xf3, 0x15, 0x28, 0x54, 0xba, 0xdd, 0xe4, 0x63, 0x6e, 0xb7, 0xfb, 0xe0, 0x06, 0xbf, 0xcf, 0xda, + 0xdd, 0xae, 0xc9, 0x30, 0xb4, 0x7d, 0x6c, 0x6c, 0xc8, 0x7d, 0xcc, 0x78, 0x03, 0x26, 0x90, 0x17, + 0x9a, 0x3e, 0x5d, 0x04, 0x5c, 0xcc, 0xc2, 0xea, 0x49, 0xab, 0x4b, 0x2c, 0xf3, 0xff, 0x2f, 0x07, + 0xa3, 0xf8, 0xfb, 0x29, 0x9d, 0x63, 0x8b, 0xda, 0x1c, 0x2b, 0x29, 0x73, 0x6c, 0x98, 0xd9, 0xf5, + 0xb7, 0x0a, 0x00, 0xd5, 0x75, 0xb3, 0xc1, 0xd5, 0x1e, 0xe4, 0x16, 0xcc, 0xda, 0xad, 0x96, 0xb7, + 0x4f, 0x1d, 0xcb, 0xf3, 0xdd, 0x1d, 0xb7, 0xc3, 0x7b, 0x4e, 0xbe, 0x30, 0xea, 0x45, 0xea, 0xbb, + 0x83, 0x28, 0x5a, 0xe7, 0x25, 0x2a, 0x9f, 0x36, 0x0d, 0x77, 0x3d, 0x47, 0x5e, 0xe0, 0x34, 0x3e, + 0xa2, 0x28, 0x83, 0xcf, 0x3d, 0x5e, 0xa2, 0xf2, 0xd9, 0xc5, 0x0b, 0xa9, 0x94, 0x1f, 0x35, 0x3e, + 0xa2, 0x28, 0x83, 0x0f, 0xbf, 0xc5, 0x06, 0x64, 0x15, 0xe6, 0x10, 0x62, 0x35, 0x7d, 0xea, 0xd0, + 0x4e, 0xe8, 0xda, 0xad, 0x40, 0x5c, 0xf9, 0x51, 0x39, 0x94, 0x2a, 0x54, 0xaf, 0x3c, 0x58, 0x58, + 0x8d, 0xcb, 0xc8, 0x55, 0x18, 0x6f, 0xdb, 0x8f, 0x2d, 0x7b, 0x87, 0xbf, 0xb5, 0x4f, 0xf3, 0x2b, + 0xa2, 0x00, 0xa9, 0x1b, 0x76, 0xdb, 0x7e, 0x5c, 0xd9, 0xa1, 0xac, 0x15, 0xf4, 0x71, 0xd7, 0x0b, + 0x94, 0x56, 0x8c, 0xc5, 0xad, 0x48, 0x14, 0xa9, 0xad, 0x10, 0x45, 0xa2, 0x15, 0xc6, 0xff, 0x33, + 0x8a, 0x53, 0x5b, 0x6c, 0x02, 0xc2, 0x3c, 0x2c, 0x97, 0x61, 0x1e, 0xf6, 0x16, 0x28, 0x47, 0x9c, + 0xaa, 0xda, 0x53, 0x0e, 0x78, 0xf5, 0x5a, 0x18, 0x23, 0x93, 0xbd, 0xa4, 0xa1, 0x58, 0x01, 0x57, + 0xce, 0x8b, 0xc9, 0x79, 0xf5, 0x44, 0x6c, 0xc4, 0x56, 0x80, 0xd4, 0x3b, 0xf8, 0x9a, 0x45, 0x1b, + 0x7b, 0x6e, 0xf7, 0x01, 0xf5, 0xdd, 0xed, 0x03, 0x31, 0x2e, 0x28, 0x44, 0xb9, 0xa2, 0xd4, 0x0a, + 0xf6, 0xdc, 0x2e, 0xbb, 0x37, 0xba, 0xdb, 0x07, 0x66, 0x06, 0x0d, 0x79, 0x1f, 0xc6, 0x4d, 0xba, + 0xef, 0xbb, 0xa1, 0x34, 0x7f, 0x98, 0x89, 0xb4, 0x1c, 0x08, 0xe5, 0x43, 0xe4, 0xf3, 0x1f, 0xea, + 0x62, 0x15, 0xe5, 0x64, 0x91, 0x9f, 0x52, 0xdc, 0xcc, 0x61, 0x3a, 0x6e, 0x6d, 0xe5, 0x61, 0xa3, + 0xdf, 0x21, 0x45, 0xae, 0xc0, 0x28, 0x1e, 0x75, 0x42, 0x80, 0x43, 0xb7, 0x01, 0x14, 0x78, 0xd4, + 0x73, 0x18, 0x31, 0xc8, 0xf3, 0x00, 0xd1, 0x73, 0x51, 0xb0, 0x50, 0x44, 0xd1, 0x4a, 0x81, 0x24, + 0xcf, 0xe9, 0x89, 0x13, 0x9d, 0xd3, 0xab, 0x50, 0x32, 0xb9, 0x07, 0x92, 0x53, 0xe9, 0xe2, 0x9b, + 0x44, 0xb0, 0x00, 0x38, 0xc1, 0x2e, 0x1e, 0x1d, 0x96, 0x9f, 0x13, 0xde, 0x49, 0x8e, 0x65, 0x77, + 0xf9, 0x53, 0x86, 0x36, 0xbb, 0x93, 0x94, 0xe4, 0x2d, 0x18, 0x61, 0x3b, 0x82, 0x30, 0x29, 0x93, + 0xba, 0xdd, 0x78, 0x93, 0xe0, 0x37, 0xed, 0xa6, 0xa7, 0x4d, 0x55, 0x24, 0xf9, 0xf4, 0xcc, 0xb4, + 0x7e, 0x3d, 0x0f, 0x2f, 0x46, 0x87, 0xe0, 0xba, 0xdf, 0xa8, 0xdc, 0x5b, 0xad, 0x3b, 0x1b, 0xe2, + 0xce, 0xb8, 0xe1, 0x7b, 0x8f, 0x5c, 0x87, 0xfa, 0x0f, 0xae, 0x1f, 0xb3, 0x85, 0xaf, 0xf2, 0xe5, + 0xc3, 0x15, 0xce, 0x79, 0xcd, 0xa0, 0x45, 0x91, 0x35, 0x84, 0xcd, 0x4d, 0xb7, 0x9b, 0xd2, 0x3f, + 0xaf, 0x3c, 0x63, 0xc6, 0x0c, 0xc8, 0x8f, 0xe6, 0xe0, 0x4c, 0xf6, 0x87, 0x08, 0x3d, 0x42, 0x59, + 0xde, 0x57, 0xfa, 0x7c, 0xed, 0xd2, 0x2b, 0x47, 0x87, 0xe5, 0x17, 0x03, 0xbb, 0xdd, 0xb2, 0x5c, + 0x87, 0xd7, 0xe6, 0x36, 0xa9, 0xd5, 0x15, 0x08, 0x5a, 0xbd, 0x7d, 0x6a, 0xfa, 0x12, 0xc8, 0x9d, + 0x7c, 0x21, 0xb7, 0x04, 0x50, 0x94, 0x3a, 0x3d, 0xe3, 0xef, 0xe4, 0x40, 0x99, 0xda, 0x45, 0x93, + 0x3a, 0xae, 0x4f, 0x9b, 0xa1, 0xd8, 0xcd, 0x85, 0x3b, 0x0e, 0x87, 0x25, 0xec, 0x97, 0x10, 0x46, + 0xde, 0x83, 0x71, 0xb1, 0xeb, 0xe0, 0xc6, 0x1d, 0x2f, 0x09, 0xa1, 0x2d, 0xe4, 0x7e, 0x5b, 0xa9, + 0x1d, 0x4b, 0x12, 0xb1, 0x5b, 0xd1, 0x9d, 0x87, 0x9b, 0xd5, 0x96, 0xed, 0xb6, 0x03, 0x71, 0xfa, + 0x61, 0xb7, 0x7e, 0xb8, 0x1f, 0x5a, 0x4d, 0x84, 0xaa, 0xb7, 0xa2, 0x08, 0xd5, 0xb8, 0x2d, 0x95, + 0x95, 0xc7, 0x18, 0xe1, 0x95, 0x61, 0xf4, 0x41, 0xac, 0xb4, 0x58, 0x9a, 0x38, 0x3a, 0x2c, 0xf3, + 0xe9, 0x62, 0x72, 0xb8, 0xf1, 0x57, 0x72, 0x30, 0xa3, 0xcf, 0x27, 0x72, 0x15, 0xc6, 0x84, 0x2b, + 0x4c, 0x0e, 0x95, 0x33, 0xac, 0x17, 0xc6, 0xb8, 0x13, 0x8c, 0xe6, 0xfa, 0x22, 0xb0, 0xd8, 0xf9, + 0x2d, 0x38, 0x88, 0xc3, 0x0b, 0xcf, 0xef, 0x26, 0x07, 0x99, 0xb2, 0x8c, 0x18, 0x4c, 0x78, 0x0f, + 0x7a, 0xad, 0x50, 0xd5, 0xd9, 0xfb, 0x08, 0x31, 0x45, 0x89, 0x51, 0x85, 0x31, 0xbe, 0x97, 0x24, + 0x8c, 0x7f, 0x72, 0x27, 0x30, 0xfe, 0x31, 0x0e, 0x73, 0x00, 0x8d, 0xc6, 0xca, 0x5d, 0x7a, 0xb0, + 0x61, 0xbb, 0x3e, 0x3e, 0x32, 0xe1, 0xbe, 0x7d, 0x57, 0x2c, 0xae, 0x29, 0xf1, 0xc8, 0xc4, 0xf7, + 0xf8, 0x3d, 0x7a, 0xa0, 0x3d, 0x32, 0x49, 0x54, 0x3c, 0x1c, 0x7c, 0xf7, 0x91, 0x1d, 0x52, 0x46, + 0x98, 0x47, 0x42, 0x7e, 0x38, 0x70, 0x68, 0x82, 0x52, 0x41, 0x26, 0xdf, 0x80, 0x99, 0xf8, 0x57, + 0xf4, 0x54, 0x36, 0x13, 0x2d, 0x60, 0xbd, 0x70, 0xe9, 0xf9, 0xa3, 0xc3, 0xf2, 0x79, 0x85, 0x6b, + 0xf2, 0x11, 0x2d, 0xc1, 0xcc, 0xf8, 0xd5, 0x1c, 0x3e, 0x10, 0xcb, 0x06, 0x5e, 0x82, 0x91, 0xc8, + 0xa4, 0x71, 0x4a, 0xec, 0x3a, 0xfa, 0x73, 0x00, 0x96, 0x93, 0x17, 0xa1, 0x10, 0xb7, 0x04, 0xf7, + 0x6a, 0xbd, 0x05, 0xac, 0x94, 0xdc, 0x86, 0xf1, 0xa1, 0xbe, 0x19, 0x97, 0x46, 0xc6, 0xb7, 0x4a, + 0x6a, 0x1c, 0x85, 0x3b, 0x0f, 0x37, 0x3f, 0xbb, 0xa3, 0xf0, 0xd3, 0x79, 0x98, 0x65, 0xfd, 0x5a, + 0xe9, 0x85, 0xbb, 0x9e, 0xef, 0x86, 0x07, 0x4f, 0xad, 0x76, 0xeb, 0x1d, 0x4d, 0x34, 0x3e, 0x2f, + 0x4f, 0x19, 0xb5, 0x6d, 0x43, 0x29, 0xb9, 0x7e, 0x7f, 0x14, 0xe6, 0x33, 0xa8, 0xc8, 0xeb, 0x9a, + 0x02, 0x7a, 0x41, 0xba, 0xba, 0x7e, 0x74, 0x58, 0x9e, 0x92, 0xe8, 0x9b, 0xb1, 0xeb, 0xeb, 0xa2, + 0x6e, 0x6d, 0xc1, 0x7b, 0x0a, 0xf5, 0xd1, 0xaa, 0xb5, 0x85, 0x6e, 0x63, 0x71, 0x05, 0x46, 0x4d, + 0xaf, 0x45, 0xa5, 0x85, 0x11, 0x4a, 0x18, 0x3e, 0x03, 0x68, 0x2f, 0xaa, 0x0c, 0x40, 0x56, 0x60, + 0x9c, 0xfd, 0x71, 0xcf, 0xee, 0x8a, 0xb7, 0x02, 0x12, 0x5d, 0xce, 0x10, 0xda, 0x75, 0x3b, 0x3b, + 0xea, 0xfd, 0xac, 0x45, 0xad, 0xb6, 0xdd, 0xd5, 0x44, 0x21, 0x8e, 0xa8, 0xdd, 0xf3, 0x8a, 0xfd, + 0xef, 0x79, 0xb9, 0x63, 0xef, 0x79, 0x0e, 0x40, 0xc3, 0xdd, 0xe9, 0xb8, 0x9d, 0x9d, 0x4a, 0x6b, + 0x47, 0x38, 0x0c, 0x5f, 0xe9, 0x3f, 0x0a, 0x57, 0x63, 0x64, 0x9c, 0xb8, 0xfc, 0x41, 0x8f, 0xc3, + 0x2c, 0xbb, 0xa5, 0x3d, 0x64, 0xc4, 0xa8, 0x64, 0x0d, 0xa0, 0xd2, 0x0c, 0xdd, 0x47, 0x6c, 0x02, + 0x07, 0x42, 0x6a, 0x91, 0x1f, 0x5c, 0xad, 0xdc, 0xa5, 0x07, 0x0d, 0x1a, 0xc6, 0x0f, 0x23, 0x36, + 0xa2, 0xb2, 0x75, 0xa0, 0xd9, 0xac, 0xc7, 0x1c, 0x48, 0x17, 0x4e, 0x57, 0x1c, 0xc7, 0x65, 0x2d, + 0xb0, 0x5b, 0x9b, 0xdc, 0xd1, 0x1b, 0x59, 0x4f, 0x65, 0xb3, 0xbe, 0x22, 0x58, 0xbf, 0x60, 0x47, + 0x54, 0x96, 0xf4, 0x0f, 0x4f, 0x54, 0x93, 0xcd, 0xd8, 0x58, 0x87, 0x19, 0xbd, 0xe9, 0xba, 0x9b, + 0xf3, 0x14, 0x14, 0xcd, 0x46, 0xc5, 0x6a, 0xac, 0x54, 0xae, 0x97, 0x72, 0xa4, 0x04, 0x53, 0xe2, + 0xd7, 0xa2, 0xb5, 0xf8, 0xe6, 0xcd, 0x52, 0x5e, 0x83, 0xbc, 0x79, 0x7d, 0x31, 0xe5, 0x5d, 0x34, + 0x5e, 0x2a, 0x72, 0xf5, 0x97, 0xf1, 0x1b, 0x39, 0x28, 0xca, 0xef, 0x26, 0x37, 0xa1, 0xd0, 0x68, + 0xac, 0x24, 0xfc, 0x81, 0xe2, 0xf3, 0x85, 0xef, 0xa4, 0x41, 0xa0, 0x1a, 0x7d, 0x32, 0x02, 0x46, + 0xb7, 0xb9, 0xda, 0x10, 0x62, 0x81, 0xa4, 0x8b, 0xb7, 0x6d, 0x4e, 0x97, 0xe1, 0x24, 0x71, 0x13, + 0x0a, 0x77, 0x1e, 0x6e, 0x8a, 0xfb, 0x84, 0xa4, 0x8b, 0x77, 0x52, 0x4e, 0xf7, 0xe1, 0xbe, 0xba, + 0xbf, 0x33, 0x02, 0xc3, 0x84, 0x49, 0x65, 0x0a, 0xf3, 0xe3, 0xb6, 0xed, 0x45, 0x7e, 0xbd, 0xe2, + 0xb8, 0x65, 0x10, 0x53, 0x94, 0x30, 0xe9, 0x60, 0xd5, 0x6b, 0xda, 0x2d, 0x71, 0x6e, 0xa3, 0x74, + 0xd0, 0x62, 0x00, 0x93, 0xc3, 0x8d, 0xdf, 0xcd, 0x41, 0x09, 0x65, 0x28, 0x34, 0xda, 0xf4, 0xf6, + 0x68, 0xe7, 0xc1, 0x75, 0xf2, 0x86, 0x5c, 0x6c, 0xb9, 0x48, 0xd5, 0x30, 0x8a, 0x8b, 0x2d, 0xf1, + 0x56, 0x21, 0x16, 0x9c, 0xe2, 0x3a, 0x9d, 0x1f, 0xde, 0xe5, 0xf2, 0x18, 0xd7, 0xe9, 0x32, 0x8c, + 0xe2, 0xe7, 0x88, 0x6d, 0x11, 0xbf, 0x3c, 0x64, 0x00, 0x93, 0xc3, 0x95, 0x5d, 0xe9, 0x67, 0xf3, + 0xa9, 0x36, 0x2c, 0x7e, 0xa6, 0xdc, 0x16, 0xf5, 0xc6, 0x0d, 0xb5, 0x53, 0x7f, 0x00, 0xa7, 0x92, + 0x5d, 0x82, 0x6a, 0xa0, 0x0a, 0xcc, 0xea, 0x70, 0xa9, 0x11, 0x3a, 0x9b, 0x59, 0xd7, 0x83, 0x45, + 0x33, 0x89, 0x6f, 0xfc, 0x6f, 0x39, 0x98, 0xc0, 0x3f, 0xcd, 0x5e, 0x0b, 0x8d, 0x67, 0x2a, 0x0f, + 0x1b, 0x42, 0xe5, 0xab, 0x8a, 0x71, 0xf6, 0x7e, 0x60, 0x09, 0xad, 0xb0, 0xb6, 0xbf, 0x44, 0xc8, + 0x82, 0x94, 0xeb, 0x72, 0xa5, 0x5a, 0x24, 0x22, 0xe5, 0x4a, 0xdf, 0x20, 0x41, 0x2a, 0x90, 0xd1, + 0xe4, 0xee, 0x61, 0x83, 0x4d, 0x3f, 0xf5, 0x35, 0x1b, 0xe9, 0xbc, 0x96, 0x6e, 0x72, 0xc7, 0xd1, + 0xf0, 0x31, 0xfb, 0x61, 0xa3, 0x62, 0xae, 0x69, 0x8f, 0xd9, 0xec, 0x1b, 0x35, 0x0b, 0x71, 0x81, + 0x64, 0xfc, 0xd2, 0x64, 0xb2, 0x03, 0xc5, 0x51, 0x77, 0xc2, 0xb5, 0xf1, 0x36, 0x8c, 0x56, 0x5a, + 0x2d, 0x6f, 0x5f, 0xec, 0x12, 0x52, 0x2b, 0x15, 0xf5, 0x1f, 0x3f, 0xc9, 0x50, 0xb1, 0xa2, 0xb9, + 0x62, 0x31, 0x00, 0xa9, 0xc2, 0x44, 0xe5, 0x61, 0xa3, 0x5e, 0xaf, 0x6d, 0x6e, 0x72, 0xb7, 0x93, + 0xc2, 0xd2, 0xcb, 0xb2, 0x7f, 0x5c, 0xd7, 0xb1, 0x92, 0xef, 0xa9, 0xb1, 0xe4, 0x1e, 0xd3, 0x91, + 0x77, 0x01, 0xee, 0x78, 0x6e, 0x87, 0x2b, 0x92, 0x44, 0xe3, 0x2f, 0x1c, 0x1d, 0x96, 0x27, 0x3f, + 0xf4, 0xdc, 0x8e, 0xd0, 0x3c, 0xb1, 0x6f, 0x8f, 0x91, 0x4c, 0xe5, 0x6f, 0xd6, 0xd3, 0x4b, 0x1e, + 0x37, 0x88, 0x19, 0x8d, 0x7b, 0x7a, 0xcb, 0x4b, 0xd9, 0xc2, 0x48, 0x34, 0xd2, 0x86, 0xd9, 0x46, + 0x6f, 0x67, 0x87, 0xb2, 0x5d, 0x5d, 0xa8, 0x4e, 0xc6, 0xc4, 0xed, 0x36, 0x0a, 0xf6, 0xc1, 0x6f, + 0x22, 0xec, 0x7e, 0x12, 0x2c, 0xbd, 0xce, 0x26, 0xf2, 0x0f, 0x0e, 0xcb, 0xe2, 0x9d, 0x96, 0x09, + 0x69, 0x81, 0xa4, 0x4f, 0x2b, 0x4e, 0x92, 0xbc, 0xc9, 0x3a, 0x8c, 0xdd, 0x76, 0xc3, 0x95, 0xde, + 0x96, 0x70, 0xa3, 0x78, 0x61, 0xc0, 0xa2, 0xe1, 0x88, 0xfc, 0xa1, 0x60, 0xc7, 0x0d, 0x77, 0x7b, + 0xaa, 0x21, 0xbb, 0x60, 0x43, 0x1e, 0x42, 0xb1, 0xea, 0xfa, 0xcd, 0x16, 0xad, 0xd6, 0xc5, 0xa9, + 0xff, 0xe2, 0x00, 0x96, 0x12, 0x95, 0xf7, 0x4b, 0x13, 0x7f, 0x35, 0x5d, 0x55, 0x0a, 0x90, 0x18, + 0xe4, 0xaf, 0xe6, 0xe0, 0xd9, 0xe8, 0xeb, 0x2b, 0x3b, 0xb4, 0x13, 0xde, 0xb3, 0xc3, 0xe6, 0x2e, + 0xf5, 0x45, 0x2f, 0x4d, 0x0c, 0xea, 0xa5, 0x2f, 0xa5, 0x7a, 0xe9, 0x72, 0xdc, 0x4b, 0x36, 0x63, + 0x66, 0xb5, 0x39, 0xb7, 0x74, 0x9f, 0x0d, 0xaa, 0x95, 0x58, 0x00, 0xf1, 0xcb, 0x8f, 0x70, 0xc3, + 0x7b, 0x79, 0x40, 0x83, 0x63, 0x64, 0x61, 0x3e, 0x1f, 0xfd, 0xd6, 0xec, 0xbf, 0x22, 0x28, 0xb9, + 0x2b, 0x7d, 0x96, 0xb8, 0x44, 0x72, 0x71, 0x00, 0x6f, 0xee, 0xc7, 0x34, 0x3f, 0xc0, 0x3b, 0x91, + 0x8f, 0xf6, 0xaa, 0xbd, 0x25, 0x84, 0x90, 0x63, 0x46, 0x7b, 0xd5, 0x8e, 0x47, 0xbb, 0x65, 0x27, + 0x47, 0x7b, 0xd5, 0xde, 0x22, 0x55, 0xee, 0x68, 0xc9, 0xbd, 0xf2, 0x9e, 0x1f, 0xc4, 0xad, 0xba, + 0xc1, 0x4f, 0xe6, 0x0c, 0x87, 0xcb, 0xaf, 0xc1, 0x44, 0xa3, 0x6b, 0x37, 0x69, 0xcb, 0xdd, 0x0e, + 0xc5, 0x53, 0xe0, 0x4b, 0x03, 0x58, 0x45, 0xb8, 0xe2, 0x19, 0x49, 0xfe, 0x54, 0x2f, 0x48, 0x11, + 0x0e, 0xfb, 0xc2, 0xcd, 0x8d, 0x7b, 0x0b, 0xb3, 0xc7, 0x7e, 0xe1, 0xe6, 0xc6, 0x3d, 0x21, 0x73, + 0x74, 0xdb, 0x9a, 0xcc, 0xb1, 0x71, 0x8f, 0x74, 0x61, 0x66, 0x93, 0xfa, 0xbe, 0xbd, 0xed, 0xf9, + 0x6d, 0xae, 0xaa, 0xe3, 0x9e, 0x1e, 0x57, 0x06, 0xf1, 0xd3, 0x08, 0xb8, 0x8e, 0x36, 0x94, 0x30, + 0x2b, 0xa9, 0xdf, 0x4b, 0xf0, 0x67, 0x7d, 0xb2, 0xe4, 0x86, 0x5b, 0xbd, 0xe6, 0x1e, 0x0d, 0x17, + 0xe6, 0x8e, 0xed, 0x93, 0x08, 0x97, 0xf7, 0xc9, 0x96, 0xfc, 0xa9, 0xf6, 0x49, 0x84, 0x63, 0xfc, + 0x56, 0x01, 0xce, 0xf6, 0xe9, 0x02, 0xb2, 0x26, 0xb7, 0xdc, 0x9c, 0xa6, 0xb0, 0xed, 0x83, 0x7e, + 0xf5, 0xd8, 0x5d, 0x78, 0x15, 0x4a, 0xcb, 0x77, 0x51, 0x4a, 0xe7, 0xaa, 0xf4, 0x6a, 0x45, 0x1e, + 0x56, 0xa8, 0x54, 0xa4, 0x7b, 0x68, 0x19, 0x27, 0x55, 0xf0, 0x4d, 0xcd, 0x05, 0x34, 0x45, 0x79, + 0xfe, 0x47, 0xf2, 0x30, 0x82, 0x07, 0x67, 0x22, 0xf0, 0x4d, 0xee, 0x44, 0x81, 0x6f, 0xbe, 0x0c, + 0x53, 0xcb, 0x77, 0xf9, 0x1d, 0x7a, 0xc5, 0x0e, 0x76, 0xc5, 0xb6, 0x8e, 0x0f, 0xcd, 0x74, 0xcf, + 0x12, 0x57, 0xee, 0x5d, 0x5b, 0x93, 0x59, 0x35, 0x0a, 0x72, 0x1f, 0xe6, 0xf9, 0xb7, 0xb9, 0xdb, + 0x6e, 0x93, 0xc7, 0xcf, 0x70, 0xed, 0x96, 0xd8, 0xe3, 0x5f, 0x3c, 0x3a, 0x2c, 0x97, 0xe9, 0x1e, + 0xda, 0xfc, 0x89, 0x72, 0x2b, 0x40, 0x04, 0xd5, 0xf8, 0x2f, 0x83, 0x5e, 0x75, 0xea, 0x37, 0x27, + 0xb0, 0x42, 0x56, 0x1b, 0xab, 0x9b, 0xe1, 0x72, 0x24, 0xe3, 0xcf, 0x46, 0xe1, 0x7c, 0xff, 0xed, + 0x99, 0x7c, 0x45, 0x1f, 0xc0, 0x4b, 0xc7, 0x6e, 0xe8, 0xc7, 0x8f, 0xe1, 0x57, 0xe1, 0xd4, 0x72, + 0x27, 0xa4, 0x7e, 0xd7, 0x77, 0x65, 0x18, 0x87, 0x15, 0x2f, 0x90, 0x36, 0x96, 0x68, 0xec, 0x48, + 0xa3, 0x72, 0xa1, 0xee, 0x44, 0x8b, 0x4f, 0x85, 0x55, 0x26, 0x07, 0xb2, 0x0c, 0x33, 0x0a, 0xbc, + 0xd5, 0xdb, 0x11, 0x02, 0x09, 0x7f, 0xd1, 0x50, 0x78, 0xb6, 0x7a, 0xea, 0xbd, 0x2d, 0x41, 0x84, + 0x76, 0x9c, 0xec, 0xb2, 0xd8, 0xbc, 0xf3, 0xf0, 0x6e, 0x43, 0x0c, 0x27, 0xbf, 0xf6, 0x21, 0xd4, + 0xfa, 0x70, 0x7f, 0x4f, 0xdb, 0x5f, 0x63, 0xe4, 0xf3, 0xbf, 0x5a, 0x10, 0x33, 0xea, 0x45, 0x28, + 0x34, 0x7a, 0x5b, 0x62, 0x26, 0xf1, 0x4b, 0x8b, 0x76, 0xc0, 0xb1, 0x52, 0xf2, 0x45, 0x00, 0x93, + 0x76, 0xbd, 0xc0, 0x0d, 0x3d, 0xff, 0x40, 0x75, 0x24, 0xf2, 0x23, 0xa8, 0x6e, 0xeb, 0x2c, 0xa1, + 0x64, 0x05, 0x66, 0xe3, 0x5f, 0xeb, 0xfb, 0x1d, 0xa1, 0xde, 0x9d, 0xe0, 0x7a, 0x95, 0x98, 0xdc, + 0xf2, 0x58, 0x99, 0x7a, 0x64, 0x27, 0xc8, 0xc8, 0x22, 0x14, 0x1f, 0x7a, 0xfe, 0xde, 0x36, 0x1b, + 0xe3, 0x91, 0x58, 0xa8, 0xd8, 0x17, 0x30, 0xf5, 0xf0, 0x94, 0x78, 0x6c, 0xb9, 0x2c, 0x77, 0x1e, + 0xb9, 0xbe, 0xd7, 0x69, 0xd3, 0x4e, 0xa8, 0xbe, 0xdf, 0xd3, 0x18, 0xac, 0xb9, 0x70, 0xc6, 0x60, + 0x72, 0x05, 0x46, 0x2b, 0xcd, 0xd0, 0xf3, 0xc5, 0xe3, 0x3d, 0x9f, 0x29, 0x0c, 0xa0, 0xcd, 0x14, + 0x06, 0x60, 0x9d, 0x68, 0xd2, 0x6d, 0xf1, 0x90, 0x81, 0x9d, 0xe8, 0xd3, 0x6d, 0xcd, 0x3f, 0x95, + 0x6e, 0x33, 0xa1, 0xc8, 0xa4, 0xdb, 0xa8, 0xf2, 0xd0, 0xc2, 0x3a, 0x6d, 0xa7, 0x94, 0x65, 0x02, + 0xcd, 0xf8, 0x31, 0xe8, 0x3b, 0xe5, 0xd9, 0x29, 0x74, 0xb2, 0x29, 0xbf, 0x6a, 0x0f, 0x31, 0xe5, + 0x5f, 0x8f, 0x2c, 0xa8, 0x55, 0xa7, 0x6c, 0x84, 0xa8, 0xc7, 0xa0, 0xb0, 0xa5, 0xd6, 0xe7, 0x5f, + 0xe1, 0x24, 0xf3, 0xef, 0xd7, 0x8a, 0x27, 0x99, 0x7f, 0xa2, 0x7f, 0xf3, 0xc3, 0xf6, 0x6f, 0x61, + 0xa8, 0xfe, 0x25, 0x4b, 0x30, 0x1d, 0xc5, 0x14, 0xdb, 0xb0, 0x43, 0x6d, 0x47, 0x8c, 0x02, 0xc1, + 0x59, 0x5d, 0x3b, 0x54, 0x77, 0x44, 0x9d, 0x84, 0xbc, 0x03, 0x93, 0xc2, 0x03, 0x01, 0x39, 0x8c, + 0xc6, 0x36, 0xa0, 0xd2, 0x5d, 0x21, 0x41, 0xaf, 0xa2, 0xb3, 0x8d, 0x60, 0xc3, 0xed, 0xd2, 0x96, + 0xdb, 0xa1, 0x0d, 0x7c, 0x7a, 0x10, 0x93, 0x0d, 0x37, 0x82, 0xae, 0x28, 0xb1, 0xf8, 0xab, 0x84, + 0xa6, 0x74, 0xd4, 0x88, 0x92, 0xf3, 0x7c, 0xfc, 0x44, 0xf3, 0x9c, 0x9b, 0x60, 0xf9, 0xab, 0xde, + 0x8e, 0x2b, 0x8d, 0x4e, 0xa5, 0x09, 0x96, 0x6f, 0xb5, 0x18, 0x34, 0x61, 0x82, 0xc5, 0x51, 0xd9, + 0xe5, 0x88, 0xfd, 0xa8, 0xd7, 0xc4, 0x7b, 0x1b, 0x5e, 0x8e, 0x90, 0x48, 0xb7, 0xf4, 0xe5, 0x48, + 0xb2, 0x9a, 0xe5, 0xb6, 0xed, 0xb6, 0x84, 0xdb, 0x6e, 0x5c, 0x0d, 0x65, 0xd0, 0x64, 0x35, 0x88, + 0x4a, 0x9a, 0x30, 0x65, 0xd2, 0xed, 0x0d, 0xdf, 0x0b, 0x69, 0x33, 0xa4, 0x8e, 0x10, 0x08, 0xe5, + 0x9d, 0x68, 0xc9, 0xf3, 0xb8, 0xb0, 0xbb, 0xf4, 0xc6, 0x1f, 0x1c, 0x96, 0x73, 0x3f, 0x38, 0x2c, + 0x03, 0x03, 0x71, 0x33, 0xf2, 0xa3, 0xc3, 0xf2, 0x59, 0x36, 0xfe, 0x5d, 0x49, 0xac, 0x1e, 0x6c, + 0x2a, 0x53, 0xf2, 0x5d, 0xb6, 0xd5, 0x47, 0x5d, 0x12, 0x57, 0x36, 0xd5, 0xa7, 0xb2, 0x37, 0x33, + 0x2b, 0x2b, 0x2b, 0xbd, 0x9d, 0x59, 0x69, 0x66, 0x25, 0xe4, 0x5d, 0x98, 0xac, 0xd6, 0xab, 0x5e, + 0x67, 0xdb, 0xdd, 0x69, 0xac, 0x54, 0x50, 0xaa, 0x14, 0x2e, 0x04, 0x4d, 0xd7, 0x6a, 0x22, 0xdc, + 0x0a, 0x76, 0x6d, 0xcd, 0x93, 0x2c, 0xc6, 0x27, 0xb7, 0x61, 0x46, 0xfe, 0x34, 0xe9, 0xf6, 0x7d, + 0xb3, 0x8e, 0xc2, 0xa4, 0xf4, 0xdb, 0x88, 0x38, 0xb0, 0x8e, 0xe8, 0xf9, 0xea, 0x25, 0x23, 0x41, + 0xc6, 0x26, 0x63, 0x8d, 0x76, 0x5b, 0xde, 0x01, 0xfb, 0xbc, 0x4d, 0x97, 0xfa, 0x28, 0x3e, 0x8a, + 0xc9, 0xe8, 0x44, 0x25, 0x56, 0xe8, 0x6a, 0x3b, 0x75, 0x82, 0x88, 0xac, 0xc1, 0x9c, 0x98, 0xe2, + 0x0f, 0xdc, 0xc0, 0xdd, 0x72, 0x5b, 0x6e, 0x78, 0x80, 0x82, 0xa3, 0x90, 0x7d, 0xe4, 0xba, 0x78, + 0x14, 0x95, 0x2a, 0xcc, 0xd2, 0xa4, 0xc6, 0x6f, 0xe4, 0xe1, 0xb9, 0x41, 0x97, 0x28, 0xd2, 0xd0, + 0xf7, 0xc1, 0xcb, 0x43, 0x5c, 0xbc, 0x8e, 0xdf, 0x09, 0x97, 0x61, 0x66, 0xdd, 0xdf, 0xb1, 0x3b, + 0xee, 0x77, 0xf0, 0x72, 0x1c, 0x59, 0xa2, 0x61, 0x67, 0x78, 0x4a, 0x89, 0x3e, 0xdb, 0x13, 0x44, + 0xe7, 0x1f, 0x89, 0x6d, 0xee, 0xe3, 0xfa, 0x34, 0xdd, 0x84, 0x89, 0xaa, 0xd7, 0x09, 0xe9, 0xe3, + 0x30, 0xe1, 0xc1, 0xcb, 0x81, 0x49, 0x7f, 0x2e, 0x89, 0x6a, 0xfc, 0x8b, 0x3c, 0x5c, 0x18, 0x78, + 0x8b, 0x20, 0x9b, 0x7a, 0xaf, 0x5d, 0x19, 0xe6, 0xea, 0x71, 0x7c, 0xb7, 0x2d, 0xa6, 0x8c, 0xa6, + 0x8e, 0x75, 0x19, 0x38, 0xff, 0xdf, 0xe5, 0x44, 0x27, 0x7d, 0x0e, 0xc6, 0xb1, 0xaa, 0xa8, 0x8b, + 0xb8, 0x82, 0x0d, 0x77, 0x61, 0x57, 0x57, 0xb0, 0x71, 0x34, 0x72, 0x03, 0x8a, 0x55, 0xbb, 0xd5, + 0x52, 0xfc, 0x9b, 0xf1, 0x22, 0xd0, 0x44, 0x58, 0xc2, 0xc6, 0x4e, 0x22, 0xb2, 0x63, 0x8b, 0xff, + 0xad, 0x9c, 0x15, 0xb8, 0x59, 0x0a, 0xb2, 0xc4, 0x71, 0xa1, 0x20, 0x63, 0x54, 0xc4, 0xa6, 0x17, + 0x79, 0x50, 0xf2, 0xa8, 0x88, 0x0c, 0xa0, 0x45, 0x45, 0x64, 0x00, 0xe3, 0x37, 0x0b, 0xf0, 0xfc, + 0xe0, 0xab, 0x30, 0xb9, 0xaf, 0x0f, 0xc1, 0xab, 0x43, 0x5d, 0xa0, 0x8f, 0x1f, 0x03, 0x19, 0x63, + 0x94, 0x77, 0xc8, 0xe5, 0xb4, 0x65, 0xff, 0x47, 0x87, 0x65, 0xc5, 0x70, 0xf3, 0x8e, 0xe7, 0x76, + 0x94, 0x87, 0x96, 0x6f, 0xa7, 0x0e, 0xf5, 0xc9, 0xc5, 0x9b, 0xc3, 0x7d, 0x59, 0x4c, 0xc7, 0xf7, + 0x95, 0x61, 0x85, 0x81, 0x2f, 0x41, 0x29, 0x49, 0x4a, 0x2e, 0xc1, 0x08, 0x7e, 0x80, 0xe2, 0x9e, + 0x90, 0xe0, 0x80, 0xe5, 0xe7, 0xef, 0x89, 0xb9, 0x83, 0x2e, 0xdf, 0xf8, 0xba, 0xaf, 0xab, 0x15, + 0x85, 0xcb, 0x37, 0x37, 0x0e, 0x48, 0xab, 0x16, 0x13, 0x44, 0xc6, 0x9f, 0xe7, 0xe0, 0x5c, 0x5f, + 0x25, 0x03, 0xd9, 0xd0, 0x07, 0xec, 0xe5, 0xe3, 0xb4, 0x12, 0xc7, 0x8e, 0xd5, 0xf9, 0x9f, 0x94, + 0x73, 0xff, 0x3d, 0x98, 0x6a, 0xf4, 0xb6, 0x92, 0x57, 0x3b, 0x1e, 0x90, 0x41, 0x81, 0xab, 0x27, + 0x98, 0x8a, 0xcf, 0xda, 0x2f, 0xcd, 0x17, 0x84, 0x99, 0x8c, 0x62, 0x32, 0x16, 0xf9, 0x24, 0xa6, + 0x5d, 0xde, 0x75, 0x22, 0xe3, 0xd7, 0xf3, 0xd9, 0x77, 0xe4, 0xdb, 0xd5, 0x8d, 0x93, 0xdc, 0x91, + 0x6f, 0x57, 0x37, 0x8e, 0x6f, 0xfb, 0x7f, 0x25, 0xdb, 0x8e, 0xaf, 0xb9, 0x62, 0xc7, 0x93, 0x3a, + 0x52, 0xf1, 0x9a, 0x2b, 0x77, 0xc7, 0x40, 0x7f, 0xcd, 0x95, 0xc8, 0xe4, 0x4d, 0x98, 0x58, 0xf5, + 0xb8, 0x37, 0xba, 0x6c, 0x31, 0x77, 0xda, 0x93, 0x40, 0x75, 0x7b, 0x8c, 0x30, 0xd9, 0xb5, 0x44, + 0x1f, 0x78, 0x69, 0x19, 0x87, 0xd7, 0x92, 0xc4, 0x74, 0xd1, 0x35, 0x89, 0x3a, 0x99, 0xf1, 0x9f, + 0x8d, 0x82, 0x71, 0xbc, 0x1e, 0x84, 0x7c, 0xa0, 0xf7, 0xdd, 0xd5, 0xa1, 0x35, 0x28, 0x43, 0x6d, + 0xb9, 0x95, 0x9e, 0xe3, 0xd2, 0x4e, 0x53, 0x77, 0x25, 0x17, 0x30, 0x75, 0x0b, 0x94, 0x78, 0x1f, + 0xc7, 0xb3, 0xeb, 0xfc, 0x7f, 0x5d, 0x88, 0x97, 0x5a, 0xe2, 0x68, 0xcc, 0x7d, 0x8c, 0xa3, 0x91, + 0xdc, 0x85, 0x92, 0x0a, 0x51, 0x9e, 0x75, 0x51, 0x72, 0xd1, 0x18, 0x25, 0x3e, 0x2a, 0x45, 0xa8, + 0x9f, 0xaf, 0x85, 0xe1, 0xcf, 0xd7, 0x58, 0x7c, 0xc7, 0xfa, 0x47, 0xd2, 0xe2, 0x7b, 0xd2, 0x7b, + 0x53, 0x41, 0x97, 0xae, 0xe7, 0x81, 0x38, 0xb4, 0x46, 0x75, 0xd7, 0xf3, 0x8c, 0x83, 0x4b, 0x45, + 0x97, 0xde, 0xf3, 0xf8, 0x53, 0x71, 0x1e, 0x8d, 0xbc, 0xe7, 0x39, 0x7d, 0x96, 0xf7, 0x7c, 0x44, + 0xc2, 0x0e, 0x40, 0xb3, 0xd7, 0xe1, 0xe1, 0x77, 0xc7, 0xe3, 0x03, 0xd0, 0xef, 0x75, 0xac, 0x64, + 0x08, 0xde, 0x08, 0xd1, 0xf8, 0xbb, 0x23, 0xd9, 0xc2, 0x41, 0xa4, 0x2a, 0x3b, 0x89, 0x70, 0x10, + 0x11, 0x7d, 0x3a, 0x33, 0xf5, 0x3e, 0xcc, 0xd7, 0xd1, 0x74, 0x34, 0x3c, 0x90, 0x66, 0x5b, 0xf7, + 0xcd, 0x55, 0x31, 0xc4, 0xa8, 0x72, 0x72, 0x45, 0x71, 0x64, 0xfa, 0x65, 0xf5, 0x7c, 0x4d, 0xe5, + 0x94, 0x41, 0x7f, 0xfe, 0xef, 0x48, 0x8d, 0x9a, 0x3a, 0x08, 0xf7, 0xef, 0x47, 0x73, 0x39, 0x31, + 0x08, 0xbd, 0x9e, 0x36, 0x8c, 0x3a, 0x09, 0xdf, 0x7b, 0xa5, 0xb6, 0x02, 0x99, 0x28, 0xb2, 0xa2, + 0xa2, 0xe3, 0x48, 0x70, 0x49, 0x10, 0x91, 0x1d, 0x38, 0x17, 0x8b, 0xd2, 0xca, 0x4d, 0x01, 0x39, + 0xf2, 0x06, 0x5f, 0x39, 0x3a, 0x2c, 0xbf, 0xac, 0x88, 0xe2, 0xea, 0x85, 0x23, 0xc1, 0xbd, 0x3f, + 0x2f, 0xb6, 0xdf, 0x2e, 0xf9, 0x76, 0xa7, 0xb9, 0xab, 0xcc, 0x79, 0xdc, 0x6f, 0xb7, 0x10, 0x9a, + 0xf2, 0xff, 0x8d, 0x91, 0x8d, 0x9f, 0xcc, 0xc3, 0x0c, 0x3f, 0xab, 0xf9, 0xc3, 0xde, 0x53, 0xfb, + 0x68, 0xfa, 0xb6, 0xf6, 0x68, 0x2a, 0x43, 0x55, 0xa9, 0x4d, 0x1b, 0xea, 0xc9, 0x74, 0x17, 0x48, + 0x9a, 0x86, 0x98, 0x30, 0xa5, 0x42, 0x07, 0xbf, 0x96, 0x5e, 0x8f, 0xa3, 0x9a, 0x09, 0x51, 0x09, + 0x9f, 0xac, 0x03, 0x53, 0xe3, 0x61, 0xfc, 0x95, 0x3c, 0x4c, 0x2b, 0xc6, 0x2d, 0x4f, 0x6d, 0xc7, + 0x7f, 0x49, 0xeb, 0xf8, 0x85, 0xc8, 0xf9, 0x29, 0x6a, 0xd9, 0x50, 0xfd, 0xde, 0x83, 0xb9, 0x14, + 0x49, 0xd2, 0x46, 0x28, 0x37, 0x8c, 0x8d, 0xd0, 0xeb, 0xe9, 0x10, 0x49, 0x3c, 0xd2, 0x78, 0x14, + 0x30, 0x43, 0x8d, 0xc9, 0xf4, 0xd3, 0x79, 0x38, 0x25, 0x7e, 0x61, 0x4c, 0x41, 0x2e, 0xac, 0x3e, + 0xb5, 0x63, 0x51, 0xd1, 0xc6, 0xa2, 0xac, 0x8f, 0x85, 0xd2, 0xc0, 0xfe, 0x43, 0x62, 0xfc, 0x38, + 0xc0, 0x42, 0x3f, 0x82, 0xa1, 0x7d, 0x8c, 0x63, 0x0f, 0xae, 0xfc, 0x10, 0x1e, 0x5c, 0xab, 0x50, + 0xc2, 0xaa, 0x44, 0xd4, 0xb0, 0xe0, 0xbe, 0x59, 0x17, 0x9d, 0x84, 0xfa, 0x05, 0x1e, 0xf8, 0x51, + 0x44, 0x31, 0x0b, 0x12, 0x3a, 0x8f, 0x14, 0x25, 0xf9, 0xd5, 0x1c, 0xcc, 0x20, 0x70, 0xf9, 0x11, + 0xed, 0x84, 0xc8, 0x6c, 0x44, 0x38, 0x1c, 0x45, 0x6f, 0xaa, 0x8d, 0xd0, 0x77, 0x3b, 0x3b, 0xe2, + 0x51, 0x75, 0x4b, 0x3c, 0xaa, 0xbe, 0xc3, 0x1f, 0x83, 0xaf, 0x36, 0xbd, 0xf6, 0xb5, 0x1d, 0xdf, + 0x7e, 0xe4, 0x72, 0xbb, 0x2d, 0xbb, 0x75, 0x2d, 0x4e, 0x90, 0xd1, 0x75, 0x13, 0xa9, 0x2b, 0x04, + 0x2b, 0x7c, 0xb0, 0xe6, 0x1f, 0x4a, 0xb1, 0xda, 0xa4, 0x6a, 0x46, 0xff, 0x22, 0xf2, 0x43, 0x70, + 0x96, 0xc7, 0xf2, 0x61, 0x37, 0x7c, 0xb7, 0xd3, 0xf3, 0x7a, 0xc1, 0x92, 0xdd, 0xdc, 0x63, 0x62, + 0x3e, 0x77, 0x9a, 0xc4, 0x96, 0x37, 0xa3, 0x42, 0x6b, 0x8b, 0x97, 0x6a, 0x4e, 0xe2, 0xd9, 0x0c, + 0xc8, 0x0a, 0xcc, 0xf1, 0xa2, 0x4a, 0x2f, 0xf4, 0x1a, 0x4d, 0xbb, 0xe5, 0x76, 0x76, 0x50, 0x96, + 0x28, 0x72, 0x51, 0xc6, 0xee, 0x85, 0x9e, 0x15, 0x70, 0xb8, 0xaa, 0xa9, 0x49, 0x11, 0x91, 0x3a, + 0xcc, 0x9a, 0xd4, 0x76, 0xee, 0xd9, 0x8f, 0xab, 0x76, 0xd7, 0x6e, 0xba, 0x21, 0x0f, 0x2e, 0x58, + 0xe0, 0x02, 0x9d, 0x4f, 0x6d, 0xc7, 0x6a, 0xdb, 0x8f, 0xad, 0xa6, 0x28, 0xd4, 0xb5, 0xfd, 0x1a, + 0x5d, 0xc4, 0xca, 0xed, 0x44, 0xac, 0x26, 0x92, 0xac, 0xdc, 0x4e, 0x7f, 0x56, 0x31, 0x9d, 0x64, + 0xb5, 0x69, 0xfb, 0x3b, 0x34, 0xe4, 0x66, 0xcf, 0x70, 0x31, 0x77, 0x39, 0xa7, 0xb0, 0x0a, 0xb1, + 0xcc, 0x42, 0x13, 0xe8, 0x24, 0x2b, 0x85, 0x8e, 0xcd, 0xbc, 0x87, 0xbe, 0x1b, 0x52, 0xb5, 0x85, + 0x93, 0xf8, 0x59, 0xd8, 0xff, 0x68, 0x30, 0xde, 0xaf, 0x89, 0x29, 0xca, 0x98, 0x9b, 0xd2, 0xc8, + 0xa9, 0x14, 0xb7, 0xec, 0x56, 0xa6, 0x28, 0x23, 0x6e, 0x6a, 0x3b, 0xa7, 0xb1, 0x9d, 0x0a, 0xb7, + 0x3e, 0x0d, 0x4d, 0x51, 0x92, 0x35, 0xd6, 0x69, 0x21, 0x93, 0x9b, 0xbc, 0x8e, 0x30, 0xfb, 0x9e, + 0xc1, 0x4f, 0x7b, 0x49, 0xd8, 0x2e, 0x96, 0x7c, 0x59, 0x6c, 0x65, 0x18, 0x81, 0x27, 0x89, 0xc9, + 0x5f, 0x80, 0xd9, 0xfb, 0x01, 0xbd, 0x55, 0xdf, 0x68, 0xc8, 0xd0, 0x3f, 0xa8, 0x5c, 0x9c, 0x59, + 0xbc, 0x7e, 0xcc, 0xa6, 0x73, 0x55, 0xa5, 0xc1, 0x7c, 0x13, 0x7c, 0xdc, 0x7a, 0x01, 0xb5, 0xb6, + 0xdd, 0x6e, 0x10, 0xc5, 0x51, 0x53, 0xc7, 0x2d, 0x51, 0x95, 0xb1, 0x02, 0x73, 0x29, 0x36, 0x64, + 0x06, 0x80, 0x01, 0xad, 0xfb, 0x6b, 0x8d, 0xe5, 0xcd, 0xd2, 0x33, 0xa4, 0x04, 0x53, 0xf8, 0x7b, + 0x79, 0xad, 0xb2, 0xb4, 0xba, 0x5c, 0x2b, 0xe5, 0xc8, 0x1c, 0x4c, 0x23, 0xa4, 0x56, 0x6f, 0x70, + 0x50, 0x9e, 0x47, 0x1b, 0x37, 0x4b, 0x7c, 0xe9, 0x86, 0x6c, 0x01, 0xe0, 0x99, 0x62, 0xfc, 0xb5, + 0x3c, 0x9c, 0x93, 0xc7, 0x0a, 0x0d, 0x99, 0xe0, 0xe8, 0x76, 0x76, 0x9e, 0xf2, 0xd3, 0xe1, 0x96, + 0x76, 0x3a, 0xbc, 0x94, 0x38, 0xa9, 0x13, 0xad, 0x1c, 0x70, 0x44, 0xfc, 0xce, 0x04, 0x5c, 0x18, + 0x48, 0x45, 0xbe, 0xc2, 0x4e, 0x73, 0x97, 0x76, 0xc2, 0xba, 0xd3, 0xa2, 0x9b, 0x6e, 0x9b, 0x7a, + 0xbd, 0x50, 0xb8, 0x19, 0xbc, 0x88, 0xfa, 0x3c, 0x2c, 0xb4, 0x5c, 0xa7, 0x45, 0xad, 0x90, 0x17, + 0x6b, 0xd3, 0x2d, 0x4d, 0xcd, 0x58, 0x46, 0xb9, 0x6f, 0xea, 0x9d, 0x90, 0xfa, 0x8f, 0xd0, 0xa0, + 0x31, 0x62, 0xb9, 0x47, 0x69, 0xd7, 0xb2, 0x59, 0xa9, 0xe5, 0x8a, 0x62, 0x9d, 0x65, 0x8a, 0x9a, + 0xdc, 0x52, 0x58, 0x56, 0xd9, 0xed, 0xff, 0x9e, 0xfd, 0x58, 0x58, 0x58, 0x89, 0x50, 0x92, 0x11, + 0x4b, 0xee, 0xd7, 0xdc, 0xb6, 0x1f, 0x9b, 0x69, 0x12, 0xf2, 0x0d, 0x38, 0x2d, 0x0e, 0x20, 0x11, + 0x97, 0x42, 0xb6, 0x98, 0x47, 0xbd, 0x78, 0xe5, 0xe8, 0xb0, 0x7c, 0x56, 0x06, 0xe1, 0x94, 0x91, + 0x48, 0xb2, 0x5a, 0x9d, 0xcd, 0x85, 0x6c, 0xb2, 0x03, 0x39, 0xd1, 0x1d, 0xf7, 0x68, 0x10, 0x48, + 0x87, 0x3b, 0x71, 0x33, 0x56, 0x3b, 0xd3, 0x6a, 0xf3, 0x72, 0xb3, 0x2f, 0x25, 0x59, 0x81, 0x99, + 0x87, 0x74, 0x4b, 0x1d, 0x9f, 0xb1, 0x68, 0xab, 0x2a, 0xed, 0xd3, 0xad, 0xfe, 0x83, 0x93, 0xa0, + 0x23, 0x2e, 0xbe, 0x0f, 0x3c, 0x3e, 0x58, 0x75, 0x83, 0x90, 0x76, 0xa8, 0x8f, 0xf1, 0x8e, 0xc6, + 0x71, 0x33, 0x58, 0x88, 0x25, 0x64, 0xbd, 0x7c, 0xe9, 0x85, 0xa3, 0xc3, 0xf2, 0x05, 0xee, 0xb9, + 0xda, 0x12, 0x70, 0x2b, 0x91, 0x39, 0x26, 0xcd, 0x95, 0x7c, 0x0b, 0x66, 0x4d, 0xaf, 0x17, 0xba, + 0x9d, 0x9d, 0x46, 0xe8, 0xdb, 0x21, 0xdd, 0xe1, 0x07, 0x52, 0x1c, 0x58, 0x29, 0x51, 0x2a, 0x5e, + 0xa5, 0x39, 0xd0, 0x0a, 0x04, 0x54, 0x3b, 0x11, 0x74, 0x02, 0xf2, 0x4d, 0x98, 0xe1, 0x11, 0x09, + 0xa2, 0x0a, 0x26, 0xb4, 0xa8, 0xf7, 0x7a, 0xe1, 0x83, 0xeb, 0xc2, 0x20, 0x06, 0xa1, 0x59, 0x15, + 0x24, 0xb8, 0x91, 0xaf, 0x89, 0xce, 0xda, 0x70, 0x3b, 0x3b, 0xd1, 0x34, 0x06, 0xec, 0xf9, 0x37, + 0xe2, 0x2e, 0xe9, 0xb2, 0xcf, 0x95, 0xd3, 0xb8, 0x8f, 0x75, 0x5f, 0x9a, 0x0f, 0x09, 0xe1, 0x42, + 0x25, 0x08, 0xdc, 0x20, 0x14, 0xce, 0x38, 0xcb, 0x8f, 0x69, 0xb3, 0xc7, 0x90, 0xd9, 0xf5, 0x96, + 0xfa, 0xdc, 0x1c, 0x7c, 0x74, 0xe9, 0xea, 0xd1, 0x61, 0xf9, 0x55, 0x1b, 0x11, 0x2d, 0xe1, 0xbf, + 0x63, 0x51, 0x89, 0x6a, 0xed, 0x73, 0x5c, 0xa5, 0x0d, 0x83, 0x99, 0x92, 0x6f, 0xc2, 0x99, 0xaa, + 0x1d, 0xd0, 0x7a, 0x27, 0xa0, 0x9d, 0xc0, 0x0d, 0xdd, 0x47, 0x54, 0x74, 0x2a, 0x1e, 0x7e, 0x45, + 0xcc, 0xb1, 0x63, 0x34, 0xed, 0x80, 0x2d, 0xcc, 0x08, 0xc5, 0x12, 0x83, 0xa2, 0x54, 0xd3, 0x87, + 0x0b, 0x31, 0x61, 0xa6, 0xd1, 0x58, 0xa9, 0xb9, 0x76, 0xb4, 0xae, 0xa6, 0xb1, 0xbf, 0x5e, 0x45, + 0xd5, 0x5e, 0xb0, 0x6b, 0x39, 0xae, 0x1d, 0x2d, 0xa8, 0x3e, 0x9d, 0x95, 0xe0, 0x60, 0x1c, 0xe6, + 0xa0, 0x94, 0x1c, 0x4a, 0xf2, 0x55, 0x98, 0xe0, 0xa6, 0x71, 0x34, 0xd8, 0x15, 0x0e, 0xfa, 0xd2, + 0xd2, 0x2a, 0x82, 0xeb, 0x44, 0xc2, 0x39, 0x8e, 0x1b, 0xde, 0x51, 0xd5, 0xd0, 0x06, 0x9d, 0xe3, + 0x24, 0x11, 0x71, 0x60, 0x8a, 0x8f, 0x16, 0xc5, 0xa8, 0x6a, 0xc2, 0x42, 0xfa, 0x05, 0x75, 0x75, + 0x88, 0xa2, 0x04, 0x7f, 0x7c, 0x35, 0x14, 0x73, 0x82, 0x23, 0x68, 0x55, 0x68, 0x5c, 0x97, 0x00, + 0x8a, 0x92, 0xd0, 0x38, 0x07, 0x67, 0xfb, 0x7c, 0xb3, 0xf1, 0x08, 0x8d, 0x10, 0xfa, 0xd4, 0x48, + 0xbe, 0x0a, 0xa7, 0x90, 0xb0, 0xea, 0x75, 0x3a, 0xb4, 0x19, 0xe2, 0x76, 0x24, 0xb5, 0xef, 0x05, + 0x6e, 0x24, 0xc3, 0xdb, 0xdb, 0x8c, 0x10, 0xac, 0xa4, 0x12, 0x3e, 0x93, 0x83, 0xf1, 0x0b, 0x79, + 0x58, 0x10, 0x3b, 0x9c, 0x49, 0x9b, 0x9e, 0xef, 0x3c, 0xfd, 0x27, 0xea, 0xb2, 0x76, 0xa2, 0xbe, + 0x18, 0x45, 0x64, 0xc9, 0x6a, 0xe4, 0x80, 0x03, 0xf5, 0xd7, 0x73, 0xf0, 0xdc, 0x20, 0x22, 0xd6, + 0x3b, 0x51, 0x14, 0xb9, 0x89, 0x54, 0xb4, 0xb8, 0x2e, 0xcc, 0xe3, 0x80, 0x56, 0x77, 0x69, 0x73, + 0x2f, 0x58, 0xf1, 0x82, 0x10, 0x1d, 0x34, 0xf2, 0x7d, 0xde, 0xba, 0x5f, 0xcf, 0x7c, 0xeb, 0x3e, + 0xc3, 0x67, 0x59, 0x13, 0x79, 0xf0, 0x38, 0x77, 0x7b, 0xf4, 0x20, 0x30, 0xb3, 0x58, 0xa3, 0xb1, + 0x7d, 0xa5, 0x17, 0xee, 0x6e, 0xf8, 0x74, 0x9b, 0xfa, 0xb4, 0xd3, 0xa4, 0x9f, 0x31, 0x63, 0x7b, + 0xbd, 0x71, 0x43, 0x69, 0x30, 0x7e, 0x7d, 0x0a, 0x4e, 0x65, 0x91, 0xb1, 0x7e, 0x51, 0x2e, 0xcd, + 0xc9, 0x14, 0x80, 0x3f, 0x96, 0x83, 0xa9, 0x06, 0x6d, 0x7a, 0x1d, 0xe7, 0x16, 0x1a, 0x23, 0x89, + 0xde, 0xb1, 0xb8, 0xd0, 0xc0, 0xe0, 0xd6, 0x76, 0xc2, 0x4a, 0xe9, 0xa3, 0xc3, 0xf2, 0x97, 0x87, + 0xbb, 0xab, 0x36, 0x3d, 0x8c, 0xaa, 0x12, 0x62, 0x88, 0xfa, 0xa8, 0x0a, 0x7c, 0x1c, 0xd4, 0x2a, + 0x25, 0x4b, 0x30, 0x2d, 0x96, 0xab, 0xa7, 0x06, 0x11, 0xe4, 0x41, 0x6b, 0x64, 0x41, 0x4a, 0x75, + 0xad, 0x91, 0x90, 0x1b, 0x50, 0xb8, 0xbf, 0x78, 0x4b, 0x8c, 0x81, 0x0c, 0xf2, 0x7f, 0x7f, 0xf1, + 0x16, 0xaa, 0xc3, 0xd8, 0x15, 0x63, 0xba, 0xb7, 0xa8, 0x19, 0xf9, 0xdc, 0x5f, 0xbc, 0x45, 0xfe, + 0x12, 0x9c, 0xae, 0xb9, 0x81, 0xa8, 0x82, 0xbb, 0x7d, 0x38, 0xe8, 0xe6, 0x38, 0xd6, 0x67, 0xf6, + 0x7e, 0x21, 0x73, 0xf6, 0xbe, 0xe0, 0x44, 0x4c, 0x2c, 0xee, 0x53, 0xe2, 0x24, 0x83, 0x25, 0x66, + 0xd7, 0x43, 0x3e, 0x84, 0x19, 0x54, 0x66, 0xa3, 0x27, 0x0c, 0x86, 0xb9, 0x1e, 0xef, 0x53, 0xf3, + 0xe7, 0x32, 0x6b, 0x3e, 0xcf, 0x83, 0x25, 0xa0, 0x3f, 0x0d, 0x86, 0xc4, 0xd6, 0x6e, 0xfd, 0x1a, + 0x67, 0x72, 0x07, 0x66, 0x85, 0xf8, 0xb5, 0xbe, 0xbd, 0xb9, 0x4b, 0x6b, 0xf6, 0x81, 0xb0, 0xcf, + 0xc1, 0x1b, 0x9d, 0x90, 0xd9, 0x2c, 0x6f, 0xdb, 0x0a, 0x77, 0xa9, 0xe5, 0xd8, 0x9a, 0xa0, 0x92, + 0x20, 0x24, 0xdf, 0x85, 0xc9, 0x55, 0xaf, 0xc9, 0x24, 0x6f, 0xdc, 0x19, 0xb8, 0xc9, 0xce, 0x07, + 0x98, 0x64, 0x8e, 0x83, 0x13, 0xe2, 0xd4, 0x47, 0x87, 0xe5, 0xb7, 0x4f, 0x3a, 0x69, 0x94, 0x0a, + 0x4c, 0xb5, 0x36, 0x52, 0x85, 0xe2, 0x43, 0xba, 0xc5, 0x5a, 0x9b, 0x4c, 0x40, 0x25, 0xc1, 0xc2, + 0x98, 0x4f, 0xfc, 0xd2, 0x8c, 0xf9, 0x04, 0x8c, 0xf8, 0x30, 0x87, 0xfd, 0xb3, 0x61, 0x07, 0xc1, + 0xbe, 0xe7, 0x3b, 0x98, 0x69, 0xa0, 0x9f, 0x35, 0xd0, 0x62, 0x66, 0xe7, 0x3f, 0xc7, 0x3b, 0xbf, + 0xab, 0x70, 0x50, 0x05, 0xc8, 0x14, 0x7b, 0xf2, 0x2d, 0x98, 0x11, 0x1e, 0xfe, 0xf7, 0x6e, 0x55, + 0x70, 0x55, 0x4e, 0x69, 0xce, 0xa2, 0x7a, 0x21, 0x97, 0x52, 0x45, 0xc0, 0x00, 0xa9, 0x81, 0xb2, + 0xda, 0xdb, 0xb6, 0xae, 0xf4, 0x57, 0x49, 0xc8, 0x06, 0x4c, 0xd6, 0x30, 0x0d, 0x2a, 0xba, 0xb4, + 0x09, 0x93, 0xf2, 0x28, 0x83, 0x4e, 0x5c, 0xc2, 0x75, 0x31, 0x22, 0x63, 0x2a, 0x3a, 0xc8, 0xe9, + 0x66, 0xbe, 0x11, 0x22, 0xb9, 0x09, 0x85, 0x7a, 0x6d, 0x43, 0x58, 0x94, 0x4b, 0x4f, 0xb1, 0xba, + 0xb3, 0x21, 0xf3, 0x8d, 0xa0, 0xfd, 0x9c, 0xeb, 0x68, 0xf6, 0xe8, 0xf5, 0xda, 0x06, 0xd9, 0x86, + 0x69, 0xec, 0x80, 0x15, 0x6a, 0xf3, 0xbe, 0x9d, 0xed, 0xd3, 0xb7, 0x57, 0x33, 0xfb, 0x76, 0x81, + 0xf7, 0xed, 0xae, 0xa0, 0xd6, 0x12, 0x28, 0xa8, 0x6c, 0x99, 0x48, 0x2b, 0x92, 0xba, 0xc8, 0xb0, + 0xff, 0x9b, 0xab, 0x68, 0x1f, 0x24, 0x44, 0x5a, 0x99, 0x03, 0x26, 0xca, 0x43, 0xd0, 0xd7, 0x61, + 0x25, 0xcd, 0x87, 0x7c, 0x09, 0x46, 0xd6, 0xf7, 0x42, 0x5b, 0xd8, 0x8e, 0xcb, 0x7e, 0x64, 0x20, + 0xd9, 0x7c, 0xd4, 0x42, 0x7a, 0x7b, 0x5a, 0x38, 0x2d, 0xa4, 0x21, 0x8b, 0x30, 0xbe, 0x51, 0x7f, + 0xd0, 0x68, 0x79, 0xe1, 0x02, 0x89, 0xee, 0x49, 0xa4, 0xeb, 0x3e, 0xb2, 0x82, 0x96, 0xa7, 0x27, + 0x86, 0x92, 0x88, 0x6c, 0xf8, 0x56, 0x6c, 0xdf, 0xd9, 0xb7, 0x7d, 0xf4, 0x44, 0x9e, 0xd7, 0xaa, + 0x55, 0x4a, 0xf8, 0xf0, 0xed, 0x0a, 0x40, 0xc2, 0x3d, 0x59, 0x65, 0x21, 0x34, 0x0c, 0x73, 0x62, + 0x9a, 0x88, 0xa6, 0xdd, 0xbb, 0x55, 0x31, 0xfe, 0x5e, 0x0e, 0x37, 0x4c, 0xf2, 0x2a, 0x46, 0xdc, + 0x89, 0x5e, 0xaa, 0x50, 0x57, 0x6a, 0x77, 0x13, 0x31, 0xae, 0x39, 0x0a, 0x79, 0x1d, 0xc6, 0x6e, + 0xd9, 0x4d, 0x1a, 0xca, 0xb7, 0x71, 0x44, 0xde, 0x46, 0x88, 0xaa, 0x58, 0xe5, 0x38, 0x4c, 0x96, + 0xe3, 0x13, 0xa9, 0x12, 0x67, 0xf2, 0xad, 0x56, 0xe4, 0xd3, 0x38, 0xca, 0x72, 0x62, 0x02, 0x2a, + 0xa9, 0x7e, 0x13, 0xc6, 0xeb, 0x99, 0x1c, 0x8c, 0x3f, 0xcb, 0xc5, 0x3b, 0x00, 0x79, 0x05, 0x46, + 0xcc, 0x8d, 0xe8, 0xfb, 0xb9, 0x57, 0x6e, 0xe2, 0xf3, 0x11, 0x81, 0x7c, 0x0d, 0x4e, 0x2b, 0x7c, + 0x52, 0x96, 0xf4, 0x2f, 0xa3, 0xdb, 0xa8, 0xf2, 0x25, 0xd9, 0xe6, 0xf4, 0xd9, 0x3c, 0x50, 0x70, + 0x8d, 0x0b, 0x6a, 0xb4, 0xe3, 0x72, 0xde, 0x4a, 0x63, 0x55, 0xde, 0x0e, 0x22, 0x24, 0x1b, 0x9b, + 0xc5, 0x81, 0x7b, 0x8e, 0x1a, 0xbf, 0x9d, 0xd3, 0x56, 0x76, 0x94, 0xfa, 0x34, 0x77, 0x4c, 0xea, + 0xd3, 0xb7, 0x00, 0x2a, 0xbd, 0xd0, 0x5b, 0xee, 0xf8, 0x5e, 0x8b, 0x6b, 0x2c, 0x44, 0x98, 0x77, + 0xd4, 0xc3, 0x52, 0x04, 0x6b, 0x0e, 0x6e, 0x11, 0x72, 0xa6, 0xd3, 0x41, 0xe1, 0xe3, 0x3a, 0x1d, + 0x18, 0x3f, 0x9f, 0xd7, 0xe6, 0x36, 0x93, 0xc8, 0xe4, 0xf2, 0x50, 0xac, 0xb3, 0xd2, 0xcb, 0x23, + 0x5e, 0x1c, 0xff, 0x76, 0x0e, 0xce, 0x70, 0xeb, 0xfd, 0xb5, 0x5e, 0x7b, 0x8b, 0xfa, 0x0f, 0xec, + 0x96, 0xeb, 0x70, 0xcf, 0x68, 0x2e, 0x6c, 0x5e, 0x4e, 0x2f, 0x94, 0x6c, 0x7c, 0x7e, 0x29, 0xe4, + 0xde, 0x04, 0x56, 0x07, 0x0b, 0xad, 0x47, 0x51, 0xa9, 0x7a, 0x29, 0xcc, 0xa6, 0x27, 0x75, 0x98, + 0xdc, 0x70, 0x3b, 0x98, 0x02, 0x21, 0xf6, 0x8b, 0x43, 0x4d, 0x4b, 0x17, 0x15, 0xac, 0xcd, 0x5d, + 0x3a, 0x60, 0xa3, 0x51, 0x69, 0x8d, 0xdf, 0xc8, 0xc1, 0x0b, 0xc7, 0x7e, 0x30, 0xb9, 0x06, 0xe3, + 0x32, 0x54, 0x7f, 0x0e, 0xc7, 0x10, 0xcd, 0x63, 0xd3, 0x61, 0xfa, 0x25, 0x16, 0xf9, 0x3a, 0x9c, + 0x56, 0x59, 0x6d, 0xfa, 0xb6, 0xab, 0x06, 0xc4, 0xcf, 0xe8, 0x80, 0x90, 0xa1, 0x24, 0x85, 0xac, + 0x6c, 0x26, 0xc6, 0xff, 0x9b, 0x53, 0xf2, 0x2a, 0x3f, 0xa5, 0xa2, 0xf7, 0x4d, 0x4d, 0xf4, 0x96, + 0x61, 0x13, 0xa3, 0x56, 0xb1, 0xb2, 0xcc, 0xeb, 0xd2, 0xac, 0x62, 0xe6, 0x8d, 0x80, 0xef, 0xe7, + 0x61, 0xf2, 0x7e, 0x40, 0x7d, 0xfe, 0xfe, 0xfa, 0xd9, 0x0a, 0x8f, 0x17, 0xb5, 0x6b, 0xa8, 0x00, + 0x66, 0x7f, 0x92, 0x43, 0xbd, 0xbc, 0x4a, 0xc1, 0x7a, 0x43, 0xc9, 0xa5, 0x86, 0xbd, 0x81, 0x59, + 0xd4, 0x10, 0xca, 0xe3, 0x66, 0xad, 0xea, 0x69, 0x15, 0x31, 0xb7, 0xe6, 0x2a, 0xf9, 0x32, 0x8c, + 0xde, 0x47, 0x2d, 0xa3, 0x1e, 0x50, 0x23, 0xe2, 0x8f, 0x85, 0x7c, 0xbf, 0xef, 0x05, 0x7a, 0xac, + 0x2f, 0x4e, 0x48, 0x1a, 0x30, 0x5e, 0xf5, 0x29, 0x66, 0x49, 0x1e, 0x19, 0xde, 0x29, 0xbc, 0xc9, + 0x49, 0x92, 0x4e, 0xe1, 0x82, 0x13, 0xdb, 0xc7, 0x48, 0xdc, 0x46, 0x4c, 0x09, 0x14, 0x3c, 0xb5, + 0x83, 0xfe, 0xbe, 0x36, 0xe8, 0x17, 0x52, 0x83, 0xce, 0x9b, 0x37, 0xd4, 0xd8, 0xff, 0x6e, 0x0e, + 0xce, 0x64, 0x13, 0x92, 0x17, 0x61, 0x6c, 0x7d, 0x73, 0x43, 0xc6, 0x64, 0x11, 0x4d, 0xf1, 0xba, + 0x78, 0xc5, 0x37, 0x45, 0x11, 0x79, 0x03, 0xc6, 0xbe, 0x62, 0x56, 0xd9, 0x91, 0xa6, 0x04, 0x9d, + 0xff, 0xb6, 0x6f, 0x35, 0xf5, 0x53, 0x4d, 0x20, 0xa9, 0x63, 0x5b, 0x78, 0x62, 0x63, 0xfb, 0xd3, + 0x79, 0x98, 0xad, 0x34, 0x9b, 0x34, 0x08, 0x98, 0xbc, 0x44, 0x83, 0xf0, 0xa9, 0x1d, 0xd8, 0xec, + 0x68, 0x2b, 0x5a, 0xdb, 0x86, 0x1a, 0xd5, 0xdf, 0xcf, 0xc1, 0x69, 0x49, 0xf5, 0xc8, 0xa5, 0xfb, + 0x9b, 0xbb, 0x3e, 0x0d, 0x76, 0xbd, 0x96, 0x33, 0x74, 0x66, 0x0b, 0x26, 0x33, 0x62, 0xb8, 0x6a, + 0xf5, 0x31, 0x7e, 0x1b, 0x21, 0x9a, 0xcc, 0xc8, 0x43, 0x5a, 0x5f, 0x83, 0xf1, 0x4a, 0xb7, 0xeb, + 0x7b, 0x8f, 0xf8, 0xb2, 0x17, 0xd1, 0xfc, 0x6c, 0x0e, 0xd2, 0x7c, 0xea, 0x39, 0x88, 0x7d, 0x46, + 0x8d, 0x76, 0x78, 0x9c, 0xba, 0x69, 0xfe, 0x19, 0x0e, 0xed, 0xa8, 0xe2, 0x30, 0x96, 0x1b, 0x0d, + 0x20, 0x1b, 0xbe, 0xd7, 0xf6, 0x42, 0xea, 0xf0, 0xf6, 0x60, 0x28, 0x82, 0x63, 0xe3, 0x5a, 0x6d, + 0xba, 0x61, 0x4b, 0x8b, 0x6b, 0x15, 0x32, 0x80, 0xc9, 0xe1, 0xc6, 0xff, 0x39, 0x0a, 0x53, 0x6a, + 0xef, 0x10, 0x83, 0x87, 0xab, 0xf7, 0x7c, 0x35, 0x1e, 0x86, 0x8d, 0x10, 0x53, 0x94, 0xc4, 0x61, + 0x64, 0xf2, 0xc7, 0x86, 0x91, 0x79, 0x08, 0xd3, 0x1b, 0xbe, 0x87, 0x61, 0x07, 0x79, 0xf6, 0x7c, + 0xbe, 0x15, 0xce, 0x2b, 0xd7, 0x45, 0x36, 0x90, 0xf8, 0x8c, 0x89, 0xca, 0x92, 0xae, 0xc0, 0xb6, + 0x92, 0xb9, 0xf5, 0x75, 0x3e, 0xdc, 0x42, 0xc2, 0x0e, 0x44, 0xec, 0xd0, 0xc8, 0x42, 0x82, 0x41, + 0x74, 0x0b, 0x09, 0x06, 0x51, 0xd7, 0xda, 0xe8, 0x93, 0x5a, 0x6b, 0xe4, 0xe7, 0x73, 0x30, 0x59, + 0xe9, 0x74, 0x44, 0x78, 0x9a, 0x63, 0xfc, 0xf3, 0xbf, 0x2e, 0x8c, 0x24, 0xde, 0xfe, 0x58, 0x46, + 0x12, 0x28, 0xb7, 0x04, 0x28, 0xf4, 0xc6, 0x15, 0xaa, 0x17, 0x26, 0xe5, 0x3b, 0xc8, 0xdb, 0x50, + 0x8a, 0x26, 0x79, 0xbd, 0xe3, 0xd0, 0xc7, 0x94, 0xa7, 0xfb, 0x9a, 0x16, 0x51, 0x83, 0x55, 0x21, + 0x37, 0x89, 0x48, 0x36, 0x01, 0xec, 0x68, 0x76, 0x25, 0xf2, 0x16, 0xa6, 0xa7, 0x9f, 0x10, 0xc4, + 0xf1, 0x37, 0xbe, 0x43, 0xa9, 0x82, 0x78, 0xcc, 0x87, 0xb4, 0x61, 0x96, 0x27, 0x0d, 0x6c, 0x84, + 0xb6, 0x1f, 0x62, 0x70, 0x7c, 0x38, 0x76, 0x1c, 0x5e, 0x11, 0x6a, 0xaf, 0x67, 0x45, 0x2a, 0xc2, + 0x80, 0xd1, 0x5a, 0x19, 0x91, 0xf2, 0x93, 0xbc, 0x79, 0x8c, 0x66, 0xf3, 0x6c, 0xfa, 0x7b, 0xf9, + 0xa4, 0xff, 0xe9, 0x1c, 0x9c, 0x51, 0x27, 0x7d, 0xa3, 0xb7, 0xd5, 0x76, 0xf1, 0x5a, 0x49, 0xae, + 0xc2, 0x84, 0x98, 0x93, 0xd1, 0x7d, 0x2c, 0x1d, 0xe3, 0x3f, 0x46, 0x21, 0xcb, 0x6c, 0x1a, 0x32, + 0x1e, 0x42, 0x80, 0x9f, 0x4f, 0xec, 0x53, 0xac, 0x28, 0x4e, 0x48, 0xeb, 0xe3, 0x6f, 0x7d, 0x7e, + 0x32, 0x88, 0xf1, 0x1e, 0xcc, 0xe9, 0x23, 0xd1, 0xa0, 0x21, 0xb9, 0x02, 0xe3, 0x72, 0xf8, 0x72, + 0xd9, 0xc3, 0x27, 0xcb, 0x8d, 0x87, 0x40, 0x52, 0xf4, 0x01, 0x5a, 0x33, 0xb1, 0xab, 0x2e, 0xb7, + 0xb6, 0x93, 0x6f, 0x89, 0x29, 0xc4, 0xa5, 0x79, 0xf1, 0x7d, 0x93, 0x9a, 0x37, 0x01, 0x23, 0x35, + 0xfe, 0x6c, 0x06, 0xe6, 0x33, 0xf6, 0xdc, 0x63, 0x64, 0xa2, 0xb2, 0xbe, 0x41, 0x4c, 0x44, 0xe1, + 0x3d, 0xe4, 0xb6, 0xf0, 0x1e, 0x8c, 0x1e, 0xbb, 0x1d, 0x70, 0x5f, 0x92, 0xc4, 0x2e, 0xc0, 0xc9, + 0x3e, 0x15, 0xb9, 0x48, 0x8d, 0xc0, 0x33, 0xfa, 0xc4, 0x22, 0xf0, 0x2c, 0xc1, 0xb4, 0x68, 0x95, + 0xd8, 0xae, 0x14, 0x9b, 0x66, 0x9f, 0x17, 0x58, 0xa9, 0x6d, 0x4b, 0x27, 0xe1, 0x3c, 0x02, 0xaf, + 0xf5, 0x88, 0x0a, 0x1e, 0xe3, 0x2a, 0x0f, 0x2c, 0xc8, 0xe4, 0xa1, 0x90, 0x90, 0xff, 0x04, 0x13, + 0x96, 0x21, 0x44, 0xdd, 0xb3, 0x8a, 0x83, 0xf6, 0x2c, 0xe7, 0xc9, 0xec, 0x59, 0x17, 0xe4, 0x37, + 0x66, 0xef, 0x5d, 0x19, 0x9f, 0x45, 0x7e, 0x2d, 0x07, 0x73, 0x3c, 0x0c, 0x8c, 0xfa, 0xb1, 0x03, + 0x43, 0x7b, 0x34, 0x9f, 0xcc, 0xc7, 0x3e, 0x27, 0x12, 0xf5, 0x64, 0x7f, 0x6b, 0xfa, 0xa3, 0xc8, + 0x0f, 0x01, 0x44, 0x2b, 0x8a, 0xc7, 0x49, 0x9d, 0x5c, 0x7c, 0x2e, 0x63, 0x17, 0x88, 0x90, 0xe2, + 0xa4, 0x02, 0x61, 0x44, 0xa7, 0xa5, 0xa9, 0x8b, 0xa0, 0xe4, 0x2f, 0xc1, 0x29, 0xb6, 0x5e, 0x22, + 0x88, 0x08, 0x5a, 0xb5, 0x30, 0x89, 0xb5, 0x7c, 0xbe, 0xbf, 0x4c, 0x74, 0x35, 0x8b, 0x8c, 0x47, + 0xd5, 0x8d, 0x33, 0x06, 0x87, 0x6a, 0x7c, 0x8b, 0xcc, 0x8a, 0x30, 0x0a, 0x1c, 0x7e, 0x3d, 0x0f, + 0xfc, 0xdf, 0x67, 0x7f, 0x3b, 0x27, 0xd7, 0x02, 0xdf, 0xdf, 0x02, 0xdd, 0xb5, 0x18, 0x41, 0xe4, + 0x2b, 0x40, 0xa2, 0xf8, 0x29, 0x1c, 0x46, 0x65, 0x52, 0x00, 0xae, 0x25, 0x8e, 0xe3, 0xb0, 0xf8, + 0xb2, 0x58, 0x9d, 0x24, 0x69, 0x62, 0x42, 0xe1, 0x94, 0x68, 0x34, 0x83, 0xca, 0x6c, 0x62, 0xc1, + 0xc2, 0x8c, 0x16, 0x12, 0x2c, 0x2e, 0x89, 0x53, 0x0b, 0x2b, 0x29, 0xc9, 0x34, 0xed, 0x55, 0x16, + 0x3b, 0x72, 0x13, 0x26, 0xd0, 0xbf, 0x77, 0x45, 0xda, 0x68, 0x09, 0x7b, 0x11, 0xf4, 0x04, 0xb6, + 0x76, 0x75, 0x4b, 0xab, 0x18, 0x95, 0x5d, 0x07, 0x6a, 0xfe, 0x81, 0xd9, 0xeb, 0xa0, 0x2e, 0x57, + 0xe8, 0x3b, 0x1c, 0xff, 0xc0, 0xf2, 0x7b, 0xba, 0xef, 0x38, 0x22, 0x91, 0x6f, 0xc1, 0xe4, 0x3d, + 0xfb, 0xb1, 0xd4, 0xb0, 0x08, 0x7d, 0xed, 0xa0, 0x1d, 0xc8, 0x90, 0xad, 0x69, 0xdb, 0x8f, 0x2d, + 0xa7, 0x97, 0x8c, 0xe9, 0x8b, 0xdb, 0x90, 0xca, 0x92, 0x7c, 0x03, 0x40, 0x51, 0x30, 0x93, 0x63, + 0x2b, 0x78, 0x41, 0x06, 0xb9, 0xcb, 0x54, 0x3c, 0x23, 0x7f, 0x85, 0x61, 0x42, 0x72, 0x38, 0xf5, + 0xe9, 0x49, 0x0e, 0xa7, 0x3f, 0x3d, 0xc9, 0xe1, 0xfc, 0x16, 0x9c, 0xeb, 0xbb, 0x74, 0x32, 0x22, + 0x0f, 0x5f, 0xd3, 0x23, 0x0f, 0x9f, 0xeb, 0x77, 0xc4, 0x06, 0x7a, 0x1e, 0x89, 0xf9, 0xd2, 0xa9, + 0xfe, 0xd2, 0xc9, 0x0f, 0xf2, 0x89, 0x23, 0x57, 0x5c, 0x2c, 0x78, 0xde, 0xa1, 0x7e, 0x32, 0x49, + 0x1e, 0x53, 0xcd, 0xf2, 0x43, 0x39, 0x1f, 0x5f, 0x68, 0x12, 0xd9, 0xf9, 0xf9, 0xf1, 0xfc, 0x49, + 0x4f, 0xdf, 0x77, 0x60, 0x86, 0x67, 0x87, 0xbc, 0x4b, 0x0f, 0xf6, 0x3d, 0xdf, 0x91, 0x29, 0xd8, + 0x51, 0x06, 0x4f, 0xa5, 0x76, 0x4e, 0xe0, 0x92, 0x9a, 0x74, 0x19, 0x1d, 0xc5, 0xda, 0xcf, 0x65, + 0xee, 0x62, 0x0c, 0x61, 0x90, 0x37, 0x29, 0x79, 0x33, 0x12, 0xd4, 0xa8, 0xaf, 0x66, 0x93, 0xf0, + 0x25, 0x30, 0x43, 0x5e, 0xa3, 0xbe, 0xf1, 0x47, 0x05, 0x20, 0xbc, 0xa6, 0xaa, 0xdd, 0xb5, 0xd1, + 0xa1, 0xda, 0xc5, 0xe8, 0x4a, 0x25, 0x81, 0x63, 0x6f, 0xb5, 0xa8, 0x1a, 0x9a, 0x4c, 0xd8, 0xc4, + 0x46, 0x65, 0x56, 0xf2, 0xa2, 0x93, 0x22, 0xec, 0xb3, 0xd5, 0xe5, 0x3f, 0xc9, 0x56, 0xf7, 0x2d, + 0x78, 0xb6, 0xd2, 0xc5, 0x34, 0xb3, 0xb2, 0x96, 0x5b, 0x9e, 0x2f, 0x37, 0x29, 0xcd, 0x55, 0xcf, + 0x8e, 0xd0, 0x52, 0x5f, 0x3a, 0x88, 0x85, 0x22, 0xa7, 0xb0, 0x79, 0xd9, 0x0d, 0xd5, 0xd0, 0x0f, + 0x52, 0x4e, 0xe9, 0x62, 0x49, 0x86, 0x9c, 0xc2, 0x49, 0x24, 0x0f, 0xd7, 0x97, 0x72, 0x0a, 0xe6, + 0x4f, 0x8a, 0x79, 0xb8, 0x3e, 0xed, 0x23, 0xeb, 0x44, 0x24, 0xe4, 0x1d, 0x98, 0xac, 0xf4, 0x42, + 0x4f, 0x30, 0x16, 0xc6, 0xdc, 0xb1, 0xd9, 0xb5, 0xf8, 0x14, 0xed, 0xea, 0x13, 0xa3, 0x1b, 0x7f, + 0x5a, 0x80, 0x73, 0xe9, 0xe1, 0x15, 0xa5, 0xd1, 0xfa, 0xc8, 0x1d, 0xb3, 0x3e, 0xb2, 0x66, 0x43, + 0x3e, 0x8e, 0xee, 0xff, 0x24, 0x66, 0x03, 0xcf, 0x56, 0xfb, 0x31, 0x67, 0x43, 0x03, 0x26, 0xd5, + 0xf3, 0x6e, 0xe4, 0xe3, 0x9e, 0x77, 0x2a, 0x17, 0x76, 0xa9, 0xe7, 0x11, 0x2f, 0x46, 0xe3, 0x57, + 0xa8, 0x64, 0xb0, 0x0b, 0x8e, 0x41, 0xfe, 0x2d, 0xb8, 0xc8, 0xf7, 0xa4, 0x64, 0x63, 0x97, 0x0e, + 0x24, 0x47, 0x31, 0x70, 0x8b, 0x47, 0x87, 0xe5, 0xab, 0x5c, 0x55, 0x62, 0xa5, 0xba, 0xcd, 0xda, + 0x3a, 0xb0, 0xe4, 0x97, 0x29, 0x95, 0x1c, 0xcb, 0xdb, 0xa8, 0xc2, 0x39, 0x51, 0x1a, 0xfb, 0x5a, + 0xcb, 0x42, 0x36, 0xc8, 0x7b, 0xb1, 0xb6, 0x0b, 0x07, 0x39, 0xa1, 0xc8, 0xc2, 0x72, 0xcc, 0x73, + 0xab, 0xe4, 0x20, 0x7d, 0x23, 0xcb, 0x55, 0x86, 0x47, 0xe8, 0xe6, 0x60, 0xdd, 0x4b, 0x46, 0xea, + 0xd4, 0xf2, 0x99, 0x3a, 0x35, 0xa9, 0x94, 0x29, 0x64, 0x2a, 0x65, 0x6a, 0x30, 0xdb, 0xe8, 0x6d, + 0xc9, 0xba, 0x93, 0x6e, 0x96, 0x41, 0x6f, 0x2b, 0xab, 0x57, 0x92, 0x24, 0xc6, 0x4f, 0xe4, 0x61, + 0x6a, 0xa3, 0xd5, 0xdb, 0x71, 0x3b, 0x35, 0x3b, 0xb4, 0x9f, 0x5a, 0x35, 0xdf, 0x5b, 0x9a, 0x9a, + 0x2f, 0xf2, 0x08, 0x8b, 0x1a, 0x36, 0x94, 0x8e, 0xef, 0xe7, 0x72, 0x30, 0x1b, 0x93, 0xf0, 0xc3, + 0x7a, 0x05, 0x46, 0xd8, 0x0f, 0x71, 0xf9, 0xbd, 0x98, 0x62, 0xcc, 0x13, 0xdf, 0x45, 0x7f, 0x09, + 0xc5, 0x9b, 0x9e, 0x55, 0x0a, 0x39, 0x9c, 0xff, 0x02, 0x4c, 0xc4, 0x6c, 0x4f, 0x92, 0xf0, 0xee, + 0x37, 0x73, 0x50, 0x4a, 0xb6, 0x84, 0xdc, 0x85, 0x71, 0xc6, 0xc9, 0xa5, 0xf2, 0x5e, 0xfe, 0x52, + 0x9f, 0x36, 0x5f, 0x15, 0x68, 0xfc, 0xf3, 0xb0, 0xf3, 0x29, 0x87, 0x98, 0x92, 0xc3, 0x79, 0x13, + 0xa6, 0x54, 0xac, 0x8c, 0xaf, 0x7b, 0x5d, 0x97, 0x50, 0xce, 0x64, 0xf7, 0x83, 0x96, 0xa6, 0x4f, + 0xfb, 0x6a, 0x21, 0x7c, 0x5c, 0xd2, 0x26, 0x57, 0xe6, 0xaa, 0xc2, 0x49, 0xb3, 0x18, 0x27, 0x0d, + 0x50, 0xe7, 0x59, 0xc6, 0x84, 0x8e, 0xf0, 0xc8, 0xeb, 0x30, 0xc6, 0xeb, 0x53, 0xd3, 0x55, 0x75, + 0x11, 0xa2, 0xca, 0xc9, 0x1c, 0xc7, 0xf8, 0xeb, 0x05, 0x38, 0x13, 0x7f, 0xde, 0xfd, 0xae, 0x63, + 0x87, 0x74, 0xc3, 0xf6, 0xed, 0x76, 0x70, 0xcc, 0x0a, 0xb8, 0x9c, 0xfa, 0x34, 0x4c, 0x5f, 0x24, + 0x3f, 0x4d, 0xf9, 0x20, 0x23, 0xf1, 0x41, 0xa8, 0x03, 0xe5, 0x1f, 0x24, 0x3f, 0x83, 0xdc, 0x85, + 0x42, 0x83, 0x86, 0x62, 0xef, 0xbd, 0x94, 0xea, 0x55, 0xf5, 0xbb, 0xae, 0x36, 0x68, 0xc8, 0x07, + 0x91, 0x87, 0x73, 0xd2, 0xc2, 0xf1, 0x31, 0x2e, 0xe4, 0x21, 0x8c, 0x2d, 0x3f, 0xee, 0xd2, 0x66, + 0x28, 0xd2, 0x35, 0x5e, 0x19, 0xcc, 0x8f, 0xe3, 0x2a, 0xd9, 0x1a, 0x29, 0x02, 0xd4, 0xce, 0xe2, + 0x28, 0xe7, 0x6f, 0x42, 0x51, 0x56, 0x7e, 0x92, 0x99, 0x7b, 0xfe, 0x2d, 0x98, 0x54, 0x2a, 0x39, + 0xd1, 0xa4, 0xff, 0x65, 0xb6, 0xaf, 0x7a, 0x2d, 0x99, 0xe1, 0x71, 0x39, 0x25, 0x2b, 0x2a, 0x19, + 0x80, 0xb8, 0xac, 0x68, 0xed, 0x89, 0xa2, 0x01, 0x42, 0x63, 0x1d, 0x66, 0x1b, 0x7b, 0x6e, 0x37, + 0x0e, 0x0d, 0xab, 0x9d, 0xc8, 0x98, 0xce, 0x45, 0x5c, 0xdc, 0x93, 0x27, 0x72, 0x92, 0xce, 0xf8, + 0xf3, 0x1c, 0x8c, 0xb1, 0xbf, 0x1e, 0xdc, 0x7c, 0x4a, 0xb7, 0xcc, 0x1b, 0xda, 0x96, 0x39, 0xa7, + 0xc4, 0x65, 0xc7, 0x8d, 0xe3, 0xe6, 0x31, 0x9b, 0xe5, 0xa1, 0x18, 0x20, 0x8e, 0x4c, 0x6e, 0xc3, + 0xb8, 0xb0, 0x04, 0x12, 0x26, 0xdb, 0x6a, 0xa0, 0x77, 0x69, 0x23, 0x14, 0xdd, 0xf0, 0xbd, 0x6e, + 0x52, 0x25, 0x22, 0xa9, 0x99, 0x5c, 0x2f, 0x83, 0xf4, 0x6a, 0x79, 0x81, 0x3d, 0xf4, 0xb1, 0xe3, + 0x81, 0xca, 0x95, 0x4c, 0xde, 0x7d, 0x5c, 0xe2, 0x2b, 0xe2, 0x35, 0xa4, 0x30, 0x88, 0xc9, 0x19, + 0x99, 0x36, 0x35, 0xf3, 0xa1, 0xe4, 0x1f, 0x9f, 0xe6, 0x21, 0xbe, 0xe5, 0x87, 0xbd, 0x0b, 0x53, + 0xb7, 0x3c, 0x7f, 0xdf, 0xf6, 0x79, 0xe0, 0x56, 0x61, 0x7e, 0xc0, 0xee, 0x9f, 0xd3, 0xdb, 0x1c, + 0xce, 0x43, 0xbf, 0x7e, 0x74, 0x58, 0x1e, 0x59, 0xf2, 0xbc, 0x96, 0xa9, 0xa1, 0x93, 0x75, 0x98, + 0xbe, 0x67, 0x3f, 0x56, 0x6e, 0xce, 0xdc, 0x69, 0xe6, 0x0a, 0x9b, 0xc0, 0xec, 0xea, 0x7d, 0xbc, + 0x59, 0x96, 0x4e, 0x4f, 0x5c, 0x98, 0xd9, 0xf0, 0xfc, 0x50, 0x54, 0xe2, 0x76, 0x76, 0x44, 0x63, + 0xd3, 0x86, 0x65, 0xd7, 0x32, 0x0d, 0xcb, 0xce, 0x75, 0x3d, 0x3f, 0xb4, 0xb6, 0x23, 0x72, 0x2d, + 0x0e, 0x9a, 0xc6, 0x98, 0xbc, 0x0b, 0x73, 0x4a, 0x74, 0xc9, 0x5b, 0x9e, 0xdf, 0xb6, 0xa5, 0x64, + 0x8f, 0xca, 0x64, 0xb4, 0x7f, 0xd9, 0x46, 0xb0, 0x99, 0xc6, 0x24, 0x5f, 0xcb, 0x72, 0x43, 0x1a, + 0x8d, 0x2d, 0xd3, 0x32, 0xdc, 0x90, 0xfa, 0x59, 0xa6, 0xa5, 0x1d, 0x92, 0x76, 0x06, 0x59, 0xae, + 0x16, 0x97, 0xae, 0x8b, 0x3b, 0xfc, 0xf1, 0x96, 0xa9, 0xd1, 0xb8, 0xf5, 0xb1, 0x50, 0x5d, 0x84, + 0xc2, 0xd2, 0xc6, 0x2d, 0x7c, 0x02, 0x91, 0x86, 0x3f, 0x9d, 0x5d, 0xbb, 0xd3, 0x44, 0x89, 0x5b, + 0x58, 0x8b, 0xab, 0x3b, 0xf2, 0xd2, 0xc6, 0x2d, 0x62, 0xc3, 0xfc, 0x06, 0xf5, 0xdb, 0x6e, 0xf8, + 0xd5, 0xeb, 0xd7, 0x95, 0x81, 0x2a, 0xe2, 0xa7, 0x5d, 0x13, 0x9f, 0x56, 0xee, 0x22, 0x8a, 0xf5, + 0xf8, 0xfa, 0xf5, 0xcc, 0xe1, 0x88, 0x3e, 0x2c, 0x8b, 0x17, 0xdb, 0x19, 0xef, 0xd9, 0x8f, 0x63, + 0x23, 0xff, 0x40, 0x38, 0x74, 0x5e, 0x90, 0x13, 0x2b, 0x76, 0x10, 0xd0, 0x76, 0x46, 0x9d, 0x88, + 0x5d, 0x98, 0xe2, 0xe9, 0x15, 0x08, 0x57, 0x98, 0xf3, 0x52, 0x2f, 0x24, 0xbd, 0x7e, 0x55, 0xa9, + 0x5f, 0x41, 0x27, 0xf7, 0xa3, 0x6b, 0x1f, 0xbf, 0x36, 0x89, 0xa4, 0xa0, 0xd7, 0xd4, 0x6b, 0x1f, + 0xd7, 0xc6, 0x68, 0xcd, 0x9a, 0x8d, 0x74, 0x05, 0xdc, 0xeb, 0xc1, 0xd4, 0xb9, 0xa4, 0x6f, 0x93, + 0x53, 0x27, 0xbf, 0x4d, 0x52, 0x18, 0x59, 0xf5, 0x9a, 0x7b, 0x22, 0x78, 0xdb, 0x57, 0xd8, 0x72, + 0x6f, 0x79, 0xcd, 0xbd, 0x27, 0x67, 0x91, 0x8b, 0xec, 0xc9, 0x1a, 0xfb, 0x54, 0x36, 0x0b, 0x44, + 0x9f, 0x08, 0x2b, 0xcf, 0x53, 0xd1, 0x75, 0x4a, 0x29, 0xe3, 0x82, 0x0f, 0x9f, 0x34, 0xb2, 0x6b, + 0x4d, 0x9d, 0x9c, 0x50, 0x28, 0xd5, 0x68, 0xb0, 0x17, 0x7a, 0xdd, 0x6a, 0xcb, 0xed, 0x6e, 0x79, + 0xb6, 0x2f, 0xa3, 0x04, 0xa7, 0xd7, 0xf7, 0x2b, 0x99, 0xeb, 0x7b, 0xce, 0xe1, 0xf4, 0x56, 0x53, + 0x32, 0x30, 0x53, 0x2c, 0xc9, 0xd7, 0x60, 0x86, 0x4d, 0xee, 0xe5, 0xc7, 0x21, 0xed, 0xf0, 0x91, + 0x9f, 0x43, 0xd1, 0xe1, 0x94, 0x92, 0x10, 0x23, 0x2a, 0xe4, 0x73, 0x0a, 0x17, 0x3b, 0x8d, 0x08, + 0xb4, 0xc0, 0x77, 0x1a, 0x2b, 0xe2, 0xc0, 0xc2, 0x3d, 0xfb, 0xb1, 0x92, 0xca, 0x54, 0x99, 0xa4, + 0x04, 0x27, 0xd8, 0xe5, 0xa3, 0xc3, 0xf2, 0x4b, 0x6c, 0x82, 0xc5, 0x81, 0xab, 0xfb, 0xcc, 0xd7, + 0xbe, 0x9c, 0xc8, 0x77, 0xe1, 0xac, 0x68, 0x56, 0x0d, 0xb3, 0x44, 0x79, 0xfe, 0x41, 0x63, 0xd7, + 0x46, 0xff, 0x9e, 0xf9, 0x93, 0x6d, 0x88, 0xb2, 0xc3, 0x1c, 0xc9, 0xc7, 0x0a, 0x38, 0x23, 0xb3, + 0x5f, 0x0d, 0xe4, 0x43, 0x98, 0xe1, 0xef, 0x3e, 0x2b, 0x5e, 0x10, 0xa2, 0x56, 0xe0, 0xd4, 0xc9, + 0xcc, 0xd6, 0xf9, 0x63, 0x12, 0x77, 0xf4, 0x48, 0x68, 0x11, 0x12, 0x9c, 0xc9, 0xdb, 0x68, 0x6b, + 0xc7, 0x43, 0x53, 0xd6, 0x37, 0x50, 0x7f, 0x29, 0xce, 0x9f, 0xae, 0xdb, 0xb1, 0xe4, 0xd5, 0xbc, + 0x1b, 0x6d, 0x17, 0x2a, 0x36, 0x79, 0x08, 0x93, 0x8d, 0xc6, 0xca, 0x2d, 0x97, 0x1d, 0x80, 0xdd, + 0x83, 0x85, 0x33, 0x7d, 0xbe, 0xf2, 0xc5, 0xcc, 0xaf, 0x9c, 0x0e, 0x82, 0x5d, 0x6b, 0xdb, 0x6d, + 0x51, 0xab, 0xe9, 0x75, 0x0f, 0x4c, 0x95, 0x53, 0x86, 0x29, 0xf7, 0xd9, 0x27, 0x6c, 0xca, 0x5d, + 0x87, 0x59, 0xc5, 0xe0, 0x13, 0x8d, 0x3d, 0x17, 0xe2, 0x78, 0x46, 0xaa, 0xe9, 0x76, 0xd2, 0x75, + 0x31, 0x49, 0x27, 0x6d, 0xb8, 0xcf, 0x9d, 0xd4, 0x86, 0xdb, 0x85, 0x39, 0x3e, 0x18, 0x62, 0x1e, + 0xe0, 0x48, 0x9f, 0xef, 0xd3, 0x87, 0x57, 0x32, 0xfb, 0x70, 0x5e, 0x8c, 0xb4, 0x9c, 0x64, 0xf8, + 0xce, 0x99, 0xe6, 0x4a, 0xb6, 0x81, 0x08, 0xa0, 0x1d, 0xda, 0x5b, 0x76, 0x40, 0xb1, 0xae, 0x67, + 0xfb, 0xd4, 0xf5, 0x52, 0x66, 0x5d, 0x33, 0xb2, 0xae, 0x2d, 0x5e, 0x4d, 0x06, 0x47, 0xd2, 0x91, + 0xf5, 0xc8, 0xf9, 0x85, 0x1d, 0xfb, 0x9c, 0xa6, 0x4c, 0x4d, 0x23, 0xf0, 0xd0, 0x40, 0xc9, 0x49, + 0x9b, 0xec, 0xf7, 0x0c, 0xce, 0xe4, 0x31, 0x9c, 0x49, 0x7f, 0x05, 0xd6, 0x79, 0x01, 0xeb, 0xbc, + 0xa0, 0xd5, 0x99, 0x44, 0xe2, 0xf3, 0x46, 0x6f, 0x56, 0xb2, 0xd6, 0x3e, 0xfc, 0xc9, 0x8f, 0xe7, + 0xe0, 0xec, 0xbd, 0x5b, 0x15, 0xcc, 0xc9, 0xe8, 0xf2, 0x48, 0x65, 0x91, 0xcb, 0xe7, 0xf3, 0x42, + 0xe1, 0x9e, 0x7c, 0x04, 0x90, 0x12, 0x07, 0x6e, 0x15, 0x4c, 0x46, 0x7c, 0xb1, 0xbd, 0x6d, 0xf3, + 0x54, 0x8f, 0x82, 0x45, 0x86, 0x5f, 0xe8, 0x2f, 0xfe, 0x71, 0x39, 0x67, 0xf6, 0xab, 0x8a, 0xb4, + 0xe0, 0xbc, 0xde, 0x2d, 0xd2, 0xca, 0x7e, 0x97, 0xb6, 0x5a, 0x0b, 0x65, 0x9c, 0xd1, 0xaf, 0x1f, + 0x1d, 0x96, 0x2f, 0xa7, 0x7a, 0x37, 0xb2, 0xdc, 0x67, 0x98, 0x4a, 0x83, 0x07, 0xf0, 0xbb, 0x33, + 0x52, 0x9c, 0x2e, 0xcd, 0x64, 0x99, 0xbb, 0xff, 0x4e, 0x3e, 0x71, 0x52, 0x91, 0x3a, 0x8c, 0x8b, + 0x09, 0x28, 0x44, 0xf7, 0xf4, 0x34, 0xbb, 0x90, 0x39, 0xcd, 0xc6, 0xc5, 0x5c, 0x36, 0x25, 0x3d, + 0xd9, 0x67, 0xac, 0xf0, 0x2b, 0xc4, 0x5d, 0xe7, 0x1b, 0xfc, 0x20, 0x42, 0x90, 0x76, 0xe4, 0xd6, + 0x4e, 0xee, 0x39, 0xa5, 0x3b, 0xe6, 0xe1, 0xd9, 0x2b, 0x6b, 0x23, 0x7b, 0x3c, 0x6d, 0x50, 0x21, + 0x72, 0xbf, 0xd1, 0x73, 0x04, 0x3d, 0xb1, 0x0a, 0x59, 0x2d, 0xc6, 0x6f, 0xe7, 0x60, 0x5a, 0x3b, + 0xea, 0xc8, 0x4d, 0xc5, 0xb7, 0x2c, 0x76, 0xb7, 0xd6, 0x70, 0x70, 0xf7, 0x4b, 0x7a, 0x9d, 0xdd, + 0x14, 0xc6, 0xeb, 0xf9, 0xfe, 0x74, 0x38, 0xfb, 0x93, 0xae, 0x86, 0x83, 0x35, 0x83, 0x51, 0x1a, + 0xc2, 0x91, 0x3e, 0x69, 0x08, 0xff, 0xde, 0x05, 0x98, 0xd1, 0xef, 0x42, 0xe4, 0x75, 0x18, 0x43, + 0xad, 0xac, 0xbc, 0x58, 0xa3, 0x42, 0x00, 0x15, 0xb7, 0x9a, 0x47, 0x03, 0xc7, 0x21, 0x2f, 0x03, + 0x44, 0xa6, 0xbf, 0xf2, 0x4d, 0x62, 0xf4, 0xe8, 0xb0, 0x9c, 0x7b, 0xc3, 0x54, 0x0a, 0xc8, 0x37, + 0x01, 0xd6, 0x3c, 0x87, 0x46, 0x39, 0x5b, 0x07, 0xbc, 0xbb, 0xbf, 0x92, 0x4a, 0xa9, 0x71, 0xba, + 0xe3, 0x39, 0x34, 0x9d, 0x3f, 0x43, 0xe1, 0x48, 0xbe, 0x04, 0xa3, 0x66, 0x8f, 0x5d, 0xe2, 0xb9, + 0xfe, 0x64, 0x52, 0x1e, 0x39, 0xbd, 0x16, 0x8d, 0x6f, 0x88, 0x7e, 0x2f, 0x69, 0x52, 0xc6, 0x00, + 0xe4, 0x7d, 0x9e, 0x6a, 0x43, 0x04, 0x75, 0x1c, 0x8d, 0x5f, 0x69, 0x14, 0x51, 0x24, 0x15, 0xd6, + 0x51, 0x21, 0x21, 0xeb, 0x30, 0xae, 0x3e, 0x2f, 0x28, 0x4e, 0xca, 0xea, 0x13, 0x94, 0x72, 0xdd, + 0x14, 0xc9, 0x5e, 0x93, 0x2f, 0x0f, 0x92, 0x0b, 0x79, 0x07, 0x26, 0x18, 0x7b, 0xb6, 0x94, 0x03, + 0x71, 0xcd, 0xc0, 0xb7, 0x18, 0xe5, 0x83, 0xd8, 0x76, 0xa0, 0x85, 0x5e, 0x8c, 0x08, 0xc8, 0xd7, + 0x30, 0x8d, 0xa8, 0xe8, 0xea, 0x81, 0xf6, 0x18, 0x97, 0x52, 0x5d, 0x8d, 0x79, 0x45, 0x53, 0x3d, + 0x1d, 0xf3, 0x23, 0x3b, 0x51, 0x8c, 0xac, 0x61, 0xd2, 0xa3, 0xbc, 0x9a, 0xaa, 0x60, 0x41, 0x86, + 0x7d, 0x4a, 0xe7, 0xde, 0xd5, 0xf8, 0x92, 0x2e, 0x94, 0x62, 0x29, 0x4f, 0xd4, 0x05, 0x83, 0xea, + 0x7a, 0x23, 0x55, 0x97, 0x3a, 0x80, 0xa9, 0xea, 0x52, 0xdc, 0x89, 0x03, 0x33, 0xf2, 0xc4, 0x10, + 0xf5, 0x4d, 0x0e, 0xaa, 0xef, 0xe5, 0x54, 0x7d, 0xf3, 0xce, 0x56, 0xba, 0x9e, 0x04, 0x4f, 0xf2, + 0x0e, 0x4c, 0x4b, 0x08, 0xcf, 0x84, 0x3b, 0x15, 0xa7, 0x3c, 0x75, 0xb6, 0x52, 0xf9, 0x6f, 0x75, + 0x64, 0x95, 0x9a, 0xcf, 0x8e, 0x69, 0x8d, 0x3a, 0x39, 0x2b, 0x74, 0x64, 0xf2, 0x01, 0x4c, 0xd6, + 0xdb, 0xac, 0x21, 0x5e, 0xc7, 0x0e, 0xa9, 0x70, 0x60, 0x93, 0xb6, 0x25, 0x4a, 0x89, 0x32, 0x55, + 0x79, 0x8e, 0xdf, 0xb8, 0x48, 0xcb, 0xf1, 0x1b, 0x83, 0x59, 0xe7, 0xf1, 0xf7, 0x24, 0x31, 0x87, + 0xa5, 0x73, 0xdb, 0x85, 0x0c, 0xfb, 0x0e, 0x85, 0xbd, 0x08, 0xe0, 0xc7, 0xa0, 0xf2, 0x3d, 0x27, + 0x11, 0x3c, 0x55, 0xe5, 0x49, 0xde, 0x85, 0x49, 0x91, 0x39, 0xaa, 0x62, 0xae, 0x05, 0x0b, 0x25, + 0x6c, 0x3c, 0xba, 0xe4, 0xcb, 0x24, 0x53, 0x96, 0xed, 0x27, 0x0c, 0x19, 0x63, 0x7c, 0xf2, 0x55, + 0x38, 0xf5, 0xd0, 0xed, 0x38, 0xde, 0x7e, 0x20, 0x8e, 0x29, 0xb1, 0xd1, 0xcd, 0xc5, 0x1e, 0x49, + 0xfb, 0xbc, 0x3c, 0x12, 0xce, 0x52, 0x1b, 0x5f, 0x26, 0x07, 0xf2, 0x17, 0x53, 0x9c, 0xf9, 0x0c, + 0x22, 0x83, 0x66, 0xd0, 0x62, 0x6a, 0x06, 0xa5, 0xab, 0x4f, 0x4e, 0xa7, 0xcc, 0x6a, 0x88, 0x07, + 0x44, 0x3f, 0xdf, 0xef, 0x78, 0x6e, 0x67, 0x61, 0x1e, 0xf7, 0xc2, 0x67, 0x93, 0x4e, 0xf0, 0x88, + 0x27, 0x72, 0x25, 0x1b, 0x47, 0x87, 0xe5, 0xe7, 0x93, 0x42, 0xf8, 0x87, 0x9e, 0xa6, 0x28, 0xcf, + 0x60, 0x4d, 0x3e, 0x80, 0x29, 0xf6, 0x7f, 0xa4, 0x25, 0x38, 0xa5, 0x59, 0x04, 0x2a, 0x98, 0xa2, + 0x1e, 0x1c, 0x23, 0x4c, 0x6d, 0x95, 0xa1, 0x40, 0xd0, 0x58, 0x91, 0xb7, 0x00, 0x98, 0x1c, 0x23, + 0xb6, 0xe3, 0xd3, 0x71, 0xac, 0x5a, 0x14, 0x83, 0xd2, 0x1b, 0x71, 0x8c, 0x4c, 0xde, 0x81, 0x49, + 0xf6, 0xab, 0xd1, 0x73, 0x3c, 0xb6, 0x36, 0xce, 0x20, 0x2d, 0xf7, 0x0b, 0x64, 0xb4, 0x01, 0x87, + 0x6b, 0x7e, 0x81, 0x31, 0x3a, 0x59, 0x81, 0x59, 0x8c, 0x29, 0x2c, 0xa2, 0x59, 0xba, 0x34, 0x58, + 0x38, 0xab, 0xbc, 0x83, 0xb3, 0x22, 0xcb, 0x8d, 0xca, 0xd4, 0xcb, 0x45, 0x82, 0x8c, 0x04, 0x30, + 0x9f, 0x7e, 0x48, 0x0c, 0x16, 0x16, 0xb0, 0x93, 0xa4, 0x48, 0x9d, 0xc6, 0xe0, 0xfb, 0x31, 0x1b, + 0x11, 0x65, 0xe3, 0x92, 0xcf, 0x09, 0x6a, 0x85, 0x59, 0xdc, 0x89, 0x09, 0xe4, 0x76, 0x75, 0x23, + 0x19, 0x74, 0xf7, 0x1c, 0xb6, 0x00, 0x87, 0x79, 0xa7, 0x19, 0x27, 0x71, 0xce, 0x08, 0xbc, 0x9b, + 0x41, 0x4d, 0xbe, 0x03, 0xa7, 0xe5, 0x0e, 0x22, 0x8a, 0xc4, 0xbc, 0x3e, 0x7f, 0xc2, 0x9d, 0xd8, + 0xd9, 0x8a, 0xaa, 0x4e, 0x4d, 0xe9, 0xec, 0x2a, 0x88, 0x0d, 0x93, 0x38, 0xac, 0xa2, 0xc6, 0x67, + 0x07, 0xd5, 0x78, 0x39, 0x55, 0xe3, 0x19, 0x9c, 0x28, 0xe9, 0xca, 0x54, 0x9e, 0x64, 0x09, 0xa6, + 0xc5, 0x3a, 0x12, 0xb3, 0xed, 0x39, 0xec, 0x2d, 0xd4, 0x2a, 0xc9, 0x15, 0x98, 0x9a, 0x70, 0x3a, + 0x89, 0xba, 0x23, 0xf3, 0x67, 0x84, 0x0b, 0xda, 0x8e, 0x9c, 0x7c, 0x3d, 0xd0, 0x91, 0xd9, 0x8e, + 0x14, 0x4b, 0x31, 0xcb, 0x8f, 0xbb, 0xbe, 0xd0, 0x19, 0x3d, 0x1f, 0x67, 0xc0, 0x51, 0x84, 0x1f, + 0x8b, 0x46, 0x18, 0xea, 0x96, 0x90, 0xc5, 0x81, 0xdc, 0x87, 0xf9, 0xe8, 0xd4, 0x56, 0x18, 0x97, + 0xe3, 0xb0, 0xae, 0xf1, 0x51, 0x9f, 0xcd, 0x37, 0x8b, 0x9e, 0xd8, 0x70, 0x56, 0x3b, 0xa7, 0x15, + 0xd6, 0x17, 0x91, 0x35, 0x26, 0x0d, 0xd7, 0x0f, 0xf9, 0x6c, 0xf6, 0xfd, 0xf8, 0x90, 0x0f, 0xe1, + 0x7c, 0xf2, 0x6c, 0x56, 0x6a, 0x79, 0x01, 0x6b, 0x79, 0xf5, 0xe8, 0xb0, 0x7c, 0x29, 0x75, 0xbc, + 0x67, 0x57, 0x34, 0x80, 0x1b, 0xf9, 0x26, 0x2c, 0xe8, 0xe7, 0xb3, 0x52, 0x93, 0x81, 0x35, 0xe1, + 0xd2, 0x89, 0x0e, 0xf6, 0xec, 0x1a, 0xfa, 0xf2, 0x20, 0x21, 0x94, 0x33, 0x67, 0xb7, 0x52, 0xcd, + 0x8b, 0x71, 0x83, 0x52, 0xab, 0x24, 0xbb, 0xba, 0xe3, 0x58, 0x92, 0x7d, 0x78, 0x3e, 0xeb, 0x98, + 0x50, 0x2a, 0x7d, 0x29, 0xd2, 0xca, 0xbe, 0x96, 0x7d, 0xe4, 0x64, 0xd7, 0x7c, 0x0c, 0x5b, 0xf2, + 0x35, 0x38, 0xad, 0xac, 0x2f, 0xa5, 0xbe, 0x97, 0xb1, 0x3e, 0xf4, 0x27, 0x56, 0x17, 0x66, 0x76, + 0x2d, 0xd9, 0x3c, 0x48, 0x1b, 0xe6, 0x65, 0xc3, 0x51, 0xfd, 0x2d, 0x8e, 0x9e, 0x4b, 0xda, 0xae, + 0x9a, 0xc6, 0x58, 0xba, 0x28, 0x76, 0xd5, 0x05, 0x67, 0xcb, 0xea, 0xc6, 0x84, 0xea, 0x4c, 0xcf, + 0xe0, 0x4b, 0x56, 0x60, 0xac, 0xb1, 0x51, 0xbf, 0x75, 0x6b, 0x79, 0xe1, 0x15, 0xac, 0x41, 0x7a, + 0x0c, 0x71, 0xa0, 0x76, 0x69, 0x12, 0x86, 0x6a, 0x5d, 0x77, 0x7b, 0x5b, 0x73, 0xcc, 0xe2, 0xa8, + 0xe4, 0xc7, 0x72, 0x70, 0xe6, 0xa1, 0xe7, 0xef, 0xb5, 0x3c, 0xdb, 0x91, 0xa1, 0x92, 0xc5, 0xae, + 0xf6, 0xfa, 0xa0, 0x5d, 0xed, 0xf3, 0xa9, 0x5d, 0xcd, 0xd8, 0x17, 0x6c, 0xac, 0x28, 0x26, 0x73, + 0x6a, 0x87, 0xeb, 0x53, 0x15, 0xf9, 0x8b, 0x70, 0x31, 0xbb, 0x44, 0x19, 0xa6, 0x37, 0x70, 0x98, + 0xae, 0x1f, 0x1d, 0x96, 0xdf, 0xe8, 0x57, 0x53, 0xf6, 0x90, 0x1d, 0xcb, 0xfa, 0xce, 0x48, 0xf1, + 0x72, 0xe9, 0xca, 0x9d, 0x91, 0xe2, 0x95, 0xd2, 0xab, 0xe6, 0x73, 0x8d, 0xca, 0xbd, 0xd5, 0xba, + 0x23, 0x8f, 0x1b, 0x19, 0x36, 0x9a, 0xd3, 0x98, 0x97, 0x06, 0x95, 0xc6, 0x1c, 0x8d, 0x5f, 0xce, + 0xc1, 0x7c, 0xc6, 0x60, 0x90, 0x4b, 0x30, 0x82, 0x99, 0x82, 0x14, 0xd3, 0x82, 0x44, 0x86, 0x20, + 0x2c, 0x27, 0x9f, 0x83, 0xf1, 0xda, 0x5a, 0xa3, 0x51, 0x59, 0x93, 0xf7, 0x56, 0xbe, 0x67, 0x77, + 0x02, 0x2b, 0xb0, 0xf5, 0x17, 0x49, 0x81, 0x46, 0xde, 0x80, 0xb1, 0xfa, 0x06, 0x12, 0x70, 0x03, + 0x39, 0xbc, 0xc7, 0xb9, 0xdd, 0x24, 0xbe, 0x40, 0x32, 0x7e, 0x32, 0x07, 0x24, 0x3d, 0xb3, 0xc8, + 0x75, 0x98, 0x54, 0xe7, 0x2f, 0xbf, 0x65, 0xe3, 0xeb, 0x99, 0x32, 0x3b, 0x4d, 0x15, 0x87, 0xd4, + 0x60, 0x14, 0xd3, 0x43, 0x46, 0x4f, 0xa1, 0x99, 0xf3, 0xe5, 0x6c, 0x6a, 0xbe, 0x8c, 0x62, 0xf2, + 0x49, 0x93, 0x13, 0x1b, 0xbf, 0x9f, 0x03, 0x92, 0x6d, 0xe0, 0x34, 0x94, 0x29, 0xc6, 0x9b, 0x8a, + 0x83, 0xb3, 0x9a, 0x0b, 0x24, 0x4a, 0xe4, 0xa4, 0xde, 0x18, 0x63, 0x57, 0xe8, 0x4b, 0x9a, 0x86, + 0xa2, 0xbf, 0x57, 0xdc, 0x15, 0x18, 0x7d, 0x40, 0xfd, 0x2d, 0x69, 0xfb, 0x89, 0xf6, 0x62, 0x8f, + 0x18, 0x40, 0xbd, 0xb1, 0x23, 0x86, 0xf1, 0xa7, 0x39, 0x38, 0x95, 0x25, 0xce, 0x1e, 0xe3, 0xbc, + 0x66, 0x24, 0xfc, 0xee, 0xd0, 0x0c, 0x83, 0x1b, 0x93, 0x45, 0xde, 0x76, 0x65, 0x18, 0x65, 0x8d, + 0x95, 0x23, 0x8c, 0x1a, 0x13, 0xd6, 0x1b, 0x81, 0xc9, 0xe1, 0x0c, 0x81, 0xc7, 0xdf, 0x1a, 0xc1, + 0xd0, 0x6d, 0x88, 0x80, 0xd2, 0x92, 0xc9, 0xe1, 0x0c, 0xe1, 0x9e, 0xe7, 0x44, 0x39, 0xd1, 0x11, + 0xa1, 0xcd, 0x00, 0x26, 0x87, 0x93, 0x4b, 0x30, 0xbe, 0xde, 0x59, 0xa5, 0xf6, 0x23, 0x19, 0x8f, + 0x1e, 0xcd, 0x46, 0xbc, 0x8e, 0xd5, 0x62, 0x30, 0x53, 0x16, 0x1a, 0x3f, 0x97, 0x83, 0xb9, 0x94, + 0x24, 0x7d, 0xbc, 0x7f, 0xde, 0x60, 0x47, 0x99, 0x61, 0xda, 0xc7, 0x3f, 0x7f, 0x24, 0xfb, 0xf3, + 0x8d, 0xff, 0x6b, 0x14, 0xce, 0xf6, 0x51, 0x6c, 0xc4, 0x8e, 0x7c, 0xb9, 0x63, 0x1d, 0xf9, 0xbe, + 0x0e, 0xd3, 0xd5, 0x96, 0xed, 0xb6, 0x83, 0x4d, 0x2f, 0xfe, 0xe2, 0xd8, 0x1f, 0x00, 0xcb, 0x64, + 0x5a, 0x78, 0x69, 0x38, 0x7e, 0xae, 0x89, 0x14, 0x56, 0xe8, 0xa5, 0xe5, 0x2a, 0x8d, 0x59, 0xca, + 0x95, 0xae, 0xf0, 0xaf, 0x89, 0x2b, 0x9d, 0xee, 0xdc, 0x31, 0xf2, 0x44, 0x9d, 0x3b, 0xb2, 0x0d, + 0x43, 0x47, 0x3f, 0x89, 0x99, 0x70, 0x15, 0xa6, 0xb9, 0xdd, 0x4c, 0x25, 0xe0, 0x83, 0x34, 0x96, + 0xb2, 0xb5, 0xb1, 0x83, 0xf4, 0x58, 0x68, 0x34, 0x64, 0x45, 0x77, 0x44, 0x18, 0xc7, 0xf7, 0xbe, + 0x4b, 0xfd, 0x1d, 0x0d, 0xf4, 0xc0, 0x10, 0xaa, 0xc3, 0xc1, 0x77, 0xe1, 0x54, 0xd6, 0xcd, 0x68, + 0xa1, 0xa8, 0x99, 0xe4, 0xf5, 0x35, 0xe5, 0x1c, 0xfe, 0x7e, 0xb5, 0x97, 0xbe, 0x5f, 0x19, 0x3f, + 0x97, 0xd7, 0x9d, 0xfc, 0xfe, 0x75, 0x9c, 0xf6, 0x57, 0x60, 0xf4, 0xe1, 0x2e, 0xf5, 0xe5, 0x66, + 0x8b, 0x1f, 0xb2, 0xcf, 0x00, 0xea, 0x87, 0x20, 0x06, 0xb9, 0x05, 0x33, 0x1b, 0x7c, 0x1a, 0xc8, + 0xb1, 0x1d, 0x89, 0x2f, 0xbb, 0x5d, 0xa1, 0x92, 0xc9, 0x18, 0xdc, 0x04, 0x95, 0x71, 0x1b, 0x2e, + 0x68, 0xbb, 0x81, 0x88, 0x6f, 0xc2, 0x9d, 0x11, 0xf8, 0x71, 0x3c, 0x13, 0xbb, 0x5f, 0xc4, 0x5b, + 0x97, 0x99, 0x80, 0x1a, 0xdb, 0xf0, 0xfc, 0x40, 0x46, 0xec, 0x14, 0x84, 0x6e, 0xf4, 0x2b, 0x61, + 0xec, 0x38, 0x90, 0xd4, 0x54, 0xe8, 0x8c, 0xef, 0xc2, 0x94, 0xda, 0xcb, 0xb8, 0xa1, 0xb3, 0xdf, + 0x62, 0x47, 0xe5, 0x1b, 0x3a, 0x03, 0x98, 0x1c, 0x1e, 0x2b, 0xd1, 0xf3, 0xd9, 0x4a, 0xf4, 0x78, + 0xf8, 0x0b, 0xc7, 0x0d, 0x3f, 0xab, 0x1c, 0xf7, 0x0b, 0xa5, 0x72, 0xfc, 0xad, 0x56, 0x8e, 0x51, + 0x47, 0x4c, 0x0e, 0x7f, 0xa2, 0x95, 0xff, 0x9e, 0x4c, 0x07, 0x84, 0xbe, 0x0e, 0x72, 0xf1, 0xc4, + 0x19, 0xd3, 0xe7, 0xb3, 0xd6, 0x42, 0x8c, 0x19, 0x9f, 0xd0, 0xf9, 0xe3, 0x4e, 0xe8, 0x93, 0x4c, + 0xc4, 0x6b, 0x30, 0x5e, 0x11, 0xa6, 0x00, 0x23, 0xb1, 0x54, 0x65, 0xa7, 0xde, 0xfd, 0x25, 0x96, + 0xf1, 0x8b, 0x39, 0x38, 0x9d, 0xa9, 0xac, 0x64, 0xb5, 0x72, 0xad, 0xa8, 0xb2, 0x0e, 0x93, 0x2a, + 0x51, 0x8e, 0x71, 0x12, 0x97, 0xf3, 0xe1, 0xdb, 0x62, 0xbc, 0x00, 0x13, 0xd1, 0x53, 0x19, 0x39, + 0x25, 0x87, 0x0e, 0xed, 0xc3, 0xe4, 0x8b, 0x4b, 0x03, 0x80, 0x7d, 0xc1, 0x13, 0xb5, 0x66, 0x34, + 0x7e, 0x2f, 0xcf, 0x53, 0x45, 0x3e, 0xb5, 0x41, 0x1f, 0xb3, 0x4d, 0x10, 0x59, 0x93, 0xfa, 0x87, + 0x7a, 0x24, 0xcb, 0x30, 0xd6, 0x08, 0xed, 0xb0, 0x27, 0x3d, 0xe5, 0xe7, 0x55, 0x32, 0x2c, 0x78, + 0xb0, 0x18, 0xfb, 0x4a, 0x07, 0x08, 0xd1, 0xae, 0x67, 0x08, 0x51, 0x2c, 0x19, 0xff, 0x51, 0x0e, + 0xa6, 0x54, 0x62, 0xf2, 0x01, 0xcc, 0xc8, 0x50, 0x76, 0x3c, 0x7e, 0x80, 0x78, 0xd7, 0x93, 0x46, + 0x31, 0x32, 0x94, 0x9d, 0x1a, 0x6f, 0x40, 0xc3, 0x57, 0xb7, 0xea, 0xae, 0x8a, 0x4c, 0x1c, 0x20, + 0xed, 0x6d, 0xdb, 0xda, 0xa7, 0xf6, 0x1e, 0x0d, 0x42, 0x8b, 0x1b, 0x2f, 0x88, 0xe7, 0x3f, 0xc9, + 0xfe, 0xde, 0xad, 0x0a, 0xb7, 0x5b, 0x60, 0x23, 0x21, 0x62, 0x12, 0xa6, 0x68, 0xd4, 0x37, 0x8d, + 0xf6, 0xb6, 0xfd, 0x90, 0x17, 0x72, 0x3a, 0xe3, 0xcf, 0xc6, 0xf8, 0x74, 0x13, 0x91, 0x2f, 0xb7, + 0x60, 0x66, 0xbd, 0x5e, 0xab, 0x2a, 0x1a, 0x4e, 0x3d, 0x71, 0xca, 0xf2, 0xe3, 0x90, 0xfa, 0x1d, + 0xbb, 0x25, 0x6f, 0x71, 0xf1, 0x11, 0xe4, 0xb9, 0x4e, 0x33, 0x5b, 0xfb, 0x99, 0xe0, 0xc8, 0xea, + 0xe0, 0x17, 0xb8, 0xa8, 0x8e, 0xfc, 0x90, 0x75, 0x04, 0x76, 0xbb, 0xd5, 0xa7, 0x0e, 0x9d, 0x23, + 0xd9, 0x85, 0xd2, 0x6d, 0x94, 0xd5, 0x94, 0x5a, 0x0a, 0x83, 0x6b, 0x79, 0x51, 0xd4, 0xf2, 0x2c, + 0x17, 0xf2, 0xb2, 0xeb, 0x49, 0x71, 0x8d, 0xf7, 0x89, 0x91, 0x63, 0xf7, 0x89, 0xbf, 0x9c, 0x83, + 0x31, 0x2e, 0x0c, 0x8a, 0x69, 0xdc, 0x47, 0xdc, 0x7c, 0xf8, 0x64, 0xc4, 0xcd, 0x12, 0x9e, 0x13, + 0xda, 0x84, 0xe6, 0x65, 0xa4, 0x96, 0x58, 0x17, 0xd2, 0x00, 0x17, 0xdf, 0x2a, 0x78, 0xc9, 0xf1, + 0xcb, 0x82, 0xd4, 0x63, 0xef, 0xf5, 0xf1, 0x63, 0x1d, 0x24, 0xa5, 0xc7, 0xff, 0xb8, 0xf0, 0x5e, + 0xd7, 0x7d, 0xd6, 0x57, 0x61, 0x42, 0xf8, 0xc4, 0x2f, 0x1d, 0x88, 0x17, 0xc9, 0x92, 0x66, 0xe4, + 0xe1, 0x2c, 0x1d, 0xc4, 0x82, 0xae, 0xf0, 0xaa, 0xb7, 0xb6, 0x0e, 0xb4, 0xcc, 0x9b, 0x12, 0x91, + 0xac, 0xf3, 0x8c, 0x74, 0x3c, 0x36, 0xa8, 0x1e, 0x0c, 0x3c, 0x82, 0x8b, 0x68, 0x3b, 0xd2, 0xb1, + 0x36, 0x23, 0x14, 0x68, 0xcc, 0x83, 0xac, 0x42, 0x09, 0x0d, 0x83, 0xa8, 0xc3, 0x57, 0x4d, 0xbd, + 0xc6, 0xfd, 0xae, 0x85, 0x71, 0x67, 0xc8, 0xcb, 0xc4, 0x72, 0x4b, 0xb8, 0x3c, 0xa5, 0x28, 0xd9, + 0xe5, 0xb4, 0x94, 0x9c, 0x7d, 0xe4, 0x1d, 0x98, 0x8c, 0x62, 0xb3, 0x46, 0x4e, 0x97, 0xf8, 0x32, + 0x11, 0x07, 0x73, 0xd5, 0xf3, 0x98, 0x29, 0xe8, 0x64, 0x11, 0x8a, 0x6c, 0x11, 0x27, 0x73, 0x7e, + 0xf6, 0x04, 0x4c, 0x75, 0x82, 0x90, 0x78, 0xa4, 0x01, 0xf3, 0x6c, 0xd1, 0x34, 0xdc, 0xce, 0x4e, + 0x8b, 0xae, 0x7a, 0x3b, 0x5e, 0x2f, 0x8c, 0xd3, 0x7a, 0xf1, 0xeb, 0x80, 0xdd, 0x6e, 0x69, 0xc5, + 0x7a, 0x52, 0xaf, 0x0c, 0x6a, 0x65, 0xab, 0xfc, 0xe3, 0x3c, 0x4c, 0x2a, 0xf3, 0x89, 0x5c, 0x81, + 0x62, 0x3d, 0x58, 0xf5, 0x9a, 0x7b, 0x51, 0x38, 0xb6, 0xe9, 0xa3, 0xc3, 0xf2, 0x84, 0x1b, 0x58, + 0x2d, 0x04, 0x9a, 0x51, 0x31, 0x59, 0x82, 0x69, 0xfe, 0x97, 0x8c, 0x99, 0x9f, 0x8f, 0x6d, 0x39, + 0x39, 0xb2, 0x8c, 0x96, 0xaf, 0xee, 0x9e, 0x1a, 0x09, 0xf9, 0x06, 0x00, 0x07, 0xa0, 0x03, 0x6f, + 0x61, 0x78, 0xd7, 0x63, 0x51, 0x41, 0x86, 0xeb, 0xae, 0xc2, 0x90, 0x7c, 0x8b, 0xc7, 0x72, 0x95, + 0xf3, 0x7f, 0x64, 0x78, 0xdf, 0x69, 0xc6, 0xdf, 0xca, 0x0e, 0xe1, 0xa0, 0xb2, 0x14, 0x41, 0x28, + 0xcf, 0x9b, 0xb4, 0xe9, 0x3d, 0xa2, 0xfe, 0x41, 0x25, 0x44, 0x44, 0x05, 0xc3, 0xf8, 0x9f, 0x73, + 0xca, 0xaa, 0x21, 0x6b, 0x98, 0xa6, 0x96, 0xcf, 0x08, 0x61, 0x9c, 0x13, 0xdd, 0x19, 0x24, 0xdc, + 0xa4, 0xdb, 0x4b, 0xcf, 0x0a, 0x53, 0xe2, 0xf9, 0x68, 0x5e, 0x25, 0xd2, 0xd7, 0x72, 0x20, 0xf9, + 0x32, 0x8c, 0x60, 0xd7, 0xe5, 0x8f, 0x6d, 0x9a, 0x3c, 0xb6, 0x47, 0x58, 0x9f, 0x61, 0x43, 0x90, + 0x92, 0x7c, 0x4e, 0x38, 0x3f, 0xf2, 0xce, 0x9f, 0x51, 0xce, 0x5e, 0xf6, 0x1d, 0xd1, 0x79, 0x1d, + 0x47, 0xf1, 0x50, 0x66, 0xcf, 0xbf, 0x97, 0x87, 0x52, 0x72, 0xad, 0x92, 0xf7, 0x61, 0x4a, 0x9e, + 0xa7, 0x2b, 0xb6, 0x08, 0xf8, 0x3e, 0x25, 0x02, 0xae, 0xcb, 0x43, 0x75, 0xd7, 0x56, 0x8d, 0x79, + 0x4c, 0x8d, 0x80, 0x09, 0x37, 0x9b, 0x22, 0xaa, 0x96, 0xb2, 0x4a, 0x42, 0x2f, 0xec, 0x26, 0xc2, + 0x81, 0x4a, 0x34, 0xf2, 0x26, 0x14, 0xee, 0xdd, 0xaa, 0x08, 0x27, 0x99, 0x52, 0xf2, 0xd4, 0xe5, + 0x46, 0x80, 0xba, 0x49, 0x22, 0xc3, 0x27, 0xab, 0x4a, 0xb4, 0xdd, 0x31, 0x2d, 0xcb, 0x96, 0x04, + 0x47, 0x8d, 0x3b, 0x3e, 0xec, 0xee, 0x9d, 0x91, 0x62, 0xa1, 0x34, 0x22, 0x62, 0x5a, 0xfe, 0xc7, + 0x05, 0x98, 0x88, 0xea, 0x27, 0x44, 0x75, 0x3d, 0xe4, 0x6e, 0x86, 0xe4, 0x1c, 0x14, 0xa5, 0xb8, + 0x26, 0x7c, 0x65, 0xc6, 0x03, 0x21, 0xaa, 0x2d, 0x80, 0x94, 0xcb, 0xf8, 0x32, 0x37, 0xe5, 0x4f, + 0x72, 0x1d, 0x22, 0xa1, 0xab, 0x9f, 0x74, 0x36, 0xc2, 0x06, 0xcc, 0x8c, 0xd0, 0xc8, 0x0c, 0xe4, + 0x5d, 0x1e, 0xdc, 0x68, 0xc2, 0xcc, 0xbb, 0x0e, 0x79, 0x1f, 0x8a, 0xb6, 0xe3, 0x50, 0xc7, 0xb2, + 0xa5, 0x95, 0xcb, 0xa0, 0x49, 0x53, 0x64, 0xdc, 0xf8, 0x21, 0x80, 0x54, 0x95, 0x90, 0x54, 0x60, + 0xa2, 0x65, 0x73, 0x43, 0x36, 0x67, 0x88, 0x13, 0x25, 0xe6, 0x50, 0x64, 0x64, 0xf7, 0x03, 0xea, + 0x90, 0x57, 0x60, 0x84, 0x8d, 0xa6, 0x38, 0x42, 0xa4, 0x94, 0xc8, 0x06, 0x93, 0x77, 0xd8, 0xca, + 0x33, 0x26, 0x22, 0x90, 0x97, 0xa0, 0xd0, 0x5b, 0xdc, 0x16, 0x87, 0x43, 0x29, 0x8e, 0x7c, 0x1d, + 0xa1, 0xb1, 0x62, 0x72, 0x03, 0x8a, 0xfb, 0x7a, 0xd0, 0xe4, 0xd3, 0x89, 0x61, 0x8c, 0xf0, 0x23, + 0xc4, 0xa5, 0x22, 0x8c, 0xf1, 0x83, 0xc0, 0x78, 0x1e, 0x20, 0xae, 0x3a, 0xed, 0xd2, 0x64, 0x7c, + 0x03, 0x26, 0xa2, 0x2a, 0xc9, 0x05, 0x80, 0x3d, 0x7a, 0x60, 0xed, 0xda, 0x1d, 0xa7, 0xc5, 0xa5, + 0xc8, 0x29, 0x73, 0x62, 0x8f, 0x1e, 0xac, 0x20, 0x80, 0x9c, 0x85, 0xf1, 0x2e, 0x1b, 0x55, 0x31, + 0x75, 0xa7, 0xcc, 0xb1, 0x6e, 0x6f, 0x8b, 0xcd, 0xd0, 0x05, 0x18, 0x47, 0xed, 0xa2, 0x58, 0x68, + 0xd3, 0xa6, 0xfc, 0x69, 0xfc, 0x37, 0x79, 0x4c, 0x18, 0xa2, 0x7c, 0x27, 0x79, 0x11, 0xa6, 0x9b, + 0x3e, 0xc5, 0x33, 0xc7, 0x66, 0x92, 0x94, 0xa8, 0x67, 0x2a, 0x06, 0xd6, 0x1d, 0x72, 0x09, 0x66, + 0xbb, 0xbd, 0xad, 0x96, 0xdb, 0x64, 0xb5, 0x59, 0xcd, 0x2d, 0x11, 0xe1, 0x7c, 0xca, 0x9c, 0xe6, + 0xe0, 0xbb, 0xf4, 0xa0, 0xba, 0x85, 0x41, 0xb9, 0x4a, 0x6a, 0x78, 0xd6, 0x30, 0x4a, 0xe5, 0x6c, + 0xce, 0x2a, 0x70, 0x34, 0x5e, 0x3b, 0x03, 0x63, 0xb6, 0xbd, 0xd3, 0x73, 0x79, 0xf0, 0x9c, 0x29, + 0x53, 0xfc, 0x22, 0xaf, 0xc1, 0x5c, 0xe0, 0xee, 0x74, 0xec, 0xb0, 0xe7, 0x8b, 0x8c, 0x2d, 0xd4, + 0xc7, 0x29, 0x35, 0x6d, 0x96, 0xa2, 0x82, 0x2a, 0x87, 0x93, 0x37, 0x80, 0xa8, 0xf5, 0x79, 0x5b, + 0x1f, 0xd2, 0x26, 0x9f, 0x6a, 0x53, 0xe6, 0x9c, 0x52, 0xb2, 0x8e, 0x05, 0xe4, 0x05, 0x98, 0xf2, + 0x69, 0x80, 0x52, 0x1c, 0x76, 0x1b, 0xe6, 0xd3, 0x32, 0x27, 0x25, 0x8c, 0xf5, 0xdd, 0x65, 0x28, + 0x29, 0xdd, 0x81, 0x11, 0x70, 0x79, 0x78, 0x6f, 0x73, 0x26, 0x86, 0x9b, 0xdd, 0xba, 0x63, 0x2c, + 0xc1, 0x5c, 0x6a, 0xe5, 0x2a, 0xe9, 0xf7, 0xf9, 0x4e, 0x34, 0x38, 0xfd, 0xbe, 0xd1, 0x81, 0x29, + 0x75, 0x27, 0x3e, 0x26, 0xca, 0xfc, 0x19, 0x0c, 0xbe, 0xc0, 0xb7, 0xa9, 0xb1, 0xa3, 0xc3, 0x72, + 0xde, 0x75, 0x30, 0xe4, 0xc2, 0x65, 0x28, 0x4a, 0xa1, 0x41, 0x9c, 0xd5, 0xa8, 0x1d, 0x96, 0x8f, + 0x30, 0x66, 0x54, 0x6a, 0xbc, 0x02, 0xe3, 0x62, 0xb3, 0x1d, 0xac, 0x13, 0x36, 0xbe, 0x97, 0x87, + 0x59, 0x93, 0xb2, 0xad, 0x80, 0xf2, 0xd4, 0x12, 0x4f, 0xed, 0x2d, 0x31, 0x3b, 0x84, 0x9f, 0xd6, + 0xb6, 0x01, 0x49, 0x1d, 0xfe, 0x76, 0x0e, 0xe6, 0x33, 0x70, 0x3f, 0x56, 0x52, 0xc3, 0x9b, 0x30, + 0x51, 0x73, 0xed, 0x56, 0xc5, 0x71, 0xa2, 0x48, 0x0c, 0x28, 0x6a, 0x62, 0xe6, 0x13, 0x9b, 0x41, + 0xd5, 0x63, 0x37, 0x42, 0x25, 0xaf, 0x8a, 0x49, 0x11, 0xa7, 0x04, 0xc6, 0x49, 0xf1, 0xd1, 0x61, + 0x19, 0xf8, 0x37, 0xc5, 0x79, 0xc2, 0x31, 0xac, 0x26, 0x07, 0xc6, 0x4e, 0x0e, 0x4f, 0xed, 0xd0, + 0x65, 0x87, 0xd5, 0x4c, 0x36, 0x6f, 0xa8, 0xbc, 0x0e, 0x3f, 0x95, 0x87, 0x33, 0xd9, 0x84, 0x1f, + 0x37, 0x3f, 0x25, 0x66, 0xd4, 0x50, 0x42, 0x01, 0x63, 0x7e, 0x4a, 0x9e, 0x7e, 0x03, 0xf1, 0x63, + 0x04, 0xb2, 0x0d, 0xd3, 0xab, 0x76, 0x10, 0xae, 0x50, 0xdb, 0x0f, 0xb7, 0xa8, 0x1d, 0x0e, 0x21, + 0x7b, 0xbe, 0x24, 0xdf, 0x9f, 0xf1, 0xf8, 0xdb, 0x95, 0x94, 0x09, 0xe9, 0x50, 0x67, 0x1b, 0x4d, + 0x94, 0x91, 0x21, 0x26, 0xca, 0xb7, 0x61, 0xb6, 0x41, 0xdb, 0x76, 0x77, 0xd7, 0xf3, 0xa5, 0x97, + 0xec, 0x55, 0x98, 0x8e, 0x40, 0x99, 0xb3, 0x45, 0x2f, 0xd6, 0xf0, 0x95, 0x8e, 0x88, 0xb7, 0x12, + 0xbd, 0xd8, 0xf8, 0x6b, 0x79, 0x38, 0x5b, 0x69, 0x0a, 0xb3, 0x30, 0x51, 0x20, 0xad, 0x57, 0x3f, + 0xe5, 0xba, 0xc9, 0x35, 0x98, 0xb8, 0x67, 0x3f, 0x5e, 0xa5, 0x76, 0x40, 0x03, 0x11, 0x67, 0x9a, + 0x0b, 0x6a, 0xf6, 0xe3, 0x58, 0x9b, 0x6f, 0xc6, 0x38, 0xea, 0x4d, 0x76, 0xe4, 0x13, 0xde, 0x64, + 0x0d, 0x18, 0x5b, 0xf1, 0x5a, 0x8e, 0x38, 0xc6, 0xc4, 0x13, 0xe2, 0x2e, 0x42, 0x4c, 0x51, 0xc2, + 0x2e, 0x80, 0x33, 0xd1, 0x17, 0xe3, 0x27, 0x7c, 0xea, 0x5d, 0x72, 0x09, 0xc6, 0xb1, 0xa2, 0x28, + 0x8d, 0x31, 0x1e, 0x1a, 0x2d, 0x8a, 0x39, 0x9e, 0x1c, 0x53, 0x16, 0xaa, 0x3d, 0x31, 0xfa, 0xc9, + 0x7a, 0xc2, 0xf8, 0x5b, 0xf8, 0x3a, 0xa9, 0xb6, 0x92, 0x9d, 0x44, 0xca, 0x87, 0xe4, 0x86, 0xfc, + 0x90, 0xfc, 0x13, 0x1b, 0x92, 0x42, 0xdf, 0x21, 0xf9, 0x7e, 0x1e, 0x26, 0xa3, 0x8f, 0xfd, 0x8c, + 0xc5, 0xa3, 0x8e, 0xda, 0x35, 0x54, 0x64, 0x8b, 0x86, 0xb2, 0x57, 0x88, 0x00, 0x12, 0x5f, 0x86, + 0x31, 0xb1, 0x98, 0x72, 0x09, 0x2b, 0xce, 0xc4, 0xe8, 0x2e, 0xcd, 0x08, 0xd6, 0x63, 0x38, 0xa0, + 0x81, 0x29, 0xe8, 0x30, 0x74, 0xc8, 0x43, 0xba, 0x25, 0x1e, 0xab, 0x9f, 0xda, 0x33, 0x2a, 0x3b, + 0x74, 0x48, 0xdc, 0xb0, 0xa1, 0x4e, 0xa7, 0x5f, 0x2a, 0x42, 0x29, 0x49, 0x72, 0x7c, 0xc4, 0xef, + 0x8d, 0xde, 0x16, 0x97, 0xc2, 0x79, 0xc4, 0xef, 0x6e, 0x6f, 0xcb, 0x64, 0x30, 0xb4, 0x65, 0xf1, + 0xdd, 0x47, 0xd8, 0xea, 0x29, 0x61, 0xcb, 0xe2, 0xbb, 0x8f, 0x34, 0x5b, 0x16, 0xdf, 0x7d, 0x84, + 0x57, 0xdf, 0xd5, 0x06, 0x7a, 0x3b, 0xa3, 0x08, 0x2e, 0xae, 0xbe, 0xad, 0x20, 0x99, 0x74, 0x47, + 0xa2, 0xb1, 0xa3, 0x72, 0x89, 0xda, 0xbe, 0x88, 0x4e, 0x2d, 0xb6, 0x33, 0x3c, 0x2a, 0xb7, 0x10, + 0xcc, 0x73, 0x64, 0x9b, 0x2a, 0x12, 0x69, 0x01, 0x51, 0x7e, 0xca, 0x05, 0x7c, 0xfc, 0x6d, 0x50, + 0x5a, 0x60, 0x9d, 0x52, 0x59, 0x5b, 0xea, 0x6a, 0xce, 0xe0, 0xfb, 0x24, 0x15, 0x90, 0x1b, 0x22, + 0xe4, 0x1e, 0xaa, 0x3c, 0x8a, 0xc7, 0x32, 0x93, 0xe1, 0x00, 0x80, 0x87, 0xe4, 0x8b, 0x14, 0x1f, + 0x31, 0x13, 0xf2, 0x1e, 0x4c, 0xaa, 0x3e, 0xec, 0xdc, 0xd3, 0xfa, 0x39, 0x1e, 0x45, 0xad, 0x4f, + 0x9a, 0x46, 0x95, 0x80, 0x6c, 0xc1, 0xd9, 0xaa, 0xd7, 0x09, 0x7a, 0x6d, 0x19, 0xaf, 0x2d, 0x8e, + 0x12, 0x0b, 0x38, 0x14, 0xe8, 0x10, 0xdb, 0x14, 0x28, 0xc2, 0x65, 0x5a, 0x9a, 0xc8, 0xeb, 0x17, + 0x90, 0x7e, 0x8c, 0xc8, 0x26, 0x4c, 0xa2, 0x12, 0x4f, 0x18, 0x77, 0x4d, 0xea, 0xdb, 0x46, 0x5c, + 0x52, 0x63, 0x0b, 0x83, 0xc7, 0x0a, 0xb2, 0xdb, 0x2d, 0x69, 0xa1, 0xad, 0x2a, 0x23, 0x15, 0x64, + 0xf2, 0x0d, 0x98, 0xe1, 0xd7, 0xcd, 0x87, 0x74, 0x8b, 0xcf, 0x9d, 0x29, 0xed, 0xee, 0xac, 0x17, + 0xf2, 0xf7, 0x64, 0xa1, 0x3a, 0xdd, 0xa7, 0x5b, 0x7c, 0xec, 0x35, 0xff, 0x08, 0x0d, 0x9f, 0xdc, + 0x87, 0xf9, 0x15, 0x3b, 0xe0, 0x40, 0xc5, 0x19, 0x79, 0x1a, 0x75, 0x8a, 0x68, 0xb7, 0xba, 0x6b, + 0x07, 0x52, 0x17, 0x9b, 0xe9, 0x7c, 0x9c, 0x45, 0x4f, 0xbe, 0x97, 0x83, 0x05, 0x4d, 0x55, 0x2b, + 0x0c, 0x87, 0xda, 0xb4, 0x13, 0xa2, 0x23, 0xc4, 0x4c, 0x94, 0x9d, 0xbb, 0x1f, 0x1a, 0x1f, 0x92, + 0x84, 0x36, 0xd8, 0x8f, 0xcb, 0x55, 0x83, 0xd0, 0x7e, 0x3c, 0x8c, 0x9b, 0xc9, 0xde, 0x13, 0x8a, + 0x96, 0x5c, 0xa4, 0x68, 0x39, 0x05, 0xa3, 0xd8, 0x47, 0x32, 0x12, 0x0a, 0xfe, 0x30, 0x3e, 0xa7, + 0xee, 0x2a, 0x42, 0xc8, 0x1b, 0xb8, 0xab, 0x18, 0xff, 0xfd, 0x18, 0xcc, 0x26, 0x06, 0x59, 0xdc, + 0x3a, 0x73, 0xa9, 0x5b, 0x67, 0x03, 0x80, 0xab, 0x1a, 0x87, 0xd4, 0x09, 0x4a, 0x97, 0xaa, 0x49, + 0xe1, 0xa1, 0x18, 0xad, 0x10, 0x85, 0x0d, 0x63, 0xca, 0xd7, 0xdf, 0x90, 0x3a, 0xda, 0x88, 0x29, + 0x5f, 0xc2, 0x0a, 0xd3, 0x98, 0x0d, 0x29, 0xc3, 0x28, 0xc6, 0x40, 0x54, 0x3d, 0xda, 0x5c, 0x06, + 0x30, 0x39, 0x9c, 0xbc, 0x08, 0x63, 0x4c, 0x24, 0xaa, 0xd7, 0xc4, 0x96, 0x86, 0x27, 0x05, 0x93, + 0x99, 0x98, 0xfc, 0x21, 0x8a, 0xc8, 0x4d, 0x98, 0xe2, 0x7f, 0x89, 0x08, 0x16, 0x63, 0xba, 0x6d, + 0x9a, 0xe5, 0x3a, 0x32, 0x88, 0x85, 0x86, 0xc7, 0xee, 0x0a, 0x8d, 0x1e, 0xea, 0x1f, 0xea, 0x35, + 0x11, 0x34, 0x17, 0xef, 0x0a, 0x01, 0x07, 0x62, 0x2e, 0xfb, 0x08, 0x81, 0x49, 0x26, 0xc2, 0xae, + 0xbc, 0x88, 0x37, 0x44, 0x94, 0x4c, 0xb8, 0x3d, 0xb9, 0x29, 0x4a, 0xc8, 0x15, 0xae, 0xda, 0x47, + 0x21, 0x8f, 0xa7, 0x0c, 0x43, 0xbd, 0x39, 0xaa, 0x19, 0x50, 0xd2, 0x8b, 0x8a, 0x59, 0xe5, 0xec, + 0xef, 0xe5, 0xb6, 0xed, 0xb6, 0xc4, 0x26, 0x81, 0x95, 0x23, 0x2e, 0x65, 0x50, 0x33, 0x46, 0x20, + 0xef, 0xc0, 0x0c, 0xfb, 0x51, 0xf5, 0xda, 0x6d, 0xaf, 0x83, 0xec, 0x27, 0xe3, 0x60, 0x48, 0x48, + 0xd2, 0xc4, 0x22, 0x5e, 0x4b, 0x02, 0x97, 0x9d, 0x0e, 0xf8, 0x6c, 0xd8, 0xe3, 0x8f, 0x0e, 0x53, + 0xf1, 0xe9, 0x80, 0xa4, 0x01, 0x87, 0x9b, 0x2a, 0x12, 0x79, 0x0b, 0xa6, 0xd9, 0xcf, 0xdb, 0xee, + 0x23, 0xca, 0x2b, 0x9c, 0x8e, 0xdf, 0xcb, 0x91, 0x6a, 0x87, 0x95, 0xf0, 0xfa, 0x74, 0x4c, 0xf2, + 0x15, 0x38, 0x8d, 0x9c, 0x9a, 0x5e, 0x97, 0x3a, 0x95, 0xed, 0x6d, 0xb7, 0xe5, 0x72, 0x63, 0x21, + 0x1e, 0xab, 0x01, 0x75, 0xc0, 0xbc, 0x62, 0xc4, 0xb0, 0xec, 0x18, 0xc5, 0xcc, 0xa6, 0x24, 0x0f, + 0xa1, 0x54, 0xed, 0x05, 0xa1, 0xd7, 0xae, 0x84, 0xa1, 0xef, 0x6e, 0xf5, 0x42, 0x1a, 0x2c, 0xcc, + 0x6a, 0x11, 0x0d, 0xd8, 0xe2, 0x88, 0x0a, 0xb9, 0x76, 0xa7, 0x89, 0x14, 0x96, 0x1d, 0x91, 0x98, + 0x29, 0x26, 0xc6, 0x3f, 0xce, 0xc1, 0xb4, 0x46, 0x4a, 0xde, 0x84, 0xa9, 0x5b, 0xbe, 0x4b, 0x3b, + 0x4e, 0xeb, 0x40, 0xb9, 0x76, 0xe2, 0x9d, 0x64, 0x5b, 0xc0, 0x79, 0xab, 0x35, 0xb4, 0x48, 0x6b, + 0x93, 0xcf, 0xb4, 0xe4, 0xbb, 0xc6, 0x1d, 0x2b, 0xc5, 0x04, 0x2d, 0xc4, 0x21, 0x56, 0x70, 0x82, + 0x8a, 0xd9, 0xa9, 0xa0, 0x90, 0x77, 0x61, 0x8c, 0x3f, 0x30, 0x0a, 0xb3, 0xb2, 0x73, 0x59, 0xcd, + 0xe4, 0x4e, 0xbc, 0x38, 0x11, 0xd1, 0x8a, 0x24, 0x30, 0x05, 0x91, 0xf1, 0xf3, 0x39, 0x20, 0x69, + 0xd4, 0x63, 0xb4, 0x58, 0xc7, 0x5a, 0xa7, 0x7c, 0x39, 0x5a, 0x8d, 0x05, 0x4d, 0x67, 0xcb, 0x6a, + 0xe2, 0x05, 0xbc, 0xe3, 0xc5, 0xaa, 0x53, 0xd5, 0x6a, 0xbc, 0xd8, 0xf8, 0xf1, 0x3c, 0x40, 0x8c, + 0x4d, 0xbe, 0xc8, 0x53, 0xcd, 0x7c, 0xa5, 0x67, 0xb7, 0xdc, 0x6d, 0x57, 0x8f, 0xbd, 0x88, 0x4c, + 0xbe, 0x2d, 0x4b, 0x4c, 0x1d, 0x91, 0xbc, 0x0f, 0xb3, 0x8d, 0x0d, 0x9d, 0x56, 0x49, 0xab, 0x11, + 0x74, 0xad, 0x04, 0x79, 0x12, 0x1b, 0xcd, 0x47, 0xd5, 0xd1, 0xe0, 0xe6, 0xa3, 0x7c, 0x20, 0x44, + 0x09, 0xdb, 0x58, 0x1a, 0x1b, 0xc2, 0x62, 0xd9, 0xa9, 0xd7, 0xc4, 0x2e, 0x85, 0x5f, 0x17, 0x74, + 0xad, 0xae, 0x30, 0x65, 0x66, 0xfb, 0x84, 0x86, 0x17, 0x77, 0xe4, 0x68, 0x1f, 0x47, 0xdd, 0x5f, + 0x40, 0x25, 0x5e, 0xdb, 0x0b, 0xa9, 0xd0, 0x5d, 0x3c, 0xb5, 0xb7, 0x98, 0xf8, 0x75, 0x7a, 0x54, + 0xf3, 0x3f, 0xd4, 0x5a, 0x27, 0x2c, 0x30, 0x6e, 0xc4, 0x57, 0x0e, 0xfe, 0x4e, 0x9d, 0x61, 0xb4, + 0xf1, 0x37, 0x72, 0x70, 0x3a, 0x93, 0x96, 0x5c, 0x05, 0x88, 0x35, 0x44, 0xa2, 0x97, 0x70, 0xc7, + 0x8c, 0x03, 0x8b, 0x98, 0x0a, 0x06, 0xf9, 0x7a, 0x52, 0xb7, 0x73, 0xfc, 0x41, 0x78, 0x5e, 0x06, + 0x8e, 0xd2, 0x75, 0x3b, 0x19, 0x1a, 0x1d, 0xe3, 0x6f, 0x17, 0x60, 0x4e, 0x89, 0x5b, 0xc2, 0xbf, + 0xf5, 0x18, 0x73, 0xde, 0x3d, 0x98, 0x62, 0xad, 0x71, 0x9b, 0xc2, 0x5d, 0x80, 0x5b, 0x52, 0xbc, + 0x9a, 0xf2, 0x20, 0x13, 0xdc, 0xae, 0xaa, 0xc8, 0x3c, 0x9c, 0x1b, 0x6e, 0x9d, 0xa8, 0x39, 0x6f, + 0xa6, 0x5d, 0x05, 0x34, 0xe6, 0x24, 0x80, 0xe9, 0xda, 0x41, 0xc7, 0x6e, 0x47, 0xb5, 0x71, 0x8b, + 0x8a, 0xd7, 0xfa, 0xd6, 0xa6, 0x61, 0xf3, 0xea, 0x62, 0x5f, 0x0b, 0x5e, 0x96, 0xe1, 0xe6, 0xab, + 0x51, 0x9d, 0x7f, 0x1f, 0xe6, 0x52, 0x1f, 0x7d, 0xa2, 0xc8, 0x72, 0x0f, 0x81, 0xa4, 0xbf, 0x23, + 0x83, 0xc3, 0x6b, 0x7a, 0xdc, 0xc2, 0xd3, 0xd1, 0xe3, 0x29, 0xe6, 0xd0, 0xe6, 0xf6, 0x19, 0x8b, + 0x6a, 0xdc, 0xb9, 0x5f, 0xc8, 0xab, 0x5e, 0x7c, 0x4f, 0xfb, 0xaa, 0xfb, 0xb2, 0x76, 0xb7, 0x7d, + 0xbe, 0xdf, 0x98, 0x0e, 0xa5, 0x43, 0xf8, 0x41, 0x01, 0xce, 0xf6, 0xa1, 0x24, 0x07, 0xc9, 0x49, + 0xc4, 0x75, 0x0a, 0xd7, 0x07, 0x57, 0xf8, 0x24, 0xa6, 0x12, 0xf9, 0x22, 0xf7, 0xe3, 0x6f, 0x62, + 0x9e, 0x66, 0x71, 0x9b, 0x46, 0xa5, 0xfc, 0x5e, 0x04, 0x4d, 0x3a, 0xf0, 0x73, 0x28, 0x79, 0x1f, + 0x46, 0xd1, 0x85, 0x33, 0x11, 0x37, 0x8d, 0x61, 0x20, 0x5c, 0x09, 0x32, 0xc7, 0x7e, 0x6a, 0x41, + 0xe6, 0x18, 0x80, 0x7c, 0x01, 0x0a, 0x95, 0x87, 0x0d, 0x31, 0x2e, 0x33, 0x2a, 0xf9, 0xc3, 0x46, + 0x1c, 0x20, 0xdf, 0xd6, 0x22, 0xd9, 0x33, 0x0a, 0x46, 0x78, 0xbb, 0xba, 0x21, 0x46, 0x45, 0x25, + 0xbc, 0x5d, 0xdd, 0x88, 0x09, 0x77, 0x9a, 0x5a, 0x1c, 0x9a, 0xdb, 0xd5, 0x8d, 0x4f, 0x6f, 0xda, + 0xff, 0x3b, 0x79, 0x1e, 0x7c, 0x80, 0x37, 0xec, 0x7d, 0x98, 0xd2, 0xe2, 0xca, 0xe6, 0x62, 0x79, + 0x2c, 0x8a, 0x01, 0x9c, 0x30, 0x41, 0xd1, 0x08, 0x64, 0xaa, 0x09, 0xf6, 0x1b, 0x25, 0x5e, 0xd5, + 0xd8, 0x23, 0xe2, 0x80, 0x32, 0x71, 0x32, 0xd5, 0x44, 0x44, 0x42, 0x6e, 0x40, 0x71, 0x93, 0x76, + 0xec, 0x4e, 0x18, 0xa9, 0x37, 0xd1, 0x5a, 0x35, 0x44, 0x98, 0x2e, 0x35, 0x44, 0x88, 0x68, 0x59, + 0xd9, 0xdb, 0x0a, 0x9a, 0xbe, 0x8b, 0x41, 0x4a, 0xa2, 0xb3, 0x98, 0x5b, 0x56, 0x2a, 0x25, 0x3a, + 0x83, 0x04, 0x91, 0xf1, 0x0b, 0x39, 0x18, 0x17, 0x03, 0xc9, 0x53, 0x04, 0xed, 0xc4, 0x67, 0x89, + 0x48, 0x11, 0xb4, 0xe3, 0x26, 0x53, 0x04, 0xed, 0xf0, 0x48, 0x20, 0x13, 0xc2, 0x8f, 0x36, 0x7a, + 0xe8, 0xe3, 0xc9, 0xe9, 0x39, 0x50, 0xaf, 0x36, 0x46, 0x1d, 0xd6, 0x5f, 0xc6, 0xf8, 0xeb, 0xe2, + 0xcb, 0x6e, 0x57, 0x37, 0xc8, 0x22, 0x14, 0x57, 0x3d, 0x1e, 0x65, 0x46, 0x4d, 0x1d, 0xd9, 0x12, + 0x30, 0xb5, 0x83, 0x24, 0x1e, 0xfb, 0xbe, 0x0d, 0xdf, 0x13, 0x77, 0x19, 0xe5, 0xfb, 0xba, 0x1c, + 0x98, 0xf8, 0xbe, 0x08, 0x75, 0xe8, 0xef, 0xa3, 0x19, 0x9b, 0xc4, 0x83, 0x1b, 0x18, 0x83, 0xff, + 0x8e, 0xea, 0x87, 0x24, 0x8a, 0xe4, 0x4e, 0x71, 0xbe, 0xdf, 0x4e, 0xf1, 0xe0, 0x86, 0x99, 0x41, + 0x85, 0xaf, 0x64, 0x31, 0xb8, 0x41, 0xfd, 0x47, 0x4f, 0xf1, 0x2e, 0x9d, 0xfd, 0x4a, 0x96, 0x6c, + 0xde, 0x50, 0x9b, 0xf4, 0x3f, 0xca, 0xc3, 0x99, 0x6c, 0x42, 0xb5, 0x2d, 0xb9, 0x01, 0x6d, 0xb9, + 0x0c, 0xc5, 0x15, 0x2f, 0x08, 0x15, 0xab, 0x33, 0x54, 0xe6, 0xef, 0x0a, 0x98, 0x19, 0x95, 0xb2, + 0x3b, 0x37, 0xfb, 0x3b, 0x5a, 0x9e, 0xc8, 0x0f, 0x5d, 0xee, 0xd9, 0x9d, 0x9b, 0x17, 0x91, 0xdb, + 0x50, 0x34, 0x85, 0x1f, 0x4c, 0xa2, 0x6b, 0x24, 0x38, 0x92, 0xa6, 0x88, 0x2f, 0x20, 0x5a, 0x78, + 0x5f, 0x01, 0x23, 0x15, 0x18, 0x17, 0xa3, 0x9f, 0x78, 0x08, 0xce, 0x98, 0x32, 0x7a, 0xc4, 0x6d, + 0x49, 0xc7, 0x76, 0x14, 0x7c, 0xd2, 0xab, 0xd7, 0xa4, 0x4b, 0x0b, 0xee, 0x28, 0xfc, 0xc9, 0x4f, + 0x37, 0xf0, 0x8b, 0x10, 0x8d, 0xef, 0xe5, 0x01, 0xa4, 0xd6, 0xe6, 0xa9, 0x9d, 0x61, 0x5f, 0xd0, + 0x66, 0x98, 0x62, 0xef, 0x32, 0x7c, 0x4a, 0xcb, 0x75, 0xb4, 0x3b, 0x19, 0x3e, 0xa1, 0x65, 0x19, + 0x46, 0x37, 0x63, 0x85, 0x96, 0xf0, 0x71, 0x40, 0xe5, 0x32, 0x87, 0x1b, 0x5b, 0x70, 0xea, 0x36, + 0x0d, 0x63, 0xf5, 0x96, 0x7c, 0x48, 0x1c, 0xcc, 0xf6, 0x75, 0x98, 0x10, 0xf8, 0xd1, 0xfe, 0xc5, + 0x75, 0x31, 0x22, 0x8a, 0x05, 0xea, 0x62, 0x24, 0x02, 0xdb, 0x8d, 0x6a, 0xb4, 0x45, 0x43, 0xfa, + 0xe9, 0x56, 0xd3, 0x00, 0xc2, 0x9b, 0x82, 0x2d, 0x1b, 0xae, 0x86, 0x63, 0xfb, 0xe7, 0x01, 0x9c, + 0x8e, 0xbe, 0xfd, 0x49, 0xf2, 0xbd, 0xc6, 0xae, 0x94, 0x22, 0x58, 0x75, 0xcc, 0x71, 0x80, 0x25, + 0xc9, 0x1f, 0xe5, 0xe0, 0xbc, 0xa4, 0x78, 0xe8, 0x46, 0x96, 0x7b, 0x43, 0x11, 0x93, 0x77, 0x60, + 0x52, 0xa1, 0x11, 0xd1, 0x96, 0x51, 0xeb, 0xbc, 0xef, 0x86, 0xbb, 0x56, 0xc0, 0xe1, 0xaa, 0xd6, + 0x59, 0x41, 0x27, 0x5b, 0x70, 0xbe, 0x51, 0xb9, 0xb7, 0x1a, 0xe7, 0xff, 0x5d, 0xf3, 0x6e, 0x79, + 0xad, 0x96, 0xb7, 0x7f, 0xdf, 0x5c, 0x95, 0xd9, 0x0f, 0xd0, 0x55, 0x1f, 0x55, 0xd8, 0x71, 0xe6, + 0x62, 0xab, 0xe3, 0x59, 0xdb, 0x88, 0x68, 0xf5, 0xfc, 0x56, 0x60, 0x0e, 0xe0, 0x62, 0xfc, 0xfd, + 0x1c, 0x3c, 0x1b, 0x79, 0xbb, 0x64, 0xb4, 0x2f, 0xd1, 0x82, 0xdc, 0x93, 0x6c, 0x41, 0xfe, 0x89, + 0xb4, 0x60, 0x2d, 0x1e, 0x9f, 0x7a, 0x27, 0x72, 0xc1, 0x95, 0xdf, 0x4f, 0xd4, 0xf1, 0x11, 0xa3, + 0xf2, 0x5c, 0xca, 0xa9, 0x57, 0xf1, 0xdd, 0x35, 0xde, 0x56, 0x3a, 0x24, 0x83, 0xa1, 0x46, 0x9c, + 0x4b, 0x12, 0x7f, 0x2f, 0x0f, 0xb3, 0xeb, 0xf5, 0x5a, 0x35, 0xb2, 0x8a, 0xfa, 0x8c, 0xa5, 0x0e, + 0xd5, 0xda, 0xd6, 0x7f, 0xe7, 0x34, 0xee, 0xc3, 0x7c, 0xa2, 0x1b, 0x50, 0x08, 0x7a, 0x8f, 0xfb, + 0x62, 0x44, 0x60, 0x29, 0x00, 0x9d, 0xc9, 0x62, 0xff, 0xe0, 0x86, 0x99, 0xc0, 0x36, 0xfe, 0xc9, + 0x44, 0x82, 0xaf, 0xd8, 0x8c, 0x5f, 0x87, 0x89, 0x7a, 0x10, 0xf4, 0xa8, 0x7f, 0xdf, 0x5c, 0x55, + 0x95, 0x1e, 0x2e, 0x02, 0xd9, 0x1c, 0x32, 0x63, 0x04, 0x72, 0x05, 0x8a, 0x22, 0x92, 0xb2, 0xdc, + 0xdd, 0x50, 0xff, 0x1c, 0x05, 0x62, 0x36, 0xa3, 0x62, 0xf2, 0x26, 0x4c, 0xf1, 0xbf, 0xf9, 0x8c, + 0x16, 0x1d, 0x8e, 0x6a, 0x4e, 0x81, 0xce, 0x57, 0x80, 0xa9, 0xa1, 0x91, 0x57, 0xa1, 0x50, 0xa9, + 0x9a, 0x42, 0xb1, 0x25, 0x24, 0x60, 0xcc, 0x2d, 0xde, 0xa3, 0xfa, 0x75, 0xa8, 0x6a, 0x32, 0x39, + 0x56, 0xba, 0xfb, 0x0b, 0x9d, 0x3c, 0x4f, 0x81, 0x2e, 0x60, 0x89, 0x63, 0x19, 0x61, 0xe4, 0x1a, + 0x8c, 0xd7, 0xdc, 0xa0, 0xdb, 0xb2, 0x0f, 0x84, 0x46, 0x9e, 0xe7, 0xc5, 0xe2, 0x20, 0xcd, 0x8b, + 0x9f, 0x83, 0xc8, 0x15, 0x99, 0x2f, 0xa8, 0x18, 0xbb, 0x74, 0xf4, 0x49, 0x0a, 0xf4, 0x3a, 0x8c, + 0x89, 0x78, 0xc3, 0x13, 0x4a, 0x26, 0x81, 0x64, 0x9c, 0x61, 0x81, 0x93, 0xf6, 0xed, 0x84, 0x27, + 0xe9, 0xdb, 0xb9, 0x05, 0x67, 0x6f, 0xa3, 0x1e, 0x4a, 0x0f, 0xd2, 0x73, 0xdf, 0xac, 0x0b, 0xcd, + 0x3e, 0x3e, 0x4f, 0x71, 0x55, 0x55, 0x32, 0xce, 0x8f, 0xd5, 0xf3, 0xd5, 0x34, 0x8f, 0xfd, 0x18, + 0x91, 0xaf, 0xc2, 0xa9, 0xac, 0x22, 0xa1, 0xff, 0xc7, 0x70, 0x34, 0xd9, 0x15, 0xa8, 0xe1, 0x68, + 0xb2, 0x38, 0x90, 0x55, 0x28, 0x71, 0x78, 0xc5, 0x69, 0xbb, 0x1d, 0xfe, 0x86, 0xc1, 0xdf, 0x07, + 0xd0, 0xc7, 0x42, 0x70, 0xb5, 0x59, 0x21, 0x7f, 0xcb, 0xd0, 0xbc, 0x72, 0x12, 0x94, 0xe4, 0x67, + 0x72, 0xec, 0x5e, 0xca, 0xa3, 0xf3, 0xe2, 0xf6, 0x39, 0x23, 0xde, 0x36, 0x23, 0x37, 0x99, 0x46, + 0xe8, 0xbb, 0x9d, 0x1d, 0xe1, 0x71, 0xb3, 0x29, 0x3c, 0x6e, 0xde, 0xf9, 0x58, 0x1e, 0x37, 0x9c, + 0x55, 0x70, 0x74, 0x58, 0x9e, 0xf2, 0x45, 0x9d, 0xb8, 0x8a, 0xb4, 0x2f, 0x60, 0x5d, 0x87, 0x6e, + 0xa7, 0xf7, 0x3b, 0x3c, 0x36, 0x28, 0x75, 0x78, 0x23, 0x67, 0x71, 0x63, 0xc7, 0xae, 0xb3, 0xf9, + 0x26, 0x1e, 0x21, 0xa4, 0x1a, 0x9a, 0xc9, 0x81, 0x5d, 0xa1, 0xa5, 0x57, 0x07, 0x77, 0x54, 0x2d, + 0xc5, 0x57, 0x68, 0xe9, 0x02, 0x62, 0xe1, 0x34, 0x52, 0x27, 0x8f, 0x46, 0x42, 0xae, 0xc1, 0xd8, + 0x3d, 0xfb, 0x71, 0x65, 0x87, 0x8a, 0x3c, 0x70, 0xd3, 0x72, 0xfb, 0x43, 0xe0, 0x52, 0xf1, 0x0f, + 0xb9, 0xd7, 0xc0, 0x33, 0xa6, 0x40, 0x23, 0x3f, 0x9c, 0x83, 0x33, 0x7c, 0x19, 0xcb, 0x56, 0x36, + 0x68, 0x18, 0xb2, 0x7e, 0x10, 0x31, 0xcd, 0xa4, 0xb7, 0x75, 0xa3, 0xb1, 0x9e, 0x8d, 0xc7, 0x13, + 0xea, 0x8b, 0x9d, 0x21, 0xea, 0xb8, 0x40, 0x94, 0x6a, 0xd1, 0x5a, 0x33, 0xe9, 0x85, 0x45, 0xfc, + 0x17, 0xe4, 0x97, 0x93, 0x37, 0x54, 0x7f, 0xca, 0x02, 0x4a, 0xec, 0xe3, 0x6d, 0xfb, 0xb1, 0x65, + 0xef, 0x50, 0xed, 0x9d, 0x5d, 0x68, 0xcc, 0x7f, 0x2e, 0x07, 0xe7, 0xfa, 0x7e, 0x1c, 0xb9, 0x09, + 0x67, 0x6d, 0xee, 0x25, 0x6c, 0xed, 0x86, 0x61, 0x37, 0xb0, 0xe4, 0xb5, 0x46, 0x78, 0x60, 0x9a, + 0xa7, 0x45, 0xf1, 0x0a, 0x2b, 0x95, 0x37, 0x9d, 0x80, 0xbc, 0x0f, 0xcf, 0xb9, 0x9d, 0x80, 0x36, + 0x7b, 0x3e, 0xb5, 0x24, 0x83, 0xa6, 0xeb, 0xf8, 0x96, 0x6f, 0x77, 0x76, 0xa4, 0x3b, 0xa9, 0x79, + 0x4e, 0xe2, 0x08, 0x4f, 0xe4, 0xaa, 0xeb, 0xf8, 0x26, 0x22, 0x18, 0x7f, 0x32, 0xc1, 0x4f, 0xc5, + 0x4a, 0x2f, 0xdc, 0x95, 0xe7, 0xe8, 0x62, 0x96, 0x77, 0x10, 0x37, 0x5b, 0x54, 0xbc, 0x83, 0x74, + 0x9f, 0x20, 0xf9, 0x30, 0x93, 0xcf, 0x7c, 0x98, 0x79, 0x1d, 0x26, 0xaa, 0xbb, 0xb4, 0xb9, 0x17, + 0x79, 0x68, 0x14, 0x85, 0xe6, 0x9b, 0x01, 0x79, 0x28, 0xe0, 0x18, 0x81, 0x5c, 0x03, 0x40, 0xa7, + 0x44, 0x2e, 0x2e, 0x2a, 0xe1, 0xfc, 0xd1, 0x87, 0x51, 0x58, 0x82, 0x28, 0x28, 0xc8, 0xbe, 0x61, + 0xde, 0x52, 0x4d, 0x47, 0x38, 0xfb, 0xc0, 0xdf, 0x16, 0xe8, 0x31, 0x02, 0x6b, 0x9e, 0xb2, 0x54, + 0xc4, 0xc6, 0x5e, 0x4a, 0xad, 0x27, 0x15, 0x09, 0xad, 0x32, 0xa5, 0x39, 0x3a, 0xee, 0xeb, 0x53, + 0xc2, 0x2a, 0x33, 0x32, 0x5d, 0x37, 0x63, 0x04, 0xf2, 0x05, 0x18, 0xaf, 0x52, 0x3f, 0xdc, 0xdc, + 0x5c, 0x45, 0xeb, 0x0e, 0x1e, 0xf3, 0xbe, 0x88, 0xf1, 0xc9, 0xc3, 0xb0, 0xf5, 0xd1, 0x61, 0x79, + 0x3a, 0x74, 0xdb, 0x34, 0x8a, 0xe5, 0x6b, 0x4a, 0x6c, 0xb2, 0x04, 0x25, 0xfe, 0x62, 0x1d, 0x5f, + 0x0b, 0x70, 0xab, 0x2f, 0xf2, 0x83, 0x47, 0x3c, 0x6f, 0xef, 0xd3, 0xad, 0x28, 0x3a, 0x7b, 0x0a, + 0x9f, 0x2c, 0xcb, 0xa4, 0x06, 0x6a, 0x23, 0x21, 0xd6, 0x53, 0x25, 0x97, 0x00, 0x6b, 0x6b, 0x9a, + 0x82, 0x54, 0x60, 0xba, 0xea, 0xb5, 0xbb, 0x76, 0xe8, 0x62, 0x9a, 0xb1, 0x03, 0xb1, 0xab, 0xa3, + 0xae, 0xad, 0xa9, 0x16, 0x68, 0x47, 0x84, 0x5a, 0x40, 0x6e, 0xc1, 0x8c, 0xe9, 0xf5, 0xd8, 0x20, + 0xc9, 0x0b, 0x32, 0xdf, 0xb8, 0xd1, 0x06, 0xc3, 0x67, 0x25, 0xec, 0x9c, 0x11, 0xb7, 0x61, 0x2d, + 0xcc, 0xa2, 0x46, 0x45, 0xd6, 0x32, 0x5e, 0x2a, 0xd4, 0xdd, 0x5a, 0x8d, 0xd1, 0x9e, 0x62, 0x96, + 0xf1, 0xc8, 0x71, 0x03, 0x26, 0x1b, 0x8d, 0xf5, 0x4d, 0x1a, 0x84, 0xb7, 0x5a, 0xde, 0x3e, 0x6e, + 0xd6, 0x45, 0x91, 0xbb, 0x26, 0xf0, 0xac, 0x90, 0x06, 0xa1, 0xb5, 0xdd, 0xf2, 0xf6, 0x4d, 0x15, + 0x8b, 0x7c, 0x93, 0xf5, 0x87, 0x22, 0xda, 0x88, 0x80, 0x92, 0x83, 0xa4, 0x2f, 0xdc, 0x12, 0xe3, + 0x25, 0xc3, 0x64, 0x30, 0xbd, 0xb3, 0x14, 0x74, 0x74, 0x37, 0x62, 0x57, 0xfb, 0x8a, 0xe3, 0xf8, + 0x34, 0x08, 0xc4, 0xae, 0xca, 0xdd, 0x8d, 0x50, 0x0f, 0x60, 0xf3, 0x02, 0xcd, 0xdd, 0x48, 0x21, + 0x20, 0xdf, 0xcf, 0xc1, 0x69, 0xd5, 0x63, 0x01, 0x17, 0x0b, 0xda, 0x93, 0xf0, 0x3d, 0xf6, 0x8d, + 0xab, 0xf2, 0x54, 0xb9, 0xaa, 0xa0, 0x5d, 0x7d, 0x74, 0xfd, 0x6a, 0x25, 0xfe, 0xd9, 0x90, 0x44, + 0x22, 0x26, 0x5b, 0x16, 0x3f, 0xf5, 0x84, 0xb0, 0x33, 0x48, 0x49, 0x95, 0x09, 0x1e, 0x6c, 0x3e, + 0xa1, 0x7d, 0x52, 0x7d, 0x03, 0xb7, 0x68, 0xa1, 0xea, 0x14, 0xb3, 0x8f, 0x5b, 0x32, 0xb9, 0x5d, + 0x5d, 0xbe, 0x50, 0x68, 0x48, 0x1d, 0x66, 0x39, 0x80, 0x6d, 0x09, 0x3c, 0xb1, 0xc9, 0x7c, 0x1c, + 0x5c, 0x5d, 0xb0, 0xc1, 0x47, 0x78, 0x4c, 0x6e, 0xa2, 0xc6, 0x3f, 0x4c, 0xd0, 0xa1, 0xe4, 0xcf, + 0x6e, 0x29, 0xb1, 0xf8, 0xfa, 0xd9, 0xf2, 0x38, 0xd0, 0xda, 0x36, 0xc0, 0xe3, 0xe0, 0x3e, 0xf7, + 0xc1, 0x54, 0xba, 0x41, 0x4a, 0xfe, 0x1a, 0x38, 0x29, 0xf9, 0x27, 0x68, 0xcc, 0x04, 0xb6, 0xf1, + 0x51, 0x31, 0xc1, 0x57, 0x58, 0x19, 0x1a, 0x30, 0xc6, 0x05, 0x7b, 0x35, 0xd5, 0x3e, 0x17, 0xfb, + 0x4d, 0x51, 0x42, 0xce, 0x41, 0xa1, 0xd1, 0x58, 0x17, 0x9d, 0x8c, 0xb6, 0x86, 0x41, 0xe0, 0x99, + 0x0c, 0xc6, 0x46, 0x08, 0x0d, 0x08, 0x95, 0x50, 0xd2, 0x6c, 0x07, 0x35, 0x11, 0xca, 0xfa, 0x5b, + 0x8a, 0xd9, 0x23, 0x71, 0x7f, 0x0b, 0x31, 0x3b, 0x16, 0xae, 0xab, 0xb0, 0x50, 0x09, 0x02, 0xea, + 0xb3, 0x09, 0x2a, 0xec, 0xd2, 0x7c, 0x21, 0x0a, 0x8a, 0x83, 0x02, 0x2b, 0xb5, 0x9b, 0x81, 0xd9, + 0x17, 0x91, 0x5c, 0x86, 0x62, 0xa5, 0xe7, 0xb8, 0xb4, 0xd3, 0xd4, 0x42, 0x24, 0xd9, 0x02, 0x66, + 0x46, 0xa5, 0xe4, 0x2b, 0x70, 0x3a, 0x11, 0x26, 0x4c, 0xf4, 0xc0, 0x78, 0xbc, 0x9a, 0xa5, 0xa8, + 0x1a, 0xbf, 0xbe, 0xf3, 0x2e, 0xc9, 0xa6, 0x24, 0x15, 0x28, 0x2d, 0xa3, 0x87, 0x4d, 0x8d, 0xf2, + 0x87, 0x00, 0xcf, 0xe7, 0x5e, 0x43, 0xfc, 0x62, 0x21, 0x82, 0xa1, 0x39, 0x51, 0xa1, 0x99, 0x42, + 0x27, 0x77, 0x61, 0x3e, 0x09, 0x63, 0x67, 0x02, 0xbf, 0x43, 0x60, 0x2c, 0xd3, 0x14, 0x17, 0x3c, + 0x15, 0xb2, 0xa8, 0xc8, 0x16, 0xcc, 0xc5, 0xd6, 0x27, 0xfa, 0xcd, 0x42, 0x9a, 0xa8, 0x46, 0xe5, + 0xf2, 0x76, 0xf1, 0xac, 0x98, 0x8c, 0xf3, 0xb1, 0x25, 0x4b, 0x74, 0xc3, 0x30, 0xd3, 0xec, 0x88, + 0x03, 0x33, 0x0d, 0x77, 0xa7, 0xe3, 0x76, 0x76, 0xee, 0xd2, 0x83, 0x0d, 0xdb, 0xf5, 0x85, 0xb1, + 0xa0, 0x34, 0x05, 0xae, 0x04, 0x07, 0xed, 0x36, 0x0d, 0x7d, 0x3c, 0x6d, 0x59, 0x39, 0x3a, 0xbc, + 0x32, 0x89, 0xf1, 0x7c, 0xc0, 0xe9, 0xd0, 0x99, 0xac, 0x6b, 0xbb, 0xda, 0xb1, 0xa2, 0xf3, 0xd4, + 0x6e, 0x77, 0x53, 0x43, 0xde, 0xee, 0x5a, 0x30, 0xb7, 0xdc, 0x69, 0xfa, 0x07, 0xf8, 0x1e, 0x23, + 0x3f, 0x6e, 0xfa, 0x98, 0x8f, 0x7b, 0x49, 0x7c, 0xdc, 0x73, 0xb6, 0x9c, 0x61, 0x59, 0x9f, 0x97, + 0x66, 0x4c, 0x1a, 0x30, 0x87, 0x12, 0x5b, 0xbd, 0xb6, 0x51, 0xef, 0xb8, 0xa1, 0x8b, 0x09, 0xe1, + 0xf9, 0x71, 0xf5, 0xb2, 0xe0, 0x79, 0x81, 0x4b, 0xf1, 0xae, 0xd3, 0xb5, 0x5c, 0x89, 0xa2, 0x32, + 0x4d, 0xd1, 0x0f, 0x12, 0xa5, 0x67, 0xff, 0xe5, 0x88, 0xd2, 0x98, 0xed, 0x2c, 0xe1, 0x08, 0x5e, + 0x8a, 0xf7, 0xf6, 0x00, 0x8b, 0xd8, 0x11, 0xe1, 0xf5, 0x50, 0x3c, 0xd1, 0xb2, 0x9d, 0xe9, 0x74, + 0xc6, 0xf7, 0x27, 0xf8, 0xde, 0xae, 0xca, 0xaf, 0xfd, 0xcc, 0x0a, 0x13, 0x72, 0x6d, 0xfe, 0x24, + 0x72, 0x6d, 0xe1, 0x78, 0xb9, 0x76, 0xe4, 0x38, 0xb9, 0x36, 0x21, 0x78, 0x8e, 0x9e, 0x58, 0xf0, + 0x1c, 0x3b, 0x81, 0xe0, 0x39, 0x7e, 0x22, 0xc1, 0x53, 0x93, 0xa0, 0x8b, 0xc7, 0x49, 0xd0, 0xff, + 0x46, 0x4c, 0x7d, 0x5a, 0xc5, 0xd4, 0x2c, 0x51, 0xe1, 0x44, 0x62, 0x6a, 0x7f, 0x29, 0xb3, 0xf4, + 0xaf, 0x5a, 0xca, 0x9c, 0x7b, 0x32, 0x52, 0x26, 0xf9, 0x98, 0x52, 0xe6, 0x5f, 0x80, 0x52, 0xf2, + 0xe0, 0x3b, 0x3e, 0x3a, 0xe2, 0x13, 0x0b, 0xa6, 0xc5, 0x8e, 0xe5, 0xe4, 0xc1, 0xc3, 0x2e, 0xd2, + 0x1b, 0xbe, 0xfb, 0xc8, 0x0e, 0xe9, 0x5d, 0x69, 0x86, 0x21, 0x22, 0x7b, 0x72, 0x28, 0x6e, 0x1f, + 0x0a, 0x4a, 0x24, 0x73, 0xe5, 0xb3, 0x64, 0x2e, 0xe3, 0x27, 0xf2, 0x30, 0xc7, 0x23, 0xd2, 0x3c, + 0xfd, 0x3a, 0xf4, 0xf7, 0x34, 0x49, 0x5a, 0x1a, 0xfd, 0x25, 0x5a, 0x37, 0x40, 0x8b, 0xfe, 0x0d, + 0x38, 0x9d, 0xea, 0x0a, 0x94, 0xa6, 0x6b, 0x32, 0x16, 0x50, 0x4a, 0x9e, 0x5e, 0xc8, 0xae, 0xe4, + 0xc1, 0x0d, 0x33, 0x45, 0x61, 0xfc, 0xd3, 0x91, 0x14, 0x7f, 0xa1, 0x4f, 0x57, 0x35, 0xe4, 0xb9, + 0x93, 0x69, 0xc8, 0xf3, 0xc3, 0x69, 0xc8, 0x13, 0xc7, 0x54, 0x61, 0x98, 0x63, 0xea, 0x2b, 0x30, + 0xbd, 0x49, 0xed, 0x76, 0xb0, 0xe9, 0x89, 0x1c, 0x06, 0xdc, 0xe8, 0x57, 0x86, 0xfa, 0x61, 0x65, + 0x52, 0x18, 0x8c, 0x8c, 0x97, 0x42, 0x46, 0xc0, 0xb6, 0x56, 0x9e, 0xd4, 0xc0, 0xd4, 0x39, 0xa8, + 0x12, 0xfe, 0xe8, 0x00, 0x09, 0xbf, 0x01, 0x53, 0x82, 0x2e, 0x0e, 0x09, 0x19, 0x8b, 0xa2, 0xac, + 0x08, 0xe1, 0xb2, 0xf6, 0x28, 0xb5, 0x66, 0x54, 0x3b, 0x97, 0x42, 0x35, 0x26, 0xac, 0x0b, 0x96, + 0x3b, 0x4e, 0xd7, 0x73, 0x3b, 0xd8, 0x05, 0xe3, 0x71, 0x17, 0x50, 0x01, 0xe6, 0x5d, 0xa0, 0x20, + 0x91, 0x77, 0x60, 0xa6, 0xb2, 0x51, 0x57, 0xc9, 0x8a, 0xb1, 0x92, 0xde, 0xee, 0xba, 0x96, 0x46, + 0x9a, 0xc0, 0x1d, 0x24, 0x95, 0x4d, 0xfc, 0xcb, 0x91, 0xca, 0x8c, 0x7f, 0x38, 0x21, 0x97, 0xf7, + 0xa7, 0xab, 0x0c, 0xd4, 0xd5, 0x7b, 0x85, 0x13, 0xaa, 0xf7, 0x46, 0x8e, 0x13, 0x4e, 0x34, 0x89, + 0x69, 0xf4, 0x04, 0x12, 0xd3, 0xd8, 0x27, 0x56, 0xd5, 0x8d, 0x9f, 0x50, 0x06, 0x4a, 0xac, 0xb4, + 0xe2, 0x30, 0x2b, 0x2d, 0x53, 0x6e, 0x9a, 0xf8, 0xe4, 0x72, 0x13, 0x9c, 0x58, 0x6e, 0x6a, 0xc4, + 0x0e, 0x71, 0x93, 0xc7, 0x5a, 0x26, 0x5f, 0x10, 0xf7, 0x95, 0xb9, 0xec, 0x60, 0x44, 0x91, 0x6b, + 0xdc, 0x67, 0x4a, 0x18, 0xfb, 0x56, 0xb6, 0x30, 0x36, 0xf8, 0xb4, 0xf9, 0x37, 0xe2, 0xd8, 0x13, + 0x11, 0xc7, 0x7c, 0x1c, 0xb0, 0x87, 0xb6, 0xdf, 0xc1, 0x2b, 0xe7, 0x35, 0x18, 0x97, 0xf1, 0xbd, + 0x72, 0xb1, 0xf6, 0x24, 0x1d, 0xd8, 0x4b, 0x62, 0x91, 0x45, 0x28, 0x4a, 0x62, 0x35, 0x1e, 0xfb, + 0xbe, 0x80, 0x69, 0xa1, 0x93, 0x04, 0xcc, 0xf8, 0x9b, 0x23, 0x72, 0x53, 0x60, 0xdf, 0x21, 0xb2, + 0xb6, 0x2f, 0x29, 0x93, 0x40, 0x11, 0x06, 0x13, 0xc3, 0x9c, 0xb0, 0x59, 0xd4, 0x49, 0x3e, 0x56, + 0xc4, 0xb5, 0x38, 0x71, 0x5a, 0x61, 0x88, 0xc4, 0x69, 0x6f, 0x69, 0x59, 0xc7, 0x46, 0xe2, 0x34, + 0x37, 0x6c, 0xa1, 0x0c, 0xce, 0x37, 0x76, 0x53, 0x4d, 0x0f, 0x36, 0x1a, 0x07, 0x1f, 0x41, 0xca, + 0x01, 0x89, 0xc1, 0x22, 0xe9, 0x76, 0xec, 0x24, 0xb1, 0x0c, 0xc7, 0xff, 0x95, 0xc6, 0x32, 0x5c, + 0x06, 0x50, 0x32, 0x65, 0xf3, 0xc7, 0x9d, 0x97, 0x59, 0x37, 0x1d, 0x9f, 0x25, 0x5b, 0x21, 0x34, + 0xfe, 0x01, 0x81, 0xb9, 0x46, 0x63, 0xbd, 0xe6, 0xda, 0x3b, 0x1d, 0x2f, 0x08, 0xdd, 0x66, 0xbd, + 0xb3, 0xed, 0x31, 0xd1, 0x2e, 0xda, 0x60, 0x94, 0xa0, 0x75, 0xf1, 0xe6, 0x12, 0x15, 0xb3, 0xab, + 0xc3, 0xb2, 0xef, 0x7b, 0xbe, 0x7a, 0x75, 0xa0, 0x0c, 0x60, 0x72, 0x38, 0x93, 0x9e, 0x1a, 0x3d, + 0x9e, 0xf2, 0x98, 0xbf, 0xb7, 0xa1, 0xf4, 0x14, 0x70, 0x90, 0x29, 0xcb, 0x08, 0x4d, 0x4f, 0x58, + 0x21, 0x4d, 0x9f, 0xd5, 0x22, 0x22, 0xc6, 0xc5, 0x7c, 0xfb, 0x14, 0xc7, 0x1b, 0x2e, 0xc5, 0x2e, + 0xc2, 0xd5, 0x07, 0xf2, 0xd4, 0x1a, 0x38, 0x80, 0xd3, 0x9a, 0x33, 0xd7, 0xb0, 0x8a, 0xc3, 0x57, + 0x85, 0xb4, 0x66, 0xa0, 0x11, 0x52, 0x86, 0xf6, 0x50, 0x4d, 0xd3, 0x91, 0x59, 0x03, 0xf9, 0x89, + 0x1c, 0x5c, 0xc8, 0x2c, 0x89, 0x56, 0xf7, 0xa4, 0x16, 0x95, 0x52, 0xd9, 0x34, 0x78, 0x42, 0x92, + 0x7e, 0x55, 0x5b, 0x19, 0x5b, 0xc1, 0xe0, 0x9a, 0xc8, 0x6f, 0xe5, 0xe0, 0xac, 0x86, 0x11, 0x6d, + 0x9f, 0x41, 0xe4, 0xb5, 0x9c, 0x39, 0xaf, 0x3f, 0x7c, 0x32, 0xf3, 0xfa, 0x45, 0xbd, 0x2d, 0xf1, + 0xee, 0xae, 0xb6, 0xa1, 0xdf, 0x17, 0x92, 0x47, 0x30, 0x87, 0x45, 0x52, 0x89, 0xc9, 0xe6, 0xac, + 0xd0, 0x7d, 0x9e, 0x8a, 0x3f, 0x9b, 0x3b, 0x28, 0x62, 0x52, 0xc9, 0xc5, 0x1f, 0x1c, 0x96, 0xa7, + 0x35, 0x74, 0x19, 0xe7, 0xd1, 0x8a, 0x35, 0xa1, 0x6e, 0x67, 0xdb, 0x53, 0x8f, 0xde, 0x54, 0x15, + 0xe4, 0xef, 0xe7, 0x60, 0x81, 0x41, 0x79, 0x33, 0x6e, 0xf9, 0x5e, 0x3b, 0x2a, 0x97, 0x96, 0x16, + 0x7d, 0xba, 0xad, 0xf5, 0x64, 0xba, 0xed, 0x65, 0xfc, 0x64, 0xbe, 0x27, 0x58, 0xdb, 0xbe, 0xd7, + 0x8e, 0x3f, 0x5f, 0xcb, 0x04, 0xdd, 0xef, 0x23, 0xc9, 0x8f, 0xe4, 0xe0, 0x9c, 0xa6, 0x79, 0x51, + 0x83, 0x6e, 0x0b, 0x37, 0xd0, 0xf9, 0xc8, 0xdd, 0x3b, 0x2e, 0x5a, 0xba, 0x2a, 0xe6, 0xff, 0x25, + 0xfc, 0x82, 0xf8, 0xb4, 0xc0, 0x6f, 0xb1, 0xda, 0x1c, 0x4b, 0xf9, 0x84, 0xfe, 0xb5, 0x10, 0x17, + 0xe6, 0xf0, 0x8d, 0x52, 0xb3, 0x08, 0x3a, 0xd5, 0xdf, 0x22, 0x28, 0x0a, 0x4e, 0x8f, 0xa1, 0x76, + 0xfb, 0x9b, 0x05, 0xa5, 0xb9, 0x92, 0xbf, 0x08, 0xe7, 0x52, 0xc0, 0x68, 0xb5, 0x9d, 0xee, 0xbb, + 0xda, 0x5e, 0x3b, 0x3a, 0x2c, 0xbf, 0x92, 0x55, 0x5b, 0xd6, 0x4a, 0xeb, 0x5f, 0x03, 0xb1, 0x01, + 0xe2, 0x42, 0x91, 0x50, 0x3a, 0x7b, 0x82, 0xbe, 0x26, 0xe6, 0x87, 0x82, 0xcf, 0xf6, 0x72, 0xe5, + 0x1b, 0xd4, 0x23, 0x2f, 0x46, 0x22, 0x14, 0xa6, 0x94, 0x30, 0xc3, 0x07, 0x98, 0x59, 0xba, 0x6f, + 0x25, 0x3f, 0x38, 0x2c, 0x6b, 0xd8, 0x4c, 0xc4, 0x56, 0xe3, 0x17, 0xab, 0x22, 0xb6, 0x86, 0x48, + 0x7e, 0x33, 0x07, 0xa7, 0x18, 0x20, 0x9e, 0x54, 0xa2, 0x51, 0x0b, 0x83, 0x66, 0xfd, 0xee, 0x93, + 0x99, 0xf5, 0x2f, 0xe0, 0x37, 0xaa, 0xb3, 0x3e, 0xd5, 0x25, 0x99, 0x1f, 0x87, 0xb3, 0x5d, 0x7b, + 0x0e, 0xd7, 0x66, 0xfb, 0xb9, 0x21, 0x66, 0x3b, 0x1f, 0x80, 0xe3, 0x67, 0x7b, 0xdf, 0x5a, 0xc8, + 0x26, 0x4c, 0x09, 0xe9, 0x9a, 0x77, 0xd8, 0xf3, 0x5a, 0x88, 0x52, 0xb5, 0x88, 0x5f, 0x79, 0x44, + 0x14, 0xe6, 0x54, 0x0b, 0x35, 0x2e, 0xa4, 0x03, 0xf3, 0xfc, 0xb7, 0xae, 0xeb, 0x28, 0xf7, 0xd5, + 0x75, 0x5c, 0x16, 0x2d, 0xba, 0x28, 0xf8, 0x27, 0x54, 0x1e, 0x6a, 0x94, 0x88, 0x0c, 0xc6, 0xa4, + 0x0b, 0x44, 0x03, 0xf3, 0x45, 0x7b, 0x71, 0xb0, 0x86, 0xe3, 0x15, 0x51, 0x67, 0x39, 0x59, 0x67, + 0x72, 0xe5, 0x66, 0xf0, 0x26, 0x36, 0xcc, 0x0a, 0x28, 0xbb, 0x4b, 0xe3, 0x0e, 0xff, 0x82, 0x16, + 0xa7, 0x23, 0x51, 0xca, 0x05, 0x73, 0x59, 0x13, 0xc6, 0x51, 0x49, 0x6c, 0xe8, 0x49, 0x7e, 0x64, + 0x1d, 0xe6, 0x2a, 0xdd, 0x6e, 0xcb, 0xa5, 0x0e, 0xb6, 0x92, 0xe7, 0xc9, 0x35, 0xe2, 0xb4, 0x20, + 0x36, 0x2f, 0x14, 0xb7, 0x85, 0x64, 0x92, 0xdc, 0x34, 0xad, 0xf1, 0xfd, 0x5c, 0xea, 0xa3, 0xc9, + 0xeb, 0x30, 0x81, 0x3f, 0x14, 0x67, 0x71, 0x54, 0x02, 0xf0, 0x4f, 0x44, 0x65, 0x44, 0x8c, 0xc0, + 0x84, 0x25, 0x35, 0xfc, 0x53, 0x81, 0x0b, 0x4b, 0xe2, 0xa6, 0x1a, 0xdf, 0x4d, 0xcb, 0xd2, 0x52, + 0xb3, 0x10, 0x0b, 0x5d, 0x68, 0xa9, 0x29, 0xec, 0x33, 0x8d, 0x1f, 0xc9, 0xeb, 0xd3, 0x8e, 0x5c, + 0x56, 0xe4, 0x76, 0x25, 0x00, 0x95, 0x94, 0xdb, 0x15, 0x69, 0xfd, 0x6f, 0xe4, 0x60, 0x7e, 0xdd, + 0xdf, 0xb1, 0x3b, 0xee, 0x77, 0x78, 0x20, 0x4b, 0x0f, 0xc7, 0x65, 0x70, 0x86, 0xa5, 0x27, 0x95, + 0x29, 0xc6, 0x53, 0x2a, 0x66, 0x33, 0x05, 0xa7, 0x8c, 0x99, 0xf5, 0x3d, 0x68, 0xc5, 0x8f, 0x1f, + 0xa6, 0x24, 0xec, 0xe1, 0xe8, 0x1c, 0x6e, 0xfc, 0x54, 0x1e, 0x26, 0x95, 0x25, 0x40, 0x3e, 0x0f, + 0x53, 0x2a, 0x1f, 0x55, 0x81, 0xa4, 0x56, 0x6b, 0x6a, 0x58, 0xa8, 0x41, 0xa2, 0x76, 0x5b, 0xd3, + 0x20, 0xb1, 0x89, 0x8e, 0xd0, 0x13, 0x5e, 0x6d, 0xde, 0xcf, 0xb8, 0xda, 0x9c, 0x28, 0xa1, 0xf2, + 0x3b, 0xe9, 0x0b, 0xce, 0xf0, 0xf9, 0x8f, 0x8d, 0x9f, 0xcd, 0x41, 0x29, 0xb9, 0x48, 0x3f, 0x95, + 0x5e, 0x39, 0xc1, 0x6b, 0xc1, 0x4f, 0xe6, 0xa3, 0x18, 0xe3, 0xd2, 0x37, 0xe9, 0x69, 0x35, 0x89, + 0x79, 0x57, 0x53, 0xe4, 0x3f, 0xab, 0x07, 0xcd, 0x51, 0xbd, 0x7a, 0xb3, 0x23, 0x65, 0x8d, 0xfc, + 0xe2, 0xaf, 0x94, 0x9f, 0x31, 0x3e, 0x80, 0x53, 0xc9, 0xee, 0x40, 0x65, 0x7e, 0x05, 0x66, 0x75, + 0x78, 0x32, 0x43, 0x41, 0x92, 0xca, 0x4c, 0xe2, 0x1b, 0x7f, 0x98, 0x4f, 0xf2, 0x16, 0xe6, 0x31, + 0x6c, 0xd3, 0xe9, 0xd8, 0x5b, 0xad, 0x28, 0x42, 0x39, 0xdf, 0x74, 0x38, 0xc8, 0x94, 0x65, 0x27, + 0xc9, 0x0c, 0x12, 0x79, 0xd8, 0x14, 0xb2, 0x3d, 0x6c, 0xc8, 0xcd, 0x84, 0x8d, 0x99, 0x12, 0x0e, + 0x62, 0x9f, 0x6e, 0x59, 0xb1, 0x9d, 0x59, 0xc2, 0xb4, 0xac, 0x0a, 0xa7, 0xb4, 0x48, 0xa5, 0x92, + 0x7e, 0x34, 0xd6, 0xdd, 0x86, 0x58, 0xc0, 0x89, 0x33, 0x91, 0xc9, 0x0a, 0x8c, 0xb3, 0xcf, 0xbc, + 0x67, 0x77, 0x85, 0x8e, 0x9e, 0x44, 0xfe, 0x76, 0xad, 0xe8, 0xc2, 0xa7, 0xb8, 0xdc, 0xb5, 0x28, + 0x3b, 0xf2, 0xb5, 0x7c, 0xe4, 0x1c, 0xd1, 0xf8, 0x67, 0x39, 0xb6, 0xfe, 0x9b, 0x7b, 0x9f, 0xb1, + 0xf4, 0x22, 0xac, 0x49, 0x03, 0xac, 0xb7, 0xfe, 0x24, 0xcf, 0x83, 0xcc, 0x8b, 0xe9, 0xf3, 0x16, + 0x8c, 0x6d, 0xda, 0xfe, 0x0e, 0x0d, 0x45, 0xf8, 0x75, 0x95, 0x0b, 0x2f, 0x88, 0x83, 0x55, 0x84, + 0xf8, 0xdb, 0x14, 0x04, 0xaa, 0x2e, 0x2c, 0x3f, 0x94, 0x2e, 0x4c, 0xd1, 0xf4, 0x16, 0x9e, 0x98, + 0xa6, 0xf7, 0x87, 0xa2, 0x78, 0xf2, 0x95, 0x70, 0x88, 0x40, 0x98, 0x17, 0x93, 0xf9, 0x18, 0x52, + 0x21, 0x4b, 0x63, 0x76, 0xe4, 0xa6, 0x9a, 0xe1, 0x41, 0x71, 0xf5, 0x38, 0x26, 0x97, 0x83, 0xf1, + 0x27, 0x05, 0xde, 0xc7, 0xa2, 0xa3, 0x2e, 0x69, 0x0e, 0x6d, 0xb8, 0x4e, 0xd8, 0x46, 0xaf, 0xfa, + 0x16, 0xa3, 0x61, 0xc7, 0x25, 0x18, 0x61, 0x73, 0x53, 0xf4, 0x26, 0xe2, 0xb1, 0xf9, 0xab, 0xe2, + 0xb1, 0x72, 0xb6, 0x96, 0xf1, 0x4c, 0x52, 0x53, 0xf7, 0xe0, 0xb1, 0xa5, 0xae, 0x65, 0xc4, 0x20, + 0x97, 0x61, 0x64, 0xcd, 0x73, 0x64, 0xc0, 0xd5, 0x53, 0xe8, 0xd6, 0xec, 0x39, 0x0a, 0xcb, 0x85, + 0x9c, 0x89, 0x18, 0xac, 0xad, 0x51, 0x88, 0x76, 0xb5, 0xad, 0xed, 0x6d, 0x3b, 0x9d, 0x92, 0x45, + 0x89, 0xe6, 0xbe, 0x0c, 0x33, 0x7a, 0xfa, 0x54, 0x61, 0xdb, 0x86, 0x1a, 0xdb, 0x44, 0x16, 0x56, + 0x55, 0xd1, 0xae, 0x13, 0x91, 0x25, 0x98, 0xd6, 0x02, 0xbd, 0x89, 0xc7, 0x32, 0x54, 0x6f, 0xea, + 0x61, 0xe2, 0x54, 0xf5, 0xa6, 0x46, 0xc2, 0xce, 0x73, 0xf1, 0xfd, 0xca, 0x93, 0x59, 0xea, 0xdb, + 0x05, 0x0e, 0xb9, 0x01, 0x45, 0xee, 0x3f, 0x5c, 0xaf, 0xa9, 0x0f, 0x1f, 0x01, 0xc2, 0x12, 0xfe, + 0xf7, 0x12, 0x51, 0xf1, 0x17, 0xfd, 0x1c, 0x94, 0xc4, 0x96, 0x14, 0xe7, 0xe8, 0x7c, 0x0e, 0x46, + 0xaa, 0xf5, 0x9a, 0xa9, 0x6e, 0x23, 0x4d, 0xd7, 0xf1, 0x4d, 0x84, 0xa2, 0xe9, 0xfe, 0x1a, 0x0d, + 0xf7, 0x3d, 0x7f, 0xcf, 0xa4, 0x41, 0xe8, 0xbb, 0x3c, 0xf3, 0x13, 0x2e, 0xc4, 0xcf, 0x93, 0x77, + 0x60, 0x14, 0x8d, 0xac, 0x12, 0x27, 0x43, 0xb2, 0x8e, 0xa5, 0x69, 0x31, 0x81, 0x47, 0xd1, 0x62, + 0xcb, 0xe4, 0x44, 0xe4, 0x2d, 0x18, 0xa9, 0xd1, 0xce, 0x41, 0x22, 0x29, 0x4d, 0x8a, 0x38, 0xda, + 0x10, 0x1c, 0xda, 0x39, 0x30, 0x91, 0xc4, 0xf8, 0xd9, 0x3c, 0x9c, 0xce, 0xf8, 0xac, 0x07, 0x9f, + 0x7f, 0x4a, 0x77, 0xc5, 0x25, 0x6d, 0x57, 0x94, 0xef, 0x9d, 0x7d, 0x3b, 0x3e, 0x73, 0x93, 0xfc, + 0xa5, 0x1c, 0x9c, 0xd5, 0x27, 0xa8, 0xb0, 0xaa, 0x7c, 0x70, 0x83, 0xbc, 0x0d, 0x63, 0x2b, 0xd4, + 0x76, 0xa8, 0x4c, 0x58, 0x71, 0x3a, 0x8a, 0xf4, 0xc3, 0x5d, 0x0a, 0x79, 0x21, 0x67, 0x1b, 0x3b, + 0xa0, 0x70, 0x28, 0xa9, 0x89, 0x8f, 0xe3, 0xf2, 0xb8, 0x21, 0x1d, 0x95, 0xb3, 0xaa, 0x1a, 0x60, + 0x35, 0xf0, 0x83, 0x1c, 0x3c, 0x3b, 0x80, 0x86, 0x0d, 0x1c, 0x1b, 0x7a, 0x75, 0xe0, 0xf0, 0x44, + 0x45, 0x28, 0x79, 0x0f, 0x66, 0x37, 0x85, 0x3c, 0x2f, 0x87, 0x23, 0x1f, 0xaf, 0x17, 0x29, 0xea, + 0x5b, 0x72, 0x5c, 0x92, 0xc8, 0x9a, 0x07, 0x7d, 0x61, 0xa0, 0x07, 0xbd, 0xea, 0x90, 0x3e, 0x32, + 0xac, 0x43, 0xfa, 0x07, 0x70, 0x4a, 0x6f, 0x9b, 0x88, 0x0b, 0x18, 0xbb, 0xe3, 0xe7, 0xfa, 0xbb, + 0xe3, 0x0f, 0x8c, 0x3e, 0x66, 0xfc, 0x54, 0x0e, 0x4a, 0x3a, 0xef, 0x4f, 0x3a, 0x9e, 0xef, 0x6a, + 0xe3, 0xf9, 0x6c, 0xf6, 0x78, 0xf6, 0x1f, 0xc8, 0xff, 0x23, 0x97, 0x6c, 0xec, 0x50, 0x23, 0x68, + 0xc0, 0x58, 0xcd, 0x6b, 0xdb, 0x6e, 0x47, 0x4d, 0xf9, 0xea, 0x20, 0xc4, 0x14, 0x25, 0xc3, 0x45, + 0x2f, 0xb8, 0x08, 0xa3, 0x6b, 0x5e, 0xa7, 0x52, 0x13, 0x46, 0x87, 0xc8, 0xa7, 0xe3, 0x75, 0x2c, + 0xdb, 0x31, 0x79, 0x01, 0x59, 0x05, 0x68, 0x34, 0x7d, 0x4a, 0x3b, 0x0d, 0xf7, 0x3b, 0x34, 0x21, + 0x69, 0xb0, 0x1e, 0x6a, 0xf5, 0x70, 0x63, 0xc1, 0x37, 0x9e, 0x00, 0x11, 0xad, 0xc0, 0xfd, 0x8e, + 0xba, 0xdf, 0x2a, 0xf4, 0x06, 0x05, 0x88, 0x89, 0x30, 0x05, 0x9d, 0xeb, 0x88, 0x9c, 0xc6, 0xd3, + 0x22, 0x05, 0x1d, 0x03, 0x68, 0x29, 0xe8, 0x18, 0x80, 0x6d, 0xed, 0x2b, 0xd4, 0xdd, 0xd9, 0xe5, + 0xd6, 0x27, 0xd3, 0x7c, 0xaa, 0xee, 0x22, 0x44, 0xdd, 0xda, 0x39, 0x8e, 0xf1, 0xcf, 0x47, 0xe1, + 0x9c, 0x49, 0x77, 0x5c, 0x26, 0x26, 0xdf, 0x0f, 0xdc, 0xce, 0x8e, 0xe6, 0x5f, 0x6e, 0x24, 0x26, + 0x92, 0x08, 0xad, 0xcc, 0x20, 0x51, 0xc7, 0x5c, 0x81, 0x22, 0x3b, 0x15, 0x95, 0xb9, 0x84, 0x6f, + 0x28, 0x98, 0xb5, 0x9e, 0x4f, 0x72, 0x59, 0x4c, 0x5e, 0x15, 0xa7, 0xb6, 0x12, 0xfc, 0x9e, 0x9d, + 0xda, 0x1f, 0x1d, 0x96, 0xa1, 0x71, 0x10, 0x84, 0x14, 0x6f, 0x6c, 0xe2, 0xe4, 0x8e, 0x44, 0xeb, + 0x91, 0x3e, 0xa2, 0xf5, 0x3d, 0x38, 0x55, 0x71, 0xf8, 0x66, 0x6d, 0xb7, 0x36, 0x7c, 0xb7, 0xd3, + 0x74, 0xbb, 0x76, 0x4b, 0x5e, 0x17, 0xb1, 0x97, 0xed, 0xa8, 0xdc, 0xea, 0x46, 0x08, 0x66, 0x26, + 0x19, 0x6b, 0x46, 0x6d, 0xad, 0x81, 0xce, 0xcb, 0xe2, 0x79, 0x0c, 0x9b, 0xe1, 0x74, 0x02, 0x6c, + 0x45, 0x60, 0x46, 0xc5, 0x28, 0xd4, 0xa3, 0x39, 0xc3, 0xe6, 0x6a, 0x23, 0xf6, 0x4e, 0xe2, 0xb1, + 0x79, 0xb9, 0xc9, 0x43, 0xd8, 0x0a, 0xd0, 0xec, 0x41, 0xc3, 0x8b, 0xe9, 0x1a, 0x8d, 0x15, 0x46, + 0x57, 0x4c, 0xd1, 0x05, 0xc1, 0xae, 0x4a, 0xc7, 0xf1, 0xc8, 0x35, 0x36, 0x15, 0xda, 0x5e, 0x48, + 0x71, 0x9e, 0x4f, 0xc4, 0x57, 0x00, 0x1f, 0xa1, 0xfc, 0x0a, 0xa0, 0xa0, 0x90, 0x77, 0x60, 0x7e, + 0xb9, 0xba, 0x28, 0x95, 0x9a, 0x35, 0xaf, 0xd9, 0xc3, 0x07, 0x6a, 0xc0, 0xfa, 0x70, 0x0c, 0x69, + 0x73, 0x91, 0x4d, 0xee, 0x2c, 0x34, 0x72, 0x09, 0xc6, 0xeb, 0x35, 0xde, 0xf7, 0x93, 0x6a, 0x02, + 0x0a, 0x61, 0xf8, 0x21, 0x0b, 0xc9, 0x7a, 0x2c, 0xa3, 0x4e, 0x1d, 0x2b, 0x4c, 0x9e, 0x1b, 0x42, + 0x3e, 0x7d, 0x0b, 0xa6, 0x97, 0xbc, 0xb0, 0xde, 0x09, 0x42, 0xbb, 0xd3, 0xa4, 0xf5, 0x9a, 0x1a, + 0x3f, 0x72, 0xcb, 0x0b, 0x2d, 0x57, 0x94, 0xb0, 0x2f, 0xd7, 0x31, 0xc9, 0x17, 0x91, 0xf4, 0x36, + 0xed, 0x50, 0x3f, 0x8e, 0x1b, 0x39, 0xca, 0xfb, 0x96, 0x91, 0xee, 0x44, 0x25, 0xa6, 0x8e, 0x28, + 0x92, 0x63, 0xf0, 0x24, 0x4c, 0x55, 0xcf, 0xa1, 0xc1, 0x83, 0xeb, 0x9f, 0xb1, 0xe4, 0x18, 0x4a, + 0xdb, 0x70, 0xcb, 0xbc, 0x9e, 0xb9, 0xbf, 0xfe, 0xfb, 0x98, 0x1c, 0x23, 0x85, 0x4b, 0xbe, 0x08, + 0xa3, 0xf8, 0x53, 0x08, 0x5b, 0xf3, 0x19, 0x6c, 0x63, 0x41, 0xab, 0xc9, 0xf3, 0x44, 0x23, 0x01, + 0xa9, 0xc3, 0xb8, 0x90, 0xf3, 0x4f, 0x12, 0xe2, 0x5d, 0x5c, 0x18, 0xf8, 0xcc, 0x10, 0xf4, 0x86, + 0x03, 0x53, 0x6a, 0x85, 0x6c, 0x45, 0xac, 0xd8, 0xc1, 0x2e, 0x75, 0xd8, 0x2f, 0x91, 0x9d, 0x05, + 0x57, 0xc4, 0x2e, 0x42, 0x2d, 0xf6, 0x1d, 0xa6, 0x82, 0xc2, 0xb6, 0xf8, 0x7a, 0x70, 0x3f, 0x10, + 0x9f, 0x22, 0x6e, 0xfe, 0x2e, 0x6a, 0x91, 0x1c, 0x53, 0x14, 0x19, 0x3f, 0x04, 0xa7, 0xd6, 0x7a, + 0xad, 0x96, 0xbd, 0xd5, 0xa2, 0x32, 0x7a, 0x37, 0x66, 0x6a, 0x5c, 0x82, 0xd1, 0x86, 0x92, 0xfb, + 0x71, 0x3e, 0x0a, 0x8f, 0x1e, 0xe3, 0xa0, 0x8d, 0x5d, 0x0e, 0x1d, 0xc9, 0x13, 0x59, 0x1f, 0x39, + 0xa9, 0xf1, 0x07, 0x71, 0xc6, 0xf2, 0x4d, 0xdf, 0x6e, 0xee, 0x45, 0x09, 0x40, 0x87, 0x4d, 0xbe, + 0x7e, 0x47, 0x7e, 0x84, 0x7e, 0x7e, 0x66, 0x7d, 0xf0, 0x71, 0x1f, 0x43, 0xde, 0x81, 0x49, 0x71, + 0x86, 0x2a, 0x81, 0x9c, 0x30, 0x8e, 0x85, 0xb8, 0x7d, 0x24, 0x8d, 0x23, 0x54, 0x74, 0x14, 0x0d, + 0xf4, 0xa6, 0x3c, 0xb8, 0xfe, 0x69, 0x88, 0x06, 0x7a, 0x1d, 0x03, 0xa6, 0xee, 0xef, 0x4c, 0x26, + 0xfb, 0x56, 0xcc, 0xdd, 0x9b, 0x6a, 0xe8, 0x96, 0x5c, 0x7c, 0x51, 0x8b, 0x43, 0xb7, 0xa8, 0x17, + 0xb5, 0x08, 0x35, 0x1a, 0x93, 0xfc, 0x31, 0x63, 0xf2, 0x9e, 0x1c, 0x93, 0x42, 0xff, 0x89, 0x31, + 0x3f, 0x60, 0x1c, 0x1a, 0xf1, 0x0a, 0x19, 0x19, 0xea, 0x96, 0xff, 0x0c, 0xc6, 0xa8, 0xe5, 0x24, + 0xc9, 0x5d, 0x54, 0x70, 0x52, 0x55, 0x07, 0xa3, 0xc3, 0x33, 0x3d, 0x66, 0x6b, 0xfe, 0x12, 0x4c, + 0x55, 0xc2, 0xd0, 0x6e, 0xee, 0x52, 0xa7, 0xc6, 0xb6, 0x27, 0x25, 0x36, 0x83, 0x2d, 0xe0, 0xea, + 0x1b, 0x8e, 0x8a, 0xcb, 0xa3, 0xa6, 0xd9, 0x81, 0xb0, 0xd6, 0x8b, 0xa2, 0xa6, 0x31, 0x88, 0x1e, + 0x35, 0x8d, 0x41, 0xc8, 0x35, 0x18, 0xaf, 0x77, 0x1e, 0xb9, 0xac, 0x4f, 0x8a, 0x71, 0x36, 0x60, + 0x97, 0x83, 0xd4, 0xcd, 0x55, 0x60, 0x91, 0xb7, 0x14, 0x19, 0x7b, 0x22, 0xbe, 0x4f, 0x73, 0x0d, + 0x4c, 0xe4, 0xd5, 0xad, 0xca, 0xcf, 0x91, 0xd0, 0x7d, 0x13, 0xc6, 0xa5, 0x62, 0x0d, 0xe2, 0x3b, + 0xb4, 0xa0, 0x4c, 0x7b, 0x7f, 0x4a, 0x64, 0x4c, 0xe6, 0xa8, 0x64, 0x99, 0x99, 0x54, 0x92, 0x39, + 0x2a, 0x59, 0x66, 0xb4, 0x64, 0x8e, 0x4a, 0xbe, 0x99, 0x48, 0x27, 0x31, 0x75, 0xac, 0x4e, 0xe2, + 0x01, 0x4c, 0x6d, 0xd8, 0x7e, 0xe8, 0x32, 0x19, 0xa5, 0x13, 0x06, 0x0b, 0xd3, 0x9a, 0x1a, 0x4f, + 0x29, 0x5a, 0x7a, 0x5e, 0xe6, 0x1f, 0xec, 0x2a, 0xf8, 0x7a, 0xa2, 0xbc, 0x18, 0x9e, 0x6d, 0xab, + 0x37, 0xf3, 0x49, 0x6c, 0xf5, 0xb0, 0x53, 0x51, 0x75, 0x33, 0x1b, 0x2b, 0x08, 0x50, 0x86, 0x4e, + 0xe8, 0x6f, 0x22, 0x44, 0xf2, 0x75, 0x98, 0x62, 0x7f, 0x6f, 0x78, 0x2d, 0xb7, 0xe9, 0xd2, 0x60, + 0xa1, 0x84, 0x8d, 0x7b, 0x3e, 0x73, 0xf5, 0x23, 0xd2, 0x41, 0x83, 0x86, 0x7c, 0x01, 0x23, 0xe3, + 0xa4, 0x4e, 0x56, 0xe3, 0x46, 0xde, 0x87, 0x29, 0x36, 0xfb, 0xb6, 0xec, 0x80, 0x8b, 0xa6, 0x73, + 0xb1, 0xb5, 0xa5, 0x23, 0xe0, 0xa9, 0xc0, 0x85, 0x2a, 0x01, 0x3b, 0xe6, 0x2b, 0x5d, 0xbe, 0x41, + 0x12, 0x65, 0xb6, 0x77, 0x53, 0x9b, 0xa3, 0x44, 0x23, 0x5f, 0x86, 0xa9, 0x4a, 0xb7, 0x1b, 0xef, + 0x38, 0xf3, 0x8a, 0x5e, 0xa6, 0xdb, 0xb5, 0x32, 0x77, 0x1d, 0x8d, 0x22, 0xb9, 0x31, 0x9f, 0x3a, + 0xd1, 0xc6, 0x4c, 0xde, 0x88, 0xa4, 0xf5, 0xd3, 0xb1, 0x92, 0x51, 0xdc, 0x63, 0x34, 0xd1, 0x9f, + 0x0b, 0xee, 0x55, 0x98, 0xe6, 0x5a, 0x37, 0x29, 0xcd, 0x9c, 0x49, 0xad, 0x9e, 0x0c, 0xa1, 0x46, + 0xa7, 0x21, 0xcb, 0x30, 0xc3, 0x1d, 0xdd, 0x5a, 0x22, 0xa2, 0xe4, 0xc2, 0xd9, 0x38, 0x7f, 0x35, + 0xf7, 0x8f, 0x6b, 0x61, 0xa0, 0x71, 0x5b, 0xe3, 0x92, 0x20, 0x32, 0xfe, 0x34, 0x07, 0x67, 0xfb, + 0x8c, 0x78, 0x14, 0x6f, 0x30, 0x37, 0x38, 0xde, 0x20, 0xdb, 0x39, 0xf4, 0x4b, 0x3a, 0xb6, 0x5f, + 0x48, 0x59, 0xea, 0x78, 0x49, 0x79, 0xcb, 0x03, 0x22, 0x22, 0xf3, 0x8b, 0xaa, 0xef, 0x78, 0xa8, + 0x29, 0x2c, 0xa4, 0x0f, 0x21, 0x81, 0xc7, 0x3f, 0x8a, 0x47, 0x69, 0x12, 0x81, 0xff, 0xa3, 0x61, + 0xfd, 0xd0, 0xd3, 0x56, 0x70, 0x06, 0x6b, 0xe3, 0x30, 0x07, 0x93, 0xca, 0x3a, 0x24, 0x17, 0x15, + 0xb7, 0xb9, 0x12, 0x4f, 0x1d, 0xa1, 0x70, 0xc8, 0xf3, 0x93, 0x08, 0x17, 0x55, 0xfe, 0x78, 0x7d, + 0xe8, 0x3d, 0x26, 0x0a, 0x29, 0x31, 0x19, 0xdb, 0x9a, 0xf2, 0xd2, 0xc4, 0x72, 0x4c, 0x9b, 0x6a, + 0x07, 0x61, 0xa5, 0x19, 0xba, 0x8f, 0xe8, 0x10, 0x87, 0x4e, 0x9c, 0x36, 0xd5, 0x0e, 0x42, 0xcb, + 0x46, 0xb2, 0x54, 0xda, 0xd4, 0x88, 0xa1, 0xf1, 0xa3, 0x39, 0x80, 0xfb, 0xf5, 0x2a, 0x06, 0x55, + 0xfd, 0xa4, 0x42, 0x41, 0x76, 0xa0, 0x3a, 0xc9, 0x7d, 0x80, 0x38, 0xf0, 0x3f, 0xe4, 0x60, 0x46, + 0x47, 0x23, 0xef, 0xc1, 0x6c, 0xa3, 0xe9, 0x7b, 0xad, 0xd6, 0x96, 0xdd, 0xdc, 0x5b, 0x75, 0x3b, + 0x94, 0x07, 0xef, 0x1a, 0xe5, 0x67, 0x51, 0x10, 0x15, 0x59, 0x2d, 0x56, 0x66, 0x26, 0x91, 0xc9, + 0x8f, 0xe5, 0x60, 0xba, 0xb1, 0xeb, 0xed, 0xc7, 0x49, 0xf3, 0xf9, 0x80, 0x7c, 0x83, 0xad, 0xed, + 0x60, 0xd7, 0xdb, 0xb7, 0x32, 0x32, 0xe7, 0x7f, 0x74, 0x58, 0x7e, 0x77, 0xb8, 0x67, 0xe2, 0xa6, + 0x87, 0x37, 0x99, 0x30, 0xb8, 0xaa, 0x55, 0x62, 0xea, 0x75, 0x1a, 0x7f, 0x9e, 0x83, 0x49, 0xbc, + 0xf3, 0xb4, 0x5a, 0x28, 0x73, 0x7d, 0x96, 0x92, 0x18, 0x45, 0xed, 0x1a, 0x30, 0xb0, 0x6f, 0xc2, + 0x6c, 0x02, 0x8d, 0x18, 0x30, 0xd6, 0x40, 0x57, 0x69, 0x55, 0x41, 0xc1, 0x9d, 0xa7, 0x4d, 0x51, + 0x62, 0x2c, 0x2b, 0x64, 0x0f, 0xae, 0xe3, 0x2b, 0xe3, 0x22, 0x80, 0x2b, 0x41, 0xf2, 0x66, 0x43, + 0x92, 0x5f, 0xf2, 0xe0, 0xba, 0xa9, 0x60, 0x19, 0x6b, 0x30, 0xd6, 0xf0, 0xfc, 0x70, 0xe9, 0x80, + 0x5f, 0x26, 0x6a, 0x34, 0x68, 0xaa, 0xcf, 0x88, 0x2e, 0xaa, 0xee, 0x9b, 0xa6, 0x28, 0x22, 0x65, + 0x18, 0xbd, 0xe5, 0xd2, 0x96, 0xa3, 0xda, 0x8b, 0x6e, 0x33, 0x80, 0xc9, 0xe1, 0xec, 0xc2, 0x75, + 0x26, 0x8e, 0x3d, 0x1e, 0x1b, 0xa6, 0x7e, 0xd2, 0x75, 0x53, 0xd5, 0xfa, 0xf7, 0x05, 0x3d, 0x47, + 0xb0, 0x56, 0xd3, 0x80, 0xae, 0xfe, 0x4f, 0x73, 0x70, 0xbe, 0x3f, 0x89, 0x6a, 0xeb, 0x9a, 0x1b, + 0x60, 0xeb, 0xfa, 0x72, 0xf2, 0xd9, 0x0b, 0xd1, 0xc4, 0xb3, 0x57, 0xfc, 0xd8, 0x55, 0x43, 0x53, + 0xe3, 0x66, 0x94, 0xc2, 0xfd, 0xe2, 0x80, 0x6f, 0x46, 0x44, 0x3e, 0xcc, 0x21, 0xd2, 0x98, 0x82, + 0xd6, 0xf8, 0xed, 0x11, 0x38, 0xd7, 0x97, 0x82, 0xac, 0x28, 0x69, 0x0c, 0x66, 0xa2, 0x00, 0xea, + 0x7d, 0xf1, 0xaf, 0xe2, 0xbf, 0x68, 0x4d, 0x96, 0x74, 0xa6, 0x59, 0x8f, 0xc2, 0xd7, 0xf3, 0x5c, + 0xfd, 0xaf, 0x1d, 0xcb, 0x8b, 0xa3, 0x23, 0x33, 0x48, 0x47, 0xb2, 0x47, 0xb7, 0x2b, 0x1a, 0xda, + 0x6e, 0x2b, 0x50, 0x97, 0x9d, 0xc3, 0x41, 0xa6, 0x2c, 0x8b, 0x0d, 0x90, 0x47, 0xb2, 0x0d, 0x90, + 0x8d, 0x7f, 0x9e, 0x83, 0x89, 0xe8, 0xb3, 0xc9, 0x79, 0x38, 0xb3, 0x69, 0x56, 0xaa, 0xcb, 0xd6, + 0xe6, 0x07, 0x1b, 0xcb, 0xd6, 0xfd, 0xb5, 0xc6, 0xc6, 0x72, 0xb5, 0x7e, 0xab, 0xbe, 0x5c, 0x2b, + 0x3d, 0x43, 0xe6, 0x60, 0xfa, 0xfe, 0xda, 0xdd, 0xb5, 0xf5, 0x87, 0x6b, 0xd6, 0xb2, 0x69, 0xae, + 0x9b, 0xa5, 0x1c, 0x99, 0x86, 0x09, 0x73, 0xa9, 0x52, 0xb5, 0xd6, 0xd6, 0x6b, 0xcb, 0xa5, 0x3c, + 0x29, 0xc1, 0x54, 0x75, 0x7d, 0x6d, 0x6d, 0xb9, 0xba, 0x59, 0x7f, 0x50, 0xdf, 0xfc, 0xa0, 0x54, + 0x20, 0x04, 0x66, 0x10, 0x61, 0xc3, 0xac, 0xaf, 0x55, 0xeb, 0x1b, 0x95, 0xd5, 0xd2, 0x08, 0x83, + 0x31, 0x7c, 0x05, 0x36, 0x1a, 0x31, 0xba, 0x7b, 0x7f, 0x69, 0xb9, 0x34, 0xc6, 0x50, 0xd8, 0x5f, + 0x0a, 0xca, 0x38, 0xab, 0x1e, 0x51, 0x6a, 0x95, 0xcd, 0xca, 0x52, 0xa5, 0xb1, 0x5c, 0x2a, 0x92, + 0xb3, 0x30, 0xaf, 0x81, 0xac, 0xd5, 0xf5, 0xdb, 0xf5, 0xb5, 0xd2, 0x04, 0x39, 0x05, 0xa5, 0x08, + 0x56, 0x5b, 0xb2, 0xee, 0x37, 0x96, 0xcd, 0x12, 0x24, 0xa1, 0x6b, 0x95, 0x7b, 0xcb, 0xa5, 0x49, + 0xe3, 0x5d, 0xee, 0xe6, 0xc4, 0xbb, 0x9a, 0x9c, 0x01, 0xd2, 0xd8, 0xac, 0x6c, 0xde, 0x6f, 0x24, + 0x1a, 0x3f, 0x09, 0xe3, 0x8d, 0xfb, 0xd5, 0xea, 0x72, 0xa3, 0x51, 0xca, 0x11, 0x80, 0xb1, 0x5b, + 0x95, 0xfa, 0xea, 0x72, 0xad, 0x94, 0x37, 0x7e, 0x26, 0x07, 0x73, 0x52, 0x02, 0x94, 0x6f, 0x18, + 0x9f, 0x70, 0x2d, 0xbe, 0xa7, 0x5d, 0x6c, 0xa5, 0x17, 0x4a, 0xa2, 0x92, 0x01, 0xcb, 0xf0, 0x97, + 0x72, 0x70, 0x3a, 0x13, 0x9b, 0x7c, 0x00, 0x25, 0xf9, 0x05, 0xf7, 0xec, 0xb0, 0xb9, 0x1b, 0xef, + 0x63, 0xcf, 0x27, 0x6a, 0x49, 0xa0, 0x71, 0xb5, 0x66, 0x9c, 0x26, 0x31, 0xc5, 0x66, 0xf8, 0xb0, + 0xbf, 0xc6, 0x2f, 0xe6, 0xe0, 0x6c, 0x9f, 0x6a, 0x48, 0x15, 0xc6, 0xa2, 0x00, 0xf0, 0x03, 0x0c, + 0xaa, 0x4e, 0xfd, 0xe0, 0xb0, 0x2c, 0x10, 0x31, 0xaf, 0x1c, 0xfe, 0x65, 0x8e, 0x45, 0x11, 0xdd, + 0x31, 0xac, 0x3a, 0xef, 0xbe, 0x73, 0x89, 0x9e, 0x17, 0x35, 0x55, 0x1e, 0x36, 0x96, 0x26, 0x45, + 0xdf, 0x15, 0xec, 0xfd, 0x00, 0xe3, 0xaa, 0x1b, 0x3f, 0x97, 0x63, 0xc2, 0x5d, 0x12, 0x91, 0xc9, + 0xbc, 0x95, 0x20, 0xe8, 0xb5, 0xa9, 0xe9, 0xb5, 0x68, 0xc5, 0x5c, 0x13, 0xc7, 0x06, 0x4a, 0xab, + 0x36, 0x16, 0xe0, 0xb5, 0xc2, 0xb2, 0xfd, 0x8e, 0xf6, 0x78, 0xaa, 0xd2, 0x90, 0xb7, 0x00, 0xa2, + 0xfc, 0xfe, 0x32, 0xa8, 0x01, 0x0f, 0xea, 0x21, 0xa0, 0xba, 0xbc, 0xad, 0x20, 0x1b, 0x7f, 0x39, + 0x07, 0x53, 0xe2, 0xd2, 0x54, 0x69, 0x51, 0x3f, 0xfc, 0x64, 0xd3, 0xeb, 0x2d, 0x6d, 0x7a, 0x45, + 0xfe, 0x03, 0x0a, 0x7f, 0x56, 0x9c, 0x39, 0xb3, 0xfe, 0xdb, 0x1c, 0x94, 0x92, 0x88, 0xe4, 0x3d, + 0x28, 0x36, 0xe8, 0x23, 0xea, 0xbb, 0xe1, 0x81, 0xd8, 0x28, 0x65, 0xaa, 0x1c, 0x8e, 0x23, 0xca, + 0xf8, 0x7c, 0x08, 0xc4, 0x2f, 0x33, 0xa2, 0x19, 0x76, 0xbf, 0x57, 0xd4, 0x1e, 0x85, 0x27, 0xa5, + 0xf6, 0x30, 0xfe, 0xd7, 0x3c, 0x9c, 0xbd, 0x4d, 0x43, 0xb5, 0x4d, 0xd1, 0x6b, 0xf7, 0xe7, 0x86, + 0x6b, 0x97, 0xd2, 0x92, 0x05, 0x18, 0xc7, 0x22, 0x39, 0xbe, 0xa6, 0xfc, 0x49, 0x96, 0xa2, 0x79, + 0x5d, 0xd0, 0x72, 0x71, 0xf4, 0xa9, 0xfb, 0xaa, 0x12, 0x9d, 0x3f, 0x9a, 0xd6, 0x97, 0x60, 0x06, + 0x03, 0xc3, 0xf6, 0xd8, 0x72, 0xa0, 0x8e, 0x50, 0xff, 0x14, 0xcd, 0x04, 0x94, 0xbc, 0x0a, 0x25, + 0x06, 0xa9, 0x34, 0xf7, 0x3a, 0xde, 0x7e, 0x8b, 0x3a, 0x3b, 0x94, 0x27, 0x64, 0x2f, 0x9a, 0x29, + 0xb8, 0xe4, 0x79, 0xbf, 0xc3, 0xaf, 0x6e, 0xd4, 0x41, 0x1d, 0x8d, 0xe0, 0x19, 0x43, 0xcf, 0xbf, + 0x05, 0x93, 0x1f, 0x33, 0xd3, 0x86, 0xf1, 0xbf, 0xe4, 0xe0, 0x14, 0x36, 0x4e, 0xa9, 0x18, 0x5f, + 0x0c, 0x3e, 0x17, 0xf7, 0x96, 0x12, 0x7c, 0xde, 0x66, 0x20, 0x7d, 0x29, 0x44, 0xbd, 0x18, 0xeb, + 0x84, 0xf2, 0x43, 0xe8, 0x84, 0x1a, 0x27, 0xc9, 0xdf, 0x3a, 0xa4, 0x4a, 0x8b, 0x67, 0xdd, 0x8f, + 0x87, 0xdc, 0xf8, 0xb1, 0x3c, 0x8c, 0x9b, 0x14, 0x13, 0x5b, 0x92, 0x4b, 0x30, 0xbe, 0xe6, 0x85, + 0x34, 0xb8, 0xa7, 0x65, 0x31, 0xed, 0x30, 0x90, 0xd5, 0x76, 0x4c, 0x59, 0xc8, 0x26, 0xfc, 0x86, + 0xef, 0x39, 0xbd, 0x66, 0xa8, 0x4e, 0xf8, 0x2e, 0x07, 0x99, 0xb2, 0x8c, 0xbc, 0x0e, 0x13, 0x82, + 0x73, 0xf4, 0xc6, 0x88, 0xb6, 0xb1, 0x3e, 0x8d, 0x12, 0xa3, 0xc6, 0x08, 0x28, 0xd3, 0x72, 0x01, + 0x63, 0x44, 0x91, 0x69, 0x53, 0x32, 0x83, 0x14, 0xd5, 0x47, 0x07, 0x88, 0xea, 0x9f, 0x83, 0xb1, + 0x4a, 0x10, 0xd0, 0x50, 0x3a, 0x69, 0x4f, 0x45, 0x11, 0x73, 0x02, 0x1a, 0x72, 0xc6, 0x36, 0x96, + 0x9b, 0x02, 0xcf, 0xf8, 0x67, 0x79, 0x18, 0xc5, 0x3f, 0xf1, 0x5d, 0xd5, 0x6f, 0xee, 0x6a, 0xef, + 0xaa, 0x7e, 0x73, 0xd7, 0x44, 0x28, 0xb9, 0x8e, 0x9a, 0x0a, 0x99, 0x27, 0x41, 0xb4, 0x1e, 0x55, + 0xf0, 0x4e, 0x0c, 0x36, 0x55, 0x9c, 0xe8, 0xc1, 0xb9, 0x90, 0x19, 0x9a, 0xe1, 0x0c, 0xe4, 0xd7, + 0x1b, 0xa2, 0xc5, 0x18, 0x42, 0xc6, 0x0b, 0xcc, 0xfc, 0x7a, 0x03, 0x7b, 0x63, 0xa5, 0xb2, 0xf8, + 0xe6, 0x4d, 0x35, 0xe1, 0x6e, 0xb0, 0x6b, 0x2f, 0xbe, 0x79, 0xd3, 0x14, 0x25, 0xac, 0x7f, 0xf1, + 0x9b, 0xf1, 0xe1, 0x95, 0x3b, 0x15, 0x63, 0xff, 0x62, 0xdb, 0xf0, 0x91, 0xd5, 0x8c, 0x11, 0xc8, + 0x22, 0x4c, 0x0a, 0x57, 0x76, 0xc4, 0x57, 0x5c, 0xcd, 0x85, 0xab, 0x3b, 0xa7, 0x50, 0x91, 0xf8, + 0x13, 0x9c, 0x18, 0x20, 0x99, 0xcd, 0x4d, 0x3c, 0xc1, 0xc9, 0x21, 0x0c, 0x4c, 0x05, 0x25, 0xf6, + 0x89, 0x8e, 0x9d, 0x85, 0x55, 0x9f, 0x68, 0x0c, 0xc2, 0x1b, 0x21, 0x18, 0xbf, 0x96, 0x87, 0xe2, + 0x46, 0xab, 0xb7, 0xe3, 0x76, 0x1e, 0x5c, 0x27, 0x04, 0xf0, 0x1a, 0x27, 0xa3, 0x34, 0xb3, 0xbf, + 0xc9, 0x39, 0x28, 0xca, 0x9b, 0x9b, 0xdc, 0x90, 0x02, 0x71, 0x6b, 0x5b, 0x00, 0x39, 0xee, 0x22, + 0x3b, 0xbf, 0xfc, 0x49, 0xae, 0x43, 0x74, 0xff, 0xea, 0x77, 0x51, 0x1b, 0x61, 0x8b, 0xc5, 0x8c, + 0xd0, 0xc8, 0x1b, 0x80, 0x87, 0x84, 0xb8, 0x3c, 0x48, 0x85, 0x36, 0xff, 0x34, 0x21, 0xa7, 0x70, + 0x12, 0x44, 0x23, 0x37, 0x40, 0x4c, 0x4c, 0x91, 0x03, 0xf4, 0xb4, 0x4e, 0xc0, 0xf3, 0x30, 0x49, + 0x12, 0x81, 0x4a, 0xde, 0x81, 0xc9, 0x38, 0xfb, 0x7e, 0x9c, 0xda, 0x53, 0xa5, 0xac, 0xc6, 0xe5, + 0x0f, 0xae, 0x9b, 0x2a, 0xba, 0xf1, 0xbd, 0x71, 0x98, 0x52, 0xbf, 0x87, 0x98, 0x30, 0x1f, 0xb4, + 0xd8, 0xdd, 0x5d, 0xd8, 0x3e, 0x75, 0xb1, 0x50, 0x1c, 0xa7, 0x17, 0xf5, 0x0f, 0x62, 0x78, 0xdc, + 0x10, 0x4a, 0xfa, 0xe0, 0xaf, 0x3c, 0x63, 0xce, 0x05, 0x31, 0x98, 0xe3, 0x91, 0x0a, 0x14, 0xbd, + 0x6e, 0xb0, 0x43, 0x3b, 0xae, 0x7c, 0x6f, 0x79, 0x51, 0x63, 0xb4, 0x2e, 0x0a, 0x53, 0xbc, 0x22, + 0x32, 0xf2, 0x26, 0x8c, 0x79, 0x5d, 0xda, 0xb1, 0x5d, 0x71, 0xc6, 0x3d, 0x9b, 0x60, 0x40, 0x3b, + 0x95, 0xba, 0x42, 0x28, 0x90, 0xc9, 0x35, 0x18, 0xf1, 0xf6, 0xa2, 0xf1, 0x3a, 0xa7, 0x13, 0xed, + 0x85, 0xb6, 0x42, 0x82, 0x88, 0x8c, 0xe0, 0x43, 0xbb, 0xbd, 0x2d, 0x46, 0x4c, 0x27, 0xb8, 0x63, + 0xb7, 0xb7, 0x55, 0x02, 0x86, 0x48, 0xde, 0x07, 0xe8, 0xda, 0x3b, 0xd4, 0xb7, 0x9c, 0x5e, 0x78, + 0x20, 0xc6, 0xed, 0x79, 0x8d, 0x6c, 0x83, 0x15, 0xd7, 0x7a, 0xe1, 0x81, 0x42, 0x3b, 0xd1, 0x95, + 0x40, 0x52, 0x01, 0x68, 0xdb, 0x61, 0x48, 0xfd, 0xb6, 0x27, 0x8c, 0xcf, 0x26, 0xa3, 0xd4, 0x99, + 0x9c, 0xc1, 0xbd, 0xa8, 0x58, 0xe1, 0xa0, 0x10, 0xe1, 0x47, 0xbb, 0xbe, 0x2d, 0x32, 0xb1, 0x26, + 0x3e, 0xda, 0xf5, 0xb5, 0x56, 0x32, 0x44, 0xf2, 0x45, 0x18, 0x77, 0xdc, 0xa0, 0xe9, 0xf9, 0x8e, + 0x08, 0xce, 0xf0, 0x9c, 0x46, 0x53, 0xe3, 0x65, 0x0a, 0x99, 0x44, 0x67, 0x5f, 0x2b, 0xe2, 0xbf, + 0xad, 0x79, 0xfb, 0xa8, 0xe6, 0x4f, 0x7e, 0x6d, 0x23, 0x2a, 0x56, 0xbf, 0x36, 0x26, 0x62, 0x43, + 0xb9, 0xe3, 0x86, 0x2d, 0x7b, 0x4b, 0xbc, 0x73, 0xeb, 0x43, 0x79, 0x1b, 0x8b, 0xd4, 0xa1, 0xe4, + 0xc8, 0xe4, 0x2d, 0x28, 0xd2, 0x4e, 0xe8, 0xdb, 0x96, 0xeb, 0x08, 0xa7, 0x3d, 0xfd, 0xa3, 0xd9, + 0x01, 0x6c, 0xd7, 0x6b, 0xea, 0x47, 0x23, 0x7e, 0xdd, 0x61, 0xfd, 0x13, 0x34, 0xdd, 0xb6, 0xf0, + 0xb5, 0xd3, 0xfb, 0xa7, 0x51, 0xad, 0xdf, 0x53, 0xfb, 0x87, 0x21, 0x92, 0xf7, 0x60, 0x9c, 0xad, + 0x5f, 0xc7, 0xdb, 0x11, 0x1e, 0xef, 0x86, 0xde, 0x3f, 0xbc, 0x2c, 0x35, 0x5d, 0x25, 0x11, 0x79, + 0x1e, 0x20, 0x7e, 0x22, 0xe7, 0x0f, 0x1a, 0xa6, 0x02, 0xf9, 0xd2, 0xc8, 0xff, 0xfe, 0x2b, 0xe5, + 0xdc, 0x12, 0x40, 0x51, 0x46, 0xb7, 0x30, 0x56, 0xe1, 0x5c, 0xdf, 0x45, 0x45, 0xae, 0x40, 0x69, + 0xdb, 0x16, 0x2a, 0xb5, 0xe6, 0xae, 0xdd, 0xe9, 0xd0, 0x96, 0xd8, 0xce, 0x66, 0x25, 0xbc, 0xca, + 0xc1, 0x9c, 0xb3, 0xf1, 0x3e, 0x9c, 0xca, 0xea, 0x4d, 0xf2, 0x02, 0x4c, 0xa9, 0x81, 0x3c, 0x04, + 0x93, 0x49, 0xbb, 0xeb, 0xca, 0x50, 0x1e, 0x82, 0xc1, 0x6f, 0xe5, 0xe0, 0xb9, 0x41, 0x6b, 0x93, + 0x9c, 0x87, 0x62, 0xd7, 0x77, 0x3d, 0x94, 0x01, 0xf9, 0x0e, 0x1a, 0xfd, 0x26, 0x17, 0x00, 0xb8, + 0xb0, 0x12, 0xda, 0x3b, 0xc2, 0x98, 0xdf, 0x9c, 0x40, 0xc8, 0xa6, 0xbd, 0x13, 0x90, 0xd7, 0x60, + 0xce, 0xa1, 0xdb, 0x76, 0xaf, 0x15, 0x5a, 0x41, 0x73, 0x97, 0x3a, 0xe8, 0x3f, 0x83, 0x46, 0x5a, + 0x66, 0x49, 0x14, 0x34, 0x24, 0x3c, 0xf5, 0xc5, 0xa3, 0x7d, 0xbe, 0xf8, 0xce, 0x48, 0x31, 0x57, + 0xca, 0x9b, 0x68, 0xab, 0x64, 0xfc, 0x70, 0x1e, 0x16, 0xfa, 0x4d, 0x46, 0xf2, 0x6e, 0x56, 0x1f, + 0xf0, 0x57, 0x01, 0x15, 0xae, 0xbe, 0x0a, 0x28, 0xb5, 0x91, 0x45, 0x88, 0xbc, 0x5f, 0x8e, 0xf3, + 0x64, 0x97, 0x30, 0x46, 0xd3, 0xb5, 0x83, 0x60, 0x9f, 0xad, 0xb7, 0x82, 0x12, 0xa8, 0x4f, 0xc0, + 0x54, 0x1a, 0x09, 0x23, 0x5f, 0x00, 0x68, 0xb6, 0xbc, 0x80, 0xe2, 0xe3, 0xbb, 0x38, 0xc8, 0xb9, + 0x09, 0x70, 0x04, 0x55, 0x5f, 0x5b, 0x11, 0x5a, 0xf5, 0x1c, 0x2a, 0x06, 0xd0, 0x86, 0xb3, 0x7d, + 0x76, 0x1f, 0x36, 0x3c, 0x71, 0x8a, 0x53, 0x99, 0x66, 0xa0, 0x17, 0x25, 0x3a, 0x4d, 0xf6, 0x78, + 0xbe, 0xdf, 0x1c, 0x39, 0x00, 0x92, 0xde, 0x62, 0x18, 0x77, 0x61, 0xc8, 0xda, 0xf3, 0x23, 0xee, + 0x1c, 0x72, 0xdf, 0x6f, 0x91, 0x32, 0x4c, 0xca, 0x84, 0x48, 0x4c, 0x50, 0xe6, 0xcc, 0x41, 0x80, + 0xee, 0x52, 0x9c, 0x3c, 0x18, 0x4f, 0x12, 0x7d, 0x9c, 0xc4, 0x11, 0x3c, 0x81, 0x90, 0xcd, 0x83, + 0xae, 0x6c, 0xdd, 0x73, 0x72, 0x7e, 0xeb, 0x1b, 0xbf, 0x28, 0xfd, 0xf9, 0x9c, 0x1c, 0xfe, 0xf4, + 0xce, 0x79, 0xdc, 0xf7, 0x11, 0x40, 0x8f, 0x14, 0xf1, 0x61, 0xf8, 0x37, 0x13, 0x09, 0xe4, 0xaa, + 0x13, 0x22, 0x81, 0xf8, 0x49, 0x2e, 0xc1, 0xac, 0xcf, 0x6d, 0x16, 0x43, 0x4f, 0xf4, 0x27, 0x8e, + 0x94, 0x39, 0xcd, 0xc1, 0x9b, 0x1e, 0xf6, 0xa9, 0xf8, 0xae, 0x3b, 0x51, 0x87, 0x29, 0x07, 0x09, + 0xb9, 0x0a, 0x13, 0xec, 0x20, 0xc1, 0x38, 0x19, 0x09, 0x53, 0x78, 0xc4, 0xc3, 0x63, 0xd9, 0x2c, + 0x7e, 0x28, 0xfe, 0x16, 0xbc, 0xfe, 0x61, 0x4e, 0x32, 0x53, 0x8f, 0x31, 0x72, 0x16, 0xc6, 0x3d, + 0x7f, 0x47, 0x69, 0xda, 0x98, 0xe7, 0xef, 0xb0, 0x76, 0x5d, 0x86, 0x12, 0xf7, 0xcc, 0xe0, 0x2e, + 0xef, 0xc1, 0x41, 0x87, 0xdf, 0x73, 0x8b, 0xe6, 0x0c, 0x87, 0x63, 0xd6, 0xd7, 0x83, 0x4e, 0x93, + 0x61, 0x06, 0x81, 0x67, 0xa9, 0xc1, 0x71, 0x44, 0xb3, 0x67, 0x82, 0xc0, 0x8b, 0xa3, 0xe4, 0x38, + 0x64, 0x09, 0xa6, 0x19, 0x9f, 0x28, 0x44, 0x8f, 0x38, 0x65, 0x2f, 0xa4, 0x4f, 0xd9, 0x83, 0x4e, + 0x53, 0x7e, 0xa2, 0x39, 0x15, 0x28, 0xbf, 0x44, 0x6b, 0x7e, 0x23, 0x0f, 0x67, 0xb2, 0xd1, 0x71, + 0xbc, 0x58, 0x25, 0xe8, 0xa0, 0xc4, 0xd5, 0xa3, 0xe6, 0x04, 0x83, 0xf0, 0x18, 0x0c, 0x59, 0x5f, + 0x9b, 0xcf, 0xfc, 0xda, 0x57, 0x61, 0x0e, 0x19, 0x09, 0xb9, 0xa6, 0xe5, 0x06, 0xa1, 0x08, 0x2d, + 0x60, 0xce, 0xb2, 0x02, 0xbe, 0xc1, 0xad, 0x32, 0x30, 0x79, 0x19, 0x66, 0xe4, 0x16, 0xe5, 0xed, + 0x77, 0x58, 0xc5, 0x7c, 0x7f, 0x9a, 0x16, 0xd0, 0x75, 0x04, 0x92, 0xd3, 0x30, 0x66, 0x77, 0xbb, + 0xac, 0x4a, 0xbe, 0x2d, 0x8d, 0xda, 0xdd, 0x6e, 0xdd, 0x21, 0x2f, 0xc2, 0x34, 0xba, 0x63, 0x59, + 0xdb, 0x68, 0x93, 0x22, 0x0c, 0xe0, 0xcc, 0x29, 0x04, 0x72, 0x3b, 0x95, 0x80, 0x2d, 0x04, 0x46, + 0x2b, 0x51, 0xc6, 0x11, 0x05, 0xec, 0x6e, 0x84, 0x70, 0x0e, 0x8a, 0xf2, 0x75, 0x94, 0x5b, 0x95, + 0x9b, 0xe3, 0x36, 0x7f, 0x19, 0x15, 0x9d, 0xf6, 0x45, 0x98, 0x15, 0x07, 0xb5, 0xd8, 0xfc, 0x91, + 0xa9, 0x98, 0x9a, 0x4c, 0x82, 0x16, 0x91, 0xd7, 0x41, 0x80, 0xea, 0x8e, 0xec, 0xee, 0x3f, 0xca, + 0xc1, 0xe9, 0xcc, 0x93, 0x9e, 0x7c, 0x0b, 0xb8, 0xe3, 0x4a, 0xe8, 0x59, 0x3e, 0x6d, 0xba, 0x5d, + 0x17, 0x5d, 0xfb, 0xb9, 0x26, 0x6c, 0x71, 0x90, 0x8c, 0x80, 0x4e, 0x30, 0x9b, 0x9e, 0x19, 0x11, + 0xf1, 0x2b, 0x7a, 0xc9, 0x4f, 0x80, 0xcf, 0x7f, 0x0d, 0x4e, 0x67, 0xa2, 0x66, 0x5c, 0x9d, 0x5f, + 0xd7, 0x73, 0xed, 0xc9, 0xb7, 0x8d, 0x44, 0xa3, 0x95, 0x2b, 0xb5, 0x68, 0xde, 0xef, 0x46, 0xcd, + 0x4b, 0xc8, 0x04, 0x64, 0x39, 0x39, 0x63, 0xb3, 0xc4, 0x5a, 0x49, 0xd4, 0x77, 0xd2, 0x92, 0xaf, + 0xc1, 0x69, 0x31, 0x8b, 0x76, 0x7c, 0xbb, 0xbb, 0x1b, 0xb3, 0xe3, 0x1f, 0xfa, 0x4a, 0x16, 0x3b, + 0x3e, 0xbd, 0x6e, 0x33, 0xfc, 0x88, 0xeb, 0xbc, 0x9d, 0x06, 0x8a, 0x36, 0xfc, 0x48, 0x5e, 0x0a, + 0x04, 0x19, 0x9f, 0x93, 0x31, 0x3f, 0x73, 0x59, 0xf3, 0x73, 0xf8, 0xc5, 0xb1, 0x06, 0x44, 0xb9, + 0x15, 0x58, 0x5c, 0x59, 0x26, 0xec, 0x70, 0xa4, 0x78, 0x27, 0x3e, 0x44, 0xb9, 0x4c, 0x34, 0x78, + 0xae, 0xa3, 0xb9, 0x66, 0x12, 0x44, 0x9e, 0x85, 0x89, 0x28, 0x9d, 0xa0, 0xd8, 0x12, 0x8b, 0x1c, + 0x50, 0x77, 0xc8, 0x45, 0x98, 0xe2, 0x92, 0x9c, 0xb6, 0x78, 0x00, 0x61, 0x15, 0xb6, 0x82, 0x64, + 0x1f, 0xe4, 0xe0, 0xe2, 0x71, 0x7d, 0x48, 0x1e, 0xc2, 0x19, 0xb4, 0x06, 0x08, 0xbc, 0x68, 0x18, + 0xac, 0xa6, 0xdd, 0xdc, 0xa5, 0x62, 0xd6, 0x1a, 0x99, 0x83, 0xd1, 0xed, 0x36, 0x1a, 0xeb, 0xca, + 0x38, 0x74, 0xbb, 0x8d, 0xc0, 0x93, 0xbf, 0xab, 0x8c, 0x5c, 0x7c, 0x83, 0x03, 0xcf, 0x0e, 0xa0, + 0x54, 0x76, 0x80, 0x9c, 0xba, 0x03, 0x5c, 0x86, 0xd2, 0x36, 0x75, 0x98, 0xb4, 0x47, 0x1d, 0xfc, + 0xb4, 0x47, 0x8b, 0x3c, 0x81, 0xa6, 0x39, 0x13, 0xc1, 0x1b, 0x81, 0xf7, 0x60, 0x51, 0xd4, 0xd2, + 0x96, 0x9b, 0xb9, 0x2a, 0x8d, 0x92, 0xab, 0x30, 0x9f, 0x08, 0x9b, 0x10, 0xfb, 0xe1, 0x9a, 0x73, + 0xac, 0x48, 0x0f, 0xb2, 0xf3, 0x02, 0x4c, 0xc9, 0x59, 0xe1, 0x47, 0xde, 0x3c, 0xe6, 0xa4, 0x80, + 0xb1, 0x55, 0x27, 0xaa, 0xeb, 0xc9, 0x46, 0x65, 0x0a, 0xb2, 0x43, 0x48, 0x89, 0xe4, 0x0d, 0x20, + 0x91, 0x44, 0x1a, 0x6d, 0x14, 0xa2, 0xc2, 0x39, 0x59, 0x12, 0xad, 0x70, 0x51, 0xed, 0xdf, 0xcc, + 0x4b, 0xa1, 0x72, 0xc9, 0xf3, 0xc2, 0x20, 0xf4, 0xed, 0xae, 0x76, 0x33, 0x25, 0x6d, 0x38, 0xe7, + 0xd9, 0xbd, 0x70, 0x77, 0xd1, 0x62, 0xff, 0x7a, 0xbe, 0xf4, 0xe8, 0x6d, 0x4a, 0xb3, 0xc4, 0xc9, + 0xc5, 0x6b, 0xfa, 0xe1, 0x52, 0x61, 0xd8, 0x15, 0x15, 0x99, 0xc9, 0x40, 0x0a, 0xd7, 0x95, 0x67, + 0xcc, 0xb3, 0x9c, 0x67, 0x0a, 0x8b, 0xac, 0xc0, 0xd4, 0x16, 0xb5, 0x7d, 0xea, 0x73, 0x53, 0xdc, + 0xcc, 0xab, 0xe9, 0x12, 0x22, 0xa0, 0x85, 0xae, 0xce, 0x75, 0x72, 0x2b, 0x2e, 0x21, 0xef, 0xc1, + 0x84, 0xeb, 0x88, 0x60, 0x84, 0xe2, 0x82, 0xaa, 0x5f, 0x8a, 0xea, 0x0e, 0x8f, 0x4d, 0x18, 0xf3, + 0x60, 0xb7, 0x5b, 0x57, 0x40, 0x97, 0xa6, 0xb5, 0x3b, 0xbc, 0xb1, 0x24, 0xe5, 0x97, 0x34, 0x19, + 0x99, 0x81, 0x7c, 0x34, 0xdb, 0xf2, 0xae, 0x43, 0xce, 0xc0, 0x58, 0xa0, 0x44, 0x47, 0x34, 0xc5, + 0x2f, 0xe3, 0x2f, 0xc0, 0xe5, 0x61, 0xfb, 0x88, 0x8d, 0x66, 0x9f, 0x0e, 0x9f, 0x30, 0xe7, 0xec, + 0x54, 0xbf, 0xbd, 0x00, 0x6a, 0x78, 0x37, 0x57, 0xce, 0x33, 0x09, 0xbb, 0xef, 0xbb, 0xc6, 0x8f, + 0x16, 0x60, 0x46, 0xd7, 0x5a, 0x90, 0xd7, 0x60, 0x24, 0x62, 0x3b, 0x13, 0x69, 0xd7, 0x55, 0x24, + 0xc6, 0xdc, 0x44, 0x24, 0x76, 0x84, 0xe2, 0x63, 0x9c, 0xd5, 0x56, 0x15, 0xe0, 0xe6, 0x14, 0x02, + 0xa5, 0xe2, 0xfb, 0x0e, 0xf0, 0x34, 0xd1, 0xb8, 0xa5, 0x87, 0x6e, 0x9b, 0x0e, 0xa1, 0xff, 0x2e, + 0xfe, 0xc1, 0x61, 0xf9, 0x19, 0xd4, 0x5c, 0x4e, 0x31, 0x5a, 0xb6, 0xab, 0xb2, 0x42, 0xe5, 0x52, + 0x3a, 0xd2, 0xff, 0x52, 0x2a, 0x9a, 0xd2, 0xe7, 0x52, 0x3a, 0x3a, 0xe0, 0x52, 0x1a, 0x53, 0xaa, + 0x97, 0x52, 0x54, 0x4d, 0x8c, 0xf7, 0x53, 0x4d, 0xc4, 0x34, 0x5c, 0x35, 0xf1, 0x92, 0x68, 0xae, + 0x6f, 0xef, 0x5b, 0xd8, 0x0f, 0xdc, 0x6c, 0x90, 0x37, 0xc4, 0xb4, 0xf7, 0xf1, 0xd9, 0x72, 0x69, + 0x02, 0xe4, 0x5b, 0xa7, 0xf1, 0x57, 0x73, 0x89, 0x6b, 0xa0, 0x1c, 0x8a, 0x97, 0x61, 0xc6, 0x6d, + 0x33, 0xf9, 0x94, 0x3a, 0x8a, 0x5c, 0x35, 0x6d, 0x4e, 0x4b, 0x28, 0x97, 0xad, 0x5e, 0x81, 0xd9, + 0x08, 0x8d, 0xfb, 0x97, 0x73, 0x1f, 0x08, 0x33, 0xa2, 0x16, 0xfe, 0xe5, 0xaf, 0xc1, 0x5c, 0x84, + 0x28, 0x44, 0x79, 0x2e, 0x5a, 0x4d, 0x9b, 0x25, 0x59, 0x20, 0x12, 0x9e, 0x06, 0xc6, 0x4e, 0xf2, + 0x70, 0xfe, 0xff, 0xd9, 0xbb, 0x9a, 0x18, 0x39, 0x8e, 0xeb, 0xcc, 0x9e, 0x99, 0x5d, 0xee, 0xbe, + 0xd9, 0x9f, 0xde, 0xe2, 0x8a, 0x5c, 0x91, 0xcb, 0xa5, 0xd4, 0xa4, 0x09, 0x72, 0x6c, 0xc9, 0xa6, + 0x18, 0x59, 0xa2, 0x12, 0x45, 0xea, 0x9d, 0xe9, 0xd9, 0x69, 0xed, 0xfc, 0xb9, 0xbb, 0x67, 0xd7, + 0x94, 0x6c, 0x77, 0x46, 0x33, 0xbd, 0xbb, 0x13, 0xcf, 0xf6, 0x8c, 0xe7, 0x87, 0x32, 0x75, 0x49, + 0x82, 0x00, 0x0e, 0x90, 0xc4, 0xf9, 0x31, 0x72, 0x10, 0x92, 0x43, 0x10, 0x44, 0x87, 0x1c, 0x72, + 0x4c, 0x4e, 0x39, 0xe5, 0x66, 0xc0, 0x30, 0xe0, 0x43, 0x4e, 0x09, 0x20, 0x24, 0x02, 0xe2, 0x43, + 0x90, 0x5b, 0x10, 0x1f, 0x7c, 0x0a, 0xea, 0x55, 0x55, 0x77, 0xf5, 0xcf, 0x0c, 0x77, 0x49, 0x29, + 0x89, 0x01, 0x9f, 0x66, 0xba, 0xea, 0x55, 0x75, 0x75, 0xfd, 0xbc, 0x7a, 0x55, 0xef, 0xbd, 0xef, + 0x7d, 0x4e, 0xad, 0xd2, 0xfe, 0x31, 0x1b, 0x11, 0x91, 0xc5, 0x6b, 0x76, 0x21, 0x4f, 0x39, 0x32, + 0xef, 0x24, 0xce, 0x56, 0x5e, 0x9c, 0xd1, 0xfd, 0x5c, 0x5b, 0x6c, 0xdb, 0x0d, 0x0b, 0xc6, 0xe3, + 0x81, 0x50, 0x1e, 0xbb, 0x6c, 0xd3, 0x61, 0x52, 0x1e, 0x4e, 0x3f, 0x51, 0x1d, 0xe3, 0x21, 0x85, + 0xf9, 0xd5, 0xe9, 0xc3, 0x21, 0xb6, 0x91, 0xce, 0x3e, 0xdc, 0x7c, 0x82, 0x27, 0xf1, 0x82, 0x16, + 0xe0, 0x89, 0x72, 0x1c, 0xad, 0x3c, 0x9b, 0x22, 0x5e, 0x24, 0x2a, 0xc7, 0x5e, 0xc2, 0x9a, 0xd5, + 0xa9, 0xf8, 0x2b, 0xaa, 0x35, 0x60, 0x65, 0xdc, 0xe9, 0x9d, 0x06, 0x15, 0xe6, 0x52, 0x2e, 0x37, + 0x92, 0x1f, 0x5f, 0x34, 0x6b, 0x56, 0x9e, 0x96, 0x13, 0xd5, 0x9c, 0xc0, 0xf3, 0xb2, 0x14, 0x1d, + 0x6d, 0xe4, 0x82, 0xc0, 0xed, 0x9b, 0xdb, 0x03, 0xa1, 0xb0, 0x8d, 0x4d, 0xbd, 0xdc, 0x8e, 0x26, + 0x70, 0x32, 0xed, 0x04, 0xae, 0xce, 0x1e, 0x12, 0x7a, 0x30, 0xf3, 0x64, 0x0f, 0x74, 0x4b, 0x3c, + 0x4a, 0xfb, 0x72, 0x46, 0xde, 0x97, 0x65, 0x99, 0x3a, 0x1b, 0x91, 0xa9, 0xb5, 0xbf, 0xce, 0xc2, + 0xcd, 0x33, 0x0c, 0xd7, 0x9c, 0x77, 0xbe, 0x0d, 0x79, 0x76, 0x25, 0xcb, 0xd8, 0x67, 0x26, 0x22, + 0x3c, 0xd1, 0x4a, 0x39, 0xaf, 0xa3, 0x82, 0x5c, 0xc8, 0xef, 0x60, 0x1c, 0xfc, 0x27, 0xbf, 0x01, + 0xeb, 0x8c, 0xa1, 0x31, 0x83, 0x8f, 0xa3, 0x69, 0xff, 0x0c, 0x1c, 0xed, 0x9a, 0xb0, 0x4e, 0x8f, + 0x15, 0x45, 0x26, 0x87, 0x1c, 0xc3, 0x0e, 0xd2, 0x88, 0x03, 0x79, 0x24, 0x3b, 0x6a, 0xf7, 0xfa, + 0x67, 0x32, 0x93, 0x16, 0xb6, 0xef, 0x72, 0x31, 0x66, 0xa7, 0x46, 0x13, 0xca, 0xf8, 0x4c, 0x8f, + 0xc1, 0xfe, 0xf4, 0x94, 0x8a, 0x73, 0x6c, 0x2e, 0x70, 0xbd, 0xda, 0x82, 0xb5, 0xea, 0x4f, 0x4f, + 0xf5, 0xe1, 0x10, 0x87, 0x14, 0x15, 0x70, 0x1b, 0x94, 0x8e, 0xad, 0x5a, 0x41, 0xb9, 0x88, 0x94, + 0xb4, 0x02, 0xb6, 0x6e, 0x39, 0xed, 0x26, 0x30, 0x73, 0x0c, 0xa6, 0x50, 0xb0, 0xd8, 0x83, 0xf6, + 0xb3, 0x8c, 0x10, 0x09, 0x67, 0xcf, 0xfb, 0x5f, 0x0e, 0x51, 0xca, 0x10, 0xdd, 0x01, 0x95, 0x76, + 0x7d, 0xc8, 0x54, 0x82, 0x31, 0x5a, 0xf3, 0xa7, 0xa7, 0x41, 0xdf, 0xc9, 0x1d, 0xbf, 0x28, 0x77, + 0xfc, 0x6b, 0x42, 0x64, 0x4c, 0x65, 0x0f, 0xb3, 0xbb, 0x5c, 0xfb, 0xcf, 0x2c, 0xdc, 0x3e, 0x1b, + 0x13, 0xf8, 0xe5, 0xb8, 0xa5, 0x8c, 0x5b, 0xec, 0x9a, 0x60, 0x21, 0x71, 0x4d, 0x90, 0xb2, 0xf6, + 0x16, 0xd3, 0xd6, 0x5e, 0xe2, 0x52, 0xe2, 0x62, 0xca, 0xa5, 0x44, 0xea, 0x02, 0x5d, 0x7a, 0xc2, + 0x02, 0x5d, 0x96, 0xe7, 0xc9, 0x4f, 0x33, 0x70, 0x29, 0x45, 0xe9, 0x44, 0xde, 0x83, 0x4b, 0x42, + 0xb4, 0x67, 0x3b, 0x07, 0x13, 0xb9, 0xd9, 0xee, 0x7b, 0x37, 0x4d, 0xa8, 0x47, 0xb2, 0x14, 0xc1, + 0x7b, 0x83, 0x8b, 0xf3, 0x61, 0xfe, 0xff, 0x1f, 0x41, 0x9e, 0x3c, 0x84, 0xcb, 0x08, 0x2c, 0xdb, + 0x71, 0xe5, 0xe3, 0xf4, 0xc8, 0x3b, 0xe2, 0xf3, 0xe1, 0xc5, 0x84, 0xd8, 0xdb, 0xeb, 0x48, 0xcd, + 0xb1, 0xbc, 0xa3, 0xca, 0x05, 0x6b, 0x73, 0x9c, 0x92, 0x1e, 0x3f, 0x23, 0xfc, 0xad, 0x02, 0xda, + 0x93, 0xfb, 0x0b, 0xcf, 0x72, 0xf1, 0x0e, 0xa7, 0x67, 0x39, 0xa9, 0xf7, 0x6e, 0xc2, 0xea, 0xc8, + 0x3b, 0x1a, 0x79, 0xe3, 0x13, 0xa9, 0xfb, 0x96, 0xad, 0x15, 0x9e, 0x28, 0x3a, 0x46, 0xc0, 0x49, + 0x9d, 0x4b, 0xc8, 0x16, 0x85, 0xb4, 0x72, 0x70, 0xf4, 0x4b, 0x1d, 0x07, 0x3a, 0x9b, 0xe4, 0x06, + 0xb2, 0x87, 0x77, 0x72, 0x4b, 0x19, 0x35, 0x6b, 0x71, 0xd0, 0xab, 0xa3, 0x5e, 0xdf, 0xd3, 0xfe, + 0x4e, 0x11, 0x12, 0x41, 0x5a, 0xe7, 0x91, 0xf7, 0x24, 0x33, 0xa9, 0x6c, 0x42, 0x0c, 0x49, 0x2b, + 0x22, 0x5b, 0x94, 0x70, 0x1c, 0x26, 0x4c, 0x88, 0xe0, 0x30, 0x61, 0xca, 0xb3, 0xd8, 0x7a, 0x3c, + 0x10, 0x5a, 0x56, 0xca, 0xed, 0x0e, 0xee, 0x91, 0xbb, 0x70, 0x91, 0x29, 0x56, 0x45, 0x43, 0xd7, + 0x23, 0x0d, 0x3d, 0xb8, 0x67, 0x89, 0x7c, 0xed, 0x23, 0x25, 0x50, 0x0d, 0xc5, 0x9b, 0x7f, 0x70, + 0x8f, 0xbc, 0x76, 0x36, 0x83, 0xa7, 0x25, 0x61, 0xf0, 0x14, 0x18, 0x3b, 0xbd, 0x1e, 0x31, 0x76, + 0xba, 0x35, 0xbf, 0x9f, 0xf8, 0x25, 0x74, 0x3c, 0x72, 0xf9, 0xcf, 0x14, 0xb8, 0x3e, 0xb7, 0x04, + 0xd9, 0x86, 0x25, 0xbd, 0x69, 0x3a, 0xe1, 0xc8, 0xd2, 0xd5, 0x22, 0x52, 0xc8, 0x1e, 0x2c, 0xef, + 0xb6, 0xc7, 0xbd, 0x0e, 0x9d, 0xc0, 0xa9, 0x77, 0x67, 0x89, 0x6a, 0x03, 0xf2, 0xca, 0x05, 0x2b, + 0x2c, 0x4b, 0x5c, 0xd8, 0xc0, 0x55, 0x90, 0x88, 0xa7, 0x1b, 0xbf, 0x30, 0x48, 0x54, 0x98, 0x28, + 0x46, 0x39, 0x4c, 0x22, 0x31, 0xbe, 0xf8, 0x1e, 0x09, 0x29, 0x64, 0x76, 0x03, 0xcf, 0x01, 0x9d, + 0x76, 0x07, 0x96, 0x9a, 0x42, 0x3d, 0x24, 0x59, 0x08, 0x0a, 0x55, 0x90, 0x15, 0xe4, 0x6a, 0x7f, + 0xa8, 0x88, 0x53, 0xfd, 0x93, 0x3f, 0x44, 0x0a, 0xb4, 0xd0, 0x9d, 0x1f, 0x68, 0xa1, 0xfb, 0x94, + 0x81, 0x16, 0xb4, 0xbf, 0xe1, 0xc0, 0xa6, 0x66, 0xb7, 0x19, 0x8b, 0xfd, 0xf5, 0xac, 0x96, 0x9e, + 0x46, 0x64, 0x76, 0xde, 0x94, 0x82, 0xbf, 0x24, 0xdf, 0x35, 0xdb, 0xe0, 0x53, 0x9a, 0xaa, 0x3f, + 0xcd, 0xc0, 0xf6, 0xbc, 0xe2, 0xa9, 0x61, 0xca, 0x94, 0xf3, 0x85, 0x29, 0xbb, 0x0b, 0x4b, 0x2c, + 0x2d, 0x1a, 0xfb, 0x99, 0x17, 0xa5, 0x1d, 0x2e, 0xb2, 0xc9, 0x4d, 0x58, 0xd4, 0x8b, 0x76, 0x18, + 0x9d, 0x02, 0xed, 0x8d, 0xda, 0x9d, 0x31, 0x5a, 0xb2, 0xf0, 0x2c, 0xf2, 0xad, 0x64, 0x40, 0x16, + 0x1e, 0x96, 0xe2, 0x9a, 0xd4, 0x21, 0x09, 0xcc, 0x61, 0x6c, 0x6f, 0x88, 0x91, 0xcb, 0x61, 0x27, + 0xad, 0x64, 0x70, 0x17, 0x0d, 0x16, 0x9b, 0x23, 0x6f, 0xec, 0x4d, 0x64, 0x5b, 0xa0, 0x21, 0xa6, + 0x58, 0x3c, 0x87, 0x5b, 0xea, 0xb4, 0x1f, 0x33, 0xc7, 0xcc, 0x45, 0xd9, 0x59, 0x1e, 0x4d, 0x7b, + 0x68, 0xb2, 0x25, 0x91, 0x68, 0xbf, 0xa7, 0xc0, 0x66, 0x5a, 0xb3, 0xc8, 0x36, 0xe4, 0xfc, 0xd4, + 0x50, 0x32, 0x3e, 0x73, 0x0f, 0xcb, 0x63, 0xdc, 0xdd, 0xa3, 0xc1, 0xe8, 0xb4, 0x3d, 0x91, 0x0d, + 0xa0, 0xa4, 0x64, 0x0b, 0xe8, 0x43, 0x19, 0xff, 0x93, 0x1b, 0x82, 0xd9, 0x66, 0x13, 0xc1, 0x67, + 0xf0, 0x47, 0xd3, 0x01, 0xcc, 0x6e, 0xb3, 0x31, 0x64, 0x10, 0xb6, 0xf7, 0x21, 0x47, 0x9b, 0x15, + 0x9b, 0x8c, 0x74, 0x3a, 0xe8, 0xb5, 0x2a, 0x27, 0x62, 0xad, 0x1a, 0xb7, 0x4f, 0xfb, 0x16, 0x12, + 0x6b, 0x87, 0xb0, 0x16, 0xa5, 0x20, 0x46, 0x14, 0xf4, 0x2c, 0xff, 0x8a, 0xca, 0x6b, 0xda, 0x1d, + 0x0c, 0x98, 0x11, 0xee, 0xee, 0xf3, 0xff, 0xfc, 0xc9, 0x0d, 0xa0, 0x8f, 0xac, 0x4c, 0x1a, 0x28, + 0x9a, 0xf6, 0xc7, 0x19, 0xd8, 0x0c, 0xfd, 0xfe, 0xc4, 0x92, 0xf8, 0x85, 0x75, 0x42, 0xd1, 0x23, + 0x4e, 0x12, 0x42, 0x62, 0x4a, 0x7e, 0xe0, 0x1c, 0xdb, 0xec, 0x3d, 0xd8, 0x9a, 0x45, 0x4f, 0xbe, + 0x98, 0x88, 0x8b, 0xcf, 0x41, 0x31, 0x82, 0x00, 0xfa, 0x52, 0x98, 0xfc, 0x1f, 0x2b, 0x70, 0x95, + 0x9b, 0x8e, 0xd6, 0xda, 0x3d, 0x1f, 0xb5, 0x0e, 0x1d, 0xef, 0xb3, 0x71, 0xa2, 0xda, 0x8b, 0xb0, + 0xa5, 0x2f, 0x44, 0x2d, 0x84, 0x13, 0x6f, 0x9b, 0xfd, 0xb5, 0xe4, 0x2e, 0xc2, 0x9f, 0x70, 0x15, + 0x4b, 0x8e, 0x39, 0xad, 0xfa, 0x34, 0x41, 0x76, 0x5a, 0x45, 0x0a, 0xed, 0xb7, 0x60, 0x67, 0xfe, + 0x0b, 0xc8, 0x37, 0x61, 0x15, 0x43, 0x06, 0xb4, 0x86, 0xc7, 0xa3, 0x76, 0xd7, 0x13, 0x77, 0x5a, + 0xe2, 0x4a, 0x51, 0xce, 0x63, 0x90, 0x2f, 0xdc, 0x89, 0xf2, 0x18, 0x83, 0x11, 0xf0, 0x42, 0x11, + 0xfb, 0x6c, 0xb9, 0x36, 0xed, 0xb7, 0x15, 0x20, 0xc9, 0x3a, 0xc8, 0x57, 0x61, 0xa5, 0xe5, 0x14, + 0xed, 0x49, 0x7b, 0x34, 0xa9, 0x0c, 0xa6, 0x23, 0x0e, 0xa5, 0xc2, 0x7c, 0xea, 0x26, 0x1d, 0xca, + 0x19, 0x46, 0x13, 0xf7, 0x64, 0x30, 0x1d, 0x59, 0x11, 0x3a, 0x8c, 0x4b, 0xe0, 0x79, 0xdf, 0xee, + 0xb6, 0x1f, 0x47, 0xe3, 0x12, 0xf0, 0xb4, 0x48, 0x5c, 0x02, 0x9e, 0xa6, 0x7d, 0xac, 0xc0, 0x35, + 0x61, 0x13, 0xd2, 0x4d, 0x69, 0x4b, 0x11, 0x3d, 0xc7, 0x47, 0x02, 0x4a, 0x6e, 0x9e, 0x6c, 0xba, + 0x21, 0xc0, 0x15, 0xb0, 0x81, 0x28, 0xa4, 0xb2, 0xb2, 0xe4, 0x6d, 0xc8, 0xd9, 0x93, 0xc1, 0xf0, + 0x0c, 0xe8, 0x0a, 0x6a, 0x30, 0xa2, 0x93, 0xc1, 0x10, 0xab, 0xc0, 0x92, 0x9a, 0x07, 0x9b, 0x72, + 0xe3, 0x44, 0x8b, 0x49, 0x0d, 0x2e, 0x72, 0xac, 0x9d, 0x98, 0x52, 0x6a, 0xce, 0x37, 0xed, 0xae, + 0x0b, 0x08, 0x07, 0x0e, 0x65, 0x66, 0x89, 0x3a, 0xb4, 0x3f, 0x52, 0x20, 0x4f, 0x85, 0x07, 0x3c, + 0x8e, 0x3d, 0xeb, 0x94, 0x8e, 0xca, 0x81, 0x42, 0xc7, 0x1a, 0x54, 0x7f, 0xa6, 0xcd, 0xf5, 0x55, + 0x58, 0x8f, 0x15, 0x20, 0x1a, 0x3a, 0xef, 0xf6, 0x7b, 0x9d, 0x36, 0x83, 0x39, 0x67, 0xfa, 0xc9, + 0x48, 0x9a, 0xf6, 0xfb, 0x0a, 0x6c, 0xd2, 0xc3, 0xbb, 0x89, 0xf7, 0xb6, 0xd6, 0xb4, 0x2f, 0xd6, + 0x3b, 0x15, 0x88, 0x84, 0x71, 0x11, 0x73, 0x2c, 0x64, 0x02, 0x11, 0x4f, 0xb3, 0x82, 0x5c, 0x52, + 0x81, 0x25, 0xbe, 0xbf, 0x8c, 0x39, 0x02, 0xd9, 0x8e, 0x74, 0x2b, 0x10, 0x56, 0xcc, 0x89, 0xe8, + 0x97, 0x20, 0x0b, 0xe3, 0x65, 0xac, 0xa0, 0xb4, 0xf6, 0x5f, 0x0a, 0x5c, 0x99, 0x51, 0x86, 0xbc, + 0x09, 0x0b, 0xe8, 0xf4, 0xc0, 0x47, 0x6f, 0x7b, 0xc6, 0x2b, 0x26, 0x9d, 0x93, 0x83, 0x7b, 0x6c, + 0x23, 0x3a, 0xa5, 0x0f, 0x16, 0x2b, 0x45, 0xde, 0x83, 0x65, 0xbd, 0xdb, 0xe5, 0xe7, 0x92, 0x4c, + 0xe4, 0x5c, 0x32, 0xe3, 0x8d, 0x2f, 0x07, 0xf4, 0xec, 0x5c, 0xc2, 0xcc, 0x6f, 0xbb, 0x5d, 0x97, + 0x3b, 0x74, 0x84, 0xf5, 0x5d, 0xfd, 0x35, 0x58, 0x8b, 0x12, 0x9f, 0xeb, 0x5c, 0xf2, 0x91, 0x02, + 0x6a, 0xb4, 0x0d, 0x9f, 0x0f, 0xf8, 0x44, 0xda, 0x30, 0x3f, 0x61, 0x52, 0xfd, 0x69, 0x06, 0x9e, + 0x4b, 0xed, 0x61, 0xf2, 0x12, 0x2c, 0xea, 0xc3, 0xa1, 0x59, 0xe2, 0xb3, 0x8a, 0x0b, 0x3c, 0x78, + 0xdd, 0x1b, 0x39, 0xb6, 0x31, 0x22, 0x72, 0x1f, 0x96, 0x70, 0x66, 0xd2, 0x02, 0x99, 0x10, 0x0b, + 0x8c, 0xdd, 0x86, 0xc4, 0xb0, 0xc0, 0x04, 0x21, 0x29, 0xc3, 0x1a, 0xf7, 0x43, 0xb7, 0xbc, 0x63, + 0xef, 0xbb, 0x01, 0x28, 0x2d, 0xe2, 0xe6, 0x8a, 0x3b, 0x64, 0x77, 0xc4, 0xf2, 0x64, 0x4f, 0xec, + 0x68, 0x29, 0x52, 0x05, 0x15, 0xeb, 0x94, 0x6b, 0x62, 0x80, 0x64, 0x88, 0x0c, 0xc0, 0x1a, 0x31, + 0xa3, 0xae, 0x44, 0xc9, 0x60, 0xb8, 0xf4, 0xf1, 0xb8, 0x77, 0xec, 0x9f, 0x7a, 0xfe, 0xe4, 0xf3, + 0x1b, 0xae, 0xf0, 0x1d, 0x67, 0x1a, 0xae, 0x3f, 0xcb, 0xb1, 0xc5, 0x1c, 0x2f, 0x46, 0x25, 0x1a, + 0x09, 0x83, 0x12, 0x25, 0x1a, 0x8c, 0x2b, 0xca, 0x3c, 0xad, 0x4b, 0x70, 0x91, 0x79, 0xc0, 0x8b, + 0x95, 0x71, 0x3d, 0xb5, 0x09, 0x8c, 0xe6, 0xe0, 0x1e, 0x13, 0x5f, 0x98, 0xf7, 0xc5, 0xd8, 0x12, + 0x45, 0xc9, 0x01, 0xe4, 0x8b, 0x7d, 0xaf, 0xed, 0x4f, 0x87, 0xce, 0xd9, 0xd4, 0x80, 0x5b, 0xfc, + 0x5b, 0x56, 0x3a, 0xac, 0x18, 0xaa, 0x0f, 0x91, 0x93, 0xcb, 0x15, 0x11, 0x27, 0x30, 0xc8, 0xce, + 0xe1, 0x95, 0xe3, 0x57, 0xe6, 0xf4, 0x4f, 0x3c, 0x11, 0xcb, 0x45, 0xbd, 0x0d, 0xb8, 0xc5, 0xb6, + 0x0b, 0x6b, 0xd5, 0xf6, 0x78, 0xe2, 0x8c, 0xda, 0xfe, 0x18, 0x91, 0xb3, 0xce, 0x80, 0x2c, 0x22, + 0x22, 0x10, 0xb3, 0xab, 0xc8, 0x49, 0x50, 0x94, 0x5d, 0x45, 0x46, 0xab, 0xa3, 0xf2, 0x52, 0xb9, + 0xe7, 0xb7, 0xfb, 0xbd, 0x0f, 0x85, 0xdf, 0x0a, 0x93, 0x97, 0x8e, 0x44, 0xa2, 0x15, 0xe6, 0x6b, + 0xdf, 0x48, 0x8c, 0x1b, 0x6b, 0x65, 0x1e, 0x2e, 0x72, 0xaf, 0x46, 0xe6, 0xe5, 0xd7, 0x34, 0xea, + 0x25, 0xb3, 0xbe, 0xa7, 0x2a, 0x64, 0x0d, 0xa0, 0x69, 0x35, 0x8a, 0x86, 0x6d, 0xd3, 0xe7, 0x0c, + 0x7d, 0xe6, 0x2e, 0x80, 0xe5, 0x56, 0x55, 0xcd, 0x4a, 0x5e, 0x80, 0x39, 0xed, 0x47, 0x0a, 0x5c, + 0x4e, 0x1f, 0x4a, 0xe2, 0x00, 0xfa, 0x81, 0x72, 0x85, 0xf0, 0x57, 0xe7, 0x8e, 0x7b, 0x6a, 0x72, + 0xdc, 0x9f, 0x74, 0xc2, 0xfc, 0x14, 0x33, 0x42, 0xeb, 0x13, 0xc6, 0xce, 0xed, 0x75, 0xb5, 0x22, + 0x6c, 0xcd, 0xaa, 0x23, 0xfa, 0xa9, 0xeb, 0x90, 0xd7, 0x9b, 0xcd, 0xaa, 0x59, 0xd4, 0x1d, 0xb3, + 0x51, 0x57, 0x15, 0xb2, 0x0c, 0x0b, 0x7b, 0x56, 0xa3, 0xd5, 0x54, 0x33, 0xda, 0x0f, 0x14, 0x58, + 0x35, 0xfd, 0x89, 0x77, 0xcc, 0xec, 0x78, 0x9f, 0x75, 0xf1, 0xbd, 0x11, 0x59, 0x7c, 0x5b, 0x81, + 0xc7, 0x74, 0xf0, 0x82, 0x33, 0xad, 0xbc, 0x7f, 0x52, 0x60, 0x23, 0x51, 0x86, 0xd8, 0x70, 0x51, + 0x3f, 0xb4, 0x1b, 0x66, 0xa9, 0xc8, 0x5b, 0x26, 0xa4, 0x72, 0x9e, 0x9a, 0x7c, 0x0b, 0xf3, 0x32, + 0xfa, 0x60, 0xec, 0x0e, 0x7a, 0x5d, 0x29, 0x62, 0x57, 0xe5, 0x82, 0x25, 0x6a, 0xc2, 0x9d, 0xec, + 0xc3, 0xe9, 0xc8, 0xc3, 0x6a, 0x33, 0x91, 0x1b, 0xcd, 0x20, 0x3d, 0x59, 0x31, 0x9a, 0xad, 0xb6, + 0x69, 0x7e, 0xb2, 0xea, 0xb0, 0xbe, 0xdd, 0x55, 0xc8, 0xf3, 0x53, 0x0b, 0x1e, 0x08, 0xbe, 0xaf, + 0xc0, 0xd6, 0xac, 0xb6, 0xd2, 0x83, 0x50, 0xd4, 0xe5, 0xf0, 0x72, 0x80, 0xb9, 0x1c, 0xf5, 0x35, + 0x14, 0x64, 0xe4, 0x2d, 0xc8, 0xb3, 0x80, 0xe4, 0xf6, 0xfd, 0x96, 0x65, 0xf2, 0x09, 0x72, 0xfd, + 0x3f, 0x3e, 0xb9, 0x71, 0x85, 0x85, 0x2f, 0x77, 0xc7, 0xf7, 0xdd, 0xe9, 0xa8, 0x17, 0xc1, 0xa7, + 0x95, 0x4b, 0x68, 0xdf, 0x53, 0xe0, 0xea, 0xec, 0x8f, 0xa4, 0xbb, 0x8c, 0xc3, 0x0c, 0x9c, 0x84, + 0x93, 0x13, 0xee, 0x32, 0x81, 0x15, 0x94, 0xbc, 0xcb, 0x08, 0x42, 0x5a, 0x28, 0x88, 0x85, 0x99, + 0x49, 0x84, 0xc0, 0x8b, 0x16, 0x12, 0x84, 0xda, 0x5f, 0x64, 0xe0, 0x32, 0x9d, 0x40, 0x7d, 0x6f, + 0x3c, 0xd6, 0xa7, 0x93, 0x13, 0xcf, 0x9f, 0x70, 0x91, 0x8a, 0xbc, 0x06, 0x8b, 0x27, 0xe7, 0xbb, + 0x0d, 0x64, 0xe4, 0x84, 0x00, 0x32, 0x65, 0x61, 0xf6, 0x4a, 0xff, 0x93, 0xeb, 0x20, 0x85, 0x1c, + 0x44, 0x9e, 0xba, 0x62, 0x2d, 0x0f, 0x83, 0xc0, 0x83, 0xaf, 0xc3, 0x02, 0x9e, 0xfe, 0x39, 0x6b, + 0x14, 0x22, 0x6d, 0x7a, 0xcb, 0xf0, 0x6e, 0xc0, 0x62, 0x05, 0xc8, 0x97, 0x01, 0x42, 0x6c, 0x5f, + 0xce, 0xfb, 0xc4, 0x31, 0x3a, 0x80, 0xf7, 0xb5, 0x96, 0x4f, 0x8f, 0xda, 0x1c, 0x30, 0xb7, 0x00, + 0x1b, 0xa2, 0x4b, 0x86, 0x02, 0x48, 0x88, 0xab, 0xa7, 0xd6, 0x59, 0x86, 0x39, 0xe4, 0x60, 0x42, + 0xda, 0xbf, 0x67, 0x60, 0xf9, 0x90, 0x0a, 0x0a, 0x78, 0xfc, 0x9d, 0x7f, 0x9c, 0x7e, 0x05, 0xf2, + 0xd5, 0x41, 0x9b, 0xdf, 0xdd, 0x8f, 0x39, 0x96, 0x19, 0xba, 0x2e, 0xf5, 0x07, 0x6d, 0xa1, 0x06, + 0x18, 0x5b, 0x32, 0xd1, 0x13, 0xdc, 0xae, 0xde, 0x81, 0x45, 0xa6, 0x4b, 0xe1, 0x17, 0x35, 0x42, + 0x54, 0x0c, 0x5a, 0xf4, 0x32, 0xcb, 0x96, 0xae, 0x9b, 0x99, 0x3e, 0x46, 0x96, 0x5b, 0x38, 0x94, + 0x99, 0x74, 0xd8, 0x5f, 0x38, 0xdb, 0x61, 0x5f, 0x82, 0x6c, 0x59, 0x3c, 0x0b, 0x64, 0xcb, 0xd5, + 0x07, 0x90, 0x97, 0xda, 0x73, 0x2e, 0xc9, 0xf1, 0x77, 0x32, 0xb0, 0x8a, 0x5f, 0x15, 0x18, 0x56, + 0xfc, 0x62, 0x5e, 0x5d, 0xbc, 0x11, 0xb9, 0xba, 0xd8, 0x92, 0xc7, 0x8b, 0x7d, 0xd9, 0x9c, 0x3b, + 0x8b, 0x77, 0x60, 0x23, 0x41, 0x48, 0x5e, 0x85, 0x05, 0xda, 0x7c, 0x71, 0xd4, 0x53, 0xe3, 0x33, + 0x20, 0x84, 0xf7, 0xa3, 0x1f, 0x3e, 0xb6, 0x18, 0xb5, 0xf6, 0xdf, 0x0a, 0xac, 0x70, 0xb0, 0x67, + 0xff, 0x68, 0xf0, 0xc4, 0xee, 0xbc, 0x1d, 0xef, 0x4e, 0xe6, 0x44, 0xcc, 0xbb, 0xf3, 0x7f, 0xbb, + 0x13, 0x1f, 0x44, 0x3a, 0xf1, 0x4a, 0x00, 0xf6, 0x23, 0x3e, 0x67, 0x4e, 0x1f, 0xfe, 0x03, 0xc2, + 0xdf, 0x45, 0x09, 0xc9, 0xb7, 0x60, 0xb9, 0xee, 0x7d, 0x10, 0x39, 0x31, 0xdd, 0x9e, 0x51, 0xe9, + 0xcb, 0x01, 0x21, 0x5b, 0x53, 0xb8, 0xd9, 0xf8, 0xde, 0x07, 0x6e, 0x42, 0x8d, 0x13, 0x56, 0x49, + 0x0f, 0x4d, 0xd1, 0x62, 0xe7, 0x99, 0xfa, 0xdc, 0xd5, 0x04, 0xfd, 0xe2, 0xff, 0x2a, 0x07, 0x10, + 0x5a, 0xe9, 0xd3, 0x05, 0x18, 0xd1, 0x60, 0x8b, 0xbb, 0x63, 0x4c, 0x92, 0xe7, 0xb8, 0x50, 0x6c, + 0xdf, 0xe6, 0x97, 0xa2, 0x99, 0xd9, 0x60, 0x4c, 0x78, 0x3d, 0x5a, 0xe4, 0x56, 0xf0, 0x5d, 0xaf, + 0xdf, 0x66, 0xbc, 0x38, 0xbb, 0x7b, 0x0b, 0xb1, 0xf7, 0x82, 0xd4, 0x19, 0x51, 0xfb, 0xd0, 0x56, + 0xbe, 0x44, 0x09, 0x12, 0x9e, 0x2f, 0xb9, 0xa7, 0xf7, 0x7c, 0x59, 0x78, 0x0a, 0xcf, 0x97, 0xc5, + 0x33, 0x7a, 0xbe, 0x34, 0x61, 0xb9, 0xe7, 0x3f, 0xf2, 0xfc, 0xc9, 0x60, 0xf4, 0x18, 0xb5, 0xd4, + 0xe1, 0x55, 0x16, 0xed, 0x6a, 0x53, 0xe4, 0xb1, 0xf1, 0xc6, 0x0d, 0x33, 0xa0, 0x97, 0x87, 0x3b, + 0x48, 0x24, 0xbf, 0x02, 0xa1, 0xd6, 0x83, 0x23, 0xb4, 0xcf, 0xde, 0x67, 0x3b, 0x42, 0x29, 0xf2, + 0x36, 0x44, 0x95, 0x1f, 0xdc, 0xef, 0x94, 0x45, 0x98, 0x95, 0x33, 0x64, 0xfc, 0xb1, 0x8e, 0xa4, + 0x1f, 0xe1, 0x66, 0xaf, 0x3f, 0xcf, 0x00, 0x49, 0x36, 0x9c, 0xbc, 0x01, 0x79, 0xc6, 0xfa, 0xdd, + 0xd1, 0xf8, 0x3b, 0xdc, 0x5d, 0x83, 0xe1, 0x1e, 0x48, 0xc9, 0x32, 0xee, 0x01, 0x4b, 0xb6, 0xc6, + 0xdf, 0xe9, 0x93, 0x6f, 0xc2, 0x25, 0x1c, 0xf8, 0xa1, 0x37, 0xea, 0x0d, 0xba, 0x2e, 0x82, 0xd4, + 0xb5, 0xfb, 0x3c, 0xf6, 0xcf, 0x4b, 0x18, 0xa4, 0x2e, 0x99, 0x3d, 0x63, 0x82, 0xa0, 0x57, 0x44, + 0x13, 0x29, 0x9b, 0x8c, 0x90, 0x38, 0xa0, 0xca, 0xe5, 0x8f, 0xa6, 0xfd, 0x3e, 0x9f, 0x73, 0x05, + 0x7a, 0xfc, 0x8d, 0xe7, 0xcd, 0xa8, 0x78, 0x2d, 0xac, 0xb8, 0x3c, 0xed, 0xf7, 0xc9, 0x6b, 0x00, + 0x03, 0xdf, 0x3d, 0xed, 0x8d, 0xc7, 0x4c, 0x91, 0x11, 0x78, 0x34, 0x85, 0xa9, 0xf2, 0xf0, 0x0d, + 0xfc, 0x1a, 0x4b, 0xa4, 0xc3, 0x37, 0x6c, 0x1f, 0x7b, 0xe8, 0x47, 0xcc, 0x8c, 0x56, 0x38, 0x9a, + 0xb7, 0x48, 0x8c, 0x4e, 0xa3, 0x63, 0xcf, 0xee, 0x7d, 0x28, 0x4c, 0x9d, 0xdf, 0x85, 0x0d, 0x6e, + 0x2e, 0x7a, 0xd8, 0x9b, 0x9c, 0x70, 0xb9, 0xfb, 0x59, 0x84, 0x76, 0x49, 0xf0, 0xfe, 0x97, 0x1c, + 0x80, 0x7e, 0x68, 0x0b, 0x88, 0x8e, 0xbb, 0xb0, 0x40, 0x4f, 0x13, 0xe2, 0x56, 0x02, 0xef, 0x74, + 0xb1, 0x5e, 0xf9, 0x4e, 0x17, 0x29, 0x28, 0x9f, 0xb0, 0xbc, 0x63, 0xbc, 0x18, 0xcb, 0x84, 0x57, + 0x18, 0x23, 0x96, 0x14, 0x91, 0x5e, 0x59, 0x12, 0xa9, 0x02, 0x84, 0xa0, 0x19, 0xfc, 0x7c, 0xbb, + 0x11, 0x7a, 0x9f, 0xf3, 0x0c, 0x0e, 0xd3, 0x1c, 0x02, 0x6f, 0xc8, 0xd3, 0x27, 0x24, 0x23, 0xfb, + 0x90, 0x73, 0xda, 0x81, 0xbf, 0xce, 0x0c, 0x28, 0x91, 0x17, 0x78, 0x6c, 0xa6, 0x10, 0x4e, 0x64, + 0x6d, 0xd2, 0x8e, 0x84, 0xb0, 0xc3, 0x4a, 0x88, 0x01, 0x8b, 0x3c, 0xee, 0xe6, 0x0c, 0x08, 0x2a, + 0x1e, 0x76, 0x93, 0x03, 0x4f, 0x62, 0xa2, 0x2c, 0xed, 0xf0, 0x08, 0x9b, 0xaf, 0x40, 0xd6, 0xb6, + 0x6b, 0xdc, 0x81, 0x76, 0x35, 0x3c, 0xab, 0xd8, 0x76, 0x4d, 0x84, 0x29, 0x3e, 0x95, 0x8a, 0x51, + 0x62, 0xf2, 0xab, 0x90, 0x97, 0x04, 0x71, 0xee, 0x7a, 0x8e, 0x7d, 0xd0, 0x0b, 0x93, 0x65, 0x76, + 0x26, 0x51, 0x93, 0x2a, 0xa8, 0xfb, 0xd3, 0xf7, 0x3d, 0x7d, 0x38, 0x44, 0x87, 0x92, 0x47, 0xde, + 0x88, 0x41, 0x48, 0x2f, 0x85, 0x98, 0x8d, 0xe8, 0xa0, 0xd0, 0x15, 0xb9, 0xf2, 0xcd, 0x4c, 0xbc, + 0x24, 0x69, 0xc2, 0x86, 0xed, 0x4d, 0xa6, 0x43, 0x66, 0x86, 0x51, 0x1e, 0x8c, 0xe8, 0xd1, 0x84, + 0x31, 0x0c, 0x84, 0xb7, 0x1b, 0xd3, 0x4c, 0x61, 0xfb, 0x72, 0x34, 0x18, 0xc5, 0x8e, 0x29, 0xc9, + 0xc2, 0x9a, 0x27, 0x0f, 0x39, 0xdd, 0xef, 0xa3, 0x07, 0x1e, 0xdc, 0xef, 0xc5, 0x81, 0x27, 0x3c, + 0xe6, 0x7c, 0x39, 0x05, 0x4c, 0x05, 0xd5, 0x68, 0x12, 0x98, 0x4a, 0x04, 0x42, 0xe5, 0xe3, 0x9c, + 0x84, 0xe7, 0xc5, 0xc7, 0xe2, 0x4d, 0x80, 0x77, 0x06, 0x3d, 0xbf, 0xe6, 0x4d, 0x4e, 0x06, 0x5d, + 0x09, 0xd3, 0x25, 0xff, 0x9b, 0x83, 0x9e, 0xef, 0x9e, 0x62, 0xf2, 0xcf, 0x3f, 0xb9, 0x21, 0x11, + 0x59, 0xd2, 0x7f, 0xf2, 0x25, 0x58, 0xa6, 0x4f, 0x4e, 0x68, 0x4c, 0xc2, 0x2e, 0x30, 0xb1, 0x34, + 0x0f, 0x77, 0x1e, 0x10, 0x90, 0x07, 0x88, 0xf3, 0xde, 0x1b, 0x4e, 0x24, 0xb1, 0x5a, 0x80, 0xba, + 0xf7, 0x86, 0x93, 0x38, 0x44, 0xa3, 0x44, 0x4c, 0x2a, 0x41, 0xd3, 0x45, 0xa4, 0x00, 0x0e, 0x27, + 0x8f, 0xb7, 0x74, 0x7c, 0xae, 0xb9, 0x02, 0x1b, 0x4e, 0x8e, 0xe9, 0x16, 0x2b, 0x86, 0x8d, 0xb0, + 0x2b, 0x25, 0xa6, 0x56, 0xe1, 0xbb, 0x1b, 0x6b, 0xc4, 0xf8, 0xa4, 0xeb, 0x76, 0x30, 0x39, 0xd2, + 0x88, 0x80, 0x98, 0xec, 0xc2, 0x3a, 0x43, 0x1e, 0x08, 0x22, 0x0e, 0xf1, 0x9d, 0x0e, 0x79, 0x5b, + 0x18, 0x92, 0x48, 0x7e, 0x7d, 0xac, 0x00, 0x29, 0xc3, 0x02, 0x1e, 0x2d, 0xb9, 0x31, 0xf8, 0x35, + 0xf9, 0x4c, 0x1d, 0x5f, 0x47, 0xc8, 0x57, 0xf0, 0x34, 0x2d, 0xf3, 0x15, 0x24, 0x25, 0x5f, 0x07, + 0x30, 0xfc, 0xd1, 0xa0, 0xdf, 0x47, 0xf4, 0xc2, 0x25, 0x3c, 0x98, 0x5d, 0x8f, 0xae, 0x47, 0xac, + 0x25, 0x24, 0xe2, 0x48, 0x3b, 0xf8, 0xec, 0xc6, 0x30, 0x0e, 0xa5, 0xba, 0x34, 0x13, 0x16, 0xd9, + 0x62, 0x44, 0x24, 0x50, 0x8e, 0x6d, 0x2e, 0xe1, 0x48, 0x32, 0x24, 0x50, 0x9e, 0x9e, 0x44, 0x02, + 0x95, 0x0a, 0x68, 0xfb, 0xb0, 0x99, 0xf6, 0x61, 0x91, 0xc3, 0xb0, 0x72, 0xd6, 0xc3, 0xf0, 0x5f, + 0x66, 0x61, 0x05, 0x6b, 0x13, 0x5c, 0x58, 0x87, 0x55, 0x7b, 0xfa, 0x7e, 0x00, 0x93, 0x21, 0xb8, + 0x31, 0xb6, 0x6f, 0x2c, 0x67, 0xc8, 0x0a, 0xaf, 0x48, 0x09, 0x62, 0xc0, 0x9a, 0xd8, 0x09, 0xf6, + 0x84, 0x81, 0x79, 0x00, 0xc2, 0x29, 0xa0, 0x9e, 0x92, 0x11, 0xd7, 0x62, 0x85, 0xc2, 0xfd, 0x20, + 0x7b, 0x9e, 0xfd, 0x20, 0x77, 0xa6, 0xfd, 0xe0, 0x3d, 0x58, 0x11, 0x6f, 0x43, 0x4e, 0xbe, 0xf0, + 0x6c, 0x9c, 0x3c, 0x52, 0x19, 0xa9, 0x06, 0x1c, 0x7d, 0x71, 0x2e, 0x47, 0x47, 0x2d, 0xa2, 0x58, + 0x65, 0x89, 0x20, 0xca, 0xbc, 0x0e, 0x0c, 0x49, 0xb4, 0x57, 0x6c, 0x3e, 0xc5, 0x2e, 0xf9, 0x2a, + 0x2c, 0x57, 0x07, 0x42, 0x81, 0x24, 0xdd, 0xdc, 0xf7, 0x45, 0xa2, 0x2c, 0x2e, 0x04, 0x94, 0xc1, + 0xee, 0x96, 0xfd, 0x2c, 0x76, 0xb7, 0x07, 0x00, 0xdc, 0x73, 0x21, 0x0c, 0x25, 0x82, 0x4b, 0x46, + 0x78, 0x31, 0x47, 0x15, 0x08, 0x12, 0x31, 0xe5, 0x4e, 0xdc, 0xd4, 0x44, 0xef, 0x74, 0x06, 0x53, + 0x7f, 0x12, 0x89, 0xbd, 0xc7, 0x01, 0x11, 0xe8, 0x96, 0x80, 0x79, 0x32, 0x7b, 0x88, 0x15, 0xfb, + 0x6c, 0x07, 0x84, 0x7c, 0x2d, 0xb0, 0x91, 0x9b, 0x1b, 0x8a, 0x5c, 0x4b, 0xf4, 0xd0, 0x4c, 0xcb, + 0x38, 0xed, 0x47, 0x8a, 0x8c, 0x80, 0xfc, 0x14, 0x43, 0xfd, 0x3a, 0x40, 0xa0, 0xc1, 0x17, 0x63, + 0xcd, 0x4e, 0x72, 0x41, 0xaa, 0xdc, 0xcb, 0x21, 0xad, 0xf4, 0x35, 0xd9, 0xcf, 0xea, 0x6b, 0x1c, + 0xc8, 0x37, 0xbe, 0x3d, 0x69, 0x87, 0x26, 0x1f, 0x60, 0x07, 0x92, 0x2c, 0x72, 0x26, 0x11, 0x32, + 0x3d, 0x94, 0x83, 0x67, 0x86, 0x4c, 0x0f, 0x0a, 0x6a, 0x7f, 0xaf, 0xc0, 0xba, 0xec, 0xc0, 0xf8, + 0xd8, 0xef, 0x90, 0x5f, 0x67, 0x80, 0x6c, 0x4a, 0xe4, 0x90, 0x23, 0x11, 0x51, 0x96, 0xfb, 0xd8, + 0xef, 0x30, 0x01, 0xa8, 0xfd, 0x81, 0xdc, 0x58, 0x5a, 0x90, 0xbc, 0x0f, 0x2b, 0xcd, 0x41, 0xbf, + 0x4f, 0xc5, 0x9a, 0xd1, 0x23, 0x7e, 0x00, 0xa0, 0x15, 0xc5, 0xf5, 0x08, 0xa2, 0x41, 0xbb, 0x37, + 0xf9, 0x09, 0xfc, 0xca, 0x90, 0xf2, 0xfb, 0x1e, 0x2f, 0x17, 0x56, 0xfb, 0x11, 0x7a, 0x46, 0xc9, + 0x75, 0x6a, 0x3f, 0x51, 0x80, 0x24, 0x9b, 0x24, 0xb3, 0x2c, 0xe5, 0xff, 0x40, 0x84, 0x8d, 0x89, + 0x7e, 0xb9, 0xf3, 0x88, 0x7e, 0x85, 0x3f, 0x51, 0x60, 0xdd, 0xd4, 0x6b, 0x1c, 0x13, 0x99, 0xa9, + 0x3b, 0x5e, 0x84, 0xeb, 0xa6, 0x5e, 0x73, 0x9b, 0x8d, 0xaa, 0x59, 0x7c, 0xe8, 0xa6, 0x42, 0x1d, + 0x5e, 0x87, 0xe7, 0x93, 0x24, 0xa1, 0x5a, 0x64, 0x1b, 0xb6, 0x92, 0xd9, 0x02, 0x0e, 0x31, 0xbd, + 0xb0, 0x40, 0x4e, 0xcc, 0x16, 0xde, 0x82, 0x75, 0x01, 0xfd, 0xe7, 0x54, 0x6d, 0x04, 0x17, 0x5e, + 0x87, 0xfc, 0x81, 0x61, 0x99, 0xe5, 0x87, 0x6e, 0xb9, 0x55, 0xad, 0xaa, 0x17, 0xc8, 0x2a, 0x2c, + 0xf3, 0x84, 0xa2, 0xae, 0x2a, 0x64, 0x05, 0x96, 0xcc, 0xba, 0x6d, 0x14, 0x5b, 0x96, 0xa1, 0x66, + 0x0a, 0x6f, 0xc1, 0x5a, 0x73, 0xd4, 0x7b, 0xd4, 0x9e, 0x78, 0xfb, 0xde, 0x63, 0xd4, 0x6a, 0x5c, + 0x84, 0xac, 0xa5, 0x1f, 0xaa, 0x17, 0x08, 0xc0, 0x62, 0x73, 0xbf, 0x68, 0xdf, 0xbb, 0xa7, 0x2a, + 0x24, 0x0f, 0x17, 0xf7, 0x8a, 0x4d, 0x77, 0xbf, 0x66, 0xab, 0x19, 0xfa, 0xa0, 0x1f, 0xda, 0xf8, + 0x90, 0x2d, 0x7c, 0x05, 0x36, 0x50, 0x20, 0xa9, 0xf6, 0xc6, 0x13, 0xcf, 0xf7, 0x46, 0xd8, 0x86, + 0x15, 0x58, 0xb2, 0x3d, 0xca, 0x49, 0x26, 0x1e, 0x6b, 0x40, 0x6d, 0xda, 0x9f, 0xf4, 0x86, 0x7d, + 0xef, 0xbb, 0xaa, 0x52, 0x78, 0x00, 0xeb, 0xd6, 0x60, 0x3a, 0xe9, 0xf9, 0xc7, 0xf6, 0x84, 0x52, + 0x1c, 0x3f, 0x26, 0xcf, 0xc1, 0x46, 0xab, 0xae, 0xd7, 0x76, 0xcd, 0xbd, 0x56, 0xa3, 0x65, 0xbb, + 0x35, 0xdd, 0x29, 0x56, 0x98, 0x4e, 0xa5, 0xd6, 0xb0, 0x1d, 0xd7, 0x32, 0x8a, 0x46, 0xdd, 0x51, + 0x95, 0xc2, 0x1f, 0x28, 0xb0, 0xd6, 0x1a, 0x73, 0x3b, 0xe0, 0x16, 0x7a, 0xf3, 0xbd, 0x00, 0xdb, + 0x2d, 0xdb, 0xb0, 0x5c, 0xa7, 0xb1, 0x6f, 0xd4, 0xdd, 0x96, 0xad, 0xef, 0xc5, 0x71, 0x36, 0x6f, + 0xc0, 0x35, 0x89, 0xc2, 0x32, 0x8a, 0x8d, 0x03, 0xc3, 0x72, 0x9b, 0xba, 0x6d, 0x1f, 0x36, 0xac, + 0x92, 0xaa, 0x90, 0xab, 0x70, 0x39, 0x85, 0xa0, 0x56, 0xd6, 0xd5, 0x4c, 0x22, 0xaf, 0x6e, 0x1c, + 0xea, 0x55, 0x77, 0xb7, 0xe1, 0xa8, 0xd9, 0x42, 0x8d, 0xee, 0xa6, 0x88, 0x2f, 0xc7, 0x02, 0x09, + 0x2c, 0x41, 0xae, 0xde, 0xa8, 0x1b, 0x71, 0xbd, 0xd7, 0x0a, 0x2c, 0xe9, 0xcd, 0xa6, 0xd5, 0x38, + 0xc0, 0x01, 0x05, 0x58, 0x2c, 0x19, 0x75, 0xda, 0xb2, 0x2c, 0xcd, 0x69, 0x5a, 0x8d, 0x5a, 0xc3, + 0x31, 0x4a, 0x6a, 0xae, 0x60, 0x89, 0x05, 0x23, 0x2a, 0xed, 0x0c, 0x98, 0x92, 0xa9, 0x64, 0x94, + 0xf5, 0x56, 0xd5, 0xe1, 0x1d, 0xf2, 0xd0, 0xb5, 0x8c, 0xaf, 0xb5, 0x0c, 0xdb, 0xb1, 0x55, 0x85, + 0xa8, 0xb0, 0x52, 0x37, 0x8c, 0x92, 0xed, 0x5a, 0xc6, 0x81, 0x69, 0x1c, 0xaa, 0x19, 0x5a, 0x27, + 0xfb, 0x4f, 0xdf, 0x50, 0xf8, 0x58, 0x01, 0xc2, 0xb0, 0xf9, 0x04, 0xe0, 0x3b, 0x8e, 0xcf, 0x0e, + 0x5c, 0xad, 0xd0, 0x8e, 0xc5, 0x4f, 0xab, 0x35, 0x4a, 0xf1, 0x2e, 0xbb, 0x0c, 0x24, 0x96, 0xdf, + 0x28, 0x97, 0x55, 0x85, 0x5c, 0x83, 0x4b, 0xb1, 0xf4, 0x92, 0xd5, 0x68, 0xaa, 0x99, 0xab, 0x99, + 0x25, 0x85, 0x5c, 0x49, 0x64, 0xee, 0x1b, 0x46, 0x53, 0xcd, 0xd2, 0x21, 0x8a, 0x65, 0x88, 0x09, + 0xc8, 0x8a, 0xe7, 0x0a, 0xdf, 0x53, 0xe0, 0x32, 0x6b, 0xa6, 0x98, 0xcd, 0x41, 0x53, 0xb7, 0x61, + 0x8b, 0x23, 0x8e, 0xa6, 0x35, 0x74, 0x13, 0xd4, 0x48, 0x2e, 0x6b, 0xe6, 0x73, 0xb0, 0x11, 0x49, + 0xc5, 0x76, 0x64, 0xe8, 0x5a, 0x8d, 0x24, 0xef, 0x1a, 0xb6, 0xe3, 0x1a, 0xe5, 0x72, 0xc3, 0x72, + 0x58, 0x43, 0xb2, 0x05, 0x0d, 0x36, 0x8a, 0xde, 0x68, 0x42, 0x0f, 0x3a, 0xfe, 0xb8, 0x37, 0xf0, + 0xb1, 0x09, 0xab, 0xb0, 0x6c, 0x7c, 0xdd, 0x31, 0xea, 0xb6, 0xd9, 0xa8, 0xab, 0x17, 0x0a, 0xdb, + 0x31, 0x1a, 0xb1, 0x6a, 0x6c, 0xbb, 0xa2, 0x5e, 0x28, 0xb4, 0x61, 0x55, 0xd8, 0xdd, 0xb2, 0x59, + 0xb1, 0x03, 0x57, 0xc5, 0x5c, 0xc3, 0xf5, 0x1b, 0xff, 0x84, 0x2d, 0xd8, 0x4c, 0xe6, 0x1b, 0x8e, + 0xaa, 0xd0, 0x51, 0x88, 0xe5, 0xd0, 0xf4, 0x4c, 0xe1, 0x77, 0x15, 0x58, 0x0d, 0x14, 0x1e, 0x78, + 0x5d, 0x7b, 0x03, 0xae, 0xd5, 0xca, 0xba, 0x5b, 0x32, 0x0e, 0xcc, 0xa2, 0xe1, 0xee, 0x9b, 0xf5, + 0x52, 0xec, 0x25, 0xcf, 0xc3, 0x73, 0x29, 0x04, 0xf8, 0x96, 0x2d, 0xd8, 0x8c, 0x67, 0x39, 0x0d, + 0x87, 0xf6, 0xd7, 0x36, 0x6c, 0xc5, 0x73, 0x0e, 0x8d, 0x5d, 0xbd, 0xe5, 0x54, 0xea, 0x6a, 0xb6, + 0xf0, 0x0d, 0x58, 0x89, 0xc4, 0x67, 0xb9, 0x02, 0x97, 0xe4, 0xe7, 0xa6, 0xe7, 0x77, 0x7b, 0xfe, + 0xb1, 0x7a, 0x21, 0x9e, 0x61, 0x4d, 0x7d, 0x9f, 0x66, 0xe0, 0xba, 0x93, 0x33, 0x1c, 0x6f, 0x74, + 0xda, 0xf3, 0xdb, 0x13, 0xaf, 0xab, 0x66, 0x0a, 0x2f, 0xc3, 0x6a, 0x04, 0x15, 0x92, 0x76, 0x70, + 0xb5, 0xc1, 0xd9, 0x52, 0xcd, 0x28, 0x99, 0xad, 0x9a, 0xba, 0x40, 0x57, 0x5c, 0xc5, 0xdc, 0xab, + 0xa8, 0x50, 0xf8, 0x81, 0x42, 0xa5, 0x6f, 0xc4, 0x7a, 0xaf, 0x95, 0x75, 0x31, 0x24, 0x74, 0x3a, + 0x30, 0xac, 0x59, 0xc3, 0xb6, 0x99, 0x5a, 0x76, 0x1b, 0xb6, 0xf8, 0x83, 0xab, 0xd7, 0x4b, 0x6e, + 0x45, 0xb7, 0x4a, 0x87, 0xba, 0x45, 0xe7, 0xc8, 0x43, 0x35, 0x83, 0x13, 0x5f, 0x4a, 0x71, 0x9d, + 0x46, 0xab, 0x58, 0x51, 0xb3, 0x74, 0x9e, 0x45, 0xd2, 0x9b, 0x66, 0x5d, 0xcd, 0xe1, 0x32, 0x4a, + 0x50, 0x63, 0xb5, 0x34, 0x7f, 0xa1, 0xf0, 0xe7, 0x0a, 0x6c, 0xcd, 0x82, 0x3f, 0x20, 0xb7, 0x41, + 0x33, 0xea, 0x8e, 0xa5, 0x9b, 0x25, 0xb7, 0x68, 0x19, 0x25, 0xa3, 0xee, 0x98, 0x7a, 0xd5, 0x76, + 0xed, 0x46, 0xcb, 0x2a, 0xd2, 0x39, 0x20, 0x14, 0xcb, 0x37, 0xe1, 0xc6, 0x1c, 0xba, 0x86, 0x59, + 0x2a, 0xaa, 0x0a, 0xb9, 0x07, 0x2f, 0xcd, 0x21, 0xb2, 0x1f, 0xda, 0x8e, 0x51, 0x93, 0x73, 0xd4, + 0x4c, 0xa1, 0x07, 0x6a, 0xdc, 0x8b, 0x3a, 0xa1, 0xbc, 0xb7, 0x5a, 0xf5, 0x3a, 0x63, 0x62, 0xeb, + 0x90, 0x6f, 0x38, 0x15, 0xc3, 0xe2, 0x50, 0xc5, 0x88, 0x4d, 0xdc, 0xaa, 0xd3, 0xb9, 0xd0, 0xb0, + 0xcc, 0x77, 0x91, 0x9b, 0x6d, 0xc1, 0xa6, 0x5d, 0xd5, 0x8b, 0xfb, 0x6e, 0xbd, 0xe1, 0xb8, 0x66, + 0xdd, 0x2d, 0x56, 0xf4, 0x7a, 0xdd, 0xa8, 0xaa, 0x80, 0xfd, 0x30, 0xcb, 0xdd, 0x8a, 0x7c, 0x09, + 0xee, 0x34, 0xf6, 0x1d, 0xdd, 0x6d, 0x56, 0x5b, 0x7b, 0x66, 0xdd, 0xb5, 0x1f, 0xd6, 0x8b, 0x62, + 0x9f, 0x2b, 0x26, 0x17, 0xfc, 0x1d, 0xb8, 0x35, 0x97, 0x3a, 0x04, 0x15, 0xbe, 0x0d, 0xda, 0x5c, + 0x4a, 0xfe, 0x21, 0x85, 0x1f, 0x2b, 0x70, 0x6d, 0x8e, 0xf6, 0x91, 0xbc, 0x04, 0x77, 0x2b, 0x86, + 0x5e, 0xaa, 0x1a, 0xb6, 0xed, 0xd2, 0xef, 0xa5, 0x3d, 0xc8, 0x94, 0xfc, 0xa9, 0xcb, 0xf9, 0x2e, + 0x7c, 0x61, 0x3e, 0x79, 0xb8, 0x31, 0xdc, 0x81, 0x5b, 0xf3, 0x49, 0xf9, 0x46, 0x91, 0x21, 0x05, + 0xb8, 0x3d, 0x9f, 0x32, 0xd8, 0x60, 0xb2, 0x85, 0xef, 0x2b, 0x70, 0x39, 0xfd, 0xd0, 0x4e, 0xdb, + 0x66, 0xd6, 0x6d, 0x47, 0xaf, 0x56, 0xdd, 0xa6, 0x6e, 0xe9, 0x35, 0xd7, 0xa8, 0x5b, 0x8d, 0x6a, + 0x35, 0x8d, 0xb1, 0xde, 0x82, 0x17, 0x66, 0x93, 0xda, 0x45, 0xcb, 0x6c, 0x52, 0xde, 0xa1, 0xc1, + 0xce, 0x6c, 0x2a, 0xc3, 0x2c, 0x1a, 0x6a, 0x66, 0xf7, 0xcd, 0x1f, 0xfe, 0xdb, 0xce, 0x85, 0x1f, + 0x7e, 0xba, 0xa3, 0xfc, 0xe4, 0xd3, 0x1d, 0xe5, 0x5f, 0x3f, 0xdd, 0x51, 0xde, 0xfd, 0xe2, 0x39, + 0xc2, 0xb6, 0xbf, 0xbf, 0x88, 0xd2, 0xe8, 0xfd, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x30, 0x24, + 0x99, 0x20, 0x0f, 0xa0, 0x01, 0x00, } func (this *PluginSpecV1) Equal(that interface{}) bool { @@ -29658,6 +29663,11 @@ func (m *HardwareKey) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.PinCacheTTL != 0 { + i = encodeVarintTypes(dAtA, i, uint64(m.PinCacheTTL)) + i-- + dAtA[i] = 0x18 + } if m.SerialNumberValidation != nil { { size, err := m.SerialNumberValidation.MarshalToSizedBuffer(dAtA[:i]) @@ -49376,6 +49386,9 @@ func (m *HardwareKey) Size() (n int) { l = m.SerialNumberValidation.Size() n += 1 + l + sovTypes(uint64(l)) } + if m.PinCacheTTL != 0 { + n += 1 + sovTypes(uint64(m.PinCacheTTL)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -76577,6 +76590,25 @@ func (m *HardwareKey) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field PinCacheTTL", wireType) + } + m.PinCacheTTL = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.PinCacheTTL |= Duration(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/api/utils/keys/alias.go b/api/utils/keys/alias.go new file mode 100644 index 0000000000000..8323a9c9e53ff --- /dev/null +++ b/api/utils/keys/alias.go @@ -0,0 +1,26 @@ +/* +Copyright 2025 Gravitational, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package keys + +import "github.com/gravitational/teleport/api/utils/keys/hardwarekey" + +// Temporary aliases for types moved to the hardwarekey or piv packages +// TODO(Joerger): Remove once /e no longer relies on them. + +// AttestationStatement is an attestation statement for a hardware private key +// that supports json marshaling through the standard json/encoding package. +type AttestationStatement = hardwarekey.AttestationStatement + +// AttestationStatementFromProto converts an AttestationStatement from its protobuf form. +var AttestationStatementFromProto = hardwarekey.AttestationStatementFromProto diff --git a/api/utils/keys/cliprompt.go b/api/utils/keys/cliprompt.go deleted file mode 100644 index 7dce20d211a7c..0000000000000 --- a/api/utils/keys/cliprompt.go +++ /dev/null @@ -1,130 +0,0 @@ -//go:build piv && !pivtest - -// Copyright 2024 Gravitational, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package keys - -import ( - "context" - "fmt" - "os" - - "github.com/go-piv/piv-go/piv" - "github.com/gravitational/trace" - - "github.com/gravitational/teleport/api/utils/prompt" -) - -type cliPrompt struct{} - -func (c *cliPrompt) AskPIN(ctx context.Context, requirement PINPromptRequirement) (string, error) { - message := "Enter your YubiKey PIV PIN" - if requirement == PINOptional { - message = "Enter your YubiKey PIV PIN [blank to use default PIN]" - } - password, err := prompt.Password(ctx, os.Stderr, prompt.Stdin(), message) - return password, trace.Wrap(err) -} - -func (c *cliPrompt) Touch(_ context.Context) error { - _, err := fmt.Fprintln(os.Stderr, "Tap your YubiKey") - return trace.Wrap(err) -} - -func (c *cliPrompt) ChangePIN(ctx context.Context) (*PINAndPUK, error) { - var pinAndPUK = &PINAndPUK{} - for { - fmt.Fprintf(os.Stderr, "Please set a new 6-8 character PIN.\n") - newPIN, err := prompt.Password(ctx, os.Stderr, prompt.Stdin(), "Enter your new YubiKey PIV PIN") - if err != nil { - return nil, trace.Wrap(err) - } - newPINConfirm, err := prompt.Password(ctx, os.Stderr, prompt.Stdin(), "Confirm your new YubiKey PIV PIN") - if err != nil { - return nil, trace.Wrap(err) - } - - if newPIN != newPINConfirm { - fmt.Fprintf(os.Stderr, "PINs do not match.\n") - continue - } - - if newPIN == piv.DefaultPIN { - fmt.Fprintf(os.Stderr, "The default PIN %q is not supported.\n", piv.DefaultPIN) - continue - } - - if !isPINLengthValid(newPIN) { - fmt.Fprintf(os.Stderr, "PIN must be 6-8 characters long.\n") - continue - } - - pinAndPUK.PIN = newPIN - break - } - - puk, err := prompt.Password(ctx, os.Stderr, prompt.Stdin(), "Enter your YubiKey PIV PUK to reset PIN [blank to use default PUK]") - if err != nil { - return nil, trace.Wrap(err) - } - pinAndPUK.PUK = puk - - switch puk { - case piv.DefaultPUK: - fmt.Fprintf(os.Stderr, "The default PUK %q is not supported.\n", piv.DefaultPUK) - fallthrough - case "": - for { - fmt.Fprintf(os.Stderr, "Please set a new 6-8 character PUK (used to reset PIN).\n") - newPUK, err := prompt.Password(ctx, os.Stderr, prompt.Stdin(), "Enter your new YubiKey PIV PUK") - if err != nil { - return nil, trace.Wrap(err) - } - newPUKConfirm, err := prompt.Password(ctx, os.Stderr, prompt.Stdin(), "Confirm your new YubiKey PIV PUK") - if err != nil { - return nil, trace.Wrap(err) - } - - if newPUK != newPUKConfirm { - fmt.Fprintf(os.Stderr, "PUKs do not match.\n") - continue - } - - if newPUK == piv.DefaultPUK { - fmt.Fprintf(os.Stderr, "The default PUK %q is not supported.\n", piv.DefaultPUK) - continue - } - - if !isPINLengthValid(newPUK) { - fmt.Fprintf(os.Stderr, "PUK must be 6-8 characters long.\n") - continue - } - - pinAndPUK.PUK = newPUK - pinAndPUK.PUKChanged = true - break - } - } - return pinAndPUK, nil -} - -func (c *cliPrompt) ConfirmSlotOverwrite(ctx context.Context, message string) (bool, error) { - confirmation, err := prompt.Confirmation(ctx, os.Stderr, prompt.Stdin(), message) - return confirmation, trace.Wrap(err) -} - -func isPINLengthValid(pin string) bool { - return len(pin) >= 6 && len(pin) <= 8 -} diff --git a/api/utils/keys/hardwarekey/attestation.go b/api/utils/keys/hardwarekey/attestation.go new file mode 100644 index 0000000000000..94de91e177dc0 --- /dev/null +++ b/api/utils/keys/hardwarekey/attestation.go @@ -0,0 +1,50 @@ +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hardwarekey + +import ( + "bytes" + + "github.com/gogo/protobuf/jsonpb" + "github.com/gravitational/trace" + + attestationv1 "github.com/gravitational/teleport/api/gen/proto/go/attestation/v1" +) + +// AttestationStatement is an attestation statement for a hardware private key +// that supports json marshaling through the standard json/encoding package. +type AttestationStatement attestationv1.AttestationStatement + +// ToProto converts this AttestationStatement to its protobuf form. +func (a *AttestationStatement) ToProto() *attestationv1.AttestationStatement { + return (*attestationv1.AttestationStatement)(a) +} + +// AttestationStatementFromProto converts an AttestationStatement from its protobuf form. +func AttestationStatementFromProto(att *attestationv1.AttestationStatement) *AttestationStatement { + return (*AttestationStatement)(att) +} + +// MarshalJSON implements custom protobuf json marshaling. +func (a *AttestationStatement) MarshalJSON() ([]byte, error) { + buf := new(bytes.Buffer) + err := (&jsonpb.Marshaler{}).Marshal(buf, a.ToProto()) + return buf.Bytes(), trace.Wrap(err) +} + +// UnmarshalJSON implements custom protobuf json unmarshaling. +func (a *AttestationStatement) UnmarshalJSON(buf []byte) error { + return (&jsonpb.Unmarshaler{AllowUnknownFields: true}).Unmarshal(bytes.NewReader(buf), a.ToProto()) +} diff --git a/api/utils/keys/hardwarekey/cliprompt.go b/api/utils/keys/hardwarekey/cliprompt.go new file mode 100644 index 0000000000000..56526c7f2b247 --- /dev/null +++ b/api/utils/keys/hardwarekey/cliprompt.go @@ -0,0 +1,176 @@ +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hardwarekey + +import ( + "context" + "fmt" + "io" + "os" + + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/utils/prompt" +) + +var ( + // DefaultPIN for the PIV applet. The PIN is used to change the Management Key, + // and slots can optionally require it to perform signing operations. + DefaultPIN = "123456" + // DefaultPUK for the PIV applet. The PUK is only used to reset the PIN when + // the card's PIN retries have been exhausted. + DefaultPUK = "12345678" +) + +type cliPrompt struct { + writer io.Writer + reader prompt.StdinReader +} + +// NewStdCLIPrompt returns a new CLIPrompt with stderr and stdout. +func NewStdCLIPrompt() *cliPrompt { + return &cliPrompt{ + writer: os.Stderr, + reader: prompt.Stdin(), + } +} + +// NewStdCLIPrompt returns a new CLIPrompt with the given writer and reader. +// Used in tests. +func NewCLIPrompt(w io.Writer, r prompt.StdinReader) *cliPrompt { + return &cliPrompt{ + writer: w, + reader: r, + } +} + +// AskPIN prompts the user for a PIN. If the requirement is [PINOptional], +// the prompt will offer the default PIN as a default value. +func (c *cliPrompt) AskPIN(ctx context.Context, requirement PINPromptRequirement, keyInfo ContextualKeyInfo) (string, error) { + msg := "Enter your YubiKey PIV PIN" + + // The user may need to set their PIN for the first time during login, + // give them a hint to continue to setting the PIN. + if requirement == PINOptional { + msg += " [blank to use default PIN]" + } + + // If this is a hardware key agent request with command context info, + // include the command in the prompt. + if keyInfo.Command != "" { + msg = fmt.Sprintf("%v to continue with command %q", msg, keyInfo.Command) + } + + password, err := prompt.Password(ctx, c.writer, c.reader, msg) + return password, trace.Wrap(err) +} + +// Touch prompts the user to touch the hardware key. +func (c *cliPrompt) Touch(_ context.Context, keyInfo ContextualKeyInfo) error { + msg := "Tap your YubiKey" + if keyInfo.Command != "" { + msg = fmt.Sprintf("%v to continue with command %q", msg, keyInfo.Command) + } + + _, err := fmt.Fprintln(c.writer, msg) + return trace.Wrap(err) +} + +// ChangePIN asks for a new PIN and the current PUK to change to the new PIN. +// If the provided PUK is the default value, it will ask for a new PUK as well. +// If an invalid PIN or PUK is provided, the user will be re-prompted until a +// valid value is provided. +func (c *cliPrompt) ChangePIN(ctx context.Context, _ ContextualKeyInfo) (*PINAndPUK, error) { + var pinAndPUK = &PINAndPUK{} + for { + fmt.Fprintf(c.writer, "Please set a new 6-8 character PIN.\n") + newPIN, err := prompt.Password(ctx, c.writer, c.reader, "Enter your new YubiKey PIV PIN") + if err != nil { + return nil, trace.Wrap(err) + } + newPINConfirm, err := prompt.Password(ctx, c.writer, c.reader, "Confirm your new YubiKey PIV PIN") + if err != nil { + return nil, trace.Wrap(err) + } + + if newPIN != newPINConfirm { + fmt.Fprintf(c.writer, "PINs do not match.\n") + continue + } + + if newPIN == DefaultPIN { + fmt.Fprintf(c.writer, "The default PIN %q is not supported.\n", DefaultPIN) + continue + } + + if !isPINLengthValid(newPIN) { + fmt.Fprintf(c.writer, "PIN must be 6-8 characters long.\n") + continue + } + + pinAndPUK.PIN = newPIN + break + } + + puk, err := prompt.Password(ctx, c.writer, c.reader, "Enter your YubiKey PIV PUK to reset PIN [blank to use default PUK]") + if err != nil { + return nil, trace.Wrap(err) + } + pinAndPUK.PUK = puk + + switch puk { + case DefaultPUK: + fmt.Fprintf(c.writer, "The default PUK %q is not supported.\n", DefaultPUK) + fallthrough + case "": + for { + fmt.Fprintf(c.writer, "Please set a new 6-8 character PUK (used to reset PIN).\n") + newPUK, err := prompt.Password(ctx, c.writer, c.reader, "Enter your new YubiKey PIV PUK") + if err != nil { + return nil, trace.Wrap(err) + } + newPUKConfirm, err := prompt.Password(ctx, c.writer, c.reader, "Confirm your new YubiKey PIV PUK") + if err != nil { + return nil, trace.Wrap(err) + } + + if newPUK != newPUKConfirm { + fmt.Fprintf(c.writer, "PUKs do not match.\n") + continue + } + + if newPUK == DefaultPUK { + fmt.Fprintf(c.writer, "The default PUK %q is not supported.\n", DefaultPUK) + continue + } + + if !isPINLengthValid(newPUK) { + fmt.Fprintf(c.writer, "PUK must be 6-8 characters long.\n") + continue + } + + pinAndPUK.PUK = newPUK + pinAndPUK.PUKChanged = true + break + } + } + return pinAndPUK, nil +} + +// ConfirmSlotOverwrite asks the user if the slot's private key and certificate can be overridden. +func (c *cliPrompt) ConfirmSlotOverwrite(ctx context.Context, message string, _ ContextualKeyInfo) (bool, error) { + confirmation, err := prompt.Confirmation(ctx, c.writer, c.reader, message) + return confirmation, trace.Wrap(err) +} diff --git a/api/utils/keys/hardwarekey/cliprompt_test.go b/api/utils/keys/hardwarekey/cliprompt_test.go new file mode 100644 index 0000000000000..e17b5f646a674 --- /dev/null +++ b/api/utils/keys/hardwarekey/cliprompt_test.go @@ -0,0 +1,161 @@ +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package hardwarekey defines types and interfaces for hardware private keys. + +package hardwarekey_test + +import ( + "bytes" + "context" + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" + "github.com/gravitational/teleport/api/utils/prompt" +) + +func TestChangePIN(t *testing.T) { + const validPINPUK = "1234567" + + for _, tc := range []struct { + name string + inputs []string + expectPINAndPUK *hardwarekey.PINAndPUK + expectOutput string + expectError error + }{ + { + name: "no input", + expectError: context.DeadlineExceeded, + }, + { + name: "set pin short", + inputs: []string{ + "123", // pin + "123", // confirm + }, + expectOutput: "PIN must be 6-8 characters long.", + expectError: context.DeadlineExceeded, + }, { + name: "set pin mismatch", + inputs: []string{ + "", // pin + hardwarekey.DefaultPIN, // confirm + }, + expectOutput: "PINs do not match.", + expectError: context.DeadlineExceeded, + }, { + name: "set pin default", + inputs: []string{ + hardwarekey.DefaultPIN, // pin + hardwarekey.DefaultPIN, // confirm + }, + expectOutput: fmt.Sprintf("The default PIN %q is not supported.", hardwarekey.DefaultPIN), + expectError: context.DeadlineExceeded, + }, { + name: "set puk short", + inputs: []string{ + validPINPUK, // pin + validPINPUK, // confirm + "", // empty puk -> trigger set puk + "123", // puk + "123", // confirm + }, + expectOutput: "PUK must be 6-8 characters long.", + expectError: context.DeadlineExceeded, + }, { + name: "set puk mismatch", + inputs: []string{ + validPINPUK, // pin + validPINPUK, // confirm + "", // empty puk -> trigger set puk + "", // puk + validPINPUK, // confirm + }, + expectOutput: "PUKs do not match.", + expectError: context.DeadlineExceeded, + }, { + name: "set puk default", + inputs: []string{ + validPINPUK, // pin + validPINPUK, // confirm + "", // empty puk -> trigger set puk + hardwarekey.DefaultPUK, // puk + hardwarekey.DefaultPUK, // confirm + }, + expectOutput: fmt.Sprintf("The default PUK %q is not supported.", hardwarekey.DefaultPUK), + expectError: context.DeadlineExceeded, + }, { + name: "set puk from empty", + inputs: []string{ + validPINPUK, // pin + validPINPUK, // confirm + "", // empty puk -> trigger set puk + validPINPUK, // puk + validPINPUK, // confirm + }, + expectPINAndPUK: &hardwarekey.PINAndPUK{ + PIN: validPINPUK, + PUK: validPINPUK, + PUKChanged: true, + }, + }, { + name: "set puk from default", + inputs: []string{ + validPINPUK, // pin + validPINPUK, // confirm + hardwarekey.DefaultPUK, // default puk -> trigger set puk + validPINPUK, // puk + validPINPUK, // confirm + }, + expectPINAndPUK: &hardwarekey.PINAndPUK{ + PIN: validPINPUK, + PUK: validPINPUK, + PUKChanged: true, + }, + }, { + name: "valid pin, valid puk", + inputs: []string{ + validPINPUK, // pin + validPINPUK, // confirm + validPINPUK, // puk + }, + expectPINAndPUK: &hardwarekey.PINAndPUK{ + PIN: validPINPUK, + PUK: validPINPUK, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + promptWriter := bytes.NewBuffer([]byte{}) + promptReader := prompt.NewFakeReader() + prompt := hardwarekey.NewCLIPrompt(promptWriter, promptReader) + + for _, input := range tc.inputs { + promptReader.AddString(input) + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) + defer cancel() + + PINAndPUK, err := prompt.ChangePIN(ctx, hardwarekey.ContextualKeyInfo{}) + require.ErrorIs(t, err, tc.expectError) + require.Equal(t, tc.expectPINAndPUK, PINAndPUK) + }) + } +} diff --git a/api/utils/keys/hardwarekey/hardwarekey.go b/api/utils/keys/hardwarekey/hardwarekey.go new file mode 100644 index 0000000000000..7b3d2f77864f5 --- /dev/null +++ b/api/utils/keys/hardwarekey/hardwarekey.go @@ -0,0 +1,274 @@ +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package hardwarekey defines types and interfaces for hardware private keys. +package hardwarekey + +import ( + "context" + "crypto" + "crypto/rand" + "crypto/x509" + "encoding/json" + "io" + "time" + + "github.com/gravitational/trace" +) + +// Service for interfacing with hardware private keys. +type Service interface { + // NewPrivateKey creates a hardware private key that satisfies the provided [config], + // if one does not already exist, and returns a corresponding [hardwarekey.Signer]. + NewPrivateKey(ctx context.Context, config PrivateKeyConfig) (*Signer, error) + // Sign performs a cryptographic signature using the specified hardware + // private key and provided signature parameters. + Sign(ctx context.Context, ref *PrivateKeyRef, keyInfo ContextualKeyInfo, rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) + // TODO(Joerger): DELETE IN v19.0.0 + // GetFullKeyRef gets the full [PrivateKeyRef] for an existing hardware private + // key in the given slot of the hardware key with the given serial number. + GetFullKeyRef(serialNumber uint32, slotKey PIVSlotKey) (*PrivateKeyRef, error) +} + +// Signer is a hardware key implementation of [crypto.Signer]. +type Signer struct { + service Service + Ref *PrivateKeyRef + KeyInfo ContextualKeyInfo +} + +// NewSigner returns a [Signer] for the given service and ref. +// keyInfo is an optional argument to supply additional contextual info +// used to add additional context to prompts, e.g. ProxyHost. +func NewSigner(s Service, ref *PrivateKeyRef, keyInfo ContextualKeyInfo) *Signer { + return &Signer{ + service: s, + Ref: ref, + KeyInfo: keyInfo, + } +} + +// Public implements [crypto.Signer]. +func (h *Signer) Public() crypto.PublicKey { + return h.Ref.PublicKey +} + +// Sign implements [crypto.Signer]. +func (h *Signer) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { + return h.service.Sign(context.TODO(), h.Ref, h.KeyInfo, rand, digest, opts) +} + +// GetAttestation returns the hardware private key attestation details. +func (h *Signer) GetAttestationStatement() *AttestationStatement { + return h.Ref.AttestationStatement +} + +// GetPrivateKeyPolicy returns the PrivateKeyPolicy satisfied by this key. +func (h *Signer) GetPromptPolicy() PromptPolicy { + return h.Ref.Policy +} + +// WarmupHardwareKey performs a bogus sign() call to prompt the user for PIN/touch (if needed). +func (h *Signer) WarmupHardwareKey(ctx context.Context) error { + if !h.Ref.Policy.PINRequired && !h.Ref.Policy.TouchRequired { + return nil + } + + // ed25519 keys only support sha512 hashing, or no hashing. Currently we don't support + // ed25519 hardware keys outside of the mocked PIV service, but we may extend support in + // the future as newer keys are being made with ed25519 support (YubiKey 5.7.x, SoloKey). + hash := crypto.SHA512 + + // We don't actually need to hash the digest, just make it match the hash size. + digest := make([]byte, hash.Size()) + + _, err := h.service.Sign(ctx, h.Ref, h.KeyInfo, rand.Reader, digest, hash) + return trace.Wrap(err, "failed to perform warmup signature with hardware private key") +} + +// EncodeSigner encodes the hardware key signer a format understood by other Teleport clients. +func EncodeSigner(p *Signer) ([]byte, error) { + return p.Ref.encode() +} + +// DecodeSigner decodes an encoded hardware key signer for the given service. +func DecodeSigner(encodedKey []byte, s Service, keyInfo ContextualKeyInfo) (*Signer, error) { + ref, err := decodeKeyRef(encodedKey, s) + if err != nil { + return nil, trace.Wrap(err) + } + + return NewSigner(s, ref, keyInfo), nil +} + +// PrivateKeyRef references a specific hardware private key. +type PrivateKeyRef struct { + // SerialNumber is the hardware key's serial number. + SerialNumber uint32 `json:"serial_number"` + // SlotKey is the key name for the hardware key PIV slot, e.g. "9a". + SlotKey PIVSlotKey `json:"slot_key"` + // PublicKey is the public key paired with the hardware private key. + PublicKey crypto.PublicKey `json:"-"` // uses custom JSON marshaling in PKIX, ASN.1 DER form + // Policy specifies the hardware private key's PIN/touch prompt policies. + Policy PromptPolicy `json:"policy"` + // AttestationStatement contains the hardware private key's attestation statement, which is + // to attest the touch and pin requirements for this hardware private key during login. + AttestationStatement *AttestationStatement `json:"attestation_statement"` + // PINCacheTTL is how long hardware key prompts should cache the PIN for this key, if at all. + PINCacheTTL time.Duration `json:"pin_cache_ttl"` +} + +// encode encodes a [PrivateKeyRef] to JSON. +func (r *PrivateKeyRef) encode() ([]byte, error) { + // Ensure that all required fields are provided to encode. + if err := r.Validate(); err != nil { + return nil, trace.Wrap(err) + } + + keyRefBytes, err := json.Marshal(r) + if err != nil { + return nil, trace.Wrap(err) + } + return keyRefBytes, nil +} + +// decodeKeyRef decodes a [PrivateKeyRef] from JSON. +func decodeKeyRef(encodedKeyRef []byte, s Service) (*PrivateKeyRef, error) { + ref := &PrivateKeyRef{} + if err := json.Unmarshal(encodedKeyRef, ref); err != nil { + return nil, trace.Wrap(err) + } + + // Ensure that all required fields are decoded. + if err := ref.Validate(); err != nil { + // If some fields are missing, this is likely an old login key with only + // the serial number and slot. Fetch missing data from the hardware key. + // This data will be saved to the login key on next login + // TODO(Joerger): DELETE IN v19.0.0 + if ref.SerialNumber != 0 && ref.SlotKey != 0 { + return s.GetFullKeyRef(ref.SerialNumber, ref.SlotKey) + } + + return nil, trace.Wrap(err) + } + + return ref, nil +} + +func (r *PrivateKeyRef) Validate() error { + if r.SerialNumber == 0 { + return trace.BadParameter("private key ref missing SerialNumber") + } + if r.SlotKey == 0 { + return trace.BadParameter("private key ref missing SlotKey") + } + if r.PublicKey == nil { + return trace.BadParameter("private key ref missing PublicKey") + } + if r.AttestationStatement == nil { + return trace.BadParameter("private key ref missing AttestationStatement") + } + return nil +} + +// These types are used for custom marshaling of the crypto.PublicKey field in [PrivateKeyRef]. +type rawPrivateKeyRef PrivateKeyRef +type hardwarePrivateKeyRefJSON struct { + // embedding an alias type instead of [HardwarePrivateKeyRef] prevents the custom marshaling + // from recursively applying, which would result in a stack overflow. + rawPrivateKeyRef + PublicKeyDER []byte `json:"public_key,omitempty"` +} + +// MarshalJSON marshals [PrivateKeyRef] with custom logic for the public key. +func (r PrivateKeyRef) MarshalJSON() ([]byte, error) { + var pubDER []byte + if r.PublicKey != nil { + var err error + if pubDER, err = x509.MarshalPKIXPublicKey(r.PublicKey); err != nil { + return nil, trace.Wrap(err) + } + } + + return json.Marshal(&hardwarePrivateKeyRefJSON{ + rawPrivateKeyRef: rawPrivateKeyRef(r), + PublicKeyDER: pubDER, + }) +} + +// UnmarshalJSON unmarshals [PrivateKeyRef] with custom logic for the public key. +func (r *PrivateKeyRef) UnmarshalJSON(b []byte) error { + var ref hardwarePrivateKeyRefJSON + err := json.Unmarshal(b, &ref) + if err != nil { + return trace.Wrap(err) + } + + if ref.PublicKeyDER != nil { + ref.rawPrivateKeyRef.PublicKey, err = x509.ParsePKIXPublicKey(ref.PublicKeyDER) + if err != nil { + return trace.Wrap(err) + } + } + + *r = PrivateKeyRef(ref.rawPrivateKeyRef) + return nil +} + +// PrivateKeyConfig contains config for creating a new hardware private key. +type PrivateKeyConfig struct { + // Policy is a prompt policy to require for the hardware private key. + Policy PromptPolicy + // CustomSlot is a specific PIV slot to generate the hardware private key in. + // If unset, the default slot for the given policy will be used. + // - !touch & !pin -> 9a + // - !touch & pin -> 9c + // - touch & pin -> 9d + // - touch & !pin -> 9e + CustomSlot PIVSlotKeyString + // Algorithm is the key algorithm to use. Defaults to [AlgorithmEC256]. + // [AlgorithmEd25519] is not supported by all hardware keys. + Algorithm SignatureAlgorithm + // ContextualKeyInfo contains additional info to associate with the key. + ContextualKeyInfo ContextualKeyInfo + // PINCacheTTL is an option to enable PIN caching for this key with the specified TTL. + PINCacheTTL time.Duration +} + +// ContextualKeyInfo contains contextual information associated with a hardware [PrivateKey]. +type ContextualKeyInfo struct { + // ProxyHost is the root proxy hostname that the key is associated with. + ProxyHost string + // Username is a Teleport username that the key is associated with. + Username string + // ClusterName is a Teleport cluster name that the key is associated with. + ClusterName string + // AgentKey specifies whether this key is being utilized through an agent. + // The hardware key service may impose additional restrictions in this case, + // such as checking that the PIV slot certificate matches the Teleport client + // metadata certificate format, to ensure the agent doesn't provide access to + // non teleport client PIV keys. + AgentKey bool + // Command is the running command utilizing this key. + Command string +} + +// SignatureAlgorithm is a signature key algorithm option. +type SignatureAlgorithm int + +const ( + SignatureAlgorithmEC256 SignatureAlgorithm = iota + 1 + SignatureAlgorithmEd25519 + SignatureAlgorithmRSA2048 +) diff --git a/api/utils/keys/hardwarekey/hardwarekey_test.go b/api/utils/keys/hardwarekey/hardwarekey_test.go new file mode 100644 index 0000000000000..0e71a3bb16529 --- /dev/null +++ b/api/utils/keys/hardwarekey/hardwarekey_test.go @@ -0,0 +1,170 @@ +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package hardwarekey defines types and interfaces for hardware private keys. + +package hardwarekey_test + +import ( + "context" + "crypto" + "crypto/rand" + "crypto/sha512" + "encoding/json" + "fmt" + "io" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" + "github.com/gravitational/teleport/api/utils/prompt" +) + +// TestPrivateKey_EncodeDecode tests encoding and decoding a hardware key signer. +// In particular, this tests that the public key is properly encoded and that the +// contextual key info and missing key info (old client logins) is handled correctly. +func TestPrivateKey_EncodeDecode(t *testing.T) { + t.Parallel() + + ctx := context.Background() + s := hardwarekey.NewMockHardwareKeyService(nil /*prompt*/) + + contextualKeyInfo := hardwarekey.ContextualKeyInfo{ + ProxyHost: "billy.io", + Username: "Billy@billy.io", + ClusterName: "billy.io", + } + + hwSigner, err := s.NewPrivateKey(ctx, hardwarekey.PrivateKeyConfig{ + Policy: hardwarekey.PromptPolicyNone, + ContextualKeyInfo: contextualKeyInfo, + }) + require.NoError(t, err) + + encoded, err := hardwarekey.EncodeSigner(hwSigner) + require.NoError(t, err) + + decodedSigner, err := hardwarekey.DecodeSigner(encoded, s, contextualKeyInfo) + require.NoError(t, err) + require.Equal(t, hwSigner, decodedSigner) +} + +// Old client logins would only have encoded the serial number and slot key. +// TODO(Joerger): DELETE IN v19.0.0 +func TestPrivateKey_DecodePartialKeyRef(t *testing.T) { + t.Parallel() + + ctx := context.Background() + s := hardwarekey.NewMockHardwareKeyService(nil /*prompt*/) + hwSigner, err := s.NewPrivateKey(ctx, hardwarekey.PrivateKeyConfig{ + Policy: hardwarekey.PromptPolicyNone, + }) + require.NoError(t, err) + + partialKeyRefJSON, err := json.Marshal(&hardwarekey.PrivateKeyRef{ + SerialNumber: hwSigner.Ref.SerialNumber, + SlotKey: hwSigner.Ref.SlotKey, + }) + require.NoError(t, err) + + decodedSigner, err := hardwarekey.DecodeSigner(partialKeyRefJSON, s, hardwarekey.ContextualKeyInfo{}) + require.NoError(t, err) + require.Equal(t, hwSigner, decodedSigner) +} + +// TestPrivateKey_Prompt tests hardware key service PIN/Touch logic with a mocked service. +func TestPrivateKey_Prompt(t *testing.T) { + t.Parallel() + + ctx := context.Background() + s := hardwarekey.NewMockHardwareKeyService(nil /*prompt*/) // a new prompt is set for each [doWithPrompt] call. + + for _, policy := range []hardwarekey.PromptPolicy{ + hardwarekey.PromptPolicyNone, + hardwarekey.PromptPolicyTouch, + hardwarekey.PromptPolicyPIN, + hardwarekey.PromptPolicyTouchAndPIN, + } { + t.Run(fmt.Sprintf("policy:%+v", policy), func(t *testing.T) { + type newPrivateKeyRet struct { + priv *hardwarekey.Signer + err error + } + + // Creating a new hardware key requires PIN/touch. + newPrivateKeyReturn := doWithPrompt(t, s, policy, func() newPrivateKeyRet { + hwSigner, err := s.NewPrivateKey(ctx, hardwarekey.PrivateKeyConfig{ + Policy: policy, + }) + return newPrivateKeyRet{ + priv: hwSigner, + err: err, + } + }) + require.NoError(t, newPrivateKeyReturn.err) + hwPriv := newPrivateKeyReturn.priv + require.NotNil(t, hwPriv) + + // Signatures requires PIN/touch. Do a bogus signature. + err := doWithPrompt(t, s, policy, func() error { + hash := sha512.Sum512(make([]byte, 512)) + _, err := hwPriv.Sign(rand.Reader, hash[:], crypto.SHA512) + return err + }) + require.NoError(t, err) + }) + } +} + +func doWithPrompt[T any](t *testing.T, s *hardwarekey.MockHardwareKeyService, policy hardwarekey.PromptPolicy, fn func() T) T { + t.Helper() + // Mock a CLI prompt. + pipeReader, pipeWriter := io.Pipe() + promptReader := prompt.NewFakeReader() + s.SetPrompt(hardwarekey.NewCLIPrompt(pipeWriter, promptReader)) + + out := make(chan T) + go func() { + out <- fn() + }() + + if policy.PINRequired { + out := make([]byte, 100) + _, err := pipeReader.Read(out) + assert.NoError(t, err) + assert.Contains(t, string(out), "Enter your YubiKey PIV PIN") + // mock service doesn't actually check the pin, it just waits for input. + promptReader.AddString("") + } + + if policy.TouchRequired { + out := make([]byte, 100) + _, err := pipeReader.Read(out) + assert.NoError(t, err) + assert.Contains(t, string(out), "Tap your YubiKey") + // mock touch. + s.MockTouch() + } + + select { + case out := <-out: + return out + case <-time.After(time.Second): + t.Error("failed to complete fn after prompts") + return *new(T) + } +} diff --git a/api/utils/keys/hardwarekey/prompt.go b/api/utils/keys/hardwarekey/prompt.go new file mode 100644 index 0000000000000..7dd288d8d9e8a --- /dev/null +++ b/api/utils/keys/hardwarekey/prompt.go @@ -0,0 +1,100 @@ +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hardwarekey + +import ( + "context" + + "github.com/gravitational/trace" +) + +var ( + // PromptPolicyNone does not require touch or pin. + PromptPolicyNone = PromptPolicy{TouchRequired: false, PINRequired: false} + // PromptPolicyTouch requires touch. + PromptPolicyTouch = PromptPolicy{TouchRequired: true, PINRequired: false} + // PromptPolicyPIN requires pin. + PromptPolicyPIN = PromptPolicy{TouchRequired: false, PINRequired: true} + // PromptPolicyTouchAndPIN requires touch and pin. + PromptPolicyTouchAndPIN = PromptPolicy{TouchRequired: true, PINRequired: true} +) + +// PromptPolicy specifies a hardware private key's PIN/touch prompt policies. +type PromptPolicy struct { + // TouchRequired means that touch is required for signatures. + TouchRequired bool + // PINRequired means that PIN is required for signatures. + PINRequired bool +} + +// Prompt provides methods to interact with a hardware [Signer]. +type Prompt interface { + // AskPIN prompts the user for a PIN. + // The requirement tells if the PIN is required or optional. + AskPIN(ctx context.Context, requirement PINPromptRequirement, keyInfo ContextualKeyInfo) (string, error) + // Touch prompts the user to touch the hardware key. + Touch(ctx context.Context, keyInfo ContextualKeyInfo) error + // ChangePIN asks for a new PIN. + // If the PUK has a default value, it should ask for the new value for it. + // It is up to the implementer how the validation is handled. + // For example, CLI prompt can ask for a valid PIN/PUK in a loop, a GUI + // prompt can use the frontend validation. + ChangePIN(ctx context.Context, keyInfo ContextualKeyInfo) (*PINAndPUK, error) + // ConfirmSlotOverwrite asks the user if the slot's private key and certificate can be overridden. + ConfirmSlotOverwrite(ctx context.Context, message string, keyInfo ContextualKeyInfo) (bool, error) +} + +// PINPromptRequirement specifies whether a PIN is required. +type PINPromptRequirement int + +const ( + // PINOptional allows the user to proceed without entering a PIN. + PINOptional PINPromptRequirement = iota + // PINRequired enforces that a PIN must be entered to proceed. + PINRequired +) + +// PINAndPUK describes a response returned from [Prompt].ChangePIN. +type PINAndPUK struct { + // New PIN set by the user. + PIN string + // PUK used to change the PIN. + // This is a new PUK if it has not been changed (from the default PUK). + PUK string + // PUKChanged is true if the user changed the default PUK. + PUKChanged bool +} + +// Validate the user-provided PIN and PUK. +func (p PINAndPUK) Validate() error { + if !isPINLengthValid(p.PIN) { + return trace.BadParameter("PIN must be 6-8 characters long") + } + if p.PIN == DefaultPIN { + return trace.BadParameter("The default PIN is not supported") + } + if !isPINLengthValid(p.PUK) { + return trace.BadParameter("PUK must be 6-8 characters long") + } + if p.PUK == DefaultPUK { + return trace.BadParameter("The default PUK is not supported") + } + return nil +} + +// isPINLengthValid returns whether the given PIV PIN, or PUK, is of valid length (6-8 characters). +func isPINLengthValid(pin string) bool { + return len(pin) >= 6 && len(pin) <= 8 +} diff --git a/api/utils/keys/hardwarekey/service_mock.go b/api/utils/keys/hardwarekey/service_mock.go new file mode 100644 index 0000000000000..0ec1a1e291133 --- /dev/null +++ b/api/utils/keys/hardwarekey/service_mock.go @@ -0,0 +1,220 @@ +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hardwarekey + +import ( + "context" + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "io" + "sync" + "time" + + "github.com/gravitational/trace" +) + +// Currently Teleport does not provide a way to choose a specific hardware key, +// so we just hard code a serial number for all tests. +const serialNumber uint32 = 12345678 + +type fakeHardwarePrivateKey struct { + crypto.Signer + ref *PrivateKeyRef +} + +// hardwareKeySlot references a specific hardware key slot on a specific hardware key. +type hardwareKeySlot struct { + serialNumber uint32 + slot PIVSlotKey +} + +type MockHardwareKeyService struct { + prompt Prompt + promptMu sync.Mutex + mockTouch chan struct{} + + fakeHardwarePrivateKeys map[hardwareKeySlot]*fakeHardwarePrivateKey + fakeHardwarePrivateKeysMux sync.Mutex +} + +// NewMockHardwareKeyService returns a [mockHardwareKeyService] for use in tests. +// If [prompt] is provided, the service will also mock PIN and touch prompts. +func NewMockHardwareKeyService(prompt Prompt) *MockHardwareKeyService { + return &MockHardwareKeyService{ + prompt: prompt, + mockTouch: make(chan struct{}), + fakeHardwarePrivateKeys: map[hardwareKeySlot]*fakeHardwarePrivateKey{}, + } +} + +func (s *MockHardwareKeyService) NewPrivateKey(ctx context.Context, config PrivateKeyConfig) (*Signer, error) { + s.fakeHardwarePrivateKeysMux.Lock() + defer s.fakeHardwarePrivateKeysMux.Unlock() + + // Get the requested or default PIV slot. + var slotKey PIVSlotKey + var err error + if config.CustomSlot != "" { + slotKey, err = config.CustomSlot.Parse() + } else { + slotKey, err = GetDefaultSlotKey(config.Policy) + } + if err != nil { + return nil, trace.Wrap(err) + } + + keySlot := hardwareKeySlot{ + serialNumber: serialNumber, + slot: slotKey, + } + + if priv, ok := s.fakeHardwarePrivateKeys[keySlot]; ok { + return NewSigner(s, priv.ref, config.ContextualKeyInfo), nil + } + + // generating a new key with PIN/touch requirements requires the corresponding prompt. + if err := s.tryPrompt(ctx, config.Policy, config.ContextualKeyInfo); err != nil { + return nil, trace.Wrap(err) + } + + var priv crypto.Signer + switch config.Algorithm { + // Use ECDSA key by default. + case SignatureAlgorithmEC256, 0: + priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case SignatureAlgorithmEd25519: + _, priv, err = ed25519.GenerateKey(rand.Reader) + case SignatureAlgorithmRSA2048: + //nolint:forbidigo // Allow /api to generate RSA key without importing Teleport. + priv, err = rsa.GenerateKey(rand.Reader, 2048) + default: + return nil, trace.BadParameter("unknown algorithm option %v", config.Algorithm) + } + + if err != nil { + return nil, trace.Wrap(err) + } + + ref := &PrivateKeyRef{ + SerialNumber: serialNumber, + SlotKey: slotKey, + PublicKey: priv.Public(), + Policy: config.Policy, + // Since this is only used in tests, we will ignore the attestation statement in the end. + // We just need it to be non-nil so that it goes through the test modules implementation + // of Attest + AttestationStatement: &AttestationStatement{}, + PINCacheTTL: config.PINCacheTTL, + } + + if err := ref.Validate(); err != nil { + return nil, trace.Wrap(err) + } + + s.fakeHardwarePrivateKeys[keySlot] = &fakeHardwarePrivateKey{ + Signer: priv, + ref: ref, + } + + return NewSigner(s, ref, config.ContextualKeyInfo), nil +} + +// Sign performs a cryptographic signature using the specified hardware +// private key and provided signature parameters. +func (s *MockHardwareKeyService) Sign(ctx context.Context, ref *PrivateKeyRef, keyInfo ContextualKeyInfo, rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { + s.fakeHardwarePrivateKeysMux.Lock() + defer s.fakeHardwarePrivateKeysMux.Unlock() + + priv, ok := s.fakeHardwarePrivateKeys[hardwareKeySlot{ + serialNumber: serialNumber, + slot: ref.SlotKey, + }] + if !ok { + return nil, trace.NotFound("key not found in slot 0x%x", ref.SlotKey) + } + + if err := s.tryPrompt(ctx, ref.Policy, keyInfo); err != nil { + return nil, trace.Wrap(err) + } + + return priv.Sign(rand, digest, opts) +} + +func (s *MockHardwareKeyService) tryPrompt(ctx context.Context, policy PromptPolicy, keyInfo ContextualKeyInfo) error { + s.promptMu.Lock() + defer s.promptMu.Unlock() + + if s.prompt == nil || (!policy.PINRequired && !policy.TouchRequired) { + return nil + } + + if policy.PINRequired { + ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) + defer cancel() + if _, err := s.prompt.AskPIN(ctx, PINRequired, keyInfo); err != nil { + return trace.Wrap(err, "failed to handle pin prompt") + } + // We don't actually check the PIN for the current tests, any input is sufficient to unblock the prompt. + } + + if policy.TouchRequired { + if err := s.prompt.Touch(ctx, keyInfo); err != nil { + return trace.Wrap(err) + } + select { + case <-s.mockTouch: + case <-time.After(100 * time.Millisecond): + return trace.Wrap(context.DeadlineExceeded, "failed to handle touch prompt") + } + } + + return nil +} + +func (s *MockHardwareKeyService) SetPrompt(prompt Prompt) { + s.promptMu.Lock() + defer s.promptMu.Unlock() + s.prompt = prompt +} + +// TODO(Joerger): DELETE IN v19.0.0 +func (s *MockHardwareKeyService) GetFullKeyRef(serialNumber uint32, slotKey PIVSlotKey) (*PrivateKeyRef, error) { + s.fakeHardwarePrivateKeysMux.Lock() + defer s.fakeHardwarePrivateKeysMux.Unlock() + + priv, ok := s.fakeHardwarePrivateKeys[hardwareKeySlot{ + serialNumber: serialNumber, + slot: slotKey, + }] + if !ok { + return nil, trace.NotFound("key not found in slot 0x%x", slotKey) + } + + return priv.ref, nil +} + +func (s *MockHardwareKeyService) MockTouch() { + s.mockTouch <- struct{}{} +} + +func (s *MockHardwareKeyService) Reset() { + s.fakeHardwarePrivateKeysMux.Lock() + defer s.fakeHardwarePrivateKeysMux.Unlock() + s.fakeHardwarePrivateKeys = map[hardwareKeySlot]*fakeHardwarePrivateKey{} +} diff --git a/api/utils/keys/hardwarekey/slot.go b/api/utils/keys/hardwarekey/slot.go new file mode 100644 index 0000000000000..7d603dfa2ea9d --- /dev/null +++ b/api/utils/keys/hardwarekey/slot.go @@ -0,0 +1,129 @@ +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package hardwarekey defines types and interfaces for hardware private keys. +package hardwarekey + +import ( + "strconv" + + "github.com/gravitational/trace" + + hardwarekeyagentv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/hardwarekeyagent/v1" +) + +// PIVSlotKey is the key reference for a specific PIV slot. +// +// See: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-73-4.pdf#page=32 +type PIVSlotKey uint32 + +const ( + pivSlot9A PIVSlotKey = 0x9a + pivSlot9C PIVSlotKey = 0x9c + pivSlot9D PIVSlotKey = 0x9d + pivSlot9E PIVSlotKey = 0x9e +) + +// Validate the slot key value. +func (k PIVSlotKey) Validate() error { + switch k { + case pivSlot9A, pivSlot9C, pivSlot9D, pivSlot9E: + return nil + default: + return trace.BadParameter("invalid PIV slot key 0x%x", k) + } +} + +// GetDefaultSlotKey gets the default PIV slot key for the given [policy]. +// - 9A for PromptPolicyNone +// - 9C for PromptPolicyTouch +// - 9D for PromptPolicyTouchAndPIN +// - 9E for PromptPolicyPIN +func GetDefaultSlotKey(policy PromptPolicy) (PIVSlotKey, error) { + switch policy { + case PromptPolicyNone: + return pivSlot9A, nil + case PromptPolicyTouch: + return pivSlot9C, nil + case PromptPolicyPIN: + return pivSlot9E, nil + case PromptPolicyTouchAndPIN: + return pivSlot9D, nil + default: + return 0, trace.BadParameter("unexpected prompt policy %v", policy) + } +} + +// PIVSlotKeyString is the string representation of a [PIVSlotKey]. +type PIVSlotKeyString string + +// Validate that [s] parses into a valid [PIVSlotKey]. +func (s PIVSlotKeyString) Validate() error { + _, err := s.Parse() + return trace.Wrap(err) +} + +// Parse [s] into a [PIVSlotKey]. +func (s PIVSlotKeyString) Parse() (PIVSlotKey, error) { + slotKeyUint, err := strconv.ParseUint(string(s), 16, 32) + if err != nil { + return 0, trace.Wrap(err, "failed to parse %q as a uint", s) + } + + slotKey := PIVSlotKey(slotKeyUint) + if err := slotKey.Validate(); err != nil { + return 0, trace.Wrap(err) + } + + return slotKey, nil +} + +// PIVSlotKeyFromProto convert the piv slot key from proto. +func PIVSlotKeyFromProto(pivSlot hardwarekeyagentv1.PIVSlotKey) (PIVSlotKey, error) { + var slotKey PIVSlotKey + switch pivSlot { + case hardwarekeyagentv1.PIVSlotKey_PIV_SLOT_KEY_9A: + slotKey = pivSlot9A + case hardwarekeyagentv1.PIVSlotKey_PIV_SLOT_KEY_9C: + slotKey = pivSlot9C + case hardwarekeyagentv1.PIVSlotKey_PIV_SLOT_KEY_9D: + slotKey = pivSlot9D + case hardwarekeyagentv1.PIVSlotKey_PIV_SLOT_KEY_9E: + slotKey = pivSlot9E + default: + return 0, trace.BadParameter("unknown piv slot key for proto enum %d", pivSlot) + } + + if err := slotKey.Validate(); err != nil { + return 0, trace.Wrap(err) + } + + return slotKey, nil +} + +// PIVSlotKeyFromProto convert the piv slot key to proto. +func PIVSlotKeyToProto(slotKey PIVSlotKey) (hardwarekeyagentv1.PIVSlotKey, error) { + switch slotKey { + case pivSlot9A: + return hardwarekeyagentv1.PIVSlotKey_PIV_SLOT_KEY_9A, nil + case pivSlot9C: + return hardwarekeyagentv1.PIVSlotKey_PIV_SLOT_KEY_9C, nil + case pivSlot9D: + return hardwarekeyagentv1.PIVSlotKey_PIV_SLOT_KEY_9D, nil + case pivSlot9E: + return hardwarekeyagentv1.PIVSlotKey_PIV_SLOT_KEY_9E, nil + default: + return 0, trace.BadParameter("unknown proto enum for piv slot key %d", slotKey) + } +} diff --git a/api/utils/keys/hardwarekey/slot_test.go b/api/utils/keys/hardwarekey/slot_test.go new file mode 100644 index 0000000000000..51ede5346b899 --- /dev/null +++ b/api/utils/keys/hardwarekey/slot_test.go @@ -0,0 +1,68 @@ +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package hardwarekey defines types and interfaces for hardware private keys. + +package hardwarekey_test + +import ( + "testing" + + "github.com/gravitational/trace" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" +) + +func TestXxx(t *testing.T) { + for _, tt := range []struct { + slotString hardwarekey.PIVSlotKeyString + expectPIVSlotKey hardwarekey.PIVSlotKey + assertError require.ErrorAssertionFunc + }{ + { + slotString: "9a", + expectPIVSlotKey: 0x9a, + assertError: require.NoError, + }, { + slotString: "9c", + expectPIVSlotKey: 0x9c, + assertError: require.NoError, + }, { + slotString: "9d", + expectPIVSlotKey: 0x9d, + assertError: require.NoError, + }, { + slotString: "9e", + expectPIVSlotKey: 0x9e, + assertError: require.NoError, + }, { + slotString: "invalid_uint", + expectPIVSlotKey: 0, + assertError: require.Error, + }, { + slotString: "9b", // unsupported slot key + expectPIVSlotKey: 0, + assertError: func(t require.TestingT, err error, i ...interface{}) { + require.True(t, trace.IsBadParameter(err)) + }, + }, + } { + t.Run(string(tt.slotString), func(t *testing.T) { + pivSlotKey, err := tt.slotString.Parse() + tt.assertError(t, err) + require.Equal(t, tt.expectPIVSlotKey, pivSlotKey) + }) + } +} diff --git a/api/utils/keys/hardwarekeyagent/agent.go b/api/utils/keys/hardwarekeyagent/agent.go new file mode 100644 index 0000000000000..fdcdb8b2f525e --- /dev/null +++ b/api/utils/keys/hardwarekeyagent/agent.go @@ -0,0 +1,150 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Package hardwarekeyagent provides a hardware key agent implementation of [hardwarekey.Service]. +package hardwarekeyagent + +import ( + "context" + "crypto" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "log/slog" + "net" + "os" + + "github.com/gravitational/trace" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + "github.com/gravitational/teleport/api/constants" + hardwarekeyagentv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/hardwarekeyagent/v1" + "github.com/gravitational/teleport/api/utils/grpc/interceptors" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" +) + +// NewClient creates a new hardware key agent client. +func NewClient(ctx context.Context, socketPath string, creds credentials.TransportCredentials) (hardwarekeyagentv1.HardwareKeyAgentServiceClient, error) { + if _, err := os.Stat(socketPath); err != nil { + return nil, trace.Wrap(err) + } + + cc, err := grpc.NewClient("passthrough:", + grpc.WithTransportCredentials(creds), + grpc.WithUnaryInterceptor(interceptors.GRPCClientUnaryErrorInterceptor), + // The [grpc] library fails to resolve unix sockets on Windows, so + // we provide "passthrough:" to skip grpc's address resolution and + // a custom [net] dialer to connect to the socket. + grpc.WithContextDialer(func(_ context.Context, addr string) (net.Conn, error) { + return net.Dial("unix", socketPath) + }), + ) + if err != nil { + return nil, trace.Wrap(err) + } + + return hardwarekeyagentv1.NewHardwareKeyAgentServiceClient(cc), nil +} + +// NewServer returns a new hardware key agent server. +func NewServer(ctx context.Context, s hardwarekey.Service, creds credentials.TransportCredentials) *grpc.Server { + grpcServer := grpc.NewServer( + grpc.Creds(creds), + grpc.UnaryInterceptor(interceptors.GRPCServerUnaryErrorInterceptor), + ) + hardwarekeyagentv1.RegisterHardwareKeyAgentServiceServer(grpcServer, &agentService{s: s}) + return grpcServer +} + +// agentService implements [hardwarekeyagentv1.HardwareKeyAgentServiceServer]. +type agentService struct { + hardwarekeyagentv1.UnimplementedHardwareKeyAgentServiceServer + s hardwarekey.Service +} + +// Sign the given digest with the specified hardware private key. +func (s *agentService) Sign(ctx context.Context, req *hardwarekeyagentv1.SignRequest) (*hardwarekeyagentv1.Signature, error) { + slotKey, err := hardwarekey.PIVSlotKeyFromProto(req.KeyRef.SlotKey) + if err != nil { + return nil, trace.Wrap(err) + } + + pub, err := x509.ParsePKIXPublicKey(req.KeyRef.PublicKeyDer) + if err != nil { + return nil, trace.Wrap(err) + } + + keyRef := &hardwarekey.PrivateKeyRef{ + SerialNumber: req.KeyRef.SerialNumber, + SlotKey: slotKey, + PublicKey: pub, + Policy: hardwarekey.PromptPolicy{ + TouchRequired: req.KeyInfo.TouchRequired, + PINRequired: req.KeyInfo.PinRequired, + }, + PINCacheTTL: req.KeyInfo.PinCacheTtl.AsDuration(), + } + + // Double check that the client didn't provide some bogus pin cache TTL. + if keyRef.PINCacheTTL > constants.MaxPIVPINCacheTTL { + return nil, trace.BadParameter("pin_cache_ttl cannot be larger than %s", constants.MaxPIVPINCacheTTL) + } + + keyInfo := hardwarekey.ContextualKeyInfo{ + ProxyHost: req.KeyInfo.ProxyHost, + Username: req.KeyInfo.Username, + ClusterName: req.KeyInfo.ClusterName, + AgentKey: true, + Command: req.Command, + } + + var signerOpts crypto.SignerOpts + switch req.Hash { + case hardwarekeyagentv1.Hash_HASH_NONE: + signerOpts = crypto.Hash(0) + case hardwarekeyagentv1.Hash_HASH_SHA256: + signerOpts = crypto.SHA256 + case hardwarekeyagentv1.Hash_HASH_SHA512: + signerOpts = crypto.SHA512 + default: + return nil, trace.BadParameter("unsupported hash %q", req.Hash.String()) + } + + if req.SaltLength > 0 { + signerOpts = &rsa.PSSOptions{ + Hash: signerOpts.HashFunc(), + SaltLength: int(req.SaltLength), + } + } + + signature, err := s.s.Sign(ctx, keyRef, keyInfo, rand.Reader, req.Digest, signerOpts) + if err != nil { + slog.DebugContext(ctx, "hardware key agent signature failed", "error", err) + return nil, trace.Wrap(err) + } + + return &hardwarekeyagentv1.Signature{ + Signature: signature, + }, nil +} + +// Ping the server and get its PID. +func (s *agentService) Ping(ctx context.Context, req *hardwarekeyagentv1.PingRequest) (*hardwarekeyagentv1.PingResponse, error) { + return &hardwarekeyagentv1.PingResponse{ + Pid: uint32(os.Getpid()), + }, nil +} diff --git a/api/utils/keys/hardwarekeyagent/service.go b/api/utils/keys/hardwarekeyagent/service.go new file mode 100644 index 0000000000000..20859733e10fd --- /dev/null +++ b/api/utils/keys/hardwarekeyagent/service.go @@ -0,0 +1,165 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package hardwarekeyagent + +import ( + "context" + "crypto" + "crypto/rsa" + "crypto/x509" + "fmt" + "io" + "log/slog" + "os" + "strings" + + "github.com/gravitational/trace" + "google.golang.org/protobuf/types/known/durationpb" + + hardwarekeyagentv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/hardwarekeyagent/v1" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" +) + +// Service is an agent implementation of [hardwarekey.AgentService]. +type Service struct { + // Used for signature requests to the service. + agentClient hardwarekeyagentv1.HardwareKeyAgentServiceClient + // Used for non signature methods and as a fallback for signatures if the + // agent client fails to handle a sign request. + fallbackService hardwarekey.Service +} + +// NewService creates a new hardware key agent service from the given +// agent client and fallback service. The fallback service is used for +// non-signature methods of [hardwarekey.Service] which are not implemented +// by the agent. Generally this fallback service is only used during login. +func NewService(agentClient hardwarekeyagentv1.HardwareKeyAgentServiceClient, fallbackService hardwarekey.Service) *Service { + return &Service{ + agentClient: agentClient, + fallbackService: fallbackService, + } +} + +// NewPrivateKey creates or retrieves a hardware private key for the given config. +func (s *Service) NewPrivateKey(ctx context.Context, config hardwarekey.PrivateKeyConfig) (*hardwarekey.Signer, error) { + return s.fallbackService.NewPrivateKey(ctx, config) +} + +// Sign performs a cryptographic signature using the specified hardware +// private key and provided signature parameters. +func (s *Service) Sign(ctx context.Context, ref *hardwarekey.PrivateKeyRef, keyInfo hardwarekey.ContextualKeyInfo, rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { + // First try to sign with the agent, then fallback to the direct service if needed. + signature, err := s.agentSign(ctx, ref, keyInfo, rand, digest, opts) + if err != nil { + slog.ErrorContext(ctx, "Failed to perform signature over hardware key agent, falling back to fallback service", "agent_err", err) + signature, err = s.fallbackService.Sign(ctx, ref, keyInfo, rand, digest, opts) + } + + return signature, trace.Wrap(err) +} + +func (s *Service) agentSign(ctx context.Context, ref *hardwarekey.PrivateKeyRef, keyInfo hardwarekey.ContextualKeyInfo, rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { + slotKey, err := hardwarekey.PIVSlotKeyToProto(ref.SlotKey) + if err != nil { + return nil, trace.Wrap(err) + } + + publicKeyDER, err := x509.MarshalPKIXPublicKey(ref.PublicKey) + if err != nil { + return nil, trace.Wrap(err) + } + + var hash hardwarekeyagentv1.Hash + switch opts.HashFunc() { + case 0: + hash = hardwarekeyagentv1.Hash_HASH_NONE + case crypto.SHA256: + hash = hardwarekeyagentv1.Hash_HASH_SHA256 + case crypto.SHA512: + hash = hardwarekeyagentv1.Hash_HASH_SHA512 + default: + return nil, trace.BadParameter("unsupported hash func %q", opts.HashFunc().String()) + } + + var saltLength int + if pssOpts, ok := opts.(*rsa.PSSOptions); ok { + if pssOpts.Hash == 0 { + return nil, trace.BadParameter("hash must be specified for PSS signature") + } + + rsaPub, ok := ref.PublicKey.(*rsa.PublicKey) + if !ok { + return nil, trace.BadParameter("cannot perform PSS signature for non-rsa key") + } + + saltLength = pssOpts.SaltLength + + // If the salt length is [rsa.PSSSaltLengthEqualsHash] or [rsa.PSSSaltLengthAuto], + // pre-calculate the salt length so we can send it over the gRPC message. + switch saltLength { + case rsa.PSSSaltLengthEqualsHash: + saltLength = pssOpts.Hash.Size() + case rsa.PSSSaltLengthAuto: + // We use the same salt length calculation as the crypto/rsa package. + // https://github.com/golang/go/blob/21483099632c11743d01ec6f38577f31de26b0d0/src/crypto/internal/fips140/rsa/pkcs1v22.go#L253 + saltLength = (rsaPub.N.BitLen()-1+7)/8 - 2 - pssOpts.Hash.Size() + } + + if saltLength < 0 { + return nil, rsa.ErrMessageTooLong + } + } + + // Trim leading path (/ or \ on windows) from command for user readability. + command := os.Args[0] + if i := strings.LastIndexAny(command, "/\\"); i != -1 { + command = command[i+1:] + } + commandString := fmt.Sprintf("%v %v", command, strings.Join(os.Args[1:], " ")) + + req := &hardwarekeyagentv1.SignRequest{ + Digest: digest, + Hash: hash, + SaltLength: uint32(saltLength), + KeyRef: &hardwarekeyagentv1.KeyRef{ + SerialNumber: ref.SerialNumber, + SlotKey: slotKey, + PublicKeyDer: publicKeyDER, + }, + KeyInfo: &hardwarekeyagentv1.KeyInfo{ + TouchRequired: ref.Policy.TouchRequired, + PinRequired: ref.Policy.PINRequired, + PinCacheTtl: durationpb.New(ref.PINCacheTTL), + ProxyHost: keyInfo.ProxyHost, + Username: keyInfo.Username, + ClusterName: keyInfo.ClusterName, + }, + Command: commandString, + } + + resp, err := s.agentClient.Sign(ctx, req) + if err != nil { + return nil, trace.Wrap(err) + } + + return resp.Signature, nil +} + +// TODO(Joerger): DELETE IN v19.0.0 +func (s *Service) GetFullKeyRef(serialNumber uint32, slotKey hardwarekey.PIVSlotKey) (*hardwarekey.PrivateKeyRef, error) { + return s.fallbackService.GetFullKeyRef(serialNumber, slotKey) +} diff --git a/api/utils/keys/hardwarekeyagent/service_test.go b/api/utils/keys/hardwarekeyagent/service_test.go new file mode 100644 index 0000000000000..a78895103bbea --- /dev/null +++ b/api/utils/keys/hardwarekeyagent/service_test.go @@ -0,0 +1,213 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package hardwarekeyagent_test + +import ( + "context" + "crypto" + "crypto/rand" + "crypto/rsa" + "net" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + "google.golang.org/grpc/credentials/insecure" + + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" + "github.com/gravitational/teleport/api/utils/keys/hardwarekeyagent" +) + +func TestHardwareKeyAgentService(t *testing.T) { + ctx := context.Background() + + // Prepare the agent server + mockService := hardwarekey.NewMockHardwareKeyService(nil /*prompt*/) + server := hardwarekeyagent.NewServer(ctx, mockService, insecure.NewCredentials()) + t.Cleanup(server.Stop) + + agentDir := t.TempDir() + socketPath := filepath.Join(agentDir, "agent.sock") + l, err := net.Listen("unix", socketPath) + require.NoError(t, err) + + serverErr := make(chan error, 1) + go func() { + serverErr <- server.Serve(l) + }() + + // Prepare the agent client + agentClient, err := hardwarekeyagent.NewClient(ctx, socketPath, insecure.NewCredentials()) + require.NoError(t, err) + + agentService := hardwarekeyagent.NewService(agentClient, hardwarekey.NewMockHardwareKeyService(nil /*prompt*/)) + agentServiceWithFallback := hardwarekeyagent.NewService(agentClient, mockService) + + for _, tc := range []struct { + name string + algorithm hardwarekey.SignatureAlgorithm + opts crypto.SignerOpts + expectErr bool + }{ + { + name: "EC256 Unsupported hash", + algorithm: hardwarekey.SignatureAlgorithmEC256, + opts: crypto.MD5, + expectErr: true, // unsupported hash + }, + { + name: "EC256 No hash", + algorithm: hardwarekey.SignatureAlgorithmEC256, + opts: crypto.Hash(0), + }, + { + name: "EC256 SHA256", + algorithm: hardwarekey.SignatureAlgorithmEC256, + opts: crypto.SHA256, + }, + { + name: "EC256 SHA512", + algorithm: hardwarekey.SignatureAlgorithmEC256, + opts: crypto.SHA512, + }, + { + name: "ED25519 No hash", + algorithm: hardwarekey.SignatureAlgorithmEd25519, + opts: crypto.Hash(0), + }, + { + name: "ED25519 SHA256", + algorithm: hardwarekey.SignatureAlgorithmEd25519, + opts: crypto.SHA256, + expectErr: true, // sha256 not supported + }, + { + name: "ED25519 SHA512", + algorithm: hardwarekey.SignatureAlgorithmEd25519, + opts: crypto.SHA512, + }, + { + name: "RSA2048 No hash", + algorithm: hardwarekey.SignatureAlgorithmRSA2048, + opts: crypto.Hash(0), + }, + { + name: "RSA2048 SHA256", + algorithm: hardwarekey.SignatureAlgorithmRSA2048, + opts: crypto.SHA256, + }, + { + name: "RSA2048 SHA512", + algorithm: hardwarekey.SignatureAlgorithmRSA2048, + opts: crypto.SHA512, + }, + { + name: "RSA2048 No hash PSS signature", + algorithm: hardwarekey.SignatureAlgorithmRSA2048, + opts: &rsa.PSSOptions{ + SaltLength: 10, + Hash: crypto.Hash(0), + }, + expectErr: true, // hash required for pss signature + }, + { + name: "RSA2048 SHA256 PSS signature", + algorithm: hardwarekey.SignatureAlgorithmRSA2048, + opts: &rsa.PSSOptions{ + SaltLength: 10, + Hash: crypto.SHA256, + }, + }, + { + name: "RSA2048 SHA256 PSSSaltLengthAuto", + algorithm: hardwarekey.SignatureAlgorithmRSA2048, + opts: &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + Hash: crypto.SHA256, + }, + }, + { + name: "RSA2048 SHA256 PSSSaltLengthEqualsHash", + algorithm: hardwarekey.SignatureAlgorithmRSA2048, + opts: &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthEqualsHash, + Hash: crypto.SHA256, + }, + }, + { + name: "RSA2048 SHA512 PSS signature", + algorithm: hardwarekey.SignatureAlgorithmRSA2048, + opts: &rsa.PSSOptions{ + SaltLength: 10, + Hash: crypto.SHA512, + }, + }, + { + name: "RSA2048 SHA512 PSSSaltLengthAuto", + algorithm: hardwarekey.SignatureAlgorithmRSA2048, + opts: &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthAuto, + Hash: crypto.SHA512, + }, + }, + { + name: "RSA2048 SHA512 PSSSaltLengthEqualsHash", + algorithm: hardwarekey.SignatureAlgorithmRSA2048, + opts: &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthEqualsHash, + Hash: crypto.SHA512, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + t.Cleanup(mockService.Reset) + + // Create a new key directly in the service. + hwSigner, err := mockService.NewPrivateKey(ctx, hardwarekey.PrivateKeyConfig{ + Algorithm: tc.algorithm, + }) + require.NoError(t, err) + + // Mock a hashed digest. + digest := make([]byte, 100) + if hash := tc.opts.HashFunc(); hash != 0 { + digest = make([]byte, hash.Size()) + } + + // Perform a signature over the agent. + _, err = agentService.Sign(ctx, hwSigner.Ref, hwSigner.KeyInfo, rand.Reader, digest, tc.opts) + if tc.expectErr { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } + + t.Run("fallback", func(t *testing.T) { + mockService.Reset() + + // Create a new key. + hwSigner, err := agentServiceWithFallback.NewPrivateKey(ctx, hardwarekey.PrivateKeyConfig{}) + require.NoError(t, err) + + // If the server stops, the service should fallback to the fallback service. + server.Stop() + err = hwSigner.WarmupHardwareKey(ctx) + require.NoError(t, err) + }) +} diff --git a/api/utils/keys/hardwaresigner.go b/api/utils/keys/hardwaresigner.go deleted file mode 100644 index 744733807470c..0000000000000 --- a/api/utils/keys/hardwaresigner.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright 2022 Gravitational, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package keys - -import ( - "bytes" - "crypto" - - "github.com/gogo/protobuf/jsonpb" - "github.com/gravitational/trace" - - attestation "github.com/gravitational/teleport/api/gen/proto/go/attestation/v1" -) - -// HardwareSigner is a crypto.Signer which can be attested as being backed by a hardware key. -// This enables the ability to enforce hardware key private key policies. -type HardwareSigner interface { - crypto.Signer - - // GetAttestationStatement returns an AttestationStatement for this private key. - GetAttestationStatement() *AttestationStatement - - // GetPrivateKeyPolicy returns the PrivateKeyPolicy supported by this private key. - GetPrivateKeyPolicy() PrivateKeyPolicy -} - -// GetAttestationStatement returns this key's AttestationStatement. If the key is -// not a hardware-backed key, this method returns nil. -func (k *PrivateKey) GetAttestationStatement() *AttestationStatement { - if attestedPriv, ok := k.Signer.(HardwareSigner); ok { - return attestedPriv.GetAttestationStatement() - } - // Just return a nil attestation statement and let this key fail any attestation checks. - return nil -} - -// GetPrivateKeyPolicy returns this key's PrivateKeyPolicy. -func (k *PrivateKey) GetPrivateKeyPolicy() PrivateKeyPolicy { - if attestedPriv, ok := k.Signer.(HardwareSigner); ok { - return attestedPriv.GetPrivateKeyPolicy() - } - return PrivateKeyPolicyNone -} - -// AttestationStatement is an attestation statement for a hardware private key -// that supports json marshaling through the standard json/encoding package. -type AttestationStatement attestation.AttestationStatement - -// ToProto converts this AttestationStatement to its protobuf form. -func (ar *AttestationStatement) ToProto() *attestation.AttestationStatement { - return (*attestation.AttestationStatement)(ar) -} - -// AttestationStatementFromProto converts an AttestationStatement from its protobuf form. -func AttestationStatementFromProto(att *attestation.AttestationStatement) *AttestationStatement { - return (*AttestationStatement)(att) -} - -// MarshalJSON implements custom protobuf json marshaling. -func (ar *AttestationStatement) MarshalJSON() ([]byte, error) { - buf := new(bytes.Buffer) - err := (&jsonpb.Marshaler{}).Marshal(buf, ar.ToProto()) - return buf.Bytes(), trace.Wrap(err) -} - -// UnmarshalJSON implements custom protobuf json unmarshaling. -func (ar *AttestationStatement) UnmarshalJSON(buf []byte) error { - return (&jsonpb.Unmarshaler{AllowUnknownFields: true}).Unmarshal(bytes.NewReader(buf), ar.ToProto()) -} - -// AttestationData is verified attestation data for a public key. -type AttestationData struct { - // PublicKeyDER is the public key in PKIX, ASN.1 DER form. - PublicKeyDER []byte `json:"public_key"` - // PrivateKeyPolicy specifies the private key policy supported by the associated private key. - PrivateKeyPolicy PrivateKeyPolicy `json:"private_key_policy"` - // SerialNumber is the serial number of the Attested hardware key. - SerialNumber uint32 `json:"serial_number"` -} diff --git a/api/utils/keys/hardwaresigner_test.go b/api/utils/keys/hardwaresigner_test.go deleted file mode 100644 index 48bb77f9f2a3d..0000000000000 --- a/api/utils/keys/hardwaresigner_test.go +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright 2022 Gravitational, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package keys_test - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/gravitational/teleport/api/utils/keys" -) - -// TestNonHardwareSigner tests the HardwareSigner interface with non-hardware keys. -// -// HardwareSigners require the piv go tag and should be tested individually in tests -// like `TestGetYubiKeyPrivateKey_Interactive`. -func TestNonHardwareSigner(t *testing.T) { - priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - require.NoError(t, err) - - key, err := keys.NewPrivateKey(priv, nil) - require.NoError(t, err) - - require.Nil(t, key.GetAttestationStatement()) - require.Equal(t, keys.PrivateKeyPolicyNone, key.GetPrivateKeyPolicy()) -} diff --git a/api/utils/keys/piv/pincache.go b/api/utils/keys/piv/pincache.go new file mode 100644 index 0000000000000..1c6b8802eda38 --- /dev/null +++ b/api/utils/keys/piv/pincache.go @@ -0,0 +1,119 @@ +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package piv + +import ( + "context" + "sync" + "time" + + "github.com/gravitational/trace" + "github.com/jonboulle/clockwork" + + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" +) + +// pinCache is a PIN cache that supports consumers with varying required TTLs. +type pinCache struct { + clock clockwork.Clock + + mu sync.Mutex + // pin is the cached PIN. + pin string + // pinSetAt is the time when the cached PIN was set. Used to determine whether + // the PIN should be considered expired for a specific caller's provided TTL. + pinSetAt time.Time + // pinExpiry is the expiration time of the cached PIN. + pinExpiry time.Time +} + +// newPINCache returns a new PINCache. +func newPINCache() *pinCache { //nolint:unused // used in yubikey.go with piv build constraint + return &pinCache{ + clock: clockwork.NewRealClock(), + } +} + +// PromptOrGetPIN retrieves the cached PIN if set. Otherwise it prompts for the PIN and caches it. +func (p *pinCache) PromptOrGetPIN(ctx context.Context, prompt hardwarekey.Prompt, requirement hardwarekey.PINPromptRequirement, keyInfo hardwarekey.ContextualKeyInfo, pinCacheTTL time.Duration) (string, error) { + // If the provided ttl is 0, it doesn't support caching, so we just prompt. + if pinCacheTTL == 0 { + return prompt.AskPIN(ctx, requirement, keyInfo) + } + + p.mu.Lock() + defer p.mu.Unlock() + + if pin := p.getPIN(pinCacheTTL); pin != "" { + return pin, nil + } + + // Add a timeout to prevent an unanswered PIN prompt from holding the lock. + const pinPromptTimeout = time.Minute + ctx, cancel := context.WithTimeout(ctx, pinPromptTimeout) + defer cancel() + + pin, err := prompt.AskPIN(ctx, requirement, keyInfo) + if err != nil { + return "", trace.Wrap(err) + } + + p.setPIN(pin, pinCacheTTL) + return pin, nil +} + +// getPIN retrieves the cached PIN. If the PIN was cached before by an amount of +// time equal to the provided TTL, the PIN will not be returned. +// Must be called under [p.mu] lock. +func (p *pinCache) getPIN(ttl time.Duration) string { + if p.pin == "" { + return "" + } + + // Check if the PIN cache is expired. If it is, wipe it. + if p.clock.Now().After(p.pinExpiry) { + p.pin = "" + p.pinExpiry = time.Time{} + p.pinSetAt = time.Time{} + return "" + } + + // The PIN is cached, but does not satisfy the provided TTL of the request. + // e.g. it has been alive for 8 minutes, but the provided TTL is 5 minutes. + // For the purposes of this request, the pin should be considered expired. + if p.clock.Since(p.pinSetAt) >= ttl { + return "" + } + + return p.pin +} + +// setPIN sets the given PIN in the cache with the given TTL. If the PIN +// is already cached, the existing expiration is only updated if the given +// TTL would exceed that expiration. +// Must be called under [p.mu] lock. +func (p *pinCache) setPIN(pin string, ttl time.Duration) { + now := p.clock.Now() + expiry := now.Add(ttl) + + // Only set the expiration if it exceeds the current expiration + // or the cached PIN is being changed. + if expiry.After(p.pinExpiry) || p.pin != pin { + p.pinExpiry = expiry + } + + p.pin = pin + p.pinSetAt = now +} diff --git a/api/utils/keys/piv/pincache_test.go b/api/utils/keys/piv/pincache_test.go new file mode 100644 index 0000000000000..fadfc3ad6a5f9 --- /dev/null +++ b/api/utils/keys/piv/pincache_test.go @@ -0,0 +1,58 @@ +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package piv + +import ( + "testing" + "time" + + "github.com/jonboulle/clockwork" + "github.com/stretchr/testify/require" +) + +func TestPINCache(t *testing.T) { + clock := clockwork.NewFakeClock() + pinCache := pinCache{clock: clock} + + testPIN := "123467" + + smallTTL := time.Second + mediumTTL := time.Minute + largeTTL := time.Hour + + // Set the PIN with the medium TTL. + pinCache.setPIN(testPIN, mediumTTL) + require.Equal(t, testPIN, pinCache.getPIN(smallTTL)) + require.Equal(t, testPIN, pinCache.getPIN(mediumTTL)) + require.Equal(t, testPIN, pinCache.getPIN(largeTTL)) + + // Advancing by the small TTL should only expire the pin for the small TTL. + clock.Advance(smallTTL) + require.Zero(t, pinCache.getPIN(smallTTL)) + require.Equal(t, testPIN, pinCache.getPIN(mediumTTL)) + require.Equal(t, testPIN, pinCache.getPIN(largeTTL)) + + // Setting the PIN with the small TTL should reset the PIN's set-at time. + // The expiration time should remain tied to the medium TTL. + pinCache.setPIN(testPIN, smallTTL) + require.Equal(t, testPIN, pinCache.getPIN(smallTTL)) + require.Equal(t, testPIN, pinCache.getPIN(mediumTTL)) + + // Advancing by the medium TTL, used to set the initial cache, should expire the PIN cache. + clock.Advance(mediumTTL) + require.Zero(t, pinCache.getPIN(smallTTL)) + require.Zero(t, pinCache.getPIN(mediumTTL)) + require.Zero(t, pinCache.getPIN(largeTTL)) +} diff --git a/api/utils/keys/piv/service.go b/api/utils/keys/piv/service.go new file mode 100644 index 0000000000000..b0c5daa0544bd --- /dev/null +++ b/api/utils/keys/piv/service.go @@ -0,0 +1,284 @@ +//go:build piv && !pivtest + +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package piv provides a PIV implementation of [hardwarekey.Service]. +package piv + +import ( + "context" + "crypto" + "errors" + "fmt" + "io" + "sync" + + "github.com/go-piv/piv-go/piv" + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" +) + +// yubiKeyService is a global YubiKeyService used to share yubikey connections +// and prompt mutex logic across the process in cases where [NewYubiKeyService] +// is called multiple times. +// +// TODO(Joerger): Ensure all clients initialize [NewYubiKeyService] only once so we can +// remove this global variable. +var yubiKeyService *YubiKeyService +var yubiKeyServiceMu sync.Mutex + +// YubiKeyService is a YubiKey PIV implementation of [hardwarekey.Service]. +type YubiKeyService struct { + prompt hardwarekey.Prompt + // TODO(Joerger): Remove prompt mutex once there is no longer a shared global service + // that needs its protection. + promptMu sync.Mutex + + // signMu prevents prompting for PIN/touch repeatedly for concurrent signatures. + // TODO(Joerger): Rather than preventing concurrent signatures, we can make the + // PIN and touch prompts durable to concurrent signatures. + signMu sync.Mutex + + // yubiKeys is a shared, thread-safe [YubiKey] cache by serial number. It allows for + // separate goroutines to share a YubiKey connection to work around the single PC/SC + // transaction (connection) per-yubikey limit. + yubiKeys map[uint32]*YubiKey + yubiKeysMu sync.Mutex +} + +// Returns a new [YubiKeyService]. If [customPrompt] is nil, the default CLI prompt will be used. +// +// Only a single service should be created for each process to ensure the cached connections +// are shared and multiple services don't compete for PIV resources. +func NewYubiKeyService(customPrompt hardwarekey.Prompt) *YubiKeyService { + yubiKeyServiceMu.Lock() + defer yubiKeyServiceMu.Unlock() + + if yubiKeyService != nil { + // If a prompt is provided, prioritize it over the existing prompt value. + if customPrompt != nil { + yubiKeyService.setPrompt(customPrompt) + } + return yubiKeyService + } + + if customPrompt == nil { + customPrompt = hardwarekey.NewStdCLIPrompt() + } + + yubiKeyService = &YubiKeyService{ + prompt: customPrompt, + yubiKeys: map[uint32]*YubiKey{}, + } + return yubiKeyService +} + +// NewPrivateKey creates a hardware private key that satisfies the provided [config], +// if one does not already exist, and returns a corresponding [hardwarekey.Signer]. +// +// If a customSlot is not provided in [config], the service uses the default slot for the given policy: +// - !touch & !pin -> 9a +// - !touch & pin -> 9c +// - touch & pin -> 9d +// - touch & !pin -> 9e +func (s *YubiKeyService) NewPrivateKey(ctx context.Context, config hardwarekey.PrivateKeyConfig) (*hardwarekey.Signer, error) { + // Use the first yubiKey we find. + y, err := s.getYubiKey(0) + if err != nil { + return nil, trace.Wrap(err) + } + + // Get the requested or default PIV slot. + var slotKey hardwarekey.PIVSlotKey + if config.CustomSlot != "" { + slotKey, err = config.CustomSlot.Parse() + } else { + slotKey, err = hardwarekey.GetDefaultSlotKey(config.Policy) + } + if err != nil { + return nil, trace.Wrap(err) + } + + pivSlot, err := parsePIVSlot(slotKey) + if err != nil { + return nil, trace.Wrap(err) + } + + // If PIN is required, check that PIN and PUK are not the defaults. + if config.Policy.PINRequired { + if err := y.checkOrSetPIN(ctx, s.getPrompt(), config.ContextualKeyInfo, config.PINCacheTTL); err != nil { + return nil, trace.Wrap(err) + } + } + + generatePrivateKey := func() (*hardwarekey.Signer, error) { + ref, err := y.generatePrivateKey(pivSlot, config.Policy, config.Algorithm, config.PINCacheTTL) + if err != nil { + return nil, trace.Wrap(err) + } + return hardwarekey.NewSigner(s, ref, config.ContextualKeyInfo), nil + } + + // If a custom slot was not specified, check for a key in the + // default slot for the given policy and generate a new one if needed. + if config.CustomSlot == "" { + switch cert, err := y.getCertificate(pivSlot); { + case errors.Is(err, piv.ErrNotFound): + return generatePrivateKey() + + case err != nil: + return nil, trace.Wrap(err) + + // Unknown cert found, this slot could be in use by a non-teleport client. + // Prompt the user before we overwrite the slot. + case !isTeleportMetadataCertificate(cert): + if err := s.promptOverwriteSlot(ctx, nonTeleportCertificateMessage(pivSlot, cert), config.ContextualKeyInfo); err != nil { + return nil, trace.Wrap(err) + } + return generatePrivateKey() + } + } + + // Check for an existing key in the slot that satisfies the required + // prompt policy, or generate a new one if needed. + keyRef, err := y.getKeyRef(pivSlot, config.PINCacheTTL) + switch { + case errors.Is(err, piv.ErrNotFound): + return generatePrivateKey() + + case err != nil: + return nil, trace.Wrap(err) + + case config.Policy.TouchRequired && !keyRef.Policy.TouchRequired: + msg := fmt.Sprintf("private key in YubiKey PIV slot %q does not require touch.", pivSlot) + if err := s.promptOverwriteSlot(ctx, msg, config.ContextualKeyInfo); err != nil { + return nil, trace.Wrap(err) + } + return generatePrivateKey() + + case config.Policy.PINRequired && !keyRef.Policy.PINRequired: + msg := fmt.Sprintf("private key in YubiKey PIV slot %q does not require PIN", pivSlot) + if err := s.promptOverwriteSlot(ctx, msg, config.ContextualKeyInfo); err != nil { + return nil, trace.Wrap(err) + } + return generatePrivateKey() + } + + return hardwarekey.NewSigner(s, keyRef, config.ContextualKeyInfo), nil +} + +// Sign performs a cryptographic signature using the specified hardware +// private key and provided signature parameters. +func (s *YubiKeyService) Sign(ctx context.Context, ref *hardwarekey.PrivateKeyRef, keyInfo hardwarekey.ContextualKeyInfo, rand io.Reader, digest []byte, opts crypto.SignerOpts) (signature []byte, err error) { + y, err := s.getYubiKey(ref.SerialNumber) + if err != nil { + return nil, trace.Wrap(err) + } + + s.signMu.Lock() + defer s.signMu.Unlock() + + return y.sign(ctx, ref, keyInfo, s.getPrompt(), rand, digest, opts) +} + +// TODO(Joerger): Re-attesting the key every time we decode a hardware key signer is very resource +// intensive. This cache is a stand-in solution for the problem, which was previously handled within +// the YubiKeyPrivateKey cache that is being phased out with this change. In a follow up, the attested +// information will be saved to the key file at login time so each client will not need to re-attest +// the hardware key at all. +var ( + keyRefs = map[baseKeyRef]*hardwarekey.PrivateKeyRef{} + keyRefsMux sync.Mutex +) + +type baseKeyRef struct { + serialNumber uint32 + slotKey hardwarekey.PIVSlotKey +} + +// GetFullKeyRef gets the full [PrivateKeyRef] for an existing hardware private +// key in the given slot of the hardware key with the given serial number. +// +// Used for backwards compatibility with old logins. +// TODO(Joerger): DELETE IN v19.0.0 +func (s *YubiKeyService) GetFullKeyRef(serialNumber uint32, slotKey hardwarekey.PIVSlotKey) (*hardwarekey.PrivateKeyRef, error) { + keyRefsMux.Lock() + defer keyRefsMux.Unlock() + + baseRef := baseKeyRef{serialNumber: serialNumber, slotKey: slotKey} + if ref, ok := keyRefs[baseRef]; ok && ref != nil { + return ref, nil + } + + y, err := s.getYubiKey(serialNumber) + if err != nil { + return nil, trace.Wrap(err) + } + + pivSlot, err := parsePIVSlot(slotKey) + if err != nil { + return nil, trace.Wrap(err) + } + + ref, err := y.getKeyRef(pivSlot, 0 /*PIN is not cached for out-of-date client keys*/) + if err != nil { + return nil, trace.Wrap(err) + } + + keyRefs[baseRef] = ref + return ref, nil +} + +// Get the given YubiKey with the serial number. If the provided serialNumber is "0", +// return the first YubiKey found in the smart card list. +func (s *YubiKeyService) getYubiKey(serialNumber uint32) (*YubiKey, error) { + s.yubiKeysMu.Lock() + defer s.yubiKeysMu.Unlock() + + if y, ok := s.yubiKeys[serialNumber]; ok { + return y, nil + } + + y, err := FindYubiKey(serialNumber) + if err != nil { + return nil, trace.Wrap(err) + } + + s.yubiKeys[y.serialNumber] = y + return y, nil +} + +func (s *YubiKeyService) promptOverwriteSlot(ctx context.Context, msg string, keyInfo hardwarekey.ContextualKeyInfo) error { + promptQuestion := fmt.Sprintf("%v\nWould you like to overwrite this slot's private key and certificate?", msg) + if confirmed, confirmErr := s.getPrompt().ConfirmSlotOverwrite(ctx, promptQuestion, keyInfo); confirmErr != nil { + return trace.Wrap(confirmErr) + } else if !confirmed { + return trace.Wrap(trace.CompareFailed(msg), "user declined to overwrite slot") + } + return nil +} + +func (s *YubiKeyService) setPrompt(prompt hardwarekey.Prompt) { + s.promptMu.Lock() + defer s.promptMu.Unlock() + s.prompt = prompt +} + +func (s *YubiKeyService) getPrompt() hardwarekey.Prompt { + s.promptMu.Lock() + defer s.promptMu.Unlock() + return s.prompt +} diff --git a/api/utils/keys/piv/service_fake.go b/api/utils/keys/piv/service_fake.go new file mode 100644 index 0000000000000..58f4fac8447df --- /dev/null +++ b/api/utils/keys/piv/service_fake.go @@ -0,0 +1,34 @@ +//go:build pivtest + +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package piv + +import ( + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" +) + +// TODO(Joerger): Rather than using a global service, clients should be updated to +// create a single YubiKeyService and ensure it is reused across the program +// execution. At this point, it may make more sense to directly inject the mocked +// hardware key service into the test instead of using the pivtest build tag to do it. +var mockedHardwareKeyService = hardwarekey.NewMockHardwareKeyService(nil /*prompt*/) + +// Returns a globally shared [hardwarekey.MockHardwareKeyService]. Test callers should +// prefer [hardwarekey.NewMockHardwareKeyService] when possible. +func NewYubiKeyService(prompt hardwarekey.Prompt) *hardwarekey.MockHardwareKeyService { + mockedHardwareKeyService.SetPrompt(prompt) + return mockedHardwareKeyService +} diff --git a/api/utils/keys/piv/service_test.go b/api/utils/keys/piv/service_test.go new file mode 100644 index 0000000000000..f2993f2b2163f --- /dev/null +++ b/api/utils/keys/piv/service_test.go @@ -0,0 +1,224 @@ +//go:build piv + +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package piv_test + +import ( + "bytes" + "context" + "crypto/x509/pkix" + "fmt" + "os" + "testing" + + pivgo "github.com/go-piv/piv-go/piv" + "github.com/gravitational/trace" + "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" + "github.com/gravitational/teleport/api/utils/keys/piv" + "github.com/gravitational/teleport/api/utils/prompt" +) + +// TestNewHardwarePrivateKey_Interactive tests generation and retrieval of YubiKey private keys. +func TestNewHardwarePrivateKey_Interactive(t *testing.T) { + // This test will overwrite any PIV data on the yubiKey. + if os.Getenv("TELEPORT_TEST_YUBIKEY_PIV") == "" { + t.Skipf("Skipping TestGenerateYubiKeyPrivateKey because TELEPORT_TEST_YUBIKEY_PIV is not set") + } + + if !testing.Verbose() { + t.Fatal("This test is interactive and must be called with the -v verbose flag to see touch prompts.") + } + fmt.Println("This test is interactive, tap your YubiKey when prompted.") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + promptReader := prompt.NewFakeReader() + prompt := hardwarekey.NewCLIPrompt(os.Stderr, promptReader) + s := piv.NewYubiKeyService(prompt) + + y, err := piv.FindYubiKey(0) + require.NoError(t, err) + + t.Cleanup(func() { resetYubikey(t, y) }) + + // Test creating a new key, retrieving it, and using it for signatures. + testNewPrivateKey := func(t *testing.T, config hardwarekey.PrivateKeyConfig) { + t.Helper() + + // NewHardwarePrivateKey should generate a new hardware private key. + priv, err := keys.NewHardwarePrivateKey(ctx, s, config) + require.NoError(t, err) + hwSigner, ok := priv.Signer.(*hardwarekey.Signer) + require.True(t, ok, "expected hardwarekey.Signer but got %T", priv.Signer) + + // Check that config was applied correctly. + require.Equal(t, config.Policy, hwSigner.Ref.Policy) + if config.CustomSlot != "" { + expectSlot, err := config.CustomSlot.Parse() + require.NoError(t, err) + require.Equal(t, expectSlot, hwSigner.Ref.SlotKey) + } + + // test Hardware Key methods + require.Equal(t, config.Policy, priv.GetPrivateKeyPolicy().GetPromptPolicy()) + require.NotNil(t, priv.GetAttestationStatement()) + require.True(t, priv.IsHardware()) + + // Test bogus sign (warmup). + require.NoError(t, priv.WarmupHardwareKey(ctx)) + + // Another call to NewHardwarePrivateKey should retrieve the previously generated key. + retrievePriv, err := keys.NewHardwarePrivateKey(ctx, s, config) + require.NoError(t, err) + require.Equal(t, priv.Public(), retrievePriv.Public()) + + // parsing the key's private key PEM should produce the same key as well. + retrievePriv, err = keys.ParsePrivateKey(priv.PrivateKeyPEM(), keys.WithHardwareKeyService(s)) + require.NoError(t, err) + require.Equal(t, priv.Public(), retrievePriv.Public()) + } + + t.Run("PromptPolicies", func(t *testing.T) { + // Warmup the hardware key to prompt touch at the start of the test, + // rather than having this interaction later. + priv, err := keys.NewHardwarePrivateKey(ctx, s, hardwarekey.PrivateKeyConfig{ + Policy: hardwarekey.PromptPolicy{TouchRequired: true}, + }) + require.NoError(t, err) + require.NoError(t, priv.WarmupHardwareKey(ctx)) + + resetYubikey(t, y) + + // Set pin. + const testPIN = "123123" + require.NoError(t, y.SetPIN(pivgo.DefaultPIN, testPIN)) + + for _, policy := range []hardwarekey.PromptPolicy{ + hardwarekey.PromptPolicyNone, + hardwarekey.PromptPolicyTouch, + hardwarekey.PromptPolicyPIN, + hardwarekey.PromptPolicyTouchAndPIN, + } { + t.Run(fmt.Sprintf("policy:%+v", policy), func(t *testing.T) { + // Handle pin prompts (1 for generating the key, 1 for signing). + if policy.PINRequired { + promptReader.AddString(testPIN).AddString(testPIN) + } + + testNewPrivateKey(t, hardwarekey.PrivateKeyConfig{ + Policy: policy, + }) + }) + } + }) + + t.Run("CustomSlot", func(t *testing.T) { + resetYubikey(t, y) + testNewPrivateKey(t, hardwarekey.PrivateKeyConfig{ + CustomSlot: "9c", + }) + }) + + t.Run("Algorithms", func(t *testing.T) { + for algorithm, config := range map[string]hardwarekey.PrivateKeyConfig{ + "EC256": { + CustomSlot: "9a", + Algorithm: hardwarekey.SignatureAlgorithmEC256, + }, + "RSA2048": { + CustomSlot: "9c", + Algorithm: hardwarekey.SignatureAlgorithmRSA2048, + }, + } { + t.Run(fmt.Sprintf("algorithm:%v", algorithm), func(t *testing.T) { + resetYubikey(t, y) + testNewPrivateKey(t, config) + }) + } + }) +} + +func TestOverwritePrompt(t *testing.T) { + // This test will overwrite any PIV data on the yubiKey. + if os.Getenv("TELEPORT_TEST_YUBIKEY_PIV") == "" { + t.Skipf("Skipping TestGenerateYubiKeyPrivateKey because TELEPORT_TEST_YUBIKEY_PIV is not set") + } + + ctx := context.Background() + + promptWriter := bytes.NewBuffer([]byte{}) + promptReader := prompt.NewFakeReader() + prompt := hardwarekey.NewCLIPrompt(promptWriter, promptReader) + s := piv.NewYubiKeyService(prompt) + + y, err := piv.FindYubiKey(0) + require.NoError(t, err) + + resetYubikey(t, y) + t.Cleanup(func() { resetYubikey(t, y) }) + + // Get the default slot used for hardware_key_touch. + touchSlot := pivgo.SlotSignature + + testOverwritePrompt := func(t *testing.T) { + // Fail to overwrite slot when user denies + promptReader.AddString("n") + _, err := keys.NewHardwarePrivateKey(ctx, s, hardwarekey.PrivateKeyConfig{ + Policy: hardwarekey.PromptPolicy{TouchRequired: true}, + }) + require.True(t, trace.IsCompareFailed(err), "Expected compare failed error but got %v", err) + + // Successfully overwrite slot when user accepts + promptReader.AddString("y") + _, err = keys.NewHardwarePrivateKey(ctx, s, hardwarekey.PrivateKeyConfig{ + Policy: hardwarekey.PromptPolicy{TouchRequired: true}, + }) + require.NoError(t, err) + } + + t.Run("invalid metadata cert", func(t *testing.T) { + resetYubikey(t, y) + + // Set a non-teleport certificate in the slot. + err = y.SetMetadataCertificate(touchSlot, pkix.Name{Organization: []string{"not-teleport"}}) + require.NoError(t, err) + + testOverwritePrompt(t) + }) + + t.Run("invalid key policies", func(t *testing.T) { + resetYubikey(t, y) + + // Generate a key that does not require touch in the slot that Teleport expects to require touch. + _, err := keys.NewHardwarePrivateKey(ctx, s, hardwarekey.PrivateKeyConfig{ + CustomSlot: hardwarekey.PIVSlotKeyString(touchSlot.String()), + Policy: hardwarekey.PromptPolicy{TouchRequired: false}, + }) + require.NoError(t, err) + + testOverwritePrompt(t) + }) +} + +// resetYubikey connects to the first yubiKey and resets it to defaults. +func resetYubikey(t *testing.T, y *piv.YubiKey) { + t.Helper() + require.NoError(t, y.Reset()) +} diff --git a/api/utils/keys/piv/service_unavailable.go b/api/utils/keys/piv/service_unavailable.go new file mode 100644 index 0000000000000..0b1e59836876f --- /dev/null +++ b/api/utils/keys/piv/service_unavailable.go @@ -0,0 +1,52 @@ +//go:build !piv && !pivtest + +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package piv + +import ( + "context" + "crypto" + "errors" + "io" + + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" +) + +var errPIVUnavailable = errors.New("PIV is unavailable in current build") + +func NewYubiKeyService(_ hardwarekey.Prompt) *unavailableYubiKeyPIVService { + return &unavailableYubiKeyPIVService{} +} + +type unavailableYubiKeyPIVService struct{} + +func (s *unavailableYubiKeyPIVService) NewPrivateKey(_ context.Context, _ hardwarekey.PrivateKeyConfig) (*hardwarekey.Signer, error) { + return nil, trace.Wrap(errPIVUnavailable) +} + +// Sign performs a cryptographic signature using the specified hardware +// private key and provided signature parameters. +func (s *unavailableYubiKeyPIVService) Sign(_ context.Context, _ *hardwarekey.PrivateKeyRef, _ hardwarekey.ContextualKeyInfo, _ io.Reader, _ []byte, _ crypto.SignerOpts) ([]byte, error) { + return nil, trace.Wrap(errPIVUnavailable) +} + +func (s *unavailableYubiKeyPIVService) GetFullKeyRef(serialNumber uint32, slotKey hardwarekey.PIVSlotKey) (*hardwarekey.PrivateKeyRef, error) { + return nil, trace.Wrap(errPIVUnavailable) +} + +func (s *unavailableYubiKeyPIVService) SetPrompt(_ hardwarekey.Prompt) {} diff --git a/api/utils/keys/yubikey.go b/api/utils/keys/piv/yubikey.go similarity index 52% rename from api/utils/keys/yubikey.go rename to api/utils/keys/piv/yubikey.go index 05e783ce578ee..d00f27f569f16 100644 --- a/api/utils/keys/yubikey.go +++ b/api/utils/keys/piv/yubikey.go @@ -1,19 +1,20 @@ //go:build piv && !pivtest -/* -Copyright 2022 Gravitational, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package keys +// Copyright 2022 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package piv import ( "context" @@ -25,14 +26,11 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/hex" - "encoding/json" - "encoding/pem" "errors" "fmt" "io" "math/big" "os" - "strconv" "strings" "sync" "time" @@ -41,268 +39,96 @@ import ( "github.com/gravitational/trace" "github.com/gravitational/teleport/api" - attestation "github.com/gravitational/teleport/api/gen/proto/go/attestation/v1" + attestationv1 "github.com/gravitational/teleport/api/gen/proto/go/attestation/v1" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" "github.com/gravitational/teleport/api/utils/retryutils" ) -const ( - // PIVCardTypeYubiKey is the PIV card type assigned to yubiKeys. - PIVCardTypeYubiKey = "yubikey" -) - -// Cache keys to prevent reconnecting to PIV module to discover a known key. -// -// Additionally, this allows the program to cache the key's PIN (if applicable) -// after the user is prompted the first time, preventing redundant prompts when -// the key is retrieved multiple times. -// -// Note: in most cases the connection caches the PIN itself, and connections can be -// reclaimed before they are fully closed (within a few seconds). However, in uncommon -// setups, this PIN caching does not actually work as expected, so we handle it instead. -// See https://github.com/go-piv/piv-go/issues/47 -var ( - cachedKeys = map[piv.Slot]*PrivateKey{} - cachedKeysMu sync.Mutex -) - -// getOrGenerateYubiKeyPrivateKey connects to a connected yubiKey and gets a private key -// matching the given touch requirement. This private key will either be newly generated -// or previously generated by a Teleport client and reused. -func getOrGenerateYubiKeyPrivateKey(ctx context.Context, requiredKeyPolicy PrivateKeyPolicy, slot PIVSlot, prompt HardwareKeyPrompt) (*PrivateKey, error) { - if prompt == nil { - prompt = &cliPrompt{} - } - cachedKeysMu.Lock() - defer cachedKeysMu.Unlock() - - // Get the default PIV slot or the piv slot requested. - pivSlot, err := GetDefaultKeySlot(requiredKeyPolicy) - if err != nil { - return nil, trace.Wrap(err) - } - if slot != "" { - pivSlot, err = slot.parse() - if err != nil { - return nil, trace.Wrap(err) - } - } - - // If the program has already retrieved and cached this key, return it. - if key, ok := cachedKeys[pivSlot]; ok && key.GetPrivateKeyPolicy() == requiredKeyPolicy { - return key, nil - } +// YubiKey is a specific YubiKey PIV card. +// The [sharedPIVConnection] field makes its methods thread-safe. +type YubiKey struct { + // conn is a shared YubiKey PIV connection. + // + // For each YubiKey, PIV connections claim an exclusive lock on the key's + // PIV module until closed. In order to improve connection sharing for this + // program without locking out other programs during extended program executions + // (like "tsh proxy ssh"), this connections is opportunistically formed and + // released after being unused for a few seconds. + conn *sharedPIVConnection + // serialNumber is the YubiKey's 8 digit serial number. + serialNumber uint32 + // version is the YubiKey's version. + version piv.Version + // pinCache can be used to skip PIN prompts for keys that have PIN caching enabled. + pinCache *pinCache +} - // Use the first yubiKey we find. - y, err := FindYubiKey(0, prompt) +// FindYubiKey finds a YubiKey PIV card by serial number. If the provided +// [serialNumber] is "0", the first YubiKey found will be returned. +func FindYubiKey(serialNumber uint32) (*YubiKey, error) { + yubiKeyCards, err := findYubiKeyCards() if err != nil { return nil, trace.Wrap(err) } - // If PIN is required, check that PIN and PUK are not the defaults. - if requiredKeyPolicy.isHardwareKeyPINVerified() { - if err := y.checkOrSetPIN(ctx); err != nil { - return nil, trace.Wrap(err) - } - } - - promptOverwriteSlot := func(msg string) error { - promptQuestion := fmt.Sprintf("%v\nWould you like to overwrite this slot's private key and certificate?", msg) - if confirmed, confirmErr := prompt.ConfirmSlotOverwrite(ctx, promptQuestion); confirmErr != nil { - return trace.Wrap(confirmErr) - } else if !confirmed { - return trace.Wrap(trace.CompareFailed(msg), "user declined to overwrite slot") + if len(yubiKeyCards) == 0 { + if serialNumber != 0 { + return nil, trace.ConnectionProblem(nil, "no YubiKey device connected with serial number %d", serialNumber) } - return nil + return nil, trace.ConnectionProblem(nil, "no YubiKey device connected") } - // If a custom slot was not specified, check for a key in the - // default slot for the given policy and generate a new one if needed. - if slot == "" { - pivSlot, err = GetDefaultKeySlot(requiredKeyPolicy) + for _, card := range yubiKeyCards { + y, err := newYubiKey(card) if err != nil { return nil, trace.Wrap(err) } - // Check the client certificate in the slot. - switch cert, err := y.getCertificate(pivSlot); { - case err == nil && (len(cert.Subject.Organization) == 0 || cert.Subject.Organization[0] != certOrgName): - // Unknown cert found, prompt the user before we overwrite the slot. - if err := promptOverwriteSlot(nonTeleportCertificateMessage(pivSlot, cert)); err != nil { - return nil, trace.Wrap(err) - } - - // user confirmed, generate a new key. - fallthrough - case errors.Is(err, piv.ErrNotFound): - // no cert found, generate a new key. - priv, err := y.generatePrivateKeyAndCert(pivSlot, requiredKeyPolicy) - return priv, trace.Wrap(err) - case err != nil: - return nil, trace.Wrap(err) - } - } - - // Get the key in the slot, or generate a new one if needed. - priv, err := y.getPrivateKey(pivSlot) - switch { - case err == nil && !requiredKeyPolicy.IsSatisfiedBy(priv.GetPrivateKeyPolicy()): - // Key does not meet the required key policy, prompt the user before we overwrite the slot. - msg := fmt.Sprintf("private key in YubiKey PIV slot %q does not meet private key policy %q.", pivSlot, requiredKeyPolicy) - if err := promptOverwriteSlot(msg); err != nil { - return nil, trace.Wrap(err) + if serialNumber == 0 || y.serialNumber == serialNumber { + return y, nil } - - // user confirmed, generate a new key. - fallthrough - case trace.IsNotFound(err): - // no key found, generate a new key. - priv, err = y.generatePrivateKeyAndCert(pivSlot, requiredKeyPolicy) - return priv, trace.Wrap(err) - case err != nil: - return nil, trace.Wrap(err) - } - - return priv, nil -} - -func GetDefaultKeySlot(policy PrivateKeyPolicy) (piv.Slot, error) { - switch policy { - case PrivateKeyPolicyHardwareKey: - // private_key_policy: hardware_key -> 9a - return piv.SlotAuthentication, nil - case PrivateKeyPolicyHardwareKeyTouch: - // private_key_policy: hardware_key_touch -> 9c - return piv.SlotSignature, nil - case PrivateKeyPolicyHardwareKeyTouchAndPIN: - // private_key_policy: hardware_key_touch_and_pin -> 9d - return piv.SlotKeyManagement, nil - case PrivateKeyPolicyHardwareKeyPIN: - // private_key_policy: hardware_key_pin -> 9e - return piv.SlotCardAuthentication, nil - default: - return piv.Slot{}, trace.BadParameter("unexpected private key policy %v", policy) - } -} - -func getKeyPolicies(policy PrivateKeyPolicy) (piv.TouchPolicy, piv.PINPolicy, error) { - switch policy { - case PrivateKeyPolicyHardwareKey: - return piv.TouchPolicyNever, piv.PINPolicyNever, nil - case PrivateKeyPolicyHardwareKeyTouch: - return piv.TouchPolicyCached, piv.PINPolicyNever, nil - case PrivateKeyPolicyHardwareKeyPIN: - return piv.TouchPolicyNever, piv.PINPolicyOnce, nil - case PrivateKeyPolicyHardwareKeyTouchAndPIN: - return piv.TouchPolicyCached, piv.PINPolicyOnce, nil - default: - return piv.TouchPolicyNever, piv.PINPolicyNever, trace.BadParameter("unexpected private key policy %v", policy) } -} - -func nonTeleportCertificateMessage(slot piv.Slot, cert *x509.Certificate) string { - // Gather a small list of user-readable x509 certificate fields to display to the user. - sum := sha256.Sum256(cert.Raw) - fingerPrint := hex.EncodeToString(sum[:]) - return fmt.Sprintf(`Certificate in YubiKey PIV slot %q is not a Teleport client cert: -Slot %s: - Algorithm: %v - Subject DN: %v - Issuer DN: %v - Serial: %v - Fingerprint: %v - Not before: %v - Not after: %v -`, - slot, slot, - cert.SignatureAlgorithm, - cert.Subject, - cert.Issuer, - cert.SerialNumber, - fingerPrint, - cert.NotBefore, - cert.NotAfter, - ) -} - -// YubiKeyPrivateKey is a YubiKey PIV private key. Cryptographical operations open -// a new temporary connection to the PIV card to perform the operation. -type YubiKeyPrivateKey struct { - // YubiKey is a specific YubiKey PIV module. - *YubiKey - - pivSlot piv.Slot - signMux sync.Mutex - - slotCert *x509.Certificate - attestationCert *x509.Certificate - attestation *piv.Attestation -} -// yubiKeyPrivateKeyData is marshalable data used to retrieve a specific yubiKey PIV private key. -type yubiKeyPrivateKeyData struct { - SerialNumber uint32 `json:"serial_number"` - SlotKey uint32 `json:"slot_key"` + return nil, trace.ConnectionProblem(nil, "no YubiKey device connected with serial number %d", serialNumber) } -func parseYubiKeyPrivateKeyData(keyDataBytes []byte, prompt HardwareKeyPrompt) (*PrivateKey, error) { - if prompt == nil { - prompt = &cliPrompt{} - } - cachedKeysMu.Lock() - defer cachedKeysMu.Unlock() - - var keyData yubiKeyPrivateKeyData - if err := json.Unmarshal(keyDataBytes, &keyData); err != nil { - return nil, trace.Wrap(err) - } +// pivCardTypeYubiKey is the PIV card type assigned to yubiKeys. +const pivCardTypeYubiKey = "yubikey" - pivSlot, err := parsePIVSlot(keyData.SlotKey) +// findYubiKeyCards returns a list of connected yubiKey PIV card names. +func findYubiKeyCards() ([]string, error) { + cards, err := piv.Cards() if err != nil { return nil, trace.Wrap(err) } - // If the program has already retrieved and cached this key, return it. - if key, ok := cachedKeys[pivSlot]; ok { - return key, nil + var yubiKeyCards []string + for _, card := range cards { + if strings.Contains(strings.ToLower(card), pivCardTypeYubiKey) { + yubiKeyCards = append(yubiKeyCards, card) + } } - y, err := FindYubiKey(keyData.SerialNumber, prompt) - if err != nil { - return nil, trace.Wrap(err) + return yubiKeyCards, nil +} + +func newYubiKey(card string) (*YubiKey, error) { + y := &YubiKey{ + pinCache: newPINCache(), + conn: &sharedPIVConnection{ + card: card, + }, } - priv, err := y.getPrivateKey(pivSlot) - if err != nil { + var err error + if y.serialNumber, err = y.conn.getSerialNumber(); err != nil { return nil, trace.Wrap(err) } - - return priv, nil -} - -// Public returns the public key corresponding to this private key. -func (y *YubiKeyPrivateKey) Public() crypto.PublicKey { - return y.slotCert.PublicKey -} - -// WarmupHardwareKey performs a bogus sign() call to prompt the user for -// a PIN/touch (if needed). -func (y *YubiKeyPrivateKey) WarmupHardwareKey(ctx context.Context) error { - hash := sha256.Sum256(make([]byte, 256)) - _, err := y.sign(ctx, rand.Reader, hash[:], crypto.SHA256) - return trace.Wrap(err, "failed to access a YubiKey private key") -} - -// Sign implements crypto.Signer. -func (y *YubiKeyPrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - signature, err := y.sign(ctx, rand, digest, opts) - if err != nil { + if y.version, err = y.conn.getVersion(); err != nil { return nil, trace.Wrap(err) } - return signature, nil + return y, nil } // YubiKeys require touch when signing with a private key that requires touch. @@ -322,25 +148,67 @@ const ( signTouchPromptDelay = time.Millisecond * 200 ) -func (y *YubiKeyPrivateKey) sign(ctx context.Context, rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { - // To prevent concurrent calls to sign from failing due to PIV only handling a - // single connection, use a lock to queue through signature requests one at a time. - y.signMux.Lock() - defer y.signMux.Unlock() +var ( + ErrMissingTeleportCert = trace.BadParameterError{ + Message: "hardware key agent cannot perform signatures on PIV slots that aren't configured for Teleport. " + + "The PIV slot should be configured automatically by the Teleport client during login. If you are " + + "are configuring the PIV slot manually, you must also generate a certificate in the slot with " + + "\"teleport\" as the organization name: " + + "e.g. \"ykman piv keys generate -a ECCP256 9a pub.pem && ykman piv certificate generate 9a pub.pem -s O=teleport\"", + } +) + +func (y *YubiKey) sign(ctx context.Context, ref *hardwarekey.PrivateKeyRef, keyInfo hardwarekey.ContextualKeyInfo, prompt hardwarekey.Prompt, rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { + pivSlot, err := parsePIVSlot(ref.SlotKey) + if err != nil { + return nil, trace.Wrap(err) + } + + // Check that the public key in the slot matches our record. + slotCert, err := y.conn.attest(pivSlot) + if err != nil { + return nil, trace.Wrap(err) + } + type cryptoPublicKeyI interface { + Equal(x crypto.PublicKey) bool + } + if slotPub, ok := slotCert.PublicKey.(cryptoPublicKeyI); !ok { + return nil, trace.BadParameter("expected crypto.PublicKey but got %T", slotCert.PublicKey) + } else if !slotPub.Equal(ref.PublicKey) { + return nil, trace.CompareFailed("public key mismatch on PIV slot 0x%x", pivSlot.Key) + } + + // If this sign request is coming from the hardware key agent, ensure that the requested PIV + // slot was configured by a Teleport client, or manually configured by the user / hardware key + // administrator. Manual configuration is used in cases where the default PIV management key + // is not used, e.g. when the hardware key is managed by a third party provider by an admin. + if keyInfo.AgentKey { + cert, err := y.getCertificate(pivSlot) + switch { + case errors.Is(err, piv.ErrNotFound): + return nil, trace.Wrap(&ErrMissingTeleportCert, "certificate not found in PIV slot 0x%x", pivSlot.Key) + case err != nil: + return nil, trace.Wrap(err) + case !isTeleportMetadataCertificate(cert): + return nil, trace.Wrap(&ErrMissingTeleportCert, nonTeleportCertificateMessage(pivSlot, cert)) + } + } + ctx, cancel := context.WithCancelCause(ctx) + defer cancel(nil) // Lock the connection for the entire duration of the sign // process. Without this, the connection will be released, // leading to a failure when providing PIN or touch input: // "verify pin: transmitting request: the supplied handle was invalid". - release, err := y.connect() + release, err := y.conn.connect() if err != nil { return nil, trace.Wrap(err) } defer release() var touchPromptDelayTimer *time.Timer - if y.attestation.TouchPolicy != piv.TouchPolicyNever { + if ref.Policy.TouchRequired { touchPromptDelayTimer = time.NewTimer(signTouchPromptDelay) defer touchPromptDelayTimer.Stop() @@ -348,7 +216,7 @@ func (y *YubiKeyPrivateKey) sign(ctx context.Context, rand io.Reader, digest []b select { case <-touchPromptDelayTimer.C: // Prompt for touch after a delay, in case the function succeeds without touch due to a cached touch. - err := y.prompt.Touch(ctx) + err := prompt.Touch(ctx, keyInfo) if err != nil { // Cancel the entire function when an error occurs. // This is typically used for aborting the prompt. @@ -370,13 +238,18 @@ func (y *YubiKeyPrivateKey) sign(ctx context.Context, rand io.Reader, digest []b defer touchPromptDelayTimer.Reset(signTouchPromptDelay) } } - pass, err := y.prompt.AskPIN(ctx, PINRequired) - return pass, trace.Wrap(err) + pin, err := y.pinCache.PromptOrGetPIN(ctx, prompt, hardwarekey.PINRequired, keyInfo, ref.PINCacheTTL) + return pin, trace.Wrap(err) + } + + pinPolicy := piv.PINPolicyNever + if ref.Policy.PINRequired { + pinPolicy = piv.PINPolicyOnce } auth := piv.KeyAuth{ PINPrompt: promptPIN, - PINPolicy: y.attestation.PINPolicy, + PINPolicy: pinPolicy, } // YubiKeys with firmware version 5.3.1 have a bug where insVerify(0x20, 0x00, 0x80, nil) @@ -386,14 +259,14 @@ func (y *YubiKeyPrivateKey) sign(ctx context.Context, rand io.Reader, digest []b // the signature fails. manualRetryWithPIN := false fw531 := piv.Version{Major: 5, Minor: 3, Patch: 1} - if auth.PINPolicy == piv.PINPolicyOnce && y.attestation.Version == fw531 { + if auth.PINPolicy == piv.PINPolicyOnce && y.conn.conn.Version() == fw531 { // Set the keys PIN policy to never to skip the insVerify check. If PIN was provided in // a previous recent call, the signature will succeed as expected of the "once" policy. auth.PINPolicy = piv.PINPolicyNever manualRetryWithPIN = true } - privateKey, err := y.privateKey(y.pivSlot, y.Public(), auth) + privateKey, err := y.conn.privateKey(pivSlot, ref.PublicKey, auth) if err != nil { return nil, trace.Wrap(err) } @@ -416,7 +289,7 @@ func (y *YubiKeyPrivateKey) sign(ctx context.Context, rand io.Reader, digest []b if err != nil { return nil, trace.Wrap(err) } - if err := y.verifyPIN(pin); err != nil { + if err := y.conn.verifyPIN(pin); err != nil { return nil, trace.Wrap(err) } signature, err := abandonableSign(ctx, signer, rand, digest, opts) @@ -458,109 +331,47 @@ func abandonableSign(ctx context.Context, signer crypto.Signer, rand io.Reader, } } -func (y *YubiKeyPrivateKey) toPrivateKey() (*PrivateKey, error) { - keyPEM, err := y.keyPEM() - if err != nil { - return nil, trace.Wrap(err) - } - - return NewPrivateKey(y, keyPEM) -} - -func (y *YubiKeyPrivateKey) keyPEM() ([]byte, error) { - keyDataBytes, err := json.Marshal(yubiKeyPrivateKeyData{ - SerialNumber: y.serialNumber, - SlotKey: y.pivSlot.Key, - }) - if err != nil { - return nil, trace.Wrap(err) - } - - return pem.EncodeToMemory(&pem.Block{ - Type: pivYubiKeyPrivateKeyType, - Headers: nil, - Bytes: keyDataBytes, - }), nil -} - -// GetAttestationStatement returns an AttestationStatement for this YubiKeyPrivateKey. -func (y *YubiKeyPrivateKey) GetAttestationStatement() *AttestationStatement { - return &AttestationStatement{ - AttestationStatement: &attestation.AttestationStatement_YubikeyAttestationStatement{ - YubikeyAttestationStatement: &attestation.YubiKeyAttestationStatement{ - SlotCert: y.slotCert.Raw, - AttestationCert: y.attestationCert.Raw, - }, - }, - } -} - -// GetPrivateKeyPolicy returns the PrivateKeyPolicy supported by this YubiKeyPrivateKey. -func (y *YubiKeyPrivateKey) GetPrivateKeyPolicy() PrivateKeyPolicy { - return GetPrivateKeyPolicyFromAttestation(y.attestation) +// Reset resets the YubiKey PIV module to default settings. +func (y *YubiKey) Reset() error { + err := y.conn.reset() + return trace.Wrap(err) } -// GetPrivateKeyPolicyFromAttestation returns the PrivateKeyPolicy satisfied by the given hardware key attestation. -func GetPrivateKeyPolicyFromAttestation(att *piv.Attestation) PrivateKeyPolicy { - isTouchPolicy := att.TouchPolicy == piv.TouchPolicyCached || - att.TouchPolicy == piv.TouchPolicyAlways - - isPINPolicy := att.PINPolicy == piv.PINPolicyOnce || - att.PINPolicy == piv.PINPolicyAlways - - switch { - case isPINPolicy && isTouchPolicy: - return PrivateKeyPolicyHardwareKeyTouchAndPIN - case isPINPolicy: - return PrivateKeyPolicyHardwareKeyPIN - case isTouchPolicy: - return PrivateKeyPolicyHardwareKeyTouch +// generatePrivateKey generates a new private key in the given PIV slot. +func (y *YubiKey) generatePrivateKey(slot piv.Slot, policy hardwarekey.PromptPolicy, algorithm hardwarekey.SignatureAlgorithm, pinCacheTTL time.Duration) (*hardwarekey.PrivateKeyRef, error) { + touchPolicy := piv.TouchPolicyNever + if policy.TouchRequired { + touchPolicy = piv.TouchPolicyCached + } + + pinPolicy := piv.PINPolicyNever + if policy.PINRequired { + pinPolicy = piv.PINPolicyOnce + } + + var alg piv.Algorithm + switch algorithm { + // Use ECDSA key by default. + case hardwarekey.SignatureAlgorithmEC256, 0: + alg = piv.AlgorithmEC256 + case hardwarekey.SignatureAlgorithmEd25519: + // TODO(Joerger): Currently algorithms are only specified in tests, but some users pre-generate + // their keys in custom slots with custom algorithms, so we should try to support Ed25519 keys. + // Currently the Ed25519 algorithm is only supported by SoloKeys and YubiKeys v5.7.x+ + return nil, trace.BadParameter("Ed25519 keys are not currently supported") + case hardwarekey.SignatureAlgorithmRSA2048: + alg = piv.AlgorithmRSA2048 default: - return PrivateKeyPolicyHardwareKey + return nil, trace.BadParameter("unknown algorithm option %v", algorithm) } -} -// YubiKey is a specific YubiKey PIV card. -type YubiKey struct { - // conn is a shared YubiKey PIV connection. - // - // PIV connections claim an exclusive lock on the PIV module until closed. - // In order to improve connection sharing for this program without locking - // out other programs during extended program executions (like "tsh proxy ssh"), - // this connections is opportunistically formed and released after being - // unused for a few seconds. - *sharedPIVConnection - // serialNumber is the yubiKey's 8 digit serial number. - serialNumber uint32 - prompt HardwareKeyPrompt -} - -func newYubiKey(card string, prompt HardwareKeyPrompt) (*YubiKey, error) { - y := &YubiKey{ - sharedPIVConnection: &sharedPIVConnection{ - card: card, - }, - prompt: prompt, + opts := piv.Key{ + Algorithm: alg, + PINPolicy: pinPolicy, + TouchPolicy: touchPolicy, } - serialNumber, err := y.serial() - if err != nil { - return nil, trace.Wrap(err) - } - - y.serialNumber = serialNumber - return y, nil -} - -// Reset resets the YubiKey PIV module to default settings. -func (y *YubiKey) Reset() error { - err := y.reset() - return trace.Wrap(err) -} - -// generatePrivateKeyAndCert generates a new private key and client metadata cert in the given PIV slot. -func (y *YubiKey) generatePrivateKeyAndCert(slot piv.Slot, requiredKeyPolicy PrivateKeyPolicy) (*PrivateKey, error) { - if err := y.generatePrivateKey(slot, requiredKeyPolicy); err != nil { + if _, err := y.conn.generateKey(piv.DefaultManagementKey, slot, opts); err != nil { return nil, trace.Wrap(err) } @@ -571,7 +382,7 @@ func (y *YubiKey) generatePrivateKeyAndCert(slot piv.Slot, requiredKeyPolicy Pri return nil, trace.Wrap(err) } - return y.getPrivateKey(slot) + return y.getKeyRef(slot, pinCacheTTL) } // SetMetadataCertificate creates a self signed certificate and stores it in the YubiKey's @@ -584,85 +395,80 @@ func (y *YubiKey) SetMetadataCertificate(slot piv.Slot, subject pkix.Name) error return trace.Wrap(err) } - err = y.setCertificate(piv.DefaultManagementKey, slot, cert) + err = y.conn.setCertificate(piv.DefaultManagementKey, slot, cert) return trace.Wrap(err) } // getCertificate gets a certificate from the given PIV slot. func (y *YubiKey) getCertificate(slot piv.Slot) (*x509.Certificate, error) { - cert, err := y.certificate(slot) + cert, err := y.conn.certificate(slot) return cert, trace.Wrap(err) } -// generatePrivateKey generates a new private key in the given PIV slot. -func (y *YubiKey) generatePrivateKey(slot piv.Slot, requiredKeyPolicy PrivateKeyPolicy) error { - touchPolicy, pinPolicy, err := getKeyPolicies(requiredKeyPolicy) +// attestKey attests the key in the given PIV slot. +// The key's public key can be found in the returned slotCert. +func (y *YubiKey) attestKey(slot piv.Slot) (slotCert *x509.Certificate, attCert *x509.Certificate, att *piv.Attestation, err error) { + slotCert, err = y.conn.attest(slot) if err != nil { - return trace.Wrap(err) + return nil, nil, nil, trace.Wrap(err) } - opts := piv.Key{ - Algorithm: piv.AlgorithmEC256, - PINPolicy: pinPolicy, - TouchPolicy: touchPolicy, - } - - _, err = y.generateKey(piv.DefaultManagementKey, slot, opts) - return trace.Wrap(err) -} - -// getPrivateKey gets an existing private key from the given PIV slot. -func (y *YubiKey) getPrivateKey(slot piv.Slot) (*PrivateKey, error) { - slotCert, err := y.attest(slot) - if errors.Is(err, piv.ErrNotFound) { - return nil, trace.NotFound("private key in YubiKey PIV slot %q not found.", slot.String()) - } else if err != nil { - return nil, trace.Wrap(err) - } - - attCert, err := y.attestationCertificate() + attCert, err = y.conn.attestationCertificate() if err != nil { - return nil, trace.Wrap(err) + return nil, nil, nil, trace.Wrap(err) } - attestation, err := piv.Verify(attCert, slotCert) + att, err = piv.Verify(attCert, slotCert) if err != nil { - return nil, trace.Wrap(err) + return nil, nil, nil, trace.Wrap(err) } - priv := &YubiKeyPrivateKey{ - YubiKey: y, - pivSlot: slot, - slotCert: slotCert, - attestationCert: attCert, - attestation: attestation, - } + return slotCert, attCert, att, nil +} - keyPEM, err := priv.keyPEM() +func (y *YubiKey) getKeyRef(slot piv.Slot, pinCacheTTL time.Duration) (*hardwarekey.PrivateKeyRef, error) { + slotCert, attCert, att, err := y.attestKey(slot) if err != nil { return nil, trace.Wrap(err) } - key, err := NewPrivateKey(priv, keyPEM) - if err != nil { + ref := &hardwarekey.PrivateKeyRef{ + SerialNumber: y.serialNumber, + SlotKey: hardwarekey.PIVSlotKey(slot.Key), + PublicKey: slotCert.PublicKey, + Policy: hardwarekey.PromptPolicy{ + TouchRequired: att.TouchPolicy != piv.TouchPolicyNever, + PINRequired: att.PINPolicy != piv.PINPolicyNever, + }, + AttestationStatement: &hardwarekey.AttestationStatement{ + AttestationStatement: &attestationv1.AttestationStatement_YubikeyAttestationStatement{ + YubikeyAttestationStatement: &attestationv1.YubiKeyAttestationStatement{ + SlotCert: slotCert.Raw, + AttestationCert: attCert.Raw, + }, + }, + }, + PINCacheTTL: pinCacheTTL, + } + + if err := ref.Validate(); err != nil { return nil, trace.Wrap(err) } - cachedKeys[slot] = key - return key, nil + return ref, nil } // SetPIN sets the YubiKey PIV PIN. This doesn't require user interaction like touch, just the correct old PIN. func (y *YubiKey) SetPIN(oldPin, newPin string) error { - err := y.setPIN(oldPin, newPin) + err := y.conn.setPIN(oldPin, newPin) return trace.Wrap(err) } // checkOrSetPIN prompts the user for PIN and verifies it with the YubiKey. // If the user provides the default PIN, they will be prompted to set a // non-default PIN and PUK before continuing. -func (y *YubiKey) checkOrSetPIN(ctx context.Context) error { - pin, err := y.prompt.AskPIN(ctx, PINOptional) +func (y *YubiKey) checkOrSetPIN(ctx context.Context, prompt hardwarekey.Prompt, keyInfo hardwarekey.ContextualKeyInfo, pinCacheTTL time.Duration) error { + pin, err := y.pinCache.PromptOrGetPIN(ctx, prompt, hardwarekey.PINOptional, keyInfo, pinCacheTTL) if err != nil { return trace.Wrap(err) } @@ -672,14 +478,44 @@ func (y *YubiKey) checkOrSetPIN(ctx context.Context) error { fmt.Fprintf(os.Stderr, "The default PIN %q is not supported.\n", piv.DefaultPIN) fallthrough case "": - if pin, err = y.setPINAndPUKFromDefault(ctx, y.prompt); err != nil { + pin, err = y.setPINAndPUKFromDefault(ctx, prompt, keyInfo) + if err != nil { return trace.Wrap(err) } + y.pinCache.setPIN(pin, pinCacheTTL) } return trace.Wrap(y.verifyPIN(pin)) } +func (y *YubiKey) setPINAndPUKFromDefault(ctx context.Context, prompt hardwarekey.Prompt, keyInfo hardwarekey.ContextualKeyInfo) (string, error) { + pinAndPUK, err := prompt.ChangePIN(ctx, keyInfo) + if err != nil { + return "", trace.Wrap(err) + } + + if err := pinAndPUK.Validate(); err != nil { + return "", trace.Wrap(err) + } + + if pinAndPUK.PUKChanged { + if err := y.conn.setPUK(piv.DefaultPUK, pinAndPUK.PUK); err != nil { + return "", trace.Wrap(err) + } + } + + if err := y.conn.unblock(pinAndPUK.PUK, pinAndPUK.PIN); err != nil { + return "", trace.Wrap(err) + } + + return pinAndPUK.PIN, nil +} + +func (y *YubiKey) verifyPIN(pin string) error { + err := y.conn.verifyPIN(pin) + return trace.Wrap(err) +} + type sharedPIVConnection struct { // card is a reader name used to find and connect to this yubiKey. // This value may change between OS's, or with other system changes. @@ -771,7 +607,7 @@ func (c *sharedPIVConnection) privateKey(slot piv.Slot, public crypto.PublicKey, return privateKey, trace.Wrap(err) } -func (c *sharedPIVConnection) serial() (uint32, error) { +func (c *sharedPIVConnection) getSerialNumber() (uint32, error) { release, err := c.connect() if err != nil { return 0, trace.Wrap(err) @@ -781,14 +617,21 @@ func (c *sharedPIVConnection) serial() (uint32, error) { return serial, trace.Wrap(err) } +func (c *sharedPIVConnection) getVersion() (piv.Version, error) { + release, err := c.connect() + if err != nil { + return piv.Version{}, trace.Wrap(err) + } + defer release() + return c.conn.Version(), nil +} + func (c *sharedPIVConnection) reset() error { release, err := c.connect() if err != nil { return trace.Wrap(err) } defer release() - // Clear cached keys. - cachedKeys = make(map[piv.Slot]*PrivateKey) return trace.Wrap(c.conn.Reset()) } @@ -877,106 +720,13 @@ func (c *sharedPIVConnection) verifyPIN(pin string) error { return trace.Wrap(c.conn.VerifyPIN(pin)) } -func (c *sharedPIVConnection) setPINAndPUKFromDefault(ctx context.Context, prompt HardwareKeyPrompt) (string, error) { - pinAndPUK, err := prompt.ChangePIN(ctx) - if err != nil { - return "", trace.Wrap(err) - } - // YubiKey requires that PIN and PUK be 6-8 characters. - // Verify that we get valid values from the prompt. - if !isPINLengthValid(pinAndPUK.PIN) { - return "", trace.BadParameter("PIN must be 6-8 characters long") - } - if pinAndPUK.PIN == piv.DefaultPIN { - return "", trace.BadParameter("The default PIN is not supported") - } - if !isPINLengthValid(pinAndPUK.PUK) { - return "", trace.BadParameter("PUK must be 6-8 characters long") - } - if pinAndPUK.PUK == piv.DefaultPUK { - return "", trace.BadParameter("The default PUK is not supported") - } - - if pinAndPUK.PUKChanged { - if err := c.setPUK(piv.DefaultPUK, pinAndPUK.PUK); err != nil { - return "", trace.Wrap(err) - } - } - - if err := c.unblock(pinAndPUK.PUK, pinAndPUK.PIN); err != nil { - return "", trace.Wrap(err) - } - - return pinAndPUK.PIN, nil -} - func isRetryError(err error) bool { const retryError = "connecting to smart card: the smart card cannot be accessed because of other connections outstanding" return strings.Contains(err.Error(), retryError) } -// FindYubiKey finds a yubiKey PIV card by serial number. If no serial -// number is provided, the first yubiKey found will be returned. -func FindYubiKey(serialNumber uint32, prompt HardwareKeyPrompt) (*YubiKey, error) { - yubiKeyCards, err := findYubiKeyCards() - if err != nil { - return nil, trace.Wrap(err) - } - - if len(yubiKeyCards) == 0 { - if serialNumber != 0 { - return nil, trace.ConnectionProblem(nil, "no YubiKey device connected with serial number %d", serialNumber) - } - return nil, trace.ConnectionProblem(nil, "no YubiKey device connected") - } - - for _, card := range yubiKeyCards { - y, err := newYubiKey(card, prompt) - if err != nil { - return nil, trace.Wrap(err) - } - - if serialNumber == 0 || y.serialNumber == serialNumber { - return y, nil - } - } - - return nil, trace.ConnectionProblem(nil, "no YubiKey device connected with serial number %d", serialNumber) -} - -// findYubiKeyCards returns a list of connected yubiKey PIV card names. -func findYubiKeyCards() ([]string, error) { - cards, err := piv.Cards() - if err != nil { - return nil, trace.Wrap(err) - } - - var yubiKeyCards []string - for _, card := range cards { - if strings.Contains(strings.ToLower(card), PIVCardTypeYubiKey) { - yubiKeyCards = append(yubiKeyCards, card) - } - } - - return yubiKeyCards, nil -} - -func (s PIVSlot) validate() error { - _, err := s.parse() - return trace.Wrap(err) -} - -func (s PIVSlot) parse() (piv.Slot, error) { - slotKey, err := strconv.ParseUint(string(s), 16, 32) - if err != nil { - return piv.Slot{}, trace.Wrap(err) - } - - return parsePIVSlot(uint32(slotKey)) -} - -func parsePIVSlot(slotKey uint32) (piv.Slot, error) { - switch slotKey { +func parsePIVSlot(slotKey hardwarekey.PIVSlotKey) (piv.Slot, error) { + switch uint32(slotKey) { case piv.SlotAuthentication.Key: return piv.SlotAuthentication, nil case piv.SlotSignature.Key: @@ -986,11 +736,7 @@ func parsePIVSlot(slotKey uint32) (piv.Slot, error) { case piv.SlotCardAuthentication.Key: return piv.SlotCardAuthentication, nil default: - retiredSlot, ok := piv.RetiredKeyManagementSlot(slotKey) - if !ok { - return piv.Slot{}, trace.BadParameter("slot %X does not exist", slotKey) - } - return retiredSlot, nil + return piv.Slot{}, trace.BadParameter("invalid slot %X", slotKey) } } @@ -1019,3 +765,32 @@ func SelfSignedMetadataCertificate(subject pkix.Name) (*x509.Certificate, error) } return cert, nil } + +func isTeleportMetadataCertificate(cert *x509.Certificate) bool { + return len(cert.Subject.Organization) > 0 && cert.Subject.Organization[0] == certOrgName +} + +func nonTeleportCertificateMessage(slot piv.Slot, cert *x509.Certificate) string { + // Gather a small list of user-readable x509 certificate fields to display to the user. + sum := sha256.Sum256(cert.Raw) + fingerPrint := hex.EncodeToString(sum[:]) + return fmt.Sprintf(`Certificate in YubiKey PIV slot %q is not a Teleport client cert: +Slot %s: + Algorithm: %v + Subject DN: %v + Issuer DN: %v + Serial: %v + Fingerprint: %v + Not before: %v + Not after: %v +`, + slot, slot, + cert.SignatureAlgorithm, + cert.Subject, + cert.Issuer, + cert.SerialNumber, + fingerPrint, + cert.NotBefore, + cert.NotAfter, + ) +} diff --git a/api/utils/keys/policy.go b/api/utils/keys/policy.go index 78029c99a1727..552fee7754e67 100644 --- a/api/utils/keys/policy.go +++ b/api/utils/keys/policy.go @@ -18,6 +18,8 @@ import ( "regexp" "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" ) // PrivateKeyPolicy is a requirement for client private key storage. @@ -199,3 +201,41 @@ func IsPrivateKeyPolicyError(err error) bool { } return privateKeyPolicyErrRegex.MatchString(err.Error()) } + +// GetPromptPolicy returns this corresponding [hardwarekey.PromptPolicy]. +func (p PrivateKeyPolicy) GetPromptPolicy() hardwarekey.PromptPolicy { + return hardwarekey.PromptPolicy{ + TouchRequired: p.isHardwareKeyTouchVerified(), + PINRequired: p.isHardwareKeyPINVerified(), + } +} + +func PrivateKeyPolicyFromPromptPolicy(policy hardwarekey.PromptPolicy) PrivateKeyPolicy { + switch policy { + case hardwarekey.PromptPolicyNone: + return PrivateKeyPolicyHardwareKey + + case hardwarekey.PromptPolicyTouch: + return PrivateKeyPolicyHardwareKeyTouch + + case hardwarekey.PromptPolicyPIN: + return PrivateKeyPolicyHardwareKeyPIN + + case hardwarekey.PromptPolicyTouchAndPIN: + return PrivateKeyPolicyHardwareKeyTouchAndPIN + + default: + // unreachable case + return PrivateKeyPolicyNone + } +} + +// AttestationData is verified attestation data for a public key. +type AttestationData struct { + // PublicKeyDER is the public key in PKIX, ASN.1 DER form. + PublicKeyDER []byte `json:"public_key"` + // PrivateKeyPolicy specifies the private key policy supported by the associated private key. + PrivateKeyPolicy PrivateKeyPolicy `json:"private_key_policy"` + // SerialNumber is the serial number of the Attested hardware key. + SerialNumber uint32 `json:"serial_number"` +} diff --git a/api/utils/keys/policy_piv.go b/api/utils/keys/policy_piv.go new file mode 100644 index 0000000000000..a99fc532f3010 --- /dev/null +++ b/api/utils/keys/policy_piv.go @@ -0,0 +1,45 @@ +//go:build piv && !pivtest + +/* +Copyright 2025 Gravitational, Inc. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package keys + +import ( + "github.com/go-piv/piv-go/piv" +) + +// GetPrivateKeyPolicyFromAttestation returns the PrivateKeyPolicy satisfied by the given hardware key attestation. +// TODO(Joerger): Move to /e where this is used. +func GetPrivateKeyPolicyFromAttestation(att *piv.Attestation) PrivateKeyPolicy { + if att == nil { + return PrivateKeyPolicyNone + } + + isTouchPolicy := att.TouchPolicy == piv.TouchPolicyCached || + att.TouchPolicy == piv.TouchPolicyAlways + + isPINPolicy := att.PINPolicy == piv.PINPolicyOnce || + att.PINPolicy == piv.PINPolicyAlways + + switch { + case isPINPolicy && isTouchPolicy: + return PrivateKeyPolicyHardwareKeyTouchAndPIN + case isPINPolicy: + return PrivateKeyPolicyHardwareKeyPIN + case isTouchPolicy: + return PrivateKeyPolicyHardwareKeyTouch + default: + return PrivateKeyPolicyHardwareKey + } +} diff --git a/api/utils/keys/privatekey.go b/api/utils/keys/privatekey.go index 83a186e8383a1..8a942412f9591 100644 --- a/api/utils/keys/privatekey.go +++ b/api/utils/keys/privatekey.go @@ -19,6 +19,7 @@ package keys import ( "bytes" + "context" "crypto" "crypto/ecdsa" "crypto/ed25519" @@ -31,6 +32,8 @@ import ( "github.com/gravitational/trace" "golang.org/x/crypto/ssh" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" + "github.com/gravitational/teleport/api/utils/keys/piv" "github.com/gravitational/teleport/api/utils/sshutils/ppk" ) @@ -72,7 +75,61 @@ func NewPrivateKey(signer crypto.Signer, keyPEM []byte) (*PrivateKey, error) { }, nil } -// SSHPublicKey returns the ssh.PublicKey representiation of the public key. +// NewHardwarePrivateKey uses the provided service to create a hardware private key that +// satisfies the provided [config], if one does not already exist, and returns a corresponding +// [hardwarekey.Signer] wrapped as a [PrivateKey]. +func NewHardwarePrivateKey(ctx context.Context, s hardwarekey.Service, keyConfig hardwarekey.PrivateKeyConfig) (*PrivateKey, error) { + if s == nil { + return nil, trace.BadParameter("cannot create a new hardware private key without a hardware key service provided") + } + + hwSigner, err := s.NewPrivateKey(ctx, keyConfig) + if err != nil { + return nil, trace.Wrap(err) + } + + keyPEM, err := MarshalPrivateKey(hwSigner) + if err != nil { + return nil, trace.Wrap(err) + } + + return NewPrivateKey(hwSigner, keyPEM) +} + +// GetAttestationStatement returns this key's AttestationStatement. If the key is +// not a [hardwarekey.Signer], this method returns nil. +func (k *PrivateKey) GetAttestationStatement() *hardwarekey.AttestationStatement { + if hwpk, ok := k.Signer.(*hardwarekey.Signer); ok { + return hwpk.GetAttestationStatement() + } + // Just return a nil attestation statement and let this key fail any attestation checks. + return nil +} + +// GetPrivateKeyPolicy returns this key's PrivateKeyPolicy. +func (k *PrivateKey) GetPrivateKeyPolicy() PrivateKeyPolicy { + if hwpk, ok := k.Signer.(*hardwarekey.Signer); ok { + return PrivateKeyPolicyFromPromptPolicy(hwpk.GetPromptPolicy()) + } + + return PrivateKeyPolicyNone +} + +// IsHardware returns true if [k] is a [hardwarekey.Signer]. +func (k *PrivateKey) IsHardware() bool { + _, ok := k.Signer.(*hardwarekey.Signer) + return ok +} + +// WarmupHardwareKey checks if this is a [hardwarekey.Signer] and warms it up if it is. +func (k *PrivateKey) WarmupHardwareKey(ctx context.Context) error { + if hwpk, ok := k.Signer.(*hardwarekey.Signer); ok { + return hwpk.WarmupHardwareKey(ctx) + } + return nil +} + +// SSHPublicKey returns the ssh.PublicKey representation of the public key. func (k *PrivateKey) SSHPublicKey() ssh.PublicKey { return k.sshPub } @@ -156,19 +213,26 @@ func LoadPrivateKey(keyFile string) (*PrivateKey, error) { // ParsePrivateKeyOptions contains config options for ParsePrivateKey. type ParsePrivateKeyOptions struct { - // CustomHardwareKeyPrompt is a custom hardware key prompt to use when asking - // for a hardware key PIN, touch, etc. - // If empty, a default CLI prompt is used. - CustomHardwareKeyPrompt HardwareKeyPrompt + // HardwareKeyService is the hardware key service to use with parsed hardware private keys. + HardwareKeyService hardwarekey.Service + // ContextualKeyInfo is contextual information associated with the key. + ContextualKeyInfo hardwarekey.ContextualKeyInfo } // ParsePrivateKeyOpt applies configuration options. type ParsePrivateKeyOpt func(o *ParsePrivateKeyOptions) -// WithCustomPrompt sets a custom hardware key prompt. -func WithCustomPrompt(prompt HardwareKeyPrompt) ParsePrivateKeyOpt { +// WithHardwareKeyService sets the hardware key service. +func WithHardwareKeyService(hwKeyService hardwarekey.Service) ParsePrivateKeyOpt { return func(o *ParsePrivateKeyOptions) { - o.CustomHardwareKeyPrompt = prompt + o.HardwareKeyService = hwKeyService + } +} + +// WithContextualKeyInfo adds contextual key info to the parsed private key. +func WithContextualKeyInfo(info hardwarekey.ContextualKeyInfo) ParsePrivateKeyOpt { + return func(o *ParsePrivateKeyOptions) { + o.ContextualKeyInfo = info } } @@ -187,8 +251,28 @@ func ParsePrivateKey(keyPEM []byte, opts ...ParsePrivateKeyOpt) (*PrivateKey, er switch block.Type { case pivYubiKeyPrivateKeyType: - priv, err := parseYubiKeyPrivateKeyData(block.Bytes, appliedOpts.CustomHardwareKeyPrompt) - return priv, trace.Wrap(err, "parsing YubiKey private key") + hwks := appliedOpts.HardwareKeyService + if hwks == nil { + // If no hardware key service was provided, use the default PIV service. + // This is used for clients which do not (yet) initialize a hardware key + // service to reuse. As a result, some features like hardware key PIN + // caching and the hardware key agent are not supported for these clients. + // e.g. tbot, integrations, and custom API client programs. + // + // TODO(Joerger): initialize client store for all clients early in process. + // If hwks is still not provided, we should successfully parse the key but + // inject the "unavailable" hardware key service, which will return a + // "piv unavailable" error on signature attempts, making this method useful + // for key info gathering in specific circumstances (e.g. ProfileStatus.AppsForCluster) + hwks = piv.NewYubiKeyService(nil /*prompt*/) + } + + hwSigner, err := hardwarekey.DecodeSigner(block.Bytes, hwks, appliedOpts.ContextualKeyInfo) + if err != nil { + return nil, trace.Wrap(err, "failed to parse hardware key signer") + } + + return NewPrivateKey(hwSigner, keyPEM) case OpenSSHPrivateKeyType: priv, err := ssh.ParseRawPrivateKey(keyPEM) if err != nil { @@ -240,7 +324,7 @@ func ParsePrivateKey(keyPEM []byte, opts ...ParsePrivateKeyOpt) (*PrivateKey, er } // MarshalPrivateKey will return a PEM encoded crypto.Signer. -// Only supports rsa, ecdsa, and ed25519 keys. +// [key] must be an *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey, or *hardwarekey.Signer func MarshalPrivateKey(key crypto.Signer) ([]byte, error) { switch privateKey := key.(type) { case *rsa.PrivateKey: @@ -259,13 +343,23 @@ func MarshalPrivateKey(key crypto.Signer) ([]byte, error) { Bytes: der, }) return privPEM, nil + case *hardwarekey.Signer: + encodedKey, err := hardwarekey.EncodeSigner(privateKey) + if err != nil { + return nil, trace.Wrap(err) + } + privPEM := pem.EncodeToMemory(&pem.Block{ + Type: pivYubiKeyPrivateKeyType, + Bytes: encodedKey, + }) + return privPEM, nil default: return nil, trace.BadParameter("unsupported private key type %T", key) } } // LoadKeyPair returns the PrivateKey for the given private and public key files. -func LoadKeyPair(privFile, sshPubFile string, customPrompt HardwareKeyPrompt) (*PrivateKey, error) { +func LoadKeyPair(privFile, sshPubFile string, opts ...ParsePrivateKeyOpt) (*PrivateKey, error) { privPEM, err := os.ReadFile(privFile) if err != nil { return nil, trace.ConvertSystemError(err) @@ -276,7 +370,7 @@ func LoadKeyPair(privFile, sshPubFile string, customPrompt HardwareKeyPrompt) (* return nil, trace.ConvertSystemError(err) } - priv, err := ParseKeyPair(privPEM, marshaledSSHPub, customPrompt) + priv, err := ParseKeyPair(privPEM, marshaledSSHPub, opts...) if err != nil { return nil, trace.Wrap(err) } @@ -284,8 +378,8 @@ func LoadKeyPair(privFile, sshPubFile string, customPrompt HardwareKeyPrompt) (* } // ParseKeyPair returns the PrivateKey for the given private and public key PEM blocks. -func ParseKeyPair(privPEM, marshaledSSHPub []byte, customPrompt HardwareKeyPrompt) (*PrivateKey, error) { - priv, err := ParsePrivateKey(privPEM, WithCustomPrompt(customPrompt)) +func ParseKeyPair(privPEM, marshaledSSHPub []byte, opts ...ParsePrivateKeyOpt) (*PrivateKey, error) { + priv, err := ParsePrivateKey(privPEM, opts...) if err != nil { return nil, trace.Wrap(err) } diff --git a/api/utils/keys/privatekey_test.go b/api/utils/keys/privatekey_test.go index 7bdadcf6fe4b4..935323d2c68c0 100644 --- a/api/utils/keys/privatekey_test.go +++ b/api/utils/keys/privatekey_test.go @@ -14,10 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -package keys +package keys_test import ( "bytes" + "context" "crypto" "crypto/ecdsa" "crypto/ed25519" @@ -34,6 +35,9 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" ) func TestMarshalAndParseKey(t *testing.T) { @@ -44,21 +48,33 @@ func TestMarshalAndParseKey(t *testing.T) { _, edKey, err := ed25519.GenerateKey(rand.Reader) require.NoError(t, err) + contextualKeyInfo := hardwarekey.ContextualKeyInfo{ + ProxyHost: "billy.io", + Username: "Billy@billy.io", + ClusterName: "billy.io", + } + s := hardwarekey.NewMockHardwareKeyService(nil /*prompt*/) + hwPriv, err := s.NewPrivateKey(context.TODO(), hardwarekey.PrivateKeyConfig{ + ContextualKeyInfo: contextualKeyInfo, + }) + require.NoError(t, err) + for keyType, key := range map[string]crypto.Signer{ - "rsa": rsaKey, - "ecdsa": ecKey, - "ed25519": edKey, + "rsa": rsaKey, + "ecdsa": ecKey, + "ed25519": edKey, + "hardware": hwPriv, } { t.Run(keyType, func(t *testing.T) { - keyPEM, err := MarshalPrivateKey(key) + keyPEM, err := keys.MarshalPrivateKey(key) require.NoError(t, err) - gotKey, err := ParsePrivateKey(keyPEM) + gotKey, err := keys.ParsePrivateKey(keyPEM, keys.WithHardwareKeyService(s), keys.WithContextualKeyInfo(contextualKeyInfo)) require.NoError(t, err) - assert.Empty(t, cmp.Diff(key, gotKey.Signer), "parsed private key is not equal to the original") + assert.Empty(t, cmp.Diff(key, gotKey.Signer, cmpopts.IgnoreUnexported(hardwarekey.Signer{})), "parsed private key is not equal to the original") - pubKeyPEM, err := MarshalPublicKey(key.Public()) + pubKeyPEM, err := keys.MarshalPublicKey(key.Public()) require.NoError(t, err) - gotPubKey, err := ParsePublicKey(pubKeyPEM) + gotPubKey, err := keys.ParsePublicKey(pubKeyPEM) require.NoError(t, err) require.Equal(t, key.Public(), gotPubKey) assert.Empty(t, cmp.Diff(key.Public(), gotPubKey), "parsed public key is not equal to the original") @@ -67,7 +83,7 @@ func TestMarshalAndParseKey(t *testing.T) { } func TestParseMismatchedPEMHeader(t *testing.T) { - rsaKey, err := ParsePrivateKey(rsaKeyPEM) + rsaKey, err := keys.ParsePrivateKey(rsaKeyPEM) require.NoError(t, err) rsaPKCS1DER := x509.MarshalPKCS1PrivateKey(rsaKey.Signer.(*rsa.PrivateKey)) rsaPKCS8DER, err := x509.MarshalPKCS8PrivateKey(rsaKey.Signer) @@ -117,7 +133,7 @@ func TestParseMismatchedPEMHeader(t *testing.T) { }, } { t.Run(desc, func(t *testing.T) { - key, err := ParsePrivateKey(tc.pem) + key, err := keys.ParsePrivateKey(tc.pem) require.NoError(t, err) require.Equal(t, tc.expectKey, key.Signer) }) @@ -143,7 +159,7 @@ func TestParseMismatchedPEMHeader(t *testing.T) { }, } { t.Run(desc, func(t *testing.T) { - pubKey, err := ParsePublicKey(tc.pem) + pubKey, err := keys.ParsePublicKey(tc.pem) require.NoError(t, err) require.Equal(t, tc.expectKey, pubKey) }) @@ -162,7 +178,7 @@ func TestParseCorruptedKey(t *testing.T) { } { t.Run(tc, func(t *testing.T) { b := pem.EncodeToMemory(&pem.Block{Type: tc, Bytes: []byte("foo")}) - _, err := ParsePrivateKey(b) + _, err := keys.ParsePrivateKey(b) require.Error(t, err) }) } @@ -173,7 +189,7 @@ func TestParseCorruptedKey(t *testing.T) { } { t.Run(tc, func(t *testing.T) { b := pem.EncodeToMemory(&pem.Block{Type: tc, Bytes: []byte("foo")}) - _, err := ParsePublicKey(b) + _, err := keys.ParsePublicKey(b) require.Error(t, err) }) } @@ -207,7 +223,7 @@ func TestX509KeyPair(t *testing.T) { expectCert, err := tls.X509KeyPair(tc.certPEM, tc.keyPEM) require.NoError(t, err) - tlsCert, err := X509KeyPair(tc.certPEM, tc.keyPEM) + tlsCert, err := keys.X509KeyPair(tc.certPEM, tc.keyPEM) require.NoError(t, err) require.Empty(t, cmp.Diff(expectCert, tlsCert, cmpopts.IgnoreFields(tls.Certificate{}, "Leaf"))) @@ -267,7 +283,7 @@ func TestX509Certificate(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - cert, rawCerts, err := X509Certificate(tc.certPEM) + cert, rawCerts, err := keys.X509Certificate(tc.certPEM) require.Len(t, rawCerts, tc.expectedLength) tc.expectedError(t, err) @@ -275,7 +291,39 @@ func TestX509Certificate(t *testing.T) { tc.validateResult(t, cert) }) } +} + +// TestHardwareKeyMethods tests hardware key related methods with non-hardware keys. +// +// Testing these methods with actual hardware keys requires the piv go tag and should +// be tested individually in tests like `TestGetYubiKeyPrivateKey_Interactive`. +func TestHardwareKeyMethods(t *testing.T) { + ctx := context.Background() + + // Test hardware key methods with a software key. + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + keyPEM, err := keys.MarshalPrivateKey(priv) + require.NoError(t, err) + key, err := keys.NewPrivateKey(priv, keyPEM) + require.NoError(t, err) + + require.Nil(t, key.GetAttestationStatement()) + require.Equal(t, keys.PrivateKeyPolicyNone, key.GetPrivateKeyPolicy()) + require.False(t, key.IsHardware()) + require.NoError(t, key.WarmupHardwareKey(ctx)) + + // Test hardware key methods with a mocked hardware key. + s := hardwarekey.NewMockHardwareKeyService(nil /*prompt*/) + hwKey, err := keys.NewHardwarePrivateKey(ctx, s, hardwarekey.PrivateKeyConfig{ + Policy: hardwarekey.PromptPolicyTouch, + }) + require.NoError(t, err) + require.NotNil(t, hwKey.GetAttestationStatement()) + require.Equal(t, keys.PrivateKeyPolicyHardwareKeyTouch, hwKey.GetPrivateKeyPolicy()) + require.True(t, hwKey.IsHardware()) + require.NoError(t, hwKey.WarmupHardwareKey(ctx)) } var ( diff --git a/api/utils/keys/yubikey_common.go b/api/utils/keys/yubikey_common.go deleted file mode 100644 index 5ed36f814580d..0000000000000 --- a/api/utils/keys/yubikey_common.go +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright 2022 Gravitational, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package keys - -import ( - "context" - - "github.com/gravitational/trace" -) - -// HardwareKeyPrompt provides methods to interact with a YubiKey hardware key. -type HardwareKeyPrompt interface { - // AskPIN prompts the user for a PIN. - // The requirement tells if the PIN is required or optional. - AskPIN(ctx context.Context, requirement PINPromptRequirement) (string, error) - // Touch prompts the user to touch the hardware key. - Touch(ctx context.Context) error - // ChangePIN asks for a new PIN. - // If the PUK has a default value, it should ask for the new value for it. - // It is up to the implementer how the validation is handled. - // For example, CLI prompt can ask for a valid PIN/PUK in a loop, a GUI - // prompt can use the frontend validation. - ChangePIN(ctx context.Context) (*PINAndPUK, error) - // ConfirmSlotOverwrite asks the user if the slot's private key and certificate can be overridden. - ConfirmSlotOverwrite(ctx context.Context, message string) (bool, error) -} - -// PINPromptRequirement specifies whether a PIN is required. -type PINPromptRequirement int - -const ( - // PINOptional allows the user to proceed without entering a PIN. - PINOptional PINPromptRequirement = iota - // PINRequired enforces that a PIN must be entered to proceed. - PINRequired -) - -// PINAndPUK describes a response returned from HardwareKeyPrompt.ChangePIN. -type PINAndPUK struct { - // New PIN set by the user. - PIN string - // PUK used to change the PIN. - // This is a new PUK if it has not been changed (from the default PUK). - PUK string - // PUKChanged is true if the user changed the default PUK. - PUKChanged bool -} - -// GetYubiKeyPrivateKey attempt to retrieve a YubiKey private key matching the given hardware key policy -// from the given slot. If slot is unspecified, the default slot for the given key policy will be used. -// If the slot is empty, a new private key matching the given policy will be generated in the slot. -// - hardware_key: 9a -// - hardware_key_touch: 9c -// - hardware_key_pin: 9d -// - hardware_key_touch_pin: 9e -func GetYubiKeyPrivateKey(ctx context.Context, policy PrivateKeyPolicy, slot PIVSlot, customPrompt HardwareKeyPrompt) (*PrivateKey, error) { - priv, err := getOrGenerateYubiKeyPrivateKey(ctx, policy, slot, customPrompt) - if err != nil { - return nil, trace.Wrap(err, "failed to get a YubiKey private key") - } - return priv, nil -} - -// PIVSlot is the string representation of a PIV slot. e.g. "9a". -type PIVSlot string - -// Validate that the PIV slot is a valid value. -func (s PIVSlot) Validate() error { - return trace.Wrap(s.validate()) -} diff --git a/api/utils/keys/yubikey_fake.go b/api/utils/keys/yubikey_fake.go deleted file mode 100644 index 341c53c204c8e..0000000000000 --- a/api/utils/keys/yubikey_fake.go +++ /dev/null @@ -1,75 +0,0 @@ -//go:build pivtest - -/* -Copyright 2024 Gravitational, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package keys - -import ( - "context" - "crypto" - "crypto/ed25519" - "crypto/rand" - "errors" - - "github.com/gravitational/trace" -) - -var errPIVUnavailable = errors.New("PIV is unavailable in current build") - -// Return a fake YubiKey private key. -func getOrGenerateYubiKeyPrivateKey(_ context.Context, policy PrivateKeyPolicy, _ PIVSlot, _ HardwareKeyPrompt) (*PrivateKey, error) { - _, priv, err := ed25519.GenerateKey(rand.Reader) - if err != nil { - return nil, trace.Wrap(err) - } - - keyPEM, err := MarshalPrivateKey(priv) - if err != nil { - return nil, trace.Wrap(err) - } - - signer := &fakeYubiKeyPrivateKey{ - Signer: priv, - privateKeyPolicy: policy, - } - - return NewPrivateKey(signer, keyPEM) -} - -func parseYubiKeyPrivateKeyData(_ []byte, _ HardwareKeyPrompt) (*PrivateKey, error) { - // TODO(Joerger): add custom marshal/unmarshal logic for fakeYubiKeyPrivateKey (if necessary). - return nil, trace.Wrap(errPIVUnavailable) -} - -func (s PIVSlot) validate() error { - return trace.Wrap(errPIVUnavailable) -} - -type fakeYubiKeyPrivateKey struct { - crypto.Signer - privateKeyPolicy PrivateKeyPolicy -} - -// GetAttestationStatement returns an AttestationStatement for this private key. -func (y *fakeYubiKeyPrivateKey) GetAttestationStatement() *AttestationStatement { - // Since this is only used in tests, we will ignore the attestation statement in the end. - // We just need it to be non-nil so that it goes through the test modules implementation - // of AttestHardwareKey. - return &AttestationStatement{} -} - -// GetPrivateKeyPolicy returns the PrivateKeyPolicy supported by this private key. -func (y *fakeYubiKeyPrivateKey) GetPrivateKeyPolicy() PrivateKeyPolicy { - return y.privateKeyPolicy -} diff --git a/api/utils/keys/yubikey_other.go b/api/utils/keys/yubikey_other.go deleted file mode 100644 index 59993e0b52191..0000000000000 --- a/api/utils/keys/yubikey_other.go +++ /dev/null @@ -1,37 +0,0 @@ -//go:build !piv && !pivtest - -/* -Copyright 2022 Gravitational, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package keys - -import ( - "context" - "errors" - - "github.com/gravitational/trace" -) - -var errPIVUnavailable = errors.New("PIV is unavailable in current build") - -func getOrGenerateYubiKeyPrivateKey(ctx context.Context, policy PrivateKeyPolicy, slot PIVSlot, _ HardwareKeyPrompt) (*PrivateKey, error) { - return nil, trace.Wrap(errPIVUnavailable) -} - -func parseYubiKeyPrivateKeyData(keyDataBytes []byte, _ HardwareKeyPrompt) (*PrivateKey, error) { - return nil, trace.Wrap(errPIVUnavailable) -} - -func (s PIVSlot) validate() error { - return trace.Wrap(errPIVUnavailable) -} diff --git a/api/utils/keys/yubikey_test.go b/api/utils/keys/yubikey_test.go deleted file mode 100644 index 72ac01537041c..0000000000000 --- a/api/utils/keys/yubikey_test.go +++ /dev/null @@ -1,167 +0,0 @@ -//go:build piv - -/* -Copyright 2022 Gravitational, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package keys_test - -import ( - "context" - "crypto/rand" - "crypto/x509/pkix" - "fmt" - "os" - "testing" - - "github.com/go-piv/piv-go/piv" - "github.com/gravitational/trace" - "github.com/stretchr/testify/require" - - "github.com/gravitational/teleport/api/utils/keys" - "github.com/gravitational/teleport/api/utils/prompt" -) - -// TestGetYubiKeyPrivateKey_Interactive tests generation and retrieval of YubiKey private keys. -func TestGetYubiKeyPrivateKey_Interactive(t *testing.T) { - // This test expects a yubiKey to be connected with default PIV - // settings and will overwrite any PIV data on the yubiKey. - if os.Getenv("TELEPORT_TEST_YUBIKEY_PIV") == "" { - t.Skipf("Skipping TestGenerateYubiKeyPrivateKey because TELEPORT_TEST_YUBIKEY_PIV is not set") - } - - if !testing.Verbose() { - t.Fatal("This test is interactive and must be called with the -v verbose flag to see touch prompts.") - } - fmt.Println("This test is interactive, tap your YubiKey when prompted.") - - ctx := context.Background() - - y, err := keys.FindYubiKey(0, nil) - require.NoError(t, err) - - t.Cleanup(func() { resetYubikey(t, y) }) - - for _, policy := range []keys.PrivateKeyPolicy{ - keys.PrivateKeyPolicyHardwareKey, - keys.PrivateKeyPolicyHardwareKeyTouch, - keys.PrivateKeyPolicyHardwareKeyPIN, - keys.PrivateKeyPolicyHardwareKeyTouchAndPIN, - } { - for _, customSlot := range []bool{true, false} { - t.Run(fmt.Sprintf("policy:%q", policy), func(t *testing.T) { - t.Run(fmt.Sprintf("custom slot:%v", customSlot), func(t *testing.T) { - resetYubikey(t, y) - setupPINPrompt(t, y) - - var slot keys.PIVSlot = "" - if customSlot { - slot = "9a" - } - - // GetYubiKeyPrivateKey should generate a new YubiKeyPrivateKey. - priv, err := keys.GetYubiKeyPrivateKey(ctx, policy, slot, nil) - require.NoError(t, err) - - // test HardwareSigner methods - require.Equal(t, policy, priv.GetPrivateKeyPolicy()) - require.NotNil(t, priv.GetAttestationStatement()) - - // Test Sign. - digest := []byte{100} - _, err = priv.Sign(rand.Reader, digest, nil) - require.NoError(t, err) - - // Another call to GetYubiKeyPrivateKey should retrieve the previously generated key. - retrievePriv, err := keys.GetYubiKeyPrivateKey(ctx, policy, slot, nil) - require.NoError(t, err) - require.Equal(t, priv.Public(), retrievePriv.Public()) - - // parsing the key's private key PEM should produce the same key as well. - retrievePriv, err = keys.ParsePrivateKey(priv.PrivateKeyPEM()) - require.NoError(t, err) - require.Equal(t, priv.Public(), retrievePriv.Public()) - }) - }) - } - } -} - -func TestOverwritePrompt(t *testing.T) { - // This test expects a yubiKey to be connected with default PIV - // settings and will overwrite any PIV data on the yubiKey. - if os.Getenv("TELEPORT_TEST_YUBIKEY_PIV") == "" { - t.Skipf("Skipping TestGenerateYubiKeyPrivateKey because TELEPORT_TEST_YUBIKEY_PIV is not set") - } - - ctx := context.Background() - - y, err := keys.FindYubiKey(0, nil) - require.NoError(t, err) - - t.Cleanup(func() { resetYubikey(t, y) }) - - // Use a custom slot. - pivSlot, err := keys.GetDefaultKeySlot(keys.PrivateKeyPolicyHardwareKeyTouch) - require.NoError(t, err) - - testOverwritePrompt := func(t *testing.T) { - // Fail to overwrite slot when user denies - prompt.SetStdin(prompt.NewFakeReader().AddString("n")) - _, err := keys.GetYubiKeyPrivateKey(ctx, keys.PrivateKeyPolicyHardwareKeyTouch, "" /* slot */, nil) - require.True(t, trace.IsCompareFailed(err), "Expected compare failed error but got %v", err) - - // Successfully overwrite slot when user accepts - prompt.SetStdin(prompt.NewFakeReader().AddString("y")) - _, err = keys.GetYubiKeyPrivateKey(ctx, keys.PrivateKeyPolicyHardwareKeyTouch, "" /* slot */, nil) - require.NoError(t, err) - } - - t.Run("invalid metadata cert", func(t *testing.T) { - resetYubikey(t, y) - - // Set a non-teleport certificate in the slot. - err = y.SetMetadataCertificate(pivSlot, pkix.Name{Organization: []string{"not-teleport"}}) - require.NoError(t, err) - - testOverwritePrompt(t) - }) - - t.Run("invalid key policies", func(t *testing.T) { - resetYubikey(t, y) - - // Generate a key that does not require touch in the slot that Teleport expects to require touch. - _, err := keys.GetYubiKeyPrivateKey(ctx, keys.PrivateKeyPolicyHardwareKey, keys.PIVSlot(pivSlot.String()), nil) - require.NoError(t, err) - - testOverwritePrompt(t) - }) -} - -// resetYubikey connects to the first yubiKey and resets it to defaults. -func resetYubikey(t *testing.T, y *keys.YubiKey) { - t.Helper() - require.NoError(t, y.Reset()) -} - -func setupPINPrompt(t *testing.T, y *keys.YubiKey) { - t.Helper() - - // Set pin for tests. - const testPIN = "123123" - require.NoError(t, y.SetPIN(piv.DefaultPIN, testPIN)) - - // Handle PIN prompt. - oldStdin := prompt.Stdin() - t.Cleanup(func() { prompt.SetStdin(oldStdin) }) - prompt.SetStdin(prompt.NewFakeReader().AddString(testPIN).AddString(testPIN)) -} diff --git a/docs/pages/admin-guides/access-controls/guides/hardware-key-support.mdx b/docs/pages/admin-guides/access-controls/guides/hardware-key-support.mdx index 9d6e37314071c..a15984da3617d 100644 --- a/docs/pages/admin-guides/access-controls/guides/hardware-key-support.mdx +++ b/docs/pages/admin-guides/access-controls/guides/hardware-key-support.mdx @@ -202,12 +202,27 @@ version: v2 Teleport clients generate keys in the slots specified using the default management key. If your PIV key uses a different management key, you must generate the key yourself. -This can be done with the [YubiKey Manager CLI](https://developers.yubico.com/yubikey-manager/): +This can be done with the [YubiKey Manager CLI](https://developers.yubico.com/yubikey-manager/). +This command will prompt you to enter your management key to complete the request: -`ykman piv keys generate -a ECCP256 [slot] --touch-policy=[never|cached|always] --pin-policy=[never|once|always] -` +```code +$ ykman piv keys generate -a ECCP256 [slot] --touch-policy=[never|cached|always] --pin-policy=[never|once|always] pub.pem +``` + +For some features to work, you must also generate a certificate on the slot to mark +the slot for use by Teleport. This certificate can be self signed, or in the example +below, signed by the key in the PIV slot. The important detail is that the certificate +has "teleport" as the organization name in the subject field. + +```code +$ ykman piv certificates generate 9e -s O=teleport pub.pem +``` + +This command will prompt you for the management key, as well as PIN or touch depending +on the policies of the key in the PIV slot. -After running this command, you're prompted to enter your management key to complete the request. -Make sure that the touch and PIN policy satisfy the hardware key requirement for your cluster and roles. +Make sure that the touch and PIN policies of the key satisfy the hardware key requirement +for your cluster and roles. ## Troubleshooting diff --git a/docs/pages/reference/terraform-provider/data-sources/auth_preference.mdx b/docs/pages/reference/terraform-provider/data-sources/auth_preference.mdx index b77b2465c3ea4..a71359bc3622d 100644 --- a/docs/pages/reference/terraform-provider/data-sources/auth_preference.mdx +++ b/docs/pages/reference/terraform-provider/data-sources/auth_preference.mdx @@ -60,6 +60,7 @@ Optional: Optional: +- `pin_cache_ttl` (String) PinCacheTTL is the amount of time in nanoseconds that Teleport clients will cache the user's PIV PIN when hardware key PIN policy is enabled. - `piv_slot` (String) PIVSlot is a PIV slot that Teleport clients should use instead of the default based on private key policy. For example, "9a" or "9e". - `serial_number_validation` (Attributes) SerialNumberValidation holds settings for hardware key serial number validation. By default, serial number validation is disabled. (see [below for nested schema](#nested-schema-for-spechardware_keyserial_number_validation)) diff --git a/docs/pages/reference/terraform-provider/resources/auth_preference.mdx b/docs/pages/reference/terraform-provider/resources/auth_preference.mdx index 3b1dd96230cab..f6972052a39ea 100644 --- a/docs/pages/reference/terraform-provider/resources/auth_preference.mdx +++ b/docs/pages/reference/terraform-provider/resources/auth_preference.mdx @@ -79,6 +79,7 @@ Optional: Optional: +- `pin_cache_ttl` (String) PinCacheTTL is the amount of time in nanoseconds that Teleport clients will cache the user's PIV PIN when hardware key PIN policy is enabled. - `piv_slot` (String) PIVSlot is a PIV slot that Teleport clients should use instead of the default based on private key policy. For example, "9a" or "9e". - `serial_number_validation` (Attributes) SerialNumberValidation holds settings for hardware key serial number validation. By default, serial number validation is disabled. (see [below for nested schema](#nested-schema-for-spechardware_keyserial_number_validation)) diff --git a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go index 4a6dac5a55d88..7db5c8a12de42 100644 --- a/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/v1/tshd_events_service.pb.go @@ -744,10 +744,13 @@ func (x *PromptMFAResponse) GetTotpCode() string { // Request for PromptHardwareKeyPIN. type PromptHardwareKeyPINRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - RootClusterUri string `protobuf:"bytes,1,opt,name=root_cluster_uri,json=rootClusterUri,proto3" json:"root_cluster_uri,omitempty"` - // Specifies if a PIN is optional, allowing the user to set it up if left empty. - PinOptional bool `protobuf:"varint,2,opt,name=pin_optional,json=pinOptional,proto3" json:"pin_optional,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + // PinOptional specified if a PIN is optional, allowing the user to set it up if left empty. + PinOptional bool `protobuf:"varint,2,opt,name=pin_optional,json=pinOptional,proto3" json:"pin_optional,omitempty"` + // ProxyHostname is the proxy hostname of the client key. + ProxyHostname string `protobuf:"bytes,3,opt,name=proxy_hostname,json=proxyHostname,proto3" json:"proxy_hostname,omitempty"` + // Command is an optional command string to provide context for the prompt. + Command string `protobuf:"bytes,4,opt,name=command,proto3" json:"command,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -782,18 +785,25 @@ func (*PromptHardwareKeyPINRequest) Descriptor() ([]byte, []int) { return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP(), []int{12} } -func (x *PromptHardwareKeyPINRequest) GetRootClusterUri() string { +func (x *PromptHardwareKeyPINRequest) GetPinOptional() bool { if x != nil { - return x.RootClusterUri + return x.PinOptional + } + return false +} + +func (x *PromptHardwareKeyPINRequest) GetProxyHostname() string { + if x != nil { + return x.ProxyHostname } return "" } -func (x *PromptHardwareKeyPINRequest) GetPinOptional() bool { +func (x *PromptHardwareKeyPINRequest) GetCommand() string { if x != nil { - return x.PinOptional + return x.Command } - return false + return "" } // Response for PromptHardwareKeyPIN. @@ -844,10 +854,13 @@ func (x *PromptHardwareKeyPINResponse) GetPin() string { // Request for PromptHardwareKeyTouchRequest. type PromptHardwareKeyTouchRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - RootClusterUri string `protobuf:"bytes,1,opt,name=root_cluster_uri,json=rootClusterUri,proto3" json:"root_cluster_uri,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + // ProxyHostname is the proxy hostname of the client key. + ProxyHostname string `protobuf:"bytes,2,opt,name=proxy_hostname,json=proxyHostname,proto3" json:"proxy_hostname,omitempty"` + // Command is an optional command string to provide context for the prompt. + Command string `protobuf:"bytes,3,opt,name=command,proto3" json:"command,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *PromptHardwareKeyTouchRequest) Reset() { @@ -880,9 +893,16 @@ func (*PromptHardwareKeyTouchRequest) Descriptor() ([]byte, []int) { return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP(), []int{14} } -func (x *PromptHardwareKeyTouchRequest) GetRootClusterUri() string { +func (x *PromptHardwareKeyTouchRequest) GetProxyHostname() string { if x != nil { - return x.RootClusterUri + return x.ProxyHostname + } + return "" +} + +func (x *PromptHardwareKeyTouchRequest) GetCommand() string { + if x != nil { + return x.Command } return "" } @@ -926,10 +946,11 @@ func (*PromptHardwareKeyTouchResponse) Descriptor() ([]byte, []int) { // Response for PromptHardwareKeyPINChange. type PromptHardwareKeyPINChangeRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - RootClusterUri string `protobuf:"bytes,1,opt,name=root_cluster_uri,json=rootClusterUri,proto3" json:"root_cluster_uri,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + // ProxyHostname is the proxy hostname of the client key. + ProxyHostname string `protobuf:"bytes,2,opt,name=proxy_hostname,json=proxyHostname,proto3" json:"proxy_hostname,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *PromptHardwareKeyPINChangeRequest) Reset() { @@ -962,9 +983,9 @@ func (*PromptHardwareKeyPINChangeRequest) Descriptor() ([]byte, []int) { return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP(), []int{16} } -func (x *PromptHardwareKeyPINChangeRequest) GetRootClusterUri() string { +func (x *PromptHardwareKeyPINChangeRequest) GetProxyHostname() string { if x != nil { - return x.RootClusterUri + return x.ProxyHostname } return "" } @@ -1036,10 +1057,11 @@ func (x *PromptHardwareKeyPINChangeResponse) GetPukChanged() bool { // Request for ConfirmHardwareKeySlotOverwrite. type ConfirmHardwareKeySlotOverwriteRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - RootClusterUri string `protobuf:"bytes,1,opt,name=root_cluster_uri,json=rootClusterUri,proto3" json:"root_cluster_uri,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` // Message to display in the prompt. - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + // ProxyHostname is the proxy hostname of the client key. + ProxyHostname string `protobuf:"bytes,3,opt,name=proxy_hostname,json=proxyHostname,proto3" json:"proxy_hostname,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1074,16 +1096,16 @@ func (*ConfirmHardwareKeySlotOverwriteRequest) Descriptor() ([]byte, []int) { return file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDescGZIP(), []int{18} } -func (x *ConfirmHardwareKeySlotOverwriteRequest) GetRootClusterUri() string { +func (x *ConfirmHardwareKeySlotOverwriteRequest) GetMessage() string { if x != nil { - return x.RootClusterUri + return x.Message } return "" } -func (x *ConfirmHardwareKeySlotOverwriteRequest) GetMessage() string { +func (x *ConfirmHardwareKeySlotOverwriteRequest) GetProxyHostname() string { if x != nil { - return x.Message + return x.ProxyHostname } return "" } @@ -1445,163 +1467,171 @@ var file_teleport_lib_teleterm_v1_tshd_events_service_proto_rawDesc = string([]b 0x5f, 0x75, 0x72, 0x69, 0x22, 0x30, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x74, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, - 0x74, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x6a, 0x0a, 0x1b, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, - 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x72, 0x6f, 0x6f, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x55, 0x72, 0x69, 0x12, - 0x21, 0x0a, 0x0c, 0x70, 0x69, 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x69, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x61, 0x6c, 0x22, 0x30, 0x0a, 0x1c, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, + 0x74, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x99, 0x01, 0x0a, 0x1b, 0x50, 0x72, 0x6f, 0x6d, 0x70, + 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x69, 0x6e, 0x5f, 0x6f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x70, 0x69, + 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, + 0x78, 0x79, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, + 0x52, 0x10, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x75, + 0x72, 0x69, 0x22, 0x30, 0x0a, 0x1c, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x70, 0x69, 0x6e, 0x22, 0x49, 0x0a, 0x1d, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, + 0x03, 0x70, 0x69, 0x6e, 0x22, 0x78, 0x0a, 0x1d, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x75, 0x63, 0x68, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x72, 0x6f, 0x6f, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x55, 0x72, 0x69, 0x22, - 0x20, 0x0a, 0x1e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, - 0x65, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x75, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x4d, 0x0a, 0x21, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, - 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, - 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x72, 0x6f, 0x6f, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x55, 0x72, 0x69, - 0x22, 0x69, 0x0a, 0x22, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x68, + 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x52, 0x10, 0x72, 0x6f, + 0x6f, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x69, 0x22, 0x20, + 0x0a, 0x1e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, + 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x75, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x62, 0x0a, 0x21, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x75, 0x6b, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x75, 0x6b, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x75, - 0x6b, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0a, 0x70, 0x75, 0x6b, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x22, 0x6c, 0x0a, 0x26, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, - 0x79, 0x53, 0x6c, 0x6f, 0x74, 0x4f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x6c, - 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x72, 0x6f, 0x6f, 0x74, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x55, 0x72, 0x69, 0x12, - 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x47, 0x0a, 0x27, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x72, 0x6d, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x53, - 0x6c, 0x6f, 0x74, 0x4f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, - 0x65, 0x64, 0x22, 0x22, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8f, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x55, 0x73, - 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, - 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x18, - 0x75, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, - 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x52, 0x16, 0x75, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x32, 0x0a, 0x16, 0x55, 0x73, 0x61, 0x67, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x68, + 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x01, + 0x10, 0x02, 0x52, 0x10, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x75, 0x72, 0x69, 0x22, 0x69, 0x0a, 0x22, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, + 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x69, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, + 0x70, 0x75, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x75, 0x6b, 0x12, 0x1f, + 0x0a, 0x0b, 0x70, 0x75, 0x6b, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0a, 0x70, 0x75, 0x6b, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x22, + 0x81, 0x01, 0x0a, 0x26, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x48, 0x61, 0x72, 0x64, 0x77, + 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x53, 0x6c, 0x6f, 0x74, 0x4f, 0x76, 0x65, 0x72, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x68, 0x6f, + 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x4a, 0x04, 0x08, 0x01, 0x10, + 0x02, 0x52, 0x10, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, + 0x75, 0x72, 0x69, 0x22, 0x47, 0x0a, 0x27, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x48, 0x61, + 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x53, 0x6c, 0x6f, 0x74, 0x4f, 0x76, 0x65, + 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1c, + 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x65, 0x64, 0x22, 0x22, 0x0a, 0x20, + 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, + 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x22, 0x8f, 0x01, 0x0a, 0x21, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x18, 0x75, 0x73, 0x61, 0x67, 0x65, 0x5f, + 0x72, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, + 0x67, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, + 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x16, 0x75, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, - 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x3b, 0x0a, 0x23, - 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, - 0x56, 0x6e, 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x26, 0x0a, 0x24, 0x52, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, - 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x32, 0x93, 0x0b, 0x0a, 0x11, 0x54, 0x73, 0x68, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5e, 0x0a, 0x07, 0x52, 0x65, 0x6c, 0x6f, 0x67, - 0x69, 0x6e, 0x12, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, - 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x74, + 0x67, 0x73, 0x22, 0x32, 0x0a, 0x16, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x18, 0x0a, 0x07, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22, 0x3b, 0x0a, 0x23, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, 0x74, 0x53, 0x68, + 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x22, 0x26, 0x0a, 0x24, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, + 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, + 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x93, 0x0b, 0x0a, 0x11, + 0x54, 0x73, 0x68, 0x64, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x12, 0x5e, 0x0a, 0x07, 0x52, 0x65, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x28, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x79, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0xac, 0x01, 0x0a, 0x21, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, - 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x42, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x79, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, + 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, - 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x43, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, - 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x64, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x12, 0x2a, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, - 0x4d, 0x46, 0x41, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x85, 0x01, 0x0a, 0x14, 0x50, 0x72, 0x6f, 0x6d, - 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, - 0x12, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, - 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, - 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, - 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x8b, 0x01, 0x0a, 0x16, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, - 0x72, 0x65, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x75, 0x63, 0x68, 0x12, 0x37, 0x2e, 0x74, 0x65, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0xac, 0x01, 0x0a, + 0x21, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, + 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x42, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, + 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, 0x64, 0x6c, 0x65, 0x73, + 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x43, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x50, 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x61, + 0x64, 0x6c, 0x65, 0x73, 0x73, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x09, 0x50, + 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x12, 0x2a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x4d, 0x46, 0x41, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x85, 0x01, 0x0a, 0x14, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, + 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, 0x12, 0x35, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, - 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x75, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, + 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x36, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, + 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, + 0x4e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8b, 0x01, 0x0a, 0x16, 0x50, 0x72, + 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x54, + 0x6f, 0x75, 0x63, 0x68, 0x12, 0x37, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, + 0x79, 0x54, 0x6f, 0x75, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, + 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x54, 0x6f, 0x75, 0x63, 0x68, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x97, 0x01, 0x0a, 0x1a, 0x50, 0x72, 0x6f, 0x6d, + 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x3b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, + 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, + 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, - 0x54, 0x6f, 0x75, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x97, 0x01, - 0x0a, 0x1a, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, - 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x3b, 0x2e, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, - 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, 0x43, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x74, 0x65, 0x6c, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, - 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x48, 0x61, 0x72, 0x64, 0x77, - 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x49, 0x4e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0xa6, 0x01, 0x0a, 0x1f, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x72, 0x6d, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x53, 0x6c, - 0x6f, 0x74, 0x4f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x12, 0x40, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x48, 0x61, - 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x53, 0x6c, 0x6f, 0x74, 0x4f, 0x76, 0x65, - 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, - 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x53, 0x6c, 0x6f, 0x74, 0x4f, - 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x94, 0x01, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, - 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, - 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x9d, 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, 0x74, - 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x3d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, - 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, + 0x50, 0x49, 0x4e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0xa6, 0x01, 0x0a, 0x1f, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x48, 0x61, 0x72, + 0x64, 0x77, 0x61, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x53, 0x6c, 0x6f, 0x74, 0x4f, 0x76, 0x65, 0x72, + 0x77, 0x72, 0x69, 0x74, 0x65, 0x12, 0x40, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, + 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, 0x72, 0x65, + 0x4b, 0x65, 0x79, 0x53, 0x6c, 0x6f, 0x74, 0x4f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x41, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, - 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, - 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x54, 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, - 0x72, 0x74, 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2f, - 0x76, 0x31, 0x3b, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x76, 0x31, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x48, 0x61, 0x72, 0x64, 0x77, 0x61, + 0x72, 0x65, 0x4b, 0x65, 0x79, 0x53, 0x6c, 0x6f, 0x74, 0x4f, 0x76, 0x65, 0x72, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x94, 0x01, 0x0a, 0x19, 0x47, + 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, 0x67, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x3a, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, + 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, + 0x72, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x6c, 0x69, 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, + 0x47, 0x65, 0x74, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x69, 0x6e, + 0x67, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x9d, 0x01, 0x0a, 0x1c, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, + 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, + 0x77, 0x6e, 0x12, 0x3d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, + 0x62, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, + 0x65, 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x3e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x6c, 0x69, 0x62, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x55, 0x6e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x56, 0x6e, 0x65, + 0x74, 0x53, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x42, 0x54, 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x6c, 0x69, 0x62, + 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x2f, 0x76, 0x31, 0x3b, 0x74, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x72, 0x6d, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( diff --git a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts index 6bc1ef96ed469..61ef541738222 100644 --- a/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts +++ b/gen/proto/ts/teleport/lib/teleterm/v1/tshd_events_service_pb.ts @@ -253,15 +253,23 @@ export interface PromptMFAResponse { */ export interface PromptHardwareKeyPINRequest { /** - * @generated from protobuf field: string root_cluster_uri = 1; - */ - rootClusterUri: string; - /** - * Specifies if a PIN is optional, allowing the user to set it up if left empty. + * PinOptional specified if a PIN is optional, allowing the user to set it up if left empty. * * @generated from protobuf field: bool pin_optional = 2; */ pinOptional: boolean; + /** + * ProxyHostname is the proxy hostname of the client key. + * + * @generated from protobuf field: string proxy_hostname = 3; + */ + proxyHostname: string; + /** + * Command is an optional command string to provide context for the prompt. + * + * @generated from protobuf field: string command = 4; + */ + command: string; } /** * Response for PromptHardwareKeyPIN. @@ -283,9 +291,17 @@ export interface PromptHardwareKeyPINResponse { */ export interface PromptHardwareKeyTouchRequest { /** - * @generated from protobuf field: string root_cluster_uri = 1; + * ProxyHostname is the proxy hostname of the client key. + * + * @generated from protobuf field: string proxy_hostname = 2; */ - rootClusterUri: string; + proxyHostname: string; + /** + * Command is an optional command string to provide context for the prompt. + * + * @generated from protobuf field: string command = 3; + */ + command: string; } /** * Response for PromptHardwareKeyTouch. @@ -301,9 +317,11 @@ export interface PromptHardwareKeyTouchResponse { */ export interface PromptHardwareKeyPINChangeRequest { /** - * @generated from protobuf field: string root_cluster_uri = 1; + * ProxyHostname is the proxy hostname of the client key. + * + * @generated from protobuf field: string proxy_hostname = 2; */ - rootClusterUri: string; + proxyHostname: string; } /** * Response for PromptHardwareKeyPINChange. @@ -337,16 +355,18 @@ export interface PromptHardwareKeyPINChangeResponse { * @generated from protobuf message teleport.lib.teleterm.v1.ConfirmHardwareKeySlotOverwriteRequest */ export interface ConfirmHardwareKeySlotOverwriteRequest { - /** - * @generated from protobuf field: string root_cluster_uri = 1; - */ - rootClusterUri: string; /** * Message to display in the prompt. * * @generated from protobuf field: string message = 2; */ message: string; + /** + * ProxyHostname is the proxy hostname of the client key. + * + * @generated from protobuf field: string proxy_hostname = 3; + */ + proxyHostname: string; } /** * Response for ConfirmHardwareKeySlotOverwrite. @@ -1036,14 +1056,16 @@ export const PromptMFAResponse = new PromptMFAResponse$Type(); class PromptHardwareKeyPINRequest$Type extends MessageType { constructor() { super("teleport.lib.teleterm.v1.PromptHardwareKeyPINRequest", [ - { no: 1, name: "root_cluster_uri", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "pin_optional", kind: "scalar", T: 8 /*ScalarType.BOOL*/ } + { no: 2, name: "pin_optional", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + { no: 3, name: "proxy_hostname", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 4, name: "command", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } create(value?: PartialMessage): PromptHardwareKeyPINRequest { const message = globalThis.Object.create((this.messagePrototype!)); - message.rootClusterUri = ""; message.pinOptional = false; + message.proxyHostname = ""; + message.command = ""; if (value !== undefined) reflectionMergePartial(this, message, value); return message; @@ -1053,12 +1075,15 @@ class PromptHardwareKeyPINRequest$Type extends MessageType { constructor() { super("teleport.lib.teleterm.v1.PromptHardwareKeyTouchRequest", [ - { no: 1, name: "root_cluster_uri", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + { no: 2, name: "proxy_hostname", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "command", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } create(value?: PartialMessage): PromptHardwareKeyTouchRequest { const message = globalThis.Object.create((this.messagePrototype!)); - message.rootClusterUri = ""; + message.proxyHostname = ""; + message.command = ""; if (value !== undefined) reflectionMergePartial(this, message, value); return message; @@ -1153,8 +1183,11 @@ class PromptHardwareKeyTouchRequest$Type extends MessageType { constructor() { super("teleport.lib.teleterm.v1.PromptHardwareKeyPINChangeRequest", [ - { no: 1, name: "root_cluster_uri", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + { no: 2, name: "proxy_hostname", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } create(value?: PartialMessage): PromptHardwareKeyPINChangeRequest { const message = globalThis.Object.create((this.messagePrototype!)); - message.rootClusterUri = ""; + message.proxyHostname = ""; if (value !== undefined) reflectionMergePartial(this, message, value); return message; @@ -1225,8 +1261,8 @@ class PromptHardwareKeyPINChangeRequest$Type extends MessageType { constructor() { super("teleport.lib.teleterm.v1.ConfirmHardwareKeySlotOverwriteRequest", [ - { no: 1, name: "root_cluster_uri", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "message", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + { no: 2, name: "message", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "proxy_hostname", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } create(value?: PartialMessage): ConfirmHardwareKeySlotOverwriteRequest { const message = globalThis.Object.create((this.messagePrototype!)); - message.rootClusterUri = ""; message.message = ""; + message.proxyHostname = ""; if (value !== undefined) reflectionMergePartial(this, message, value); return message; @@ -1337,12 +1373,12 @@ class ConfirmHardwareKeySlotOverwriteRequest$Type extends MessageType 1 { + return trace.BadParameter("only one jump host is supported, got %v", len(c.JumpHosts)) + } + + c.Namespace = types.ProcessNamespace(c.Namespace) + + // set defaults + if c.Stdout == nil { + c.Stdout = os.Stdout + } + if c.Stderr == nil { + c.Stderr = os.Stderr + } + if c.Stdin == nil { + c.Stdin = os.Stdin + } + if c.AddKeysToAgent == "" { + c.AddKeysToAgent = AddKeysToAgentAuto + } + if c.Tracer == nil { + c.Tracer = tracing.NoopProvider().Tracer("TeleportClient") } + return nil } // VirtualPathKind is the suffix component for env vars denoting the type of @@ -821,13 +874,16 @@ func IsErrorResolvableWithRelogin(err error) bool { IsNoCredentialsError(err) } -// GetProfile gets the profile for the specified proxy address, or -// the current profile if no proxy is specified. -func (c *Config) GetProfile(ps ProfileStore, proxyAddr string) (*profile.Profile, error) { +// GetProfile gets the client profile for the specified proxy address. +func (c *Config) GetProfile(proxyAddr string) (*profile.Profile, error) { + if c.ClientStore == nil { + return nil, trace.BadParameter("client store can not be nil") + } + var proxyHost string var err error if proxyAddr == "" { - proxyHost, err = ps.CurrentProfile() + proxyHost, err = c.ClientStore.CurrentProfile() if err != nil { return nil, trace.Wrap(err) } @@ -838,18 +894,17 @@ func (c *Config) GetProfile(ps ProfileStore, proxyAddr string) (*profile.Profile } } - profile, err := ps.GetProfile(proxyHost) + profile, err := c.ClientStore.GetProfile(proxyHost) if err != nil { return nil, trace.Wrap(err) } return profile, nil } -// LoadProfile populates Config with the values stored in the given -// profiles directory. If profileDir is an empty string, the default profile -// directory ~/.tsh is used. -func (c *Config) LoadProfile(ps ProfileStore, proxyAddr string) error { - profile, err := c.GetProfile(ps, proxyAddr) +// LoadProfile populates Config with the values stored in the client +// profile for the specified proxy address. +func (c *Config) LoadProfile(proxyAddr string) error { + profile, err := c.GetProfile(proxyAddr) if err != nil { return trace.Wrap(err) } @@ -864,11 +919,11 @@ func (c *Config) LoadProfile(ps ProfileStore, proxyAddr string) error { c.MongoProxyAddr = profile.MongoProxyAddr c.TLSRoutingEnabled = profile.TLSRoutingEnabled c.TLSRoutingConnUpgradeRequired = profile.TLSRoutingConnUpgradeRequired - c.KeysDir = profile.Dir c.AuthConnector = profile.AuthConnector c.LoadAllCAs = profile.LoadAllCAs c.PrivateKeyPolicy = profile.PrivateKeyPolicy c.PIVSlot = profile.PIVSlot + c.PIVPINCacheTTL = profile.PIVPINCacheTTL c.SAMLSingleLogoutEnabled = profile.SAMLSingleLogoutEnabled c.SSHDialTimeout = profile.SSHDialTimeout c.AuthenticatorAttachment, err = parseMFAMode(profile.MFAMode) @@ -919,6 +974,7 @@ func (c *Config) Profile() *profile.Profile { LoadAllCAs: c.LoadAllCAs, PrivateKeyPolicy: c.PrivateKeyPolicy, PIVSlot: c.PIVSlot, + PIVPINCacheTTL: c.PIVPINCacheTTL, SAMLSingleLogoutEnabled: c.SAMLSingleLogoutEnabled, SSHDialTimeout: c.SSHDialTimeout, } @@ -1209,65 +1265,14 @@ type ShellCreatedCallback func(s *tracessh.Session, c *tracessh.Client, terminal // NewClient creates a TeleportClient object and fully configures it func NewClient(c *Config) (tc *TeleportClient, err error) { - if len(c.JumpHosts) > 1 { - return nil, trace.BadParameter("only one jump host is supported, got %v", len(c.JumpHosts)) - } - // validate configuration - if c.Username == "" { - c.Username, err = Username() - if err != nil { - return nil, trace.Wrap(err) - } - log.Infof("No teleport login given. defaulting to %s", c.Username) - } - if c.WebProxyAddr == "" { - return nil, trace.BadParameter("No proxy address specified, missed --proxy flag?") - } - if c.HostLogin == "" { - c.HostLogin, err = Username() - if err != nil { - return nil, trace.Wrap(err) - } - log.Infof("no host login given. defaulting to %s", c.HostLogin) - } - - c.Namespace = types.ProcessNamespace(c.Namespace) - - if c.Tracer == nil { - c.Tracer = tracing.NoopProvider().Tracer(teleport.ComponentTeleport) + if err := c.CheckAndSetDefaults(); err != nil { + return nil, trace.Wrap(err) } tc = &TeleportClient{ Config: *c, } - if tc.Stdout == nil { - tc.Stdout = os.Stdout - } - if tc.Stderr == nil { - tc.Stderr = os.Stderr - } - if tc.Stdin == nil { - tc.Stdin = os.Stdin - } - - if tc.ClientStore == nil { - if tc.TLS != nil || tc.AuthMethods != nil { - // Client will use static auth methods instead of client store. - // Initialize empty client store to prevent panics. - tc.ClientStore = NewMemClientStore() - } else { - tc.ClientStore = NewFSClientStore(c.KeysDir) - if c.CustomHardwareKeyPrompt != nil { - tc.ClientStore.SetCustomHardwareKeyPrompt(tc.CustomHardwareKeyPrompt) - } - if c.AddKeysToAgent == AddKeysToAgentOnly { - // Store client keys in memory, but still save trusted certs and profile to disk. - tc.ClientStore.KeyStore = NewMemKeyStore() - } - } - } - // Create a buffered channel to hold events that occurred during this session. // This channel must be buffered because the SSH connection directly feeds // into it. Delays in pulling messages off the global SSH request channel @@ -3939,7 +3944,16 @@ func (tc *TeleportClient) GetNewLoginKey(ctx context.Context) (priv *keys.Privat if tc.PIVSlot != "" { log.Debugf("Using PIV slot %q specified by client or server settings.", tc.PIVSlot) } - priv, err = keys.GetYubiKeyPrivateKey(ctx, tc.PrivateKeyPolicy, tc.PIVSlot, tc.CustomHardwareKeyPrompt) + priv, err := tc.ClientStore.NewHardwarePrivateKey(ctx, hardwarekey.PrivateKeyConfig{ + Policy: tc.PrivateKeyPolicy.GetPromptPolicy(), + CustomSlot: tc.PIVSlot, + ContextualKeyInfo: hardwarekey.ContextualKeyInfo{ + ProxyHost: tc.WebProxyHost(), + Username: tc.Username, + ClusterName: tc.SiteName, + }, + PINCacheTTL: tc.PIVPINCacheTTL, + }) if err != nil { return nil, trace.Wrap(err) } @@ -4598,6 +4612,7 @@ func (tc *TeleportClient) applyProxySettings(proxySettings webclient.ProxySettin // authentication settings, overriding existing fields in tc. func (tc *TeleportClient) applyAuthSettings(authSettings webclient.AuthenticationSettings) { tc.LoadAllCAs = authSettings.LoadAllCAs + tc.PIVPINCacheTTL = authSettings.PIVPINCacheTTL // If PIVSlot is not already set, default to the server setting. if tc.PIVSlot == "" { diff --git a/lib/client/api_login_test.go b/lib/client/api_login_test.go index 04cf604bc10a9..11f26a35c1959 100644 --- a/lib/client/api_login_test.go +++ b/lib/client/api_login_test.go @@ -273,7 +273,8 @@ func TestTeleportClient_Login_local(t *testing.T) { otpKey := sa.OTPKey // Prepare client config. - cfg := client.MakeDefaultConfig() + cfg := &client.Config{} + cfg.ClientStore = client.NewFSClientStore(t.TempDir()) cfg.Stdout = io.Discard cfg.Stderr = io.Discard cfg.Stdin = &bytes.Buffer{} @@ -283,7 +284,6 @@ func TestTeleportClient_Login_local(t *testing.T) { // Replace "127.0.0.1" with "localhost". The proxy address becomes the origin // for Webauthn requests, and Webauthn doesn't take IP addresses. cfg.WebProxyAddr = strings.Replace(sa.ProxyWebAddr, "127.0.0.1", "localhost", 1 /* n */) - cfg.KeysDir = t.TempDir() cfg.InsecureSkipVerify = true // Prepare the client proper. @@ -340,7 +340,8 @@ func TestTeleportClient_DeviceLogin(t *testing.T) { require.NoError(t, err, "UpsertAuthPreference failed") // Prepare client config, it won't change throughout the test. - cfg := client.MakeDefaultConfig() + cfg := &client.Config{} + cfg.ClientStore = client.NewFSClientStore(t.TempDir()) cfg.Stdout = io.Discard cfg.Stderr = io.Discard cfg.Stdin = &bytes.Buffer{} @@ -348,7 +349,6 @@ func TestTeleportClient_DeviceLogin(t *testing.T) { cfg.HostLogin = username cfg.AddKeysToAgent = client.AddKeysToAgentNo cfg.WebProxyAddr = sa.ProxyWebAddr - cfg.KeysDir = t.TempDir() cfg.InsecureSkipVerify = true teleportClient, err := client.NewClient(cfg) @@ -686,11 +686,11 @@ func TestRetryWithRelogin(t *testing.T) { clock := clockwork.NewFakeClockAt(time.Now()) sa := newStandaloneTeleport(t, clock) - cfg := client.MakeDefaultConfig() + cfg := &client.Config{} + cfg.ClientStore = client.NewFSClientStore(t.TempDir()) cfg.Username = sa.Username cfg.HostLogin = sa.Username cfg.WebProxyAddr = sa.ProxyWebAddr - cfg.KeysDir = t.TempDir() cfg.InsecureSkipVerify = true cfg.AllowStdinHijack = true diff --git a/lib/client/api_test.go b/lib/client/api_test.go index e1d8db77ee031..0bb605615658d 100644 --- a/lib/client/api_test.go +++ b/lib/client/api_test.go @@ -194,13 +194,13 @@ func TestParseProxyHostString(t *testing.T) { func TestNew(t *testing.T) { conf := Config{ - Host: "localhost", - HostLogin: "vincent", - HostPort: 22, - KeysDir: t.TempDir(), - Username: "localuser", - SiteName: "site", - Tracer: tracing.NoopProvider().Tracer("test"), + Host: "localhost", + HostLogin: "vincent", + HostPort: 22, + Username: "localuser", + SiteName: "site", + Tracer: tracing.NoopProvider().Tracer("test"), + ClientStore: NewMemClientStore(), } err := conf.ParseProxyHost("proxy") require.NoError(t, err) @@ -1134,13 +1134,11 @@ func TestLoadTLSConfigForClusters(t *testing.T) { } func TestConnectToProxyCancelledContext(t *testing.T) { - cfg := MakeDefaultConfig() - + cfg := &Config{} cfg.Agent = &mockAgent{} cfg.AuthMethods = []ssh.AuthMethod{ssh.Password("xyz")} cfg.AddKeysToAgent = AddKeysToAgentNo cfg.WebProxyAddr = "dummy" - cfg.KeysDir = t.TempDir() cfg.TLSRoutingEnabled = true clt, err := NewClient(cfg) diff --git a/lib/client/client.go b/lib/client/client.go index 32bcd4fa36a83..92bc7995560b6 100644 --- a/lib/client/client.go +++ b/lib/client/client.go @@ -408,7 +408,7 @@ func (c *NodeClient) RunInteractiveShell(ctx context.Context, mode types.Session env[teleport.SSHSessionWebProxyAddr] = c.ProxyPublicAddr } - nodeSession, err := newSession(ctx, c, sessToJoin, env, c.TC.Stdin, c.TC.Stdout, c.TC.Stderr, c.TC.EnableEscapeSequences) + nodeSession, err := newSession(ctx, c, sessToJoin, env, c.TC.Stdin, c.TC.Stdout, c.TC.Stderr, !c.TC.DisableEscapeSequences) if err != nil { return trace.Wrap(err) } @@ -609,7 +609,7 @@ func (c *NodeClient) RunCommand(ctx context.Context, command []string, opts ...R } } - nodeSession, err := newSession(ctx, c, nil, c.TC.newSessionEnv(), c.TC.Stdin, stdout, stderr, c.TC.EnableEscapeSequences) + nodeSession, err := newSession(ctx, c, nil, c.TC.newSessionEnv(), c.TC.Stdin, stdout, stderr, !c.TC.DisableEscapeSequences) if err != nil { return trace.Wrap(err) } diff --git a/lib/client/client_store.go b/lib/client/client_store.go index cbc9f63f3dc90..6d9be761ea618 100644 --- a/lib/client/client_store.go +++ b/lib/client/client_store.go @@ -19,6 +19,7 @@ package client import ( + "context" "errors" "fmt" "net/url" @@ -32,6 +33,7 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/profile" "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/utils" ) @@ -43,34 +45,80 @@ import ( // when using `tsh --add-keys-to-agent=only`, Store will be made up of an in-memory // key store and an FS (~/.tsh) profile and trusted certs store. type Store struct { - log *logrus.Entry - + StoreConfig KeyStore TrustedCertsStore ProfileStore } -// NewMemClientStore initializes an FS backed client store with the given base dir. -func NewFSClientStore(dirPath string) *Store { - dirPath = profile.FullProfilePath(dirPath) - return &Store{ - log: logrus.WithField(teleport.ComponentKey, teleport.ComponentKeyStore), - KeyStore: NewFSKeyStore(dirPath), - TrustedCertsStore: NewFSTrustedCertsStore(dirPath), - ProfileStore: NewFSProfileStore(dirPath), +// StoreConfig contains shared config options for Store. +type StoreConfig struct { + log *logrus.Entry + HardwareKeyService hardwarekey.Service +} + +// StoreConfigOpt applies configuration options. +type StoreConfigOpt func(o *StoreConfig) + +// WithHardwareKeyService sets the hardware key service. +func WithHardwareKeyService(hwKeyService hardwarekey.Service) StoreConfigOpt { + return func(o *StoreConfig) { + o.HardwareKeyService = hwKeyService } } +// NewFSClientStore initializes an FS backed client store with the given base dir. +// +// [WithHardwareKeyService] should be provided in order to successfully create and +// parse hardware private keys. It is not needed for tests and select commands +// which do not interact with hardware keys +func NewFSClientStore(dirPath string, opts ...StoreConfigOpt) *Store { + return newClientStore( + NewFSKeyStore(dirPath), + NewFSTrustedCertsStore(dirPath), + NewFSProfileStore(dirPath), + opts..., + ) +} + // NewMemClientStore initializes a new in-memory client store. -func NewMemClientStore() *Store { +// +// [WithHardwareKeyService] should be provided in order to successfully create and +// parse hardware private keys. It is not needed for tests and select commands +// which do not interact with hardware keys +func NewMemClientStore(opts ...StoreConfigOpt) *Store { + return newClientStore( + NewMemKeyStore(), + NewMemTrustedCertsStore(), + NewMemProfileStore(), + opts..., + ) +} + +func newClientStore(ks KeyStore, tcs TrustedCertsStore, ps ProfileStore, opts ...StoreConfigOpt) *Store { + // Start with default config + config := StoreConfig{ + log: logrus.WithField(teleport.ComponentKey, teleport.ComponentKeyStore), + } + + // Apply opts + for _, opt := range opts { + opt(&config) + } + return &Store{ - log: logrus.WithField(teleport.ComponentKey, teleport.ComponentKeyStore), - KeyStore: NewMemKeyStore(), - TrustedCertsStore: NewMemTrustedCertsStore(), - ProfileStore: NewMemProfileStore(), + StoreConfig: config, + KeyStore: ks, + TrustedCertsStore: tcs, + ProfileStore: ps, } } +// NewHardwarePrivateKey create a new hardware private key with the given configuration in this client store. +func (s *Store) NewHardwarePrivateKey(ctx context.Context, config hardwarekey.PrivateKeyConfig) (*keys.PrivateKey, error) { + return keys.NewHardwarePrivateKey(ctx, s.HardwareKeyService, config) +} + // AddKey adds the given key to the key store. The key's trusted certificates are // added to the trusted certs store. func (s *Store) AddKey(key *Key) error { @@ -83,12 +131,6 @@ func (s *Store) AddKey(key *Key) error { return nil } -// SetCustomHardwareKeyPrompt sets a custom hardware key prompt -// used to interact with a YubiKey private key. -func (s *Store) SetCustomHardwareKeyPrompt(prompt keys.HardwareKeyPrompt) { - s.KeyStore.SetCustomHardwareKeyPrompt(prompt) -} - // ErrNoProfile is returned by the client store when a specific profile is not found. var ErrNoProfile = &trace.NotFoundError{Message: "no profile"} @@ -121,7 +163,7 @@ func IsNoCredentialsError(err error) bool { // found or is missing data (certificates, etc.), then an ErrNoCredentials error // is returned. func (s *Store) GetKey(idx KeyIndex, opts ...CertOption) (*Key, error) { - key, err := s.KeyStore.GetKey(idx, opts...) + key, err := s.KeyStore.GetKey(idx, s.HardwareKeyService, opts...) if trace.IsNotFound(err) { return nil, newNoCredentialsError(err) } else if err != nil { @@ -231,6 +273,7 @@ func (s *Store) ReadProfileStatus(profileName string) (*ProfileStatus, error) { KubeProxyAddr: profile.KubeProxyAddr, SAMLSingleLogoutEnabled: profile.SAMLSingleLogoutEnabled, IsVirtual: !onDisk, + TLSRoutingEnabled: profile.TLSRoutingEnabled, }) } diff --git a/lib/client/client_store_test.go b/lib/client/client_store_test.go index f0874fb499231..eb31dfe0d5c62 100644 --- a/lib/client/client_store_test.go +++ b/lib/client/client_store_test.go @@ -21,6 +21,7 @@ package client import ( "context" "crypto/x509/pkix" + "net" "sync/atomic" "testing" "time" @@ -33,6 +34,7 @@ import ( "github.com/gravitational/teleport/api/profile" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" apisshutils "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/testauthority" @@ -65,7 +67,17 @@ func (s *testAuthority) makeSignedKey(t *testing.T, idx KeyIndex, makeExpired bo priv, err := s.keygen.GeneratePrivateKey() require.NoError(t, err) - allowedLogins := []string{idx.Username, "root"} + key := NewKey(priv) + key.KeyIndex = idx + + s.signKey(t, key, makeExpired) + return key +} + +func (s *testAuthority) signKey(t *testing.T, key *Key, makeExpired bool) { + t.Helper() + + allowedLogins := []string{key.Username, "root"} ttl := 20 * time.Minute if makeExpired { ttl = -ttl @@ -74,14 +86,14 @@ func (s *testAuthority) makeSignedKey(t *testing.T, idx KeyIndex, makeExpired bo // reuse the same RSA keys for SSH and TLS keys clock := clockwork.NewRealClock() identity := tlsca.Identity{ - Username: idx.Username, + Username: key.Username, Groups: []string{"groups"}, } subject, err := identity.Subject() require.NoError(t, err) tlsCert, err := s.tlsCA.GenerateCertificate(tlsca.CertificateRequest{ Clock: clock, - PublicKey: priv.Public(), + PublicKey: key.Public(), Subject: subject, NotAfter: clock.Now().UTC().Add(ttl), }) @@ -92,10 +104,10 @@ func (s *testAuthority) makeSignedKey(t *testing.T, idx KeyIndex, makeExpired bo cert, err := s.keygen.GenerateUserCert(sshca.UserCertificateRequest{ CASigner: caSigner, - PublicUserKey: ssh.MarshalAuthorizedKey(priv.SSHPublicKey()), + PublicUserKey: ssh.MarshalAuthorizedKey(key.SSHPublicKey()), TTL: ttl, Identity: sshca.Identity{ - Username: idx.Username, + Username: key.Username, Principals: allowedLogins, PermitAgentForwarding: false, PermitPortForwarding: true, @@ -103,14 +115,10 @@ func (s *testAuthority) makeSignedKey(t *testing.T, idx KeyIndex, makeExpired bo }) require.NoError(t, err) - key := NewKey(priv) - key.KeyIndex = idx - key.PrivateKey = priv key.Cert = cert key.TLSCert = tlsCert key.TrustedCerts = []authclient.TrustedCerts{s.trustedCerts} key.DBTLSCerts["example-db"] = tlsCert - return key } func newSelfSignedCA(privateKey []byte, cluster string) (*tlsca.CertAuthority, authclient.TrustedCerts, error) { @@ -158,98 +166,130 @@ func testEachClientStore(t *testing.T, testFunc func(t *testing.T, clientStore * func TestClientStore(t *testing.T) { t.Parallel() + ctx := context.Background() a := newTestAuthority(t) - - testEachClientStore(t, func(t *testing.T, clientStore *Store) { - t.Parallel() - - idx := KeyIndex{ - ProxyHost: "proxy.example.com", - ClusterName: "root", - Username: "test-user", - } - key := a.makeSignedKey(t, idx, false) - - // Add key should add the key and trusted certs to their respective stores. - err := clientStore.AddKey(key) - require.NoError(t, err) - - // the key's trusted certs should be added to the trusted certs store. - retrievedTrustedCerts, err := clientStore.GetTrustedCerts(idx.ProxyHost) - require.NoError(t, err) - require.Equal(t, key.TrustedCerts, retrievedTrustedCerts) - - // Getting the key from the key store should have no trusted certs. - retrievedKey, err := clientStore.KeyStore.GetKey(idx, WithAllCerts...) - require.NoError(t, err) - expectKey := key.Copy() - expectKey.TrustedCerts = nil - require.Equal(t, expectKey, retrievedKey) - - // Getting the key from the client store should fill in the trusted certs. - retrievedKey, err = clientStore.GetKey(idx, WithAllCerts...) - require.NoError(t, err) - require.Equal(t, key, retrievedKey) - - var profileDir string - if fs, ok := clientStore.KeyStore.(*FSKeyStore); ok { - profileDir = fs.KeyDir - } - - // Create and save a corresponding profile for the key. - profile := &profile.Profile{ - WebProxyAddr: idx.ProxyHost + ":3080", - SiteName: idx.ClusterName, - Username: idx.Username, - } - err = clientStore.SaveProfile(profile, true) - require.NoError(t, err) - expectStatus, err := profileStatusFromKey(key, profileOptions{ - ProfileName: profile.Name(), - WebProxyAddr: profile.WebProxyAddr, - ProfileDir: profileDir, - Username: profile.Username, - SiteName: profile.SiteName, - KubeProxyAddr: profile.KubeProxyAddr, - IsVirtual: profileDir == "", - }) - require.NoError(t, err) - - // ReadProfileStatus should prepare a *ProfileStatus using the saved - // profile and key together. - profileStatus, err := clientStore.ReadProfileStatus(profile.Name()) - require.NoError(t, err) - require.Equal(t, expectStatus, profileStatus) - - // FullProfileStatus should return the current profile status, and any - // other available profiles' statuses. - otherKey := key.Copy() - otherKey.ProxyHost = "other.example.com" - err = clientStore.AddKey(otherKey) - require.NoError(t, err) - - otherProfile := profile.Copy() - otherProfile.WebProxyAddr = "other.example.com:3080" - err = clientStore.SaveProfile(otherProfile, false) - require.NoError(t, err) - - expectOtherStatus, err := profileStatusFromKey(key, profileOptions{ - ProfileName: otherProfile.Name(), - WebProxyAddr: otherProfile.WebProxyAddr, - ProfileDir: profileDir, - Username: otherProfile.Username, - SiteName: otherProfile.SiteName, - KubeProxyAddr: otherProfile.KubeProxyAddr, - IsVirtual: profileDir == "", - }) - require.NoError(t, err) - - currentStatus, otherStatuses, err := clientStore.FullProfileStatus() - require.NoError(t, err) - require.Equal(t, expectStatus, currentStatus) - require.Len(t, otherStatuses, 1) - require.Equal(t, expectOtherStatus, otherStatuses[0]) + hwks := hardwarekey.NewMockHardwareKeyService(nil /*prompt*/) + + // create a test software and hardware key. + idx := KeyIndex{"test.proxy.com", "test-user", "root"} + softKey := a.makeSignedKey(t, idx, false) + keyInfo := idx.contextualKeyInfo() + hwPriv, err := keys.NewHardwarePrivateKey(ctx, hwks, hardwarekey.PrivateKeyConfig{ + ContextualKeyInfo: keyInfo, }) + require.NoError(t, err) + hardKey := NewKey(hwPriv) + hardKey.KeyIndex = idx + a.signKey(t, hardKey, false) + + for name, key := range map[string]*Key{ + "software key": softKey, + "hardware key": hardKey, + } { + key := key + t.Run(name, func(t *testing.T) { + t.Parallel() + + testEachClientStore(t, func(t *testing.T, clientStore *Store) { + clientStore.HardwareKeyService = hwks + + // Add key should add the key and trusted certs to their respective stores. + err := clientStore.AddKey(key) + require.NoError(t, err) + + // the key's trusted certs should be added to the trusted certs store. + retrievedTrustedCerts, err := clientStore.GetTrustedCerts(idx.ProxyHost) + require.NoError(t, err) + require.Equal(t, key.TrustedCerts, retrievedTrustedCerts) + + // Getting the key from the key store should have no trusted certs. + retrievedKey, err := clientStore.KeyStore.GetKey(idx, clientStore.HardwareKeyService, WithAllCerts...) + require.NoError(t, err) + expectKey := key.Copy() + expectKey.TrustedCerts = nil + require.Equal(t, expectKey, retrievedKey) + + // Getting the key from the client store should fill in the trusted certs. + retrievedKey, err = clientStore.GetKey(idx, WithAllCerts...) + require.NoError(t, err) + require.Equal(t, key, retrievedKey) + + // Get the key, now without cluster name. It should retrieve the key without certs + // and without a cluster name in the KeyIndex or ContextualKeyInfo (hardware keys). + retrievedKey, err = clientStore.GetKey(KeyIndex{idx.ProxyHost, idx.Username, ""}) + require.NoError(t, err) + expectKey = key.Copy() + expectKey.ClusterName = "" + expectKey.Cert = nil + expectKey.DBTLSCerts = make(map[string][]byte) + if hwPriv, ok := expectKey.PrivateKey.Signer.(*hardwarekey.Signer); ok { + hwPriv.KeyInfo.ClusterName = "" + } + require.Equal(t, expectKey, retrievedKey) + + var profileDir string + if fs, ok := clientStore.KeyStore.(*FSKeyStore); ok { + profileDir = fs.KeyDir + } + + // Create and save a corresponding profile for the key. + profile := &profile.Profile{ + WebProxyAddr: net.JoinHostPort(idx.ProxyHost, "3080"), + SiteName: idx.ClusterName, + Username: idx.Username, + } + err = clientStore.SaveProfile(profile, true) + require.NoError(t, err) + expectStatus, err := profileStatusFromKey(key, profileOptions{ + ProfileName: profile.Name(), + WebProxyAddr: profile.WebProxyAddr, + ProfileDir: profileDir, + Username: profile.Username, + SiteName: profile.SiteName, + KubeProxyAddr: profile.KubeProxyAddr, + IsVirtual: profileDir == "", + TLSRoutingEnabled: profile.TLSRoutingEnabled, + }) + require.NoError(t, err) + + // ReadProfileStatus should prepare a *ProfileStatus using the saved + // profile and key together. + profileStatus, err := clientStore.ReadProfileStatus(profile.Name()) + require.NoError(t, err) + require.Equal(t, expectStatus, profileStatus) + + // FullProfileStatus should return the current profile status, and any + // other available profiles' statuses. + otherKey := key.Copy() + otherKey.ProxyHost = "other.example.com" + err = clientStore.AddKey(otherKey) + require.NoError(t, err) + + otherProfile := profile.Copy() + otherProfile.WebProxyAddr = "other.example.com:3080" + err = clientStore.SaveProfile(otherProfile, false) + require.NoError(t, err) + + expectOtherStatus, err := profileStatusFromKey(key, profileOptions{ + ProfileName: otherProfile.Name(), + WebProxyAddr: otherProfile.WebProxyAddr, + ProfileDir: profileDir, + Username: otherProfile.Username, + SiteName: otherProfile.SiteName, + KubeProxyAddr: otherProfile.KubeProxyAddr, + IsVirtual: profileDir == "", + TLSRoutingEnabled: otherProfile.TLSRoutingEnabled, + }) + require.NoError(t, err) + + currentStatus, otherStatuses, err := clientStore.FullProfileStatus() + require.NoError(t, err) + require.Equal(t, expectStatus, currentStatus) + require.Len(t, otherStatuses, 1) + require.Equal(t, expectOtherStatus, otherStatuses[0]) + }) + }) + } } // TestProxySSHConfig tests proxy client SSH config function diff --git a/lib/client/conntest/ssh.go b/lib/client/conntest/ssh.go index 5b22d24de7ba4..e8b67ce7fa8cc 100644 --- a/lib/client/conntest/ssh.go +++ b/lib/client/conntest/ssh.go @@ -170,7 +170,7 @@ func (s *SSHConnectionTester) TestConnection(ctx context.Context, req TestConnec processStdout := &bytes.Buffer{} - clientConf := client.MakeDefaultConfig() + clientConf := &client.Config{} clientConf.AddKeysToAgent = client.AddKeysToAgentNo clientConf.AuthMethods = []ssh.AuthMethod{keyAuthMethod} clientConf.Host = req.ResourceName diff --git a/lib/client/db/dbcmd/dbcmd_test.go b/lib/client/db/dbcmd/dbcmd_test.go index e5466ffe63054..ee877a33db754 100644 --- a/lib/client/db/dbcmd/dbcmd_test.go +++ b/lib/client/db/dbcmd/dbcmd_test.go @@ -61,11 +61,11 @@ func (f fakeExec) LookPath(path string) (string, error) { func TestCLICommandBuilderGetConnectCommand(t *testing.T) { conf := &client.Config{ - HomePath: t.TempDir(), Host: "localhost", WebProxyAddr: "proxy.example.com", SiteName: "db.example.com", Tracer: tracing.NoopProvider().Tracer("test"), + ClientStore: client.NewMemClientStore(), } tc, err := client.NewClient(conf) @@ -801,11 +801,11 @@ func TestCLICommandBuilderGetConnectCommand(t *testing.T) { func TestCLICommandBuilderGetConnectCommandAlternatives(t *testing.T) { conf := &client.Config{ - HomePath: t.TempDir(), Host: "localhost", WebProxyAddr: "proxy.example.com", SiteName: "db.example.com", Tracer: tracing.NoopProvider().Tracer("test"), + ClientStore: client.NewMemClientStore(), } tc, err := client.NewClient(conf) @@ -970,13 +970,12 @@ func TestCLICommandBuilderGetConnectCommandAlternatives(t *testing.T) { func TestConvertCommandError(t *testing.T) { t.Parallel() - homePath := t.TempDir() conf := &client.Config{ - HomePath: homePath, Host: "localhost", WebProxyAddr: "localhost", SiteName: "db.example.com", Tracer: tracing.NoopProvider().Tracer("test"), + ClientStore: client.NewMemClientStore(), } tc, err := client.NewClient(conf) @@ -985,7 +984,6 @@ func TestConvertCommandError(t *testing.T) { profile := &client.ProfileStatus{ Name: "example.com", Username: "bob", - Dir: homePath, Cluster: "example.com", } diff --git a/lib/client/identityfile/identity.go b/lib/client/identityfile/identity.go index 59760cddd575b..2d66ea73fadb4 100644 --- a/lib/client/identityfile/identity.go +++ b/lib/client/identityfile/identity.go @@ -801,26 +801,15 @@ func KeyFromIdentityFile(identityPath, proxyHost, clusterName string) (*client.K return key, nil } -// NewClientStoreFromIdentityFile initializes a new in-memory client store -// and loads data from the given identity file into it. A temporary profile -// is also added to its profile store with the limited profile data available -// in the identity file. +// LoadIdentityFileIntoClientStore loads the identityFile from the given path +// into the given client store, assimilating it with other keys in the store. +// A temporary profile is also added to its profile store with the limited profile +// data available in the identity file. // // Use [proxyAddr] to specify the host:port-like address of the proxy. // This is necessary because identity files do not store the proxy address. // Additionally, the [clusterName] argument can ve used to target a leaf cluster // rather than the default root cluster. -func NewClientStoreFromIdentityFile(identityFile, proxyAddr, clusterName string) (*client.Store, error) { - clientStore := client.NewMemClientStore() - if err := LoadIdentityFileIntoClientStore(clientStore, identityFile, proxyAddr, clusterName); err != nil { - return nil, trace.Wrap(err) - } - - return clientStore, nil -} - -// LoadIdentityFileIntoClientStore loads the identityFile from the given path -// into the given client store, assimilating it with other keys in the store. func LoadIdentityFileIntoClientStore(store *client.Store, identityFile, proxyAddr, clusterName string) error { if proxyAddr == "" { return trace.BadParameter("missing a Proxy address when loading an Identity File.") diff --git a/lib/client/identityfile/identity_test.go b/lib/client/identityfile/identity_test.go index 25a363eaa3372..e2bd28d5a605f 100644 --- a/lib/client/identityfile/identity_test.go +++ b/lib/client/identityfile/identity_test.go @@ -423,7 +423,7 @@ func TestKeyFromIdentityFile(t *testing.T) { }) } -func TestNewClientStoreFromIdentityFile(t *testing.T) { +func TestLoadIdentityFileIntoClientStore(t *testing.T) { t.Parallel() key := newClientKey(t) key.ProxyHost = "proxy.example.com" @@ -440,7 +440,8 @@ func TestNewClientStoreFromIdentityFile(t *testing.T) { }) require.NoError(t, err) - clientStore, err := NewClientStoreFromIdentityFile(identityFilePath, key.ProxyHost+":3080", key.ClusterName) + clientStore := client.NewMemClientStore() + err = LoadIdentityFileIntoClientStore(clientStore, identityFilePath, key.ProxyHost+":3080", key.ClusterName) require.NoError(t, err) currentProfile, err := clientStore.CurrentProfile() diff --git a/lib/client/interfaces.go b/lib/client/interfaces.go index 136e905a67d47..3309745ee83fd 100644 --- a/lib/client/interfaces.go +++ b/lib/client/interfaces.go @@ -37,6 +37,7 @@ import ( "github.com/gravitational/teleport/api/constants" apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/auth/native" @@ -79,6 +80,14 @@ func (idx KeyIndex) Match(matchKey KeyIndex) bool { (matchKey.Username == "" || matchKey.Username == idx.Username) } +func (idx KeyIndex) contextualKeyInfo() hardwarekey.ContextualKeyInfo { + return hardwarekey.ContextualKeyInfo{ + ProxyHost: idx.ProxyHost, + Username: idx.Username, + ClusterName: idx.ClusterName, + } +} + // Key describes a complete (signed) client key type Key struct { KeyIndex diff --git a/lib/client/keyagent.go b/lib/client/keyagent.go index fc7381c6599c5..5cfe7919e5344 100644 --- a/lib/client/keyagent.go +++ b/lib/client/keyagent.go @@ -570,6 +570,8 @@ func (a *LocalKeyAgent) addKey(key *Key) error { // DeleteKey removes the key with all its certs from the key store // and unloads the key from the agent. func (a *LocalKeyAgent) DeleteKey() error { + // TODO(Joerger): Delete profile? Delete current profile if it matches? + // remove key from key store err := a.clientStore.DeleteKey(KeyIndex{ProxyHost: a.proxyHost, Username: a.username}) if err != nil { diff --git a/lib/client/keystore.go b/lib/client/keystore.go index 6c14a54d55386..dad6d5135eca7 100644 --- a/lib/client/keystore.go +++ b/lib/client/keystore.go @@ -35,6 +35,7 @@ import ( "github.com/gravitational/teleport/api/profile" "github.com/gravitational/teleport/api/utils/keypaths" "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" apisshutils "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib/utils" ) @@ -68,7 +69,7 @@ type KeyStore interface { // GetKey returns the user's key including the specified certs. The key's // TrustedCerts will be nil and should be filled in using a TrustedCertsStore. - GetKey(idx KeyIndex, opts ...CertOption) (*Key, error) + GetKey(idx KeyIndex, hwks hardwarekey.Service, opts ...CertOption) (*Key, error) // DeleteKey deletes the user's key with all its certs. DeleteKey(idx KeyIndex) error @@ -83,10 +84,6 @@ type KeyStore interface { // GetSSHCertificates gets all certificates signed for the given user and proxy, // including certificates for trusted clusters. GetSSHCertificates(proxyHost, username string) ([]*ssh.Certificate, error) - - // SetCustomHardwareKeyPrompt sets a custom hardware key prompt - // used to interact with a YubiKey private key. - SetCustomHardwareKeyPrompt(prompt keys.HardwareKeyPrompt) } // FSKeyStore is an on-disk implementation of the KeyStore interface. @@ -98,10 +95,6 @@ type FSKeyStore struct { // KeyDir is the directory where all keys are stored. KeyDir string - // CustomHardwareKeyPrompt is a custom hardware key prompt to use when asking - // for a hardware key PIN, touch, etc. - // If nil, a default CLI prompt is used. - CustomHardwareKeyPrompt keys.HardwareKeyPrompt } // NewFSKeyStore initializes a new FSClientStore. @@ -171,12 +164,6 @@ func (fs *FSKeyStore) kubeCertPath(idx KeyIndex, kubename string) string { return keypaths.KubeCertPath(fs.KeyDir, idx.ProxyHost, idx.Username, idx.ClusterName, kubename) } -// SetCustomHardwareKeyPrompt sets a custom hardware key prompt -// used to interact with a YubiKey private key. -func (fs *FSKeyStore) SetCustomHardwareKeyPrompt(prompt keys.HardwareKeyPrompt) { - fs.CustomHardwareKeyPrompt = prompt -} - // AddKey adds the given key to the store. func (fs *FSKeyStore) AddKey(key *Key) error { if err := key.KeyIndex.Check(); err != nil { @@ -358,7 +345,11 @@ func (e *FutureCertPathError) Unwrap() error { // GetKey returns the user's key including the specified certs. // If the key is not found, returns trace.NotFound error. -func (fs *FSKeyStore) GetKey(idx KeyIndex, opts ...CertOption) (*Key, error) { +// +// If [hwks] is not provided, the keystore may fail to parse hardware keys into +// a fully functional, signable keyring. This is ok if the keyring is only being +// retrieved to gather keyring info, e.g. to see what app certs are present. +func (fs *FSKeyStore) GetKey(idx KeyIndex, hwks hardwarekey.Service, opts ...CertOption) (*Key, error) { if len(opts) > 0 { if err := idx.Check(); err != nil { return nil, trace.Wrap(err, "GetKey with CertOptions requires a fully specified KeyIndex") @@ -381,7 +372,7 @@ func (fs *FSKeyStore) GetKey(idx KeyIndex, opts ...CertOption) (*Key, error) { return nil, trace.ConvertSystemError(err) } - priv, err := keys.LoadKeyPair(fs.userKeyPath(idx), fs.publicKeyPath(idx), fs.CustomHardwareKeyPrompt) + priv, err := keys.LoadKeyPair(fs.userKeyPath(idx), fs.publicKeyPath(idx), keys.WithHardwareKeyService(hwks), keys.WithContextualKeyInfo(idx.contextualKeyInfo())) if err != nil { return nil, trace.ConvertSystemError(err) } @@ -619,7 +610,7 @@ func (ms *MemKeyStore) AddKey(key *Key) error { } // GetKey returns the user's key including the specified certs. -func (ms *MemKeyStore) GetKey(idx KeyIndex, opts ...CertOption) (*Key, error) { +func (ms *MemKeyStore) GetKey(idx KeyIndex, hwks hardwarekey.Service, opts ...CertOption) (*Key, error) { if len(opts) > 0 { if err := idx.Check(); err != nil { return nil, trace.Wrap(err, "GetKey with CertOptions requires a fully specified KeyIndex") @@ -721,7 +712,3 @@ func (ms *MemKeyStore) GetSSHCertificates(proxyHost, username string) ([]*ssh.Ce return sshCerts, nil } - -// SetCustomHardwareKeyPrompt implements the KeyStore.SetCustomHardwareKeyPrompt interface. -// Does nothing. -func (ms *MemKeyStore) SetCustomHardwareKeyPrompt(_ keys.HardwareKeyPrompt) {} diff --git a/lib/client/keystore_test.go b/lib/client/keystore_test.go index fe708e7d7bce2..89515bc6078a8 100644 --- a/lib/client/keystore_test.go +++ b/lib/client/keystore_test.go @@ -19,6 +19,7 @@ package client import ( + "context" "fmt" "os" "path/filepath" @@ -28,6 +29,8 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/crypto/ssh" + "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" "github.com/gravitational/teleport/lib/utils/cert" ) @@ -48,58 +51,80 @@ func testEachKeyStore(t *testing.T, testFunc func(t *testing.T, keyStore KeyStor func TestKeyStore(t *testing.T) { t.Parallel() - s := newTestAuthority(t) - - testEachKeyStore(t, func(t *testing.T, keyStore KeyStore) { - t.Parallel() - - // create a test key - idx := KeyIndex{"test.proxy.com", "test-user", "root"} - key := s.makeSignedKey(t, idx, false) - - // add the test key to the memory store - err := keyStore.AddKey(key) - require.NoError(t, err) - - // check that the key exists in the store and is the same, - // except the key's trusted certs should be empty, to be - // filled in by a trusted certs store. - retrievedKey, err := keyStore.GetKey(idx, WithAllCerts...) - require.NoError(t, err) - key.TrustedCerts = nil - require.Equal(t, key, retrievedKey) - - // Delete just the db cert, reload & verify it's gone - err = keyStore.DeleteUserCerts(idx, WithDBCerts{}) - require.NoError(t, err) - retrievedKey, err = keyStore.GetKey(idx, WithSSHCerts{}, WithDBCerts{}) - require.NoError(t, err) - expectKey := key.Copy() - expectKey.DBTLSCerts = make(map[string][]byte) - require.Equal(t, expectKey, retrievedKey) - // check for the key, now without cluster name - retrievedKey, err = keyStore.GetKey(KeyIndex{idx.ProxyHost, idx.Username, ""}) - require.NoError(t, err) - expectKey.ClusterName = "" - expectKey.Cert = nil - require.Equal(t, expectKey, retrievedKey) - - // delete the key - err = keyStore.DeleteKey(idx) - require.NoError(t, err) - - // check that the key doesn't exist in the store - retrievedKey, err = keyStore.GetKey(idx) - require.Error(t, err) - require.True(t, trace.IsNotFound(err)) - require.Nil(t, retrievedKey) + ctx := context.Background() + s := newTestAuthority(t) + hwks := hardwarekey.NewMockHardwareKeyService(nil /*prompt*/) - // Delete non-existing - err = keyStore.DeleteKey(idx) - require.Error(t, err) - require.True(t, trace.IsNotFound(err)) + // create a test software and hardware key. + idx := KeyIndex{"test.proxy.com", "test-user", "root"} + softKey := s.makeSignedKey(t, idx, false) + hwPriv, err := keys.NewHardwarePrivateKey(ctx, hwks, hardwarekey.PrivateKeyConfig{ + ContextualKeyInfo: idx.contextualKeyInfo(), }) + require.NoError(t, err) + hardKey := NewKey(hwPriv) + hardKey.KeyIndex = idx + s.signKey(t, hardKey, false) + + for name, key := range map[string]*Key{ + "software key": softKey, + "hardware key": hardKey, + } { + key := key + t.Run(name, func(t *testing.T) { + t.Parallel() + + testEachKeyStore(t, func(t *testing.T, keyStore KeyStore) { + // add the test key to the memory store + err := keyStore.AddKey(key) + require.NoError(t, err) + + // check that the key exists in the store and is the same, + // except the key's trusted certs should be empty, to be + // filled in by a trusted certs store. + retrievedKey, err := keyStore.GetKey(idx, hwks, WithAllCerts...) + require.NoError(t, err) + key.TrustedCerts = nil + require.Equal(t, key, retrievedKey) + + // Delete just the db cred, reload & verify it's gone + err = keyStore.DeleteUserCerts(idx, WithDBCerts{}) + require.NoError(t, err) + retrievedKey, err = keyStore.GetKey(idx, hwks, WithSSHCerts{}, WithDBCerts{}) + require.NoError(t, err) + expectKey := key.Copy() + expectKey.DBTLSCerts = make(map[string][]byte) + require.Equal(t, expectKey, retrievedKey) + + // Get the key, now without cluster name. It should retrieve the key without certs + // and without a cluster name in the KeyIndex or ContextualKeyInfo (hardware keys). + retrievedKey, err = keyStore.GetKey(KeyIndex{idx.ProxyHost, idx.Username, ""}, hwks) + require.NoError(t, err) + expectKey.ClusterName = "" + expectKey.Cert = nil + if hwPriv, ok := expectKey.PrivateKey.Signer.(*hardwarekey.Signer); ok { + hwPriv.KeyInfo.ClusterName = "" + } + require.Equal(t, expectKey, retrievedKey) + + // delete the key + err = keyStore.DeleteKey(idx) + require.NoError(t, err) + + // check that the key doesn't exist in the store + retrievedKey, err = keyStore.GetKey(idx, hwks) + require.Error(t, err) + require.True(t, trace.IsNotFound(err)) + require.Nil(t, retrievedKey) + + // Delete non-existing + err = keyStore.DeleteKey(idx) + require.Error(t, err) + require.True(t, trace.IsNotFound(err)) + }) + }) + } } func TestListKeys(t *testing.T) { @@ -125,14 +150,14 @@ func TestListKeys(t *testing.T) { // read all bob keys: for i := 0; i < keyNum; i++ { - key, err := keyStore.GetKey(keys[i].KeyIndex, WithSSHCerts{}, WithDBCerts{}) + key, err := keyStore.GetKey(keys[i].KeyIndex, nil /*hwks*/, WithSSHCerts{}, WithDBCerts{}) require.NoError(t, err) key.TrustedCerts = keys[i].TrustedCerts require.Equal(t, &keys[i], key) } // read sam's key and make sure it's the same: - skey, err := keyStore.GetKey(samIdx, WithSSHCerts{}) + skey, err := keyStore.GetKey(samIdx, nil /*hwks*/, WithSSHCerts{}) require.NoError(t, err) require.Equal(t, samKey.Cert, skey.Cert) require.Equal(t, samKey.MarshalSSHPublicKey(), skey.MarshalSSHPublicKey()) @@ -185,9 +210,9 @@ func TestDeleteAll(t *testing.T) { require.NoError(t, err) // check keys exist - _, err = keyStore.GetKey(idxFoo) + _, err = keyStore.GetKey(idxFoo, nil /*hwks*/) require.NoError(t, err) - _, err = keyStore.GetKey(idxBar) + _, err = keyStore.GetKey(idxBar, nil /*hwks*/) require.NoError(t, err) // delete all keys @@ -195,9 +220,9 @@ func TestDeleteAll(t *testing.T) { require.NoError(t, err) // verify keys are gone - _, err = keyStore.GetKey(idxFoo) + _, err = keyStore.GetKey(idxFoo, nil /*hwks*/) require.True(t, trace.IsNotFound(err)) - _, err = keyStore.GetKey(idxBar) + _, err = keyStore.GetKey(idxBar, nil /*hwks*/) require.Error(t, err) }) } @@ -220,7 +245,7 @@ func TestCheckKey(t *testing.T) { err = keyStore.AddKey(key) require.NoError(t, err) - _, err = keyStore.GetKey(idx) + _, err = keyStore.GetKey(idx, nil /*hwks*/) require.NoError(t, err) }) } @@ -249,7 +274,7 @@ func TestCheckKeyFIPS(t *testing.T) { require.NoError(t, err) // Should return trace.BadParameter error because only RSA keys are supported. - _, err = keyStore.GetKey(idx) + _, err = keyStore.GetKey(idx, nil /*hwks*/) require.True(t, trace.IsBadParameter(err)) }) } @@ -271,7 +296,7 @@ func TestAddKey_withoutSSHCert(t *testing.T) { require.ErrorIs(t, err, os.ErrNotExist) // check db certs - keyCopy, err := keyStore.GetKey(idx, WithDBCerts{}) + keyCopy, err := keyStore.GetKey(idx, nil /*hwks*/, WithDBCerts{}) require.NoError(t, err) require.Len(t, keyCopy.DBTLSCerts, 1) } diff --git a/lib/client/profile.go b/lib/client/profile.go index 02f0ac9d57f38..ee8572e1b6016 100644 --- a/lib/client/profile.go +++ b/lib/client/profile.go @@ -24,6 +24,7 @@ import ( "path/filepath" "slices" "sort" + "strings" "sync" "time" @@ -55,6 +56,10 @@ type ProfileStore interface { // SaveProfile saves the given profile. If makeCurrent // is true, it makes this profile current. SaveProfile(profile *profile.Profile, setCurrent bool) error + + // DeleteProfile deletes the given profile. If it is the + // current profile, it also deletes that record. + DeleteProfile(profileName string) error } // MemProfileStore is an in-memory implementation of ProfileStore. @@ -107,6 +112,21 @@ func (ms *MemProfileStore) SaveProfile(profile *profile.Profile, makecurrent boo return nil } +// DeleteProfile deletes the given profile. If it is the +// current profile, it also deletes that record. +func (ms *MemProfileStore) DeleteProfile(profileName string) error { + if _, ok := ms.profiles[profileName]; !ok { + return trace.NotFound("profile for proxy host %q not found", profileName) + } + + if ms.currentProfile == profileName { + ms.currentProfile = "" + } + + delete(ms.profiles, profileName) + return nil +} + // FSProfileStore is an on-disk implementation of the ProfileStore interface. // // The FS store uses the file layout outlined in `api/utils/keypaths.go`. @@ -138,11 +158,26 @@ func (fs *FSProfileStore) CurrentProfile() (string, error) { // ListProfiles returns a list of all profiles. func (fs *FSProfileStore) ListProfiles() ([]string, error) { - profileNames, err := profile.ListProfileNames(fs.Dir) + files, err := os.ReadDir(fs.Dir) if err != nil { return nil, trace.Wrap(err) } - return profileNames, nil + + var names []string + for _, file := range files { + if file.IsDir() { + continue + } + + if file.Type()&os.ModeSymlink != 0 { + continue + } + if !strings.HasSuffix(file.Name(), ".yaml") { + continue + } + names = append(names, strings.TrimSuffix(file.Name(), ".yaml")) + } + return names, nil } // GetProfile returns the requested profile. @@ -165,6 +200,24 @@ func (fs *FSProfileStore) SaveProfile(profile *profile.Profile, makeCurrent bool return trace.Wrap(err) } +// DeleteProfile deletes the given profile. If it is the +// current profile, it also deletes that record. +func (fs *FSProfileStore) DeleteProfile(profileName string) error { + if _, err := profile.FromDir(fs.Dir, profileName); err != nil { + return trace.Wrap(err) + } + + if current, err := fs.CurrentProfile(); err != nil && !trace.IsNotFound(err) { + return trace.Wrap(err) + } else if current == profileName { + if err := os.Remove(keypaths.CurrentProfileFilePath(fs.Dir)); err != nil { + return trace.ConvertSystemError(err) + } + } + + return trace.ConvertSystemError(os.Remove(keypaths.ProfileFilePath(fs.Dir, profileName))) +} + // ProfileStatus combines metadata from the logged in profile and associated // SSH certificate. type ProfileStatus struct { @@ -247,6 +300,10 @@ type ProfileStatus struct { // SAMLSingleLogoutEnabled is whether SAML SLO (single logout) is enabled, this can only be true if this is a SAML SSO session // using an auth connector with a SAML SLO URL configured. SAMLSingleLogoutEnabled bool + + // TLSRoutingEnabled indicates that proxy supports ALPN SNI server where + // all proxy services are exposed on a single TLS listener (Proxy Web Listener). + TLSRoutingEnabled bool } // profileOptions contains fields needed to initialize a profile beyond those @@ -260,6 +317,7 @@ type profileOptions struct { KubeProxyAddr string IsVirtual bool SAMLSingleLogoutEnabled bool + TLSRoutingEnabled bool } // profileFromkey returns a ProfileStatus for the given key and options. @@ -353,6 +411,7 @@ func profileStatusFromKey(key *Key, opts profileOptions) (*ProfileStatus, error) IsVirtual: opts.IsVirtual, AllowedResourceIDs: sshIdent.AllowedResourceIDs, SAMLSingleLogoutEnabled: opts.SAMLSingleLogoutEnabled, + TLSRoutingEnabled: opts.TLSRoutingEnabled, }, nil } @@ -529,7 +588,7 @@ func (p *ProfileStatus) DatabaseServices() (result []string) { // DatabasesForCluster returns a list of databases for this profile, for the // specified cluster name. -func (p *ProfileStatus) DatabasesForCluster(clusterName string) ([]tlsca.RouteToDatabase, error) { +func (p *ProfileStatus) DatabasesForCluster(clusterName string, store *Store) ([]tlsca.RouteToDatabase, error) { if clusterName == "" || clusterName == p.Cluster { return p.Databases, nil } @@ -540,7 +599,6 @@ func (p *ProfileStatus) DatabasesForCluster(clusterName string) ([]tlsca.RouteTo ClusterName: clusterName, } - store := NewFSKeyStore(p.Dir) key, err := store.GetKey(idx, WithDBCerts{}) if err != nil { return nil, trace.Wrap(err) @@ -550,7 +608,7 @@ func (p *ProfileStatus) DatabasesForCluster(clusterName string) ([]tlsca.RouteTo // AppsForCluster returns a list of apps for this profile, for the // specified cluster name. -func (p *ProfileStatus) AppsForCluster(clusterName string) ([]tlsca.RouteToApp, error) { +func (p *ProfileStatus) AppsForCluster(clusterName string, store *Store) ([]tlsca.RouteToApp, error) { if clusterName == "" || clusterName == p.Cluster { return p.Apps, nil } @@ -561,7 +619,6 @@ func (p *ProfileStatus) AppsForCluster(clusterName string) ([]tlsca.RouteToApp, ClusterName: clusterName, } - store := NewFSKeyStore(p.Dir) key, err := store.GetKey(idx, WithAppCerts{}) if err != nil { return nil, trace.Wrap(err) diff --git a/lib/client/weblogin.go b/lib/client/weblogin.go index d0a32626a743e..5394ed4560e7f 100644 --- a/lib/client/weblogin.go +++ b/lib/client/weblogin.go @@ -44,7 +44,7 @@ import ( "github.com/gravitational/teleport/api/client/webclient" "github.com/gravitational/teleport/api/mfa" "github.com/gravitational/teleport/api/types" - "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" "github.com/gravitational/teleport/lib/auth/authclient" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" @@ -75,7 +75,7 @@ type SSOLoginConsoleReq struct { // credentials to. KubernetesCluster string // AttestationStatement is an attestation statement associated with the given public key. - AttestationStatement *keys.AttestationStatement `json:"attestation_statement,omitempty"` + AttestationStatement *hardwarekey.AttestationStatement `json:"attestation_statement,omitempty"` } // CheckAndSetDefaults makes sure that the request is valid @@ -162,7 +162,7 @@ type CreateSSHCertReq struct { // credentials to. KubernetesCluster string // AttestationStatement is an attestation statement associated with the given public key. - AttestationStatement *keys.AttestationStatement `json:"attestation_statement,omitempty"` + AttestationStatement *hardwarekey.AttestationStatement `json:"attestation_statement,omitempty"` } // AuthenticateSSHUserRequest are passed by web client to authenticate against @@ -191,7 +191,7 @@ type AuthenticateSSHUserRequest struct { // credentials to. KubernetesCluster string // AttestationStatement is an attestation statement associated with the given public key. - AttestationStatement *keys.AttestationStatement `json:"attestation_statement,omitempty"` + AttestationStatement *hardwarekey.AttestationStatement `json:"attestation_statement,omitempty"` } type AuthenticateWebUserRequest struct { @@ -229,7 +229,7 @@ type SSHLogin struct { // credentials to. KubernetesCluster string // AttestationStatement is an attestation statement. - AttestationStatement *keys.AttestationStatement + AttestationStatement *hardwarekey.AttestationStatement // ExtraHeaders is a map of extra HTTP headers to be included in requests. ExtraHeaders map[string]string } diff --git a/lib/client/weblogin_test.go b/lib/client/weblogin_test.go index f5677229e8618..e694dc2525456 100644 --- a/lib/client/weblogin_test.go +++ b/lib/client/weblogin_test.go @@ -128,12 +128,12 @@ func TestSSHAgentPasswordlessLogin(t *testing.T) { ctx := context.Background() // Prepare client config, it won't change throughout the test. - cfg := client.MakeDefaultConfig() + cfg := &client.Config{} + cfg.ClientStore = client.NewFSClientStore(t.TempDir()) cfg.AddKeysToAgent = client.AddKeysToAgentNo // Replace "127.0.0.1" with "localhost". The proxy address becomes the origin // for Webauthn requests, and Webauthn doesn't take IP addresses. cfg.WebProxyAddr = strings.Replace(sa.ProxyWebAddr, "127.0.0.1", "localhost", 1 /* n */) - cfg.KeysDir = t.TempDir() cfg.InsecureSkipVerify = true solvePwdless := func(ctx context.Context, origin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { diff --git a/lib/config/fileconf.go b/lib/config/fileconf.go index 6071e07390b40..691c8838bbbf3 100644 --- a/lib/config/fileconf.go +++ b/lib/config/fileconf.go @@ -43,7 +43,7 @@ import ( "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/api/types" apiutils "github.com/gravitational/teleport/api/utils" - "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" "github.com/gravitational/teleport/api/utils/tlsutils" "github.com/gravitational/teleport/lib/automaticupgrades" "github.com/gravitational/teleport/lib/backend" @@ -1040,8 +1040,7 @@ type AuthenticationConfig struct { DefaultSessionTTL types.Duration `yaml:"default_session_ttl"` // Deprecated. HardwareKey.PIVSlot should be used instead. - // TODO(Joerger): DELETE IN 17.0.0 - PIVSlot keys.PIVSlot `yaml:"piv_slot,omitempty"` + PIVSlot hardwarekey.PIVSlotKeyString `yaml:"piv_slot,omitempty"` // HardwareKey holds settings related to hardware key support. // Requires Teleport Enterprise. @@ -1241,11 +1240,14 @@ func (dt *DeviceTrust) Parse() (*types.DeviceTrust, error) { type HardwareKey struct { // PIVSlot is a PIV slot that Teleport clients should use instead of the // default based on private key policy. For example, "9a" or "9e". - PIVSlot keys.PIVSlot `yaml:"piv_slot,omitempty"` + PIVSlot hardwarekey.PIVSlotKeyString `yaml:"piv_slot,omitempty"` // SerialNumberValidation contains optional settings for hardware key // serial number validation, including whether it is enabled. SerialNumberValidation *HardwareKeySerialNumberValidation `yaml:"serial_number_validation,omitempty"` + + // PINCacheTTL specifies how long to cache the user's PIV PIN. + PINCacheTTL time.Duration `yaml:"pin_cache_ttl,omitempty"` } func (h *HardwareKey) Parse() (*types.HardwareKey, error) { @@ -1255,7 +1257,10 @@ func (h *HardwareKey) Parse() (*types.HardwareKey, error) { } } - hk := &types.HardwareKey{PIVSlot: string(h.PIVSlot)} + hk := &types.HardwareKey{ + PIVSlot: string(h.PIVSlot), + PinCacheTTL: types.Duration(h.PINCacheTTL), + } if h.SerialNumberValidation != nil { var err error diff --git a/lib/config/fileconf_test.go b/lib/config/fileconf_test.go index 2107b7f728e9b..87e15782bca83 100644 --- a/lib/config/fileconf_test.go +++ b/lib/config/fileconf_test.go @@ -884,6 +884,119 @@ func TestSSHSection(t *testing.T) { } } +func TestHardwareKeyConfig(t *testing.T) { + for _, tc := range []struct { + name string + mutate func(cfgMap) + expectReadError require.ErrorAssertionFunc + expectParseError require.ErrorAssertionFunc + expectHardwareKeyConfig *types.HardwareKey + }{ + { + name: "OK empty", + mutate: func(cfg cfgMap) { + cfg["auth_service"].(cfgMap)["authentication"] = cfgMap{ + "hardware_key": cfgMap{}, + } + }, + expectHardwareKeyConfig: &types.HardwareKey{}, + }, + { + name: "OK piv_slot", + mutate: func(cfg cfgMap) { + cfg["auth_service"].(cfgMap)["authentication"] = cfgMap{ + "hardware_key": cfgMap{ + "piv_slot": "9a", + }, + } + }, + expectHardwareKeyConfig: &types.HardwareKey{ + PIVSlot: "9a", + }, + }, + { + name: "NOK piv_slot unsupported slot", + mutate: func(cfg cfgMap) { + cfg["auth_service"].(cfgMap)["authentication"] = cfgMap{ + "hardware_key": cfgMap{ + "piv_slot": "8f", + }, + } + }, + expectParseError: func(t require.TestingT, err error, i ...interface{}) { + require.Error(t, err) + require.True(t, trace.IsBadParameter(err), "got err = %v, want BadParameter", err) + }, + }, + { + name: "OK serial_number_validation", + mutate: func(cfg cfgMap) { + cfg["auth_service"].(cfgMap)["authentication"] = cfgMap{ + "hardware_key": cfgMap{ + "serial_number_validation": cfgMap{ + "enabled": true, + "serial_number_trait_name": "custom_trait_name", + }, + }, + } + }, + expectHardwareKeyConfig: &types.HardwareKey{ + SerialNumberValidation: &types.HardwareKeySerialNumberValidation{ + Enabled: true, + SerialNumberTraitName: "custom_trait_name", + }, + }, + }, + { + name: "OK pin_cache_ttl", + mutate: func(cfg cfgMap) { + cfg["auth_service"].(cfgMap)["authentication"] = cfgMap{ + "hardware_key": cfgMap{ + "pin_cache_ttl": "1m", + }, + } + }, + expectHardwareKeyConfig: &types.HardwareKey{ + PinCacheTTL: types.Duration(time.Minute), + }, + }, + { + name: "NOK pin_cache_ttl not a duration", + mutate: func(cfg cfgMap) { + cfg["auth_service"].(cfgMap)["authentication"] = cfgMap{ + "hardware_key": cfgMap{ + "pin_cache_ttl": "1minute", + }, + } + }, + expectReadError: require.Error, + }, + } { + t.Run(tc.name, func(t *testing.T) { + text := bytes.NewBuffer(editConfig(t, tc.mutate)) + + cfg, err := ReadConfig(text) + if tc.expectReadError != nil { + tc.expectReadError(t, err) + return + } + require.NoError(t, err) + + cap, err := cfg.Auth.Authentication.Parse() + if tc.expectParseError != nil { + tc.expectParseError(t, err) + return + } + require.NoError(t, err) + + hardwareKeyConfig, err := cap.GetHardwareKey() + require.NoError(t, err) + + require.Equal(t, tc.expectHardwareKeyConfig, hardwareKeyConfig) + }) + } +} + func TestX11Config(t *testing.T) { testCases := []struct { desc string diff --git a/lib/hardwarekey/agent.go b/lib/hardwarekey/agent.go new file mode 100644 index 0000000000000..e86cc4099c743 --- /dev/null +++ b/lib/hardwarekey/agent.go @@ -0,0 +1,167 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package hardwarekey + +import ( + "context" + "crypto/tls" + "errors" + "fmt" + "log/slog" + "net" + "os" + "path/filepath" + + "github.com/gravitational/trace" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + hardwarekeyagentv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/hardwarekeyagent/v1" + "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" + "github.com/gravitational/teleport/api/utils/keys/hardwarekeyagent" + "github.com/gravitational/teleport/lib/utils/cert" +) + +const ( + dirName = ".Teleport-PIV" + sockName = "agent.sock" + certFileName = "cert.pem" +) + +// DefaultAgentDir is the default dir for the hardware key agent's socket and certificate files. +func DefaultAgentDir() string { + return filepath.Join(os.TempDir(), dirName) +} + +// NewAgentClient opens a new hardware key agent client connected to the +// server based out of the given directory. +// +// [DefaultAgentDir] should be used for [keyAgentDir] outside of tests. +func NewAgentClient(ctx context.Context, keyAgentDir string) (hardwarekeyagentv1.HardwareKeyAgentServiceClient, error) { + socketPath := filepath.Join(keyAgentDir, sockName) + certPath := filepath.Join(keyAgentDir, certFileName) + + creds, err := credentials.NewClientTLSFromFile(certPath, "localhost") + if err != nil { + return nil, err + } + + return hardwarekeyagent.NewClient(ctx, socketPath, creds) +} + +// Server implementation [hardwarekeyagentv1.HardwareKeyAgentServiceServer]. +type Server struct { + grpcServer *grpc.Server + listener net.Listener + dir string +} + +// NewAgentServer returns a new hardware key agent server based out of the given directory. +// The given directory will be created when the server is served and destroyed with the server is stopped. +// +// [DefaultAgentDir] should be used for [keyAgentDir] outside of tests. +func NewAgentServer(ctx context.Context, s hardwarekey.Service, keyAgentDir string) (*Server, error) { + if err := os.MkdirAll(keyAgentDir, 0o700); err != nil { + return nil, trace.Wrap(err) + } + + l, err := newAgentListener(ctx, keyAgentDir) + if err != nil { + return nil, trace.Wrap(err) + } + + cert, err := generateServerCert(keyAgentDir) + if err != nil { + l.Close() + return nil, trace.Wrap(err) + } + + grpcServer := hardwarekeyagent.NewServer(ctx, s, credentials.NewServerTLSFromCert(&cert)) + return &Server{ + grpcServer: grpcServer, + listener: l, + dir: keyAgentDir, + }, nil +} + +func newAgentListener(ctx context.Context, keyAgentDir string) (net.Listener, error) { + socketPath := filepath.Join(keyAgentDir, sockName) + l, err := net.Listen("unix", socketPath) + if err == nil { + return l, nil + } else if !errors.Is(err, errAddrInUse) { + return nil, trace.Wrap(err) + } + + // A hardware key agent already exists in the given path. Before replacing it, + // try to connect to it and see if it is active. + client, err := NewAgentClient(ctx, keyAgentDir) + if err == nil { + pong, err := client.Ping(ctx, &hardwarekeyagentv1.PingRequest{}) + if err == nil { + return nil, trace.AlreadyExists("another agent instance is already running; PID: %d", pong.Pid) + } + } + + // If it isn't running, remove the socket and try again. + if err := os.Remove(socketPath); err != nil { + return nil, trace.Wrap(err) + } + + if l, err = net.Listen("unix", socketPath); err != nil { + return nil, trace.Wrap(err) + } + + return l, nil +} + +func generateServerCert(keyAgentDir string) (tls.Certificate, error) { + creds, err := cert.GenerateSelfSignedCert([]string{"localhost"}, nil /*ipAddresses*/) + if err != nil { + return tls.Certificate{}, trace.Wrap(err, "failed to generate the certificate") + } + + certPath := filepath.Join(keyAgentDir, certFileName) + f, err := os.OpenFile(certPath, os.O_RDWR|os.O_CREATE, 0o600) + if err != nil { + return tls.Certificate{}, trace.Wrap(err) + } + if _, err = f.Write(creds.Cert); err != nil { + return tls.Certificate{}, trace.Wrap(err) + } + if err = f.Close(); err != nil { + return tls.Certificate{}, trace.Wrap(err) + } + + return keys.X509KeyPair(creds.Cert, creds.PrivateKey) +} + +// Serve the hardware key agent server. +func (s *Server) Serve(ctx context.Context) error { + fmt.Fprintln(os.Stderr, "Listening for hardware key agent requests") + context.AfterFunc(ctx, s.Stop) + return trace.Wrap(s.grpcServer.Serve(s.listener)) +} + +// Stop the hardware key agent server. +func (s *Server) Stop() { + s.grpcServer.GracefulStop() + if err := os.RemoveAll(s.dir); err != nil { + slog.DebugContext(context.TODO(), "failed to clear hardware key agent directory") + } +} diff --git a/lib/hardwarekey/agent_test.go b/lib/hardwarekey/agent_test.go new file mode 100644 index 0000000000000..99a551fa9db8d --- /dev/null +++ b/lib/hardwarekey/agent_test.go @@ -0,0 +1,75 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package hardwarekey_test + +import ( + "context" + "errors" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + + hardwarekeyagentv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/hardwarekeyagent/v1" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" + libhwk "github.com/gravitational/teleport/lib/hardwarekey" +) + +func TestHardwareKeyAgent_Server(t *testing.T) { + ctx := context.Background() + agentDir := t.TempDir() + + // Prepare the agent server + mockService := hardwarekey.NewMockHardwareKeyService(nil /*prompt*/) + server, err := libhwk.NewAgentServer(ctx, mockService, agentDir) + require.NoError(t, err) + t.Cleanup(server.Stop) + + serverErr := make(chan error, 1) + go func() { + serverErr <- server.Serve(ctx) + }() + + // Should fail to open a new server in the same directory. + _, err = libhwk.NewAgentServer(ctx, mockService, agentDir) + require.Error(t, err) + + // Existing server should be unaffected. + clt, err := libhwk.NewAgentClient(ctx, agentDir) + require.NoError(t, err) + _, err = clt.Ping(ctx, &hardwarekeyagentv1.PingRequest{}) + require.NoError(t, err) + + // If the server stops gracefully, the directory should be cleaned up and a new server can be started. + server.Stop() + require.Eventually(t, func() bool { + _, err := os.Stat(agentDir) + return errors.Is(err, os.ErrNotExist) + }, 5*time.Second, 100*time.Millisecond) + server, err = libhwk.NewAgentServer(ctx, mockService, agentDir) + require.NoError(t, err) + t.Cleanup(server.Stop) + + // If the server is unresponsive, it should be replaced by a call to NewServer. + // Use a timeoutCtx so that the failed Ping request fails quickly. + timeoutCtx, cancel := context.WithTimeout(ctx, 500*time.Millisecond) + defer cancel() + server, err = libhwk.NewAgentServer(timeoutCtx, mockService, agentDir) + require.NoError(t, err) + t.Cleanup(server.Stop) +} diff --git a/lib/hardwarekey/errors_common.go b/lib/hardwarekey/errors_common.go new file mode 100644 index 0000000000000..3fd5ba4757646 --- /dev/null +++ b/lib/hardwarekey/errors_common.go @@ -0,0 +1,24 @@ +//go:build !windows + +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hardwarekey + +import "syscall" + +const ( + // errAddrInUse is [syscall.EADDRINUSE] + errAddrInUse = syscall.EADDRINUSE +) diff --git a/lib/hardwarekey/errors_windows.go b/lib/hardwarekey/errors_windows.go new file mode 100644 index 0000000000000..f964daee5a00a --- /dev/null +++ b/lib/hardwarekey/errors_windows.go @@ -0,0 +1,24 @@ +//go:build windows + +// Copyright 2025 Gravitational, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hardwarekey + +import "golang.org/x/sys/windows" + +const ( + // errAddrInUse is [windows.WSAEADDRINUSE] + errAddrInUse = windows.WSAEADDRINUSE +) diff --git a/lib/hardwarekey/service.go b/lib/hardwarekey/service.go new file mode 100644 index 0000000000000..6af7853045ba5 --- /dev/null +++ b/lib/hardwarekey/service.go @@ -0,0 +1,41 @@ +// Teleport +// Copyright (C) 2025 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Package hardwarekey provides common hardware key types and functions. +// Hardware key types and functions which are not used within /api should be placed here. +package hardwarekey + +import ( + "context" + + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" + "github.com/gravitational/teleport/api/utils/keys/hardwarekeyagent" + "github.com/gravitational/teleport/api/utils/keys/piv" +) + +// NewService prepares a new hardware key service. If a running hardware key agent +// is found, this will return a hardware key agent service with a direct PIV service as backup. +// Otherwise, the direct PIV service will be returned. +func NewService(ctx context.Context, prompt hardwarekey.Prompt) hardwarekey.Service { + hwks := piv.NewYubiKeyService(prompt) + + agentClient, err := NewAgentClient(ctx, DefaultAgentDir()) + if err == nil { + return hardwarekeyagent.NewService(agentClient, hwks) + } + + return hwks +} diff --git a/lib/modules/modules.go b/lib/modules/modules.go index 6370a3a1da895..a75bde490a36b 100644 --- a/lib/modules/modules.go +++ b/lib/modules/modules.go @@ -38,6 +38,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/accesslist" "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" "github.com/gravitational/teleport/entitlements" "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/automaticupgrades" @@ -282,7 +283,7 @@ type Modules interface { // IsOSSBuild returns if the binary was built without enterprise modules IsOSSBuild() bool // AttestHardwareKey attests a hardware key and returns its associated private key policy. - AttestHardwareKey(context.Context, interface{}, *keys.AttestationStatement, crypto.PublicKey, time.Duration) (*keys.AttestationData, error) + AttestHardwareKey(context.Context, interface{}, *hardwarekey.AttestationStatement, crypto.PublicKey, time.Duration) (*keys.AttestationData, error) // GenerateAccessRequestPromotions generates a list of valid promotions for given access request. GenerateAccessRequestPromotions(context.Context, AccessResourcesGetter, types.AccessRequest) (*types.AccessRequestAllowedPromotions, error) // GetSuggestedAccessLists generates a list of valid promotions for given access request. @@ -422,7 +423,7 @@ func (p *defaultModules) IsBoringBinary() bool { } // AttestHardwareKey attests a hardware key. -func (p *defaultModules) AttestHardwareKey(_ context.Context, _ interface{}, _ *keys.AttestationStatement, _ crypto.PublicKey, _ time.Duration) (*keys.AttestationData, error) { +func (p *defaultModules) AttestHardwareKey(_ context.Context, _ interface{}, _ *hardwarekey.AttestationStatement, _ crypto.PublicKey, _ time.Duration) (*keys.AttestationData, error) { // Default modules do not support attesting hardware keys. return nil, trace.NotFound("no attestation data for the given key") } diff --git a/lib/modules/test.go b/lib/modules/test.go index 82a4afd24d53f..efbe9c1e26684 100644 --- a/lib/modules/test.go +++ b/lib/modules/test.go @@ -27,6 +27,7 @@ import ( "github.com/gravitational/trace" "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" ) // TestModules implements the Modules interface for testing. @@ -104,7 +105,7 @@ func (m *TestModules) IsOSSBuild() bool { } // AttestHardwareKey attests a hardware key. -func (m *TestModules) AttestHardwareKey(ctx context.Context, obj interface{}, as *keys.AttestationStatement, pk crypto.PublicKey, d time.Duration) (*keys.AttestationData, error) { +func (m *TestModules) AttestHardwareKey(ctx context.Context, obj interface{}, as *hardwarekey.AttestationStatement, pk crypto.PublicKey, d time.Duration) (*keys.AttestationData, error) { if m.MockAttestationData != nil { return m.MockAttestationData, nil } diff --git a/lib/teleterm/clusters/cluster.go b/lib/teleterm/clusters/cluster.go index d1db6d8f04f89..1cf6a5c701dec 100644 --- a/lib/teleterm/clusters/cluster.go +++ b/lib/teleterm/clusters/cluster.go @@ -51,8 +51,6 @@ type Cluster struct { ProfileName string // Log is a component logger Log *logrus.Entry - // dir is the directory where cluster certificates are stored - dir string // Status is the cluster status status client.ProfileStatus // If not empty, it means that there was a problem with reading the cluster status. diff --git a/lib/teleterm/clusters/config.go b/lib/teleterm/clusters/config.go index 6af0ad1bbfad3..9275a45fd0fa7 100644 --- a/lib/teleterm/clusters/config.go +++ b/lib/teleterm/clusters/config.go @@ -24,15 +24,11 @@ import ( "github.com/sirupsen/logrus" "github.com/gravitational/teleport" - "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/client" - "github.com/gravitational/teleport/lib/teleterm/api/uri" ) // Config is the cluster service config type Config struct { - // Dir is the directory to store cluster profiles - Dir string // Clock is a clock for time-related operations Clock clockwork.Clock // InsecureSkipVerify is an option to skip TLS cert check @@ -44,19 +40,14 @@ type Config struct { WebauthnLogin client.WebauthnLoginFunc // AddKeysToAgent is passed to [client.Config]. AddKeysToAgent string - // CustomHardwareKeyPrompt is a custom hardware key prompt to use when asking - // for a hardware key PIN, touch, etc. - HardwareKeyPromptConstructor func(rootClusterURI uri.ResourceURI) keys.HardwareKeyPrompt + // ClientStore stores client data. + ClientStore *client.Store } // CheckAndSetDefaults checks the configuration for its validity and sets default values if needed func (c *Config) CheckAndSetDefaults() error { - if c.Dir == "" { - return trace.BadParameter("missing working directory") - } - - if c.HardwareKeyPromptConstructor == nil { - return trace.BadParameter("missing hardware key prompt constructor") + if c.ClientStore == nil { + return trace.BadParameter("missing client store") } if c.Clock == nil { diff --git a/lib/teleterm/clusters/storage.go b/lib/teleterm/clusters/storage.go index ba3cda2a0075d..b2a1b5d83715e 100644 --- a/lib/teleterm/clusters/storage.go +++ b/lib/teleterm/clusters/storage.go @@ -25,7 +25,6 @@ import ( "github.com/gravitational/trace" - "github.com/gravitational/teleport/api/profile" "github.com/gravitational/teleport/lib/client" dtauthn "github.com/gravitational/teleport/lib/devicetrust/authn" dtenroll "github.com/gravitational/teleport/lib/devicetrust/enroll" @@ -43,13 +42,12 @@ func NewStorage(cfg Config) (*Storage, error) { // ListProfileNames returns just the names of profiles in s.Dir. func (s *Storage) ListProfileNames() ([]string, error) { - pfNames, err := profile.ListProfileNames(s.Dir) - return pfNames, trace.Wrap(err) + return s.ClientStore.ListProfiles() } // ListRootClusters reads root clusters from profiles. func (s *Storage) ListRootClusters() ([]*Cluster, error) { - pfNames, err := s.ListProfileNames() + pfNames, err := s.ClientStore.ListProfiles() if err != nil { return nil, trace.Wrap(err) } @@ -112,11 +110,7 @@ func (s *Storage) ResolveCluster(resourceURI uri.ResourceURI) (*Cluster, *client // Remove removes a cluster func (s *Storage) Remove(ctx context.Context, profileName string) error { - if err := profile.RemoveProfile(s.Dir, profileName); err != nil { - return trace.Wrap(err) - } - - return nil + return s.ClientStore.DeleteProfile(profileName) } // Add adds a cluster @@ -125,7 +119,7 @@ func (s *Storage) Remove(ctx context.Context, profileName string) error { // clusters.Cluster a regular struct with no extra behavior and a much smaller interface. // https://github.com/gravitational/teleport/issues/13278 func (s *Storage) Add(ctx context.Context, webProxyAddress string) (*Cluster, *client.TeleportClient, error) { - profiles, err := profile.ListProfileNames(s.Dir) + profiles, err := s.ListProfileNames() if err != nil { return nil, nil, trace.Wrap(err) } @@ -141,7 +135,7 @@ func (s *Storage) Add(ctx context.Context, webProxyAddress string) (*Cluster, *c } } - cluster, clusterClient, err := s.addCluster(ctx, s.Dir, webProxyAddress) + cluster, clusterClient, err := s.addCluster(ctx, webProxyAddress) if err != nil { return nil, nil, trace.Wrap(err) } @@ -152,19 +146,15 @@ func (s *Storage) Add(ctx context.Context, webProxyAddress string) (*Cluster, *c // addCluster adds a new cluster. This makes the underlying profile .yaml file to be saved to the // tsh home dir without logging in the user yet. Adding a cluster makes it show up in the UI as the // list of clusters depends on the profiles in the home dir of tsh. -func (s *Storage) addCluster(ctx context.Context, dir, webProxyAddress string) (*Cluster, *client.TeleportClient, error) { +func (s *Storage) addCluster(ctx context.Context, webProxyAddress string) (*Cluster, *client.TeleportClient, error) { if webProxyAddress == "" { return nil, nil, trace.BadParameter("cluster address is missing") } - if dir == "" { - return nil, nil, trace.BadParameter("cluster directory is missing") - } - profileName := parseName(webProxyAddress) clusterURI := uri.NewClusterURI(profileName) - cfg := s.makeDefaultClientConfig(clusterURI) + cfg := s.makeClientConfig() cfg.WebProxyAddr = webProxyAddress clusterClient, err := client.NewClient(cfg) @@ -199,7 +189,6 @@ func (s *Storage) addCluster(ctx context.Context, dir, webProxyAddress string) ( Name: pingResponse.ClusterName, ProfileName: profileName, clusterClient: clusterClient, - dir: s.Dir, clock: s.Clock, Log: clusterLog, }, clusterClient, nil @@ -214,10 +203,8 @@ func (s *Storage) fromProfile(profileName, leafClusterName string) (*Cluster, *c clusterNameForKey := profileName clusterURI := uri.NewClusterURI(profileName) - profileStore := client.NewFSProfileStore(s.Dir) - - cfg := s.makeDefaultClientConfig(clusterURI) - if err := cfg.LoadProfile(profileStore, profileName); err != nil { + cfg := s.makeClientConfig() + if err := cfg.LoadProfile(profileName); err != nil { return nil, nil, trace.Wrap(err) } @@ -238,7 +225,6 @@ func (s *Storage) fromProfile(profileName, leafClusterName string) (*Cluster, *c Name: clusterClient.SiteName, ProfileName: profileName, clusterClient: clusterClient, - dir: s.Dir, clock: s.Clock, statusError: err, Log: s.Log.WithField("cluster", clusterURI), @@ -277,11 +263,8 @@ func (s *Storage) loadProfileStatusAndClusterKey(clusterClient *client.TeleportC return status, nil } -func (s *Storage) makeDefaultClientConfig(rootClusterURI uri.ResourceURI) *client.Config { - cfg := client.MakeDefaultConfig() - - cfg.HomePath = s.Dir - cfg.KeysDir = s.Dir +func (s *Storage) makeClientConfig() *client.Config { + cfg := &client.Config{} cfg.InsecureSkipVerify = s.InsecureSkipVerify cfg.AddKeysToAgent = s.AddKeysToAgent cfg.WebauthnLogin = s.WebauthnLogin @@ -295,8 +278,7 @@ func (s *Storage) makeDefaultClientConfig(rootClusterURI uri.ResourceURI) *clien // Since tsh daemon ran by Connect never expects data over stdin, it can always set this flag to // true. cfg.AllowStdinHijack = true - - cfg.CustomHardwareKeyPrompt = s.HardwareKeyPromptConstructor(rootClusterURI) + cfg.ClientStore = s.ClientStore cfg.DTAuthnRunCeremony = dtauthn.NewCeremony().Run cfg.DTAutoEnroll = dtenroll.AutoEnroll return cfg diff --git a/lib/teleterm/config.go b/lib/teleterm/config.go index 42320f4a17192..3c4fa7d284fda 100644 --- a/lib/teleterm/config.go +++ b/lib/teleterm/config.go @@ -49,6 +49,8 @@ type Config struct { InstallationID string // AddKeysToAgent is passed to [client.Config]. AddKeysToAgent string + // HardwareKeyAgent determines whether the daemon will run the hardware key agent. + HardwareKeyAgent bool } // CheckAndSetDefaults checks and sets default config values. diff --git a/lib/teleterm/daemon/config.go b/lib/teleterm/daemon/config.go index 80cc79d081946..cc49c2a712747 100644 --- a/lib/teleterm/daemon/config.go +++ b/lib/teleterm/daemon/config.go @@ -63,10 +63,13 @@ type Config struct { AgentsDir string GatewayCreator GatewayCreator - // CreateTshdEventsClientCredsFunc lazily creates creds for the tshd events server ran by the - // Electron app. This is to ensure that the server public key is written to the disk under the - // expected location by the time we get around to creating the client. - CreateTshdEventsClientCredsFunc CreateTshdEventsClientCredsFunc + + // TshdEventsClient holds a client to send events to the Electron App. + // + // The startup of the app is orchestrated so that the client is loaded before any other method on + // daemon.Service. This way all the other code in daemon.Service can assume that the tshd events + // client is available right from the beginning, without the need for nil checks. + TshdEventsClient *TshdEventsClient ConnectMyComputerRoleSetup *connectmycomputer.RoleSetup ConnectMyComputerTokenProvisioner *connectmycomputer.TokenProvisioner diff --git a/lib/teleterm/daemon/daemon.go b/lib/teleterm/daemon/daemon.go index 3d23a06c78586..2abcc4987b740 100644 --- a/lib/teleterm/daemon/daemon.go +++ b/lib/teleterm/daemon/daemon.go @@ -26,7 +26,6 @@ import ( "time" "github.com/gravitational/trace" - "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -88,6 +87,7 @@ func New(cfg Config) (*Service, error) { gateways: make(map[string]gateway.Gateway), usageReporter: connectUsageReporter, headlessWatcherClosers: make(map[string]context.CancelFunc), + headlessAuthSemaphore: newWaitSemaphore(maxConcurrentImportantModals, imporantModalWaitDuraiton), } // TODO(gzdunek): The client cache should be created outside of daemon.New. @@ -118,7 +118,7 @@ func (s *Service) relogin(ctx context.Context, req *api.ReloginRequest) error { timeoutCtx, cancelTshdEventsCtx := context.WithTimeout(ctx, reloginUserTimeout) defer cancelTshdEventsCtx() - if _, err := s.tshdEventsClient.Relogin(timeoutCtx, req); err != nil { + if _, err := s.cfg.TshdEventsClient.client.Relogin(timeoutCtx, req); err != nil { if status.Code(err) == codes.DeadlineExceeded { return trace.Wrap(err, "the user did not refresh the session within %s", reloginUserTimeout.String()) } @@ -305,8 +305,8 @@ func (s *Service) ClusterLogout(ctx context.Context, uri string) error { // CreateGateway creates a gateway to given targetURI func (s *Service) CreateGateway(ctx context.Context, params CreateGatewayParams) (gateway.Gateway, error) { - s.mu.Lock() - defer s.mu.Unlock() + s.gatewaysMu.Lock() + defer s.gatewaysMu.Unlock() gateway, err := s.createGateway(ctx, params) if err != nil { @@ -425,8 +425,8 @@ func (s *Service) reissueGatewayCerts(ctx context.Context, g gateway.Gateway) (t // RemoveGateway removes cluster gateway func (s *Service) RemoveGateway(gatewayURI string) error { - s.mu.Lock() - defer s.mu.Unlock() + s.gatewaysMu.Lock() + defer s.gatewaysMu.Unlock() gateway, err := s.findGateway(gatewayURI) if err != nil { @@ -464,8 +464,8 @@ func (s *Service) findGateway(gatewayURI string) (gateway.Gateway, error) { // ListGateways lists gateways func (s *Service) ListGateways() []gateway.Gateway { - s.mu.RLock() - defer s.mu.RUnlock() + s.gatewaysMu.RLock() + defer s.gatewaysMu.RUnlock() gws := make([]gateway.Gateway, 0, len(s.gateways)) for _, gateway := range s.gateways { @@ -509,8 +509,8 @@ func (s *Service) GetGatewayCLICommand(ctx context.Context, gateway gateway.Gate // SetGatewayTargetSubresourceName updates the TargetSubresourceName field of a gateway stored in // s.gateways. func (s *Service) SetGatewayTargetSubresourceName(gatewayURI, targetSubresourceName string) (gateway.Gateway, error) { - s.mu.Lock() - defer s.mu.Unlock() + s.gatewaysMu.Lock() + defer s.gatewaysMu.Unlock() gateway, err := s.findGateway(gatewayURI) if err != nil { @@ -532,8 +532,8 @@ func (s *Service) SetGatewayTargetSubresourceName(gatewayURI, targetSubresourceN // // SetGatewayLocalPort is a noop if port is equal to the existing port. func (s *Service) SetGatewayLocalPort(gatewayURI, localPort string) (gateway.Gateway, error) { - s.mu.Lock() - defer s.mu.Unlock() + s.gatewaysMu.Lock() + defer s.gatewaysMu.Unlock() oldGateway, err := s.findGateway(gatewayURI) if err != nil { @@ -794,8 +794,8 @@ func (s *Service) AssumeRole(ctx context.Context, req *api.AssumeRoleRequest) er // // We don't know which gateways are affected by the access request, // so we need to clear certs for all of them. - s.mu.RLock() - defer s.mu.RUnlock() + s.gatewaysMu.RLock() + defer s.gatewaysMu.RUnlock() for _, gw := range s.gateways { targetURI := gw.TargetURI() if !(targetURI.IsKube() && targetURI.GetRootClusterURI() == cluster.URI) { @@ -878,8 +878,8 @@ func (s *Service) ReportUsageEvent(req *api.ReportUsageEventRequest) error { // Stop terminates all cluster open connections func (s *Service) Stop() { - s.mu.RLock() - defer s.mu.RUnlock() + s.gatewaysMu.RLock() + defer s.gatewaysMu.RUnlock() s.cfg.Log.Info("Stopping") @@ -908,43 +908,17 @@ func (s *Service) Stop() { // UpdateAndDialTshdEventsServerAddress allows the Electron app to provide the tshd events server // address. -// -// The startup of the app is orchestrated so that this method is called before any other method on -// daemon.Service. This way all the other code in daemon.Service can assume that the tshd events -// client is available right from the beginning, without the need for nil checks. func (s *Service) UpdateAndDialTshdEventsServerAddress(serverAddress string) error { - s.mu.Lock() - defer s.mu.Unlock() - - withCreds, err := s.cfg.CreateTshdEventsClientCredsFunc() - if err != nil { - return trace.Wrap(err) - } - - conn, err := grpc.Dial(serverAddress, withCreds) - if err != nil { - return trace.Wrap(err) - } - - client := api.NewTshdEventsServiceClient(conn) - - s.tshdEventsClient = client - s.headlessAuthSemaphore = newWaitSemaphore(maxConcurrentImportantModals, imporantModalWaitDuraiton) - - return nil + return s.cfg.TshdEventsClient.Connect(serverAddress) } // TshdEventsClient returns the client if it was initialized earlied by calling // UpdateAndDialTshdEventsServerAddress, otherwise it returns an error. // // The startup of Connect is orchestrated in a way that makes it safe to call this method from any -// RPC. Code inside daemon.Service should just use s.tshdEventsClient directly. -func (s *Service) TshdEventsClient() (api.TshdEventsServiceClient, error) { - if s.tshdEventsClient == nil { - return nil, trace.NotFound("tshd events client has not been initialized yet") - } - - return s.tshdEventsClient, nil +// RPC. Code inside daemon.Service should just use s.cfg.tshdEventsClient directly. +func (s *Service) TshdEventsClient(ctx context.Context) (api.TshdEventsServiceClient, error) { + return s.cfg.TshdEventsClient.GetClient(ctx) } // NotifyApp sends a notification (usually an error) to the Electron App. @@ -952,7 +926,7 @@ func (s *Service) NotifyApp(ctx context.Context, notification *api.SendNotificat tshdEventsCtx, cancelTshdEventsCtx := context.WithTimeout(ctx, tshdEventsTimeout) defer cancelTshdEventsCtx() - _, err := s.tshdEventsClient.SendNotification(tshdEventsCtx, notification) + _, err := s.cfg.TshdEventsClient.client.SendNotification(tshdEventsCtx, notification) return trace.Wrap(err) } @@ -1226,18 +1200,17 @@ func (s *Service) ClearCachedClientsForRoot(clusterURI uri.ResourceURI) error { // Service is the daemon service type Service struct { cfg *Config - // mu guards gateways and the creation of tshdEventsClient. - mu sync.RWMutex // closeContext is canceled when Service is getting stopped. It is used as a context for the calls // to the tshd events gRPC client. closeContext context.Context cancel context.CancelFunc + // gateways holds the long-running gateways for resources on different clusters. So far it's been // used mostly for database gateways but it has potential to be used for app access as well. gateways map[string]gateway.Gateway - // tshdEventsClient is a client to send events to the Electron App. - tshdEventsClient api.TshdEventsServiceClient + // gatewaysMu guards gateways. + gatewaysMu sync.RWMutex // The Electron App can display multiple important modals by showing the latest one and hiding the others. // However, we should be careful with it, and generally try to limit the number of prompts on the tshd side, diff --git a/lib/teleterm/daemon/daemon_headless.go b/lib/teleterm/daemon/daemon_headless.go index 2662791ca17bb..46b7e4a426e0e 100644 --- a/lib/teleterm/daemon/daemon_headless.go +++ b/lib/teleterm/daemon/daemon_headless.go @@ -277,7 +277,7 @@ func (s *Service) sendPendingHeadlessAuthentication(ctx context.Context, ha *typ } defer s.headlessAuthSemaphore.Release() - _, err := s.tshdEventsClient.SendPendingHeadlessAuthentication(ctx, req) + _, err := s.cfg.TshdEventsClient.client.SendPendingHeadlessAuthentication(ctx, req) return trace.Wrap(err) } diff --git a/lib/teleterm/daemon/daemon_test.go b/lib/teleterm/daemon/daemon_test.go index fc60afe15d209..058ac49625e69 100644 --- a/lib/teleterm/daemon/daemon_test.go +++ b/lib/teleterm/daemon/daemon_test.go @@ -39,7 +39,6 @@ import ( "google.golang.org/grpc/status" "github.com/gravitational/teleport/api/types" - "github.com/gravitational/teleport/api/utils/keys" api "github.com/gravitational/teleport/gen/proto/go/teleport/lib/teleterm/v1" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/client/clientcache" @@ -294,14 +293,9 @@ func TestGatewayCRUD(t *testing.T) { } func TestUpdateTshdEventsServerAddress(t *testing.T) { - homeDir := t.TempDir() - storage, err := clusters.NewStorage(clusters.Config{ - Dir: homeDir, + ClientStore: client.NewFSClientStore(t.TempDir()), InsecureSkipVerify: true, - HardwareKeyPromptConstructor: func(rootClusterURI uri.ResourceURI) keys.HardwareKeyPrompt { - return nil - }, }) require.NoError(t, err) @@ -311,11 +305,12 @@ func TestUpdateTshdEventsServerAddress(t *testing.T) { return grpc.WithTransportCredentials(insecure.NewCredentials()), nil } + tshdEventsClient := NewTshdEventsClient(createTshdEventsClientCredsFunc) daemon, err := New(Config{ - Storage: storage, - CreateTshdEventsClientCredsFunc: createTshdEventsClientCredsFunc, - KubeconfigsDir: t.TempDir(), - AgentsDir: t.TempDir(), + Storage: storage, + TshdEventsClient: tshdEventsClient, + KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), }) require.NoError(t, err) @@ -325,20 +320,15 @@ func TestUpdateTshdEventsServerAddress(t *testing.T) { err = daemon.UpdateAndDialTshdEventsServerAddress(ls.Addr().String()) require.NoError(t, err) - require.NotNil(t, daemon.tshdEventsClient) + require.NotNil(t, daemon.cfg.TshdEventsClient) require.Equal(t, 1, createTshdEventsClientCredsFuncCallCount, "Expected createTshdEventsClientCredsFunc to be called exactly once") } func TestUpdateTshdEventsServerAddress_CredsErr(t *testing.T) { - homeDir := t.TempDir() - storage, err := clusters.NewStorage(clusters.Config{ - Dir: homeDir, + ClientStore: client.NewFSClientStore(t.TempDir()), InsecureSkipVerify: true, - HardwareKeyPromptConstructor: func(rootClusterURI uri.ResourceURI) keys.HardwareKeyPrompt { - return nil - }, }) require.NoError(t, err) @@ -346,11 +336,12 @@ func TestUpdateTshdEventsServerAddress_CredsErr(t *testing.T) { return nil, trace.Errorf("Error while creating creds") } + tshdEventsClient := NewTshdEventsClient(createTshdEventsClientCredsFunc) daemon, err := New(Config{ - Storage: storage, - CreateTshdEventsClientCredsFunc: createTshdEventsClientCredsFunc, - KubeconfigsDir: t.TempDir(), - AgentsDir: t.TempDir(), + Storage: storage, + TshdEventsClient: tshdEventsClient, + KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), }) require.NoError(t, err) @@ -438,21 +429,20 @@ func TestRetryWithRelogin(t *testing.T) { t.Parallel() storage, err := clusters.NewStorage(clusters.Config{ - Dir: t.TempDir(), + ClientStore: client.NewFSClientStore(t.TempDir()), InsecureSkipVerify: true, - HardwareKeyPromptConstructor: func(rootClusterURI uri.ResourceURI) keys.HardwareKeyPrompt { - return nil - }, }) require.NoError(t, err) + tshdEventsClient := NewTshdEventsClient(func() (grpc.DialOption, error) { + return grpc.WithTransportCredentials(insecure.NewCredentials()), nil + }) + daemon, err := New(Config{ - Storage: storage, - CreateTshdEventsClientCredsFunc: func() (grpc.DialOption, error) { - return grpc.WithTransportCredentials(insecure.NewCredentials()), nil - }, - KubeconfigsDir: t.TempDir(), - AgentsDir: t.TempDir(), + Storage: storage, + TshdEventsClient: tshdEventsClient, + KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), CreateClientCacheFunc: func(newClientFunc clientcache.NewClientFunc) (ClientCache, error) { return fakeClientCache{}, nil }, @@ -494,21 +484,20 @@ func TestConcurrentHeadlessAuthPrompts(t *testing.T) { ctx := context.Background() storage, err := clusters.NewStorage(clusters.Config{ - Dir: t.TempDir(), + ClientStore: client.NewFSClientStore(t.TempDir()), InsecureSkipVerify: true, - HardwareKeyPromptConstructor: func(rootClusterURI uri.ResourceURI) keys.HardwareKeyPrompt { - return nil - }, }) require.NoError(t, err) + tshdEventsClient := NewTshdEventsClient(func() (grpc.DialOption, error) { + return grpc.WithTransportCredentials(insecure.NewCredentials()), nil + }) + daemon, err := New(Config{ - Storage: storage, - CreateTshdEventsClientCredsFunc: func() (grpc.DialOption, error) { - return grpc.WithTransportCredentials(insecure.NewCredentials()), nil - }, - KubeconfigsDir: t.TempDir(), - AgentsDir: t.TempDir(), + Storage: storage, + TshdEventsClient: tshdEventsClient, + KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), CreateClientCacheFunc: func(newClientFunc clientcache.NewClientFunc) (ClientCache, error) { return fakeClientCache{}, nil }, @@ -652,13 +641,15 @@ func (c *mockTSHDEventsService) SendPendingHeadlessAuthentication(context.Contex func TestGetGatewayCLICommand(t *testing.T) { t.Parallel() + tshdEventsClient := NewTshdEventsClient(func() (grpc.DialOption, error) { + return grpc.WithTransportCredentials(insecure.NewCredentials()), nil + }) + daemon, err := New(Config{ - Storage: fakeStorage{}, - CreateTshdEventsClientCredsFunc: func() (grpc.DialOption, error) { - return grpc.WithTransportCredentials(insecure.NewCredentials()), nil - }, - KubeconfigsDir: t.TempDir(), - AgentsDir: t.TempDir(), + Storage: fakeStorage{}, + TshdEventsClient: tshdEventsClient, + KubeconfigsDir: t.TempDir(), + AgentsDir: t.TempDir(), CreateClientCacheFunc: func(newClientFunc clientcache.NewClientFunc) (ClientCache, error) { return fakeClientCache{}, nil }, diff --git a/lib/teleterm/daemon/events_client.go b/lib/teleterm/daemon/events_client.go new file mode 100644 index 0000000000000..ecbb994b6ece9 --- /dev/null +++ b/lib/teleterm/daemon/events_client.go @@ -0,0 +1,94 @@ +/* + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package daemon + +import ( + "context" + "sync" + "time" + + "github.com/gravitational/trace" + "google.golang.org/grpc" + + api "github.com/gravitational/teleport/gen/proto/go/teleport/lib/teleterm/v1" +) + +// TshdEventsClient holds a lazily loaded [api.TshdEventsServiceClient]. +type TshdEventsClient struct { + client api.TshdEventsServiceClient + // connectedChan is closed once the client is connected + connectedChan chan struct{} + // connectMu is used during connection to prevent a race between callers. + connectMu sync.Mutex + + // credsFn lazily creates creds for the tshd events server ran by the Electron app. + // This is to ensure that the server public key is written to the disk under the + // expected location by the time we get around to creating the client. + credsFn CreateTshdEventsClientCredsFunc +} + +func NewTshdEventsClient(credsFn CreateTshdEventsClientCredsFunc) *TshdEventsClient { + return &TshdEventsClient{ + credsFn: credsFn, + connectedChan: make(chan struct{}), + } +} + +// Connect connects to the given server address. +func (c *TshdEventsClient) Connect(serverAddress string) error { + c.connectMu.Lock() + defer c.connectMu.Unlock() + + select { + case <-c.connectedChan: + // already connected, no-op. + return nil + default: + } + + withCreds, err := c.credsFn() + if err != nil { + return trace.Wrap(err) + } + + conn, err := grpc.NewClient(serverAddress, withCreds) + if err != nil { + return trace.Wrap(err) + } + + // Successfully connected set the client and signal to any waiters. + c.client = api.NewTshdEventsServiceClient(conn) + close(c.connectedChan) + return nil +} + +// GetClient retrieves the lazily loaded client. If the client is not yet loaded, +// this method will wait until it is loaded, the given context is closed, or it +// times out. +func (c *TshdEventsClient) GetClient(ctx context.Context) (api.TshdEventsServiceClient, error) { + ctx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + + select { + case <-c.connectedChan: + return c.client, nil + case <-ctx.Done(): + return nil, trace.Wrap(ctx.Err(), "tshd events client has not been initialized yet") + } +} diff --git a/lib/teleterm/daemon/events_client_test.go b/lib/teleterm/daemon/events_client_test.go new file mode 100644 index 0000000000000..550a4167b2545 --- /dev/null +++ b/lib/teleterm/daemon/events_client_test.go @@ -0,0 +1,88 @@ +/* + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package daemon + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + api "github.com/gravitational/teleport/gen/proto/go/teleport/lib/teleterm/v1" +) + +func TestTshdEventsClient(t *testing.T) { + t.Parallel() + + ctx := context.Background() + _, addr := newMockTSHDEventsServiceServer(t) + + c := NewTshdEventsClient(func() (grpc.DialOption, error) { + return grpc.WithTransportCredentials(insecure.NewCredentials()), nil + }) + + // GetClient should timeout if client is not connected. + timeoutCtx, cancel := context.WithTimeout(ctx, 500*time.Millisecond) + defer cancel() + + _, err := c.GetClient(timeoutCtx) + require.ErrorIs(t, err, context.DeadlineExceeded) + + // Make 2 calls to GetClient to wait for a client connection. + timeoutCtx, cancel = context.WithTimeout(ctx, 5*time.Second) + defer cancel() + type getClientRet struct { + clt api.TshdEventsServiceClient + err error + } + retC := make(chan getClientRet) + for range 2 { + go func() { + clt, err := c.GetClient(timeoutCtx) + retC <- getClientRet{ + clt: clt, + err: err, + } + }() + } + + // Connect client, GetClient calls should complete. + err = c.Connect(addr) + require.NoError(t, err) + + for range 2 { + select { + case <-timeoutCtx.Done(): + t.Error("timeout waiting for client connection") + case ret := <-retC: + require.NoError(t, ret.err) + require.NotNil(t, ret.clt) + } + } + + // GetClient should complete immediately once connected. + timeoutCtx, cancel = context.WithTimeout(ctx, 500*time.Millisecond) + defer cancel() + + _, err = c.GetClient(timeoutCtx) + require.NoError(t, err) +} diff --git a/lib/teleterm/daemon/hardwarekeyprompt.go b/lib/teleterm/daemon/hardwarekeyprompt.go index ad75ccf6f699b..035c59d09f47f 100644 --- a/lib/teleterm/daemon/hardwarekeyprompt.go +++ b/lib/teleterm/daemon/hardwarekeyprompt.go @@ -20,16 +20,15 @@ package daemon import ( "context" + "strings" "github.com/gravitational/trace" - "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" api "github.com/gravitational/teleport/gen/proto/go/teleport/lib/teleterm/v1" - "github.com/gravitational/teleport/lib/teleterm/api/uri" ) -// NewHardwareKeyPromptConstructor returns a new hardware key prompt constructor -// for this service and the given root cluster URI. +// NewHardwareKeyPrompt returns a new hardware key prompt. // // TODO(gzdunek): Improve multi-cluster and multi-hardware keys support. // The code in yubikey.go doesn't really support using multiple hardware keys (like one per cluster): @@ -46,19 +45,29 @@ import ( // Because the code in yubikey.go assumes you use a single key, we don't have any mutex here. // (unlike other modals triggered by tshd). // We don't expect receiving prompts from different hardware keys. -func (s *Service) NewHardwareKeyPromptConstructor(rootClusterURI uri.ResourceURI) keys.HardwareKeyPrompt { - return &hardwareKeyPrompter{s: s, rootClusterURI: rootClusterURI} +func (c *TshdEventsClient) NewHardwareKeyPrompt() hardwarekey.Prompt { + return &hardwareKeyPrompter{c: c} } type hardwareKeyPrompter struct { - s *Service - rootClusterURI uri.ResourceURI + c *TshdEventsClient } // Touch prompts the user to touch the hardware key. -func (h *hardwareKeyPrompter) Touch(ctx context.Context) error { - _, err := h.s.tshdEventsClient.PromptHardwareKeyTouch(ctx, &api.PromptHardwareKeyTouchRequest{ - RootClusterUri: h.rootClusterURI.String(), +func (h *hardwareKeyPrompter) Touch(ctx context.Context, keyInfo hardwarekey.ContextualKeyInfo) error { + // Don't include "tsh daemon" commands. + if strings.Contains(keyInfo.Command, "tsh daemon") { + keyInfo.Command = "" + } + + clt, err := h.c.GetClient(ctx) + if err != nil { + return trace.Wrap(err) + } + + _, err = clt.PromptHardwareKeyTouch(ctx, &api.PromptHardwareKeyTouchRequest{ + ProxyHostname: keyInfo.ProxyHost, + Command: keyInfo.Command, }) if err != nil { return trace.Wrap(err) @@ -67,10 +76,21 @@ func (h *hardwareKeyPrompter) Touch(ctx context.Context) error { } // AskPIN prompts the user for a PIN. -func (h *hardwareKeyPrompter) AskPIN(ctx context.Context, requirement keys.PINPromptRequirement) (string, error) { - res, err := h.s.tshdEventsClient.PromptHardwareKeyPIN(ctx, &api.PromptHardwareKeyPINRequest{ - RootClusterUri: h.rootClusterURI.String(), - PinOptional: requirement == keys.PINOptional, +func (h *hardwareKeyPrompter) AskPIN(ctx context.Context, requirement hardwarekey.PINPromptRequirement, keyInfo hardwarekey.ContextualKeyInfo) (string, error) { + // Don't include "tsh daemon" commands. + if strings.Contains(keyInfo.Command, "tsh daemon") { + keyInfo.Command = "" + } + + clt, err := h.c.GetClient(ctx) + if err != nil { + return "", trace.Wrap(err) + } + + res, err := clt.PromptHardwareKeyPIN(ctx, &api.PromptHardwareKeyPINRequest{ + ProxyHostname: keyInfo.ProxyHost, + PinOptional: requirement == hardwarekey.PINOptional, + Command: keyInfo.Command, }) if err != nil { return "", trace.Wrap(err) @@ -81,14 +101,19 @@ func (h *hardwareKeyPrompter) AskPIN(ctx context.Context, requirement keys.PINPr // ChangePIN asks for a new PIN. // The Electron app prompt must handle default values for PIN and PUK, // preventing the user from submitting empty/default values. -func (h *hardwareKeyPrompter) ChangePIN(ctx context.Context) (*keys.PINAndPUK, error) { - res, err := h.s.tshdEventsClient.PromptHardwareKeyPINChange(ctx, &api.PromptHardwareKeyPINChangeRequest{ - RootClusterUri: h.rootClusterURI.String(), +func (h *hardwareKeyPrompter) ChangePIN(ctx context.Context, keyInfo hardwarekey.ContextualKeyInfo) (*hardwarekey.PINAndPUK, error) { + clt, err := h.c.GetClient(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + + res, err := clt.PromptHardwareKeyPINChange(ctx, &api.PromptHardwareKeyPINChangeRequest{ + ProxyHostname: keyInfo.ProxyHost, }) if err != nil { return nil, trace.Wrap(err) } - return &keys.PINAndPUK{ + return &hardwarekey.PINAndPUK{ PIN: res.Pin, PUK: res.Puk, PUKChanged: res.PukChanged, @@ -96,10 +121,15 @@ func (h *hardwareKeyPrompter) ChangePIN(ctx context.Context) (*keys.PINAndPUK, e } // ConfirmSlotOverwrite asks the user if the slot's private key and certificate can be overridden. -func (h *hardwareKeyPrompter) ConfirmSlotOverwrite(ctx context.Context, message string) (bool, error) { - res, err := h.s.tshdEventsClient.ConfirmHardwareKeySlotOverwrite(ctx, &api.ConfirmHardwareKeySlotOverwriteRequest{ - RootClusterUri: h.rootClusterURI.String(), - Message: message, +func (h *hardwareKeyPrompter) ConfirmSlotOverwrite(ctx context.Context, message string, keyInfo hardwarekey.ContextualKeyInfo) (bool, error) { + clt, err := h.c.GetClient(ctx) + if err != nil { + return false, trace.Wrap(err) + } + + res, err := clt.ConfirmHardwareKeySlotOverwrite(ctx, &api.ConfirmHardwareKeySlotOverwriteRequest{ + ProxyHostname: keyInfo.ProxyHost, + Message: message, }) if err != nil { return false, trace.Wrap(err) diff --git a/lib/teleterm/daemon/mfaprompt.go b/lib/teleterm/daemon/mfaprompt.go index a055c1a00d827..ea839530b86ca 100644 --- a/lib/teleterm/daemon/mfaprompt.go +++ b/lib/teleterm/daemon/mfaprompt.go @@ -64,7 +64,7 @@ func (s *Service) promptAppMFA(ctx context.Context, in *api.PromptMFARequest) (* s.mfaMu.Lock() defer s.mfaMu.Unlock() - return s.tshdEventsClient.PromptMFA(ctx, in) + return s.cfg.TshdEventsClient.client.PromptMFA(ctx, in) } // Run prompts the user to complete an MFA authentication challenge. diff --git a/lib/teleterm/teleterm.go b/lib/teleterm/teleterm.go index c8bc92b4e0e8a..e81153bd30440 100644 --- a/lib/teleterm/teleterm.go +++ b/lib/teleterm/teleterm.go @@ -20,6 +20,7 @@ package teleterm import ( "context" + "log/slog" "os" "os/signal" "path/filepath" @@ -32,8 +33,9 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "github.com/gravitational/teleport/api/utils/keys" - "github.com/gravitational/teleport/lib/teleterm/api/uri" + "github.com/gravitational/teleport/api/utils/keys/piv" + "github.com/gravitational/teleport/lib/client" + libhwk "github.com/gravitational/teleport/lib/hardwarekey" "github.com/gravitational/teleport/lib/teleterm/apiserver" "github.com/gravitational/teleport/lib/teleterm/clusteridcache" "github.com/gravitational/teleport/lib/teleterm/clusters" @@ -42,7 +44,6 @@ import ( // Serve starts daemon service func Serve(ctx context.Context, cfg Config) error { - var hardwareKeyPromptConstructor func(clusterURI uri.ResourceURI) keys.HardwareKeyPrompt if err := cfg.CheckAndSetDefaults(); err != nil { return trace.Wrap(err) } @@ -54,14 +55,17 @@ func Serve(ctx context.Context, cfg Config) error { clock := clockwork.NewRealClock() + // Prepare tshdEventsClient with lazy loading. + tshdEventsClient := daemon.NewTshdEventsClient(grpcCredentials.tshdEvents) + + // Always use the direct YubiKey PIV service since Connect provides the best UX. + hwks := piv.NewYubiKeyService(tshdEventsClient.NewHardwareKeyPrompt()) + storage, err := clusters.NewStorage(clusters.Config{ - Dir: cfg.HomeDir, Clock: clock, InsecureSkipVerify: cfg.InsecureSkipVerify, AddKeysToAgent: cfg.AddKeysToAgent, - HardwareKeyPromptConstructor: func(rootClusterURI uri.ResourceURI) keys.HardwareKeyPrompt { - return hardwareKeyPromptConstructor(rootClusterURI) - }, + ClientStore: client.NewFSClientStore(cfg.HomeDir, client.WithHardwareKeyService(hwks)), }) if err != nil { return trace.Wrap(err) @@ -70,20 +74,17 @@ func Serve(ctx context.Context, cfg Config) error { clusterIDCache := &clusteridcache.Cache{} daemonService, err := daemon.New(daemon.Config{ - Storage: storage, - CreateTshdEventsClientCredsFunc: grpcCredentials.tshdEvents, - PrehogAddr: cfg.PrehogAddr, - KubeconfigsDir: cfg.KubeconfigsDir, - AgentsDir: cfg.AgentsDir, - ClusterIDCache: clusterIDCache, + Storage: storage, + PrehogAddr: cfg.PrehogAddr, + KubeconfigsDir: cfg.KubeconfigsDir, + AgentsDir: cfg.AgentsDir, + ClusterIDCache: clusterIDCache, + TshdEventsClient: tshdEventsClient, }) if err != nil { return trace.Wrap(err) } - // TODO(gzdunek): Move tshdEventsClient out of daemonService so that we can - // construct the prompt before creating Storage. - hardwareKeyPromptConstructor = daemonService.NewHardwareKeyPromptConstructor apiServer, err := apiserver.New(apiserver.Config{ HostAddr: cfg.Addr, InsecureSkipVerify: cfg.InsecureSkipVerify, @@ -104,6 +105,20 @@ func Serve(ctx context.Context, cfg Config) error { serverAPIWait <- err }() + var hardwareKeyAgentServer *libhwk.Server + if cfg.HardwareKeyAgent { + hardwareKeyAgentServer, err = libhwk.NewAgentServer(ctx, hwks, libhwk.DefaultAgentDir()) + if err != nil { + slog.WarnContext(ctx, "failed to create the hardware key agent server", "err", err) + } else { + go func() { + if err := hardwareKeyAgentServer.Serve(ctx); err != nil { + slog.WarnContext(ctx, "hardware key agent server error", "err", err) + } + }() + } + } + // Wait for shutdown signals go func() { shutdownSignals := []os.Signal{os.Interrupt, syscall.SIGTERM} @@ -119,6 +134,10 @@ func Serve(ctx context.Context, cfg Config) error { daemonService.Stop() apiServer.Stop() + + if hardwareKeyAgentServer != nil { + hardwareKeyAgentServer.Stop() + } }() errAPI := <-serverAPIWait diff --git a/lib/teleterm/vnet/service.go b/lib/teleterm/vnet/service.go index ed8f99b301855..434d330dba189 100644 --- a/lib/teleterm/vnet/service.go +++ b/lib/teleterm/vnet/service.go @@ -324,7 +324,7 @@ func (s *Service) Close() error { } func (s *Service) isUsageReportingEnabled(ctx context.Context) (bool, error) { - tshdEventsClient, err := s.cfg.DaemonService.TshdEventsClient() + tshdEventsClient, err := s.cfg.DaemonService.TshdEventsClient(ctx) if err != nil { return false, trace.Wrap(err) } @@ -338,7 +338,7 @@ func (s *Service) isUsageReportingEnabled(ctx context.Context) (bool, error) { } func (s *Service) reportUnexpectedShutdown(ctx context.Context, shutdownErr error) error { - tshdEventsClient, err := s.cfg.DaemonService.TshdEventsClient() + tshdEventsClient, err := s.cfg.DaemonService.TshdEventsClient(ctx) if err != nil { return trace.Wrap(err, "obtaining tshd events client") } diff --git a/lib/vnet/osconfig.go b/lib/vnet/osconfig.go index 0642ebd0980dd..5d0082dec7bee 100644 --- a/lib/vnet/osconfig.go +++ b/lib/vnet/osconfig.go @@ -23,7 +23,6 @@ import ( "github.com/gravitational/trace" "github.com/jonboulle/clockwork" - "github.com/gravitational/teleport/api/profile" "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/client/clientcache" @@ -106,7 +105,7 @@ func (c *osConfigurator) updateOSConfiguration(ctx context.Context) error { // to access c.homePath that it sent when starting the daemon. // Otherwise a client could make the daemon read a profile out of any directory. if err := c.doWithDroppedRootPrivileges(ctx, func() error { - profileNames, err := profile.ListProfileNames(c.homePath) + profileNames, err := c.clientStore.ListProfiles() if err != nil { return trace.Wrap(err, "listing user profiles") } @@ -246,7 +245,7 @@ func (c *osConfigurator) getClient(ctx context.Context, profileName, leafCluster clientConfig := &client.Config{ ClientStore: c.clientStore, } - if err := clientConfig.LoadProfile(c.clientStore, profileName); err != nil { + if err := clientConfig.LoadProfile(profileName); err != nil { return nil, trace.Wrap(err, "loading client profile") } if leafClusterName != "" { diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index f56f959092e1f..f1e3fc3853037 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -1309,6 +1309,7 @@ func localSettings(cap types.AuthPreference) (webclient.AuthenticationSettings, Local: &webclient.LocalSettings{}, PrivateKeyPolicy: cap.GetPrivateKeyPolicy(), PIVSlot: cap.GetPIVSlot(), + PIVPINCacheTTL: cap.GetPIVPINCacheTTL(), DeviceTrust: deviceTrustSettings(cap), } @@ -1350,6 +1351,7 @@ func oidcSettings(connector types.OIDCConnector, cap types.AuthPreference) webcl PreferredLocalMFA: cap.GetPreferredLocalMFA(), PrivateKeyPolicy: cap.GetPrivateKeyPolicy(), PIVSlot: cap.GetPIVSlot(), + PIVPINCacheTTL: cap.GetPIVPINCacheTTL(), DeviceTrust: deviceTrustSettings(cap), } } @@ -1367,6 +1369,7 @@ func samlSettings(connector types.SAMLConnector, cap types.AuthPreference) webcl PreferredLocalMFA: cap.GetPreferredLocalMFA(), PrivateKeyPolicy: cap.GetPrivateKeyPolicy(), PIVSlot: cap.GetPIVSlot(), + PIVPINCacheTTL: cap.GetPIVPINCacheTTL(), DeviceTrust: deviceTrustSettings(cap), } } @@ -1383,6 +1386,7 @@ func githubSettings(connector types.GithubConnector, cap types.AuthPreference) w PreferredLocalMFA: cap.GetPreferredLocalMFA(), PrivateKeyPolicy: cap.GetPrivateKeyPolicy(), PIVSlot: cap.GetPIVSlot(), + PIVPINCacheTTL: cap.GetPIVPINCacheTTL(), DeviceTrust: deviceTrustSettings(cap), } } diff --git a/proto/teleport/lib/teleterm/v1/tshd_events_service.proto b/proto/teleport/lib/teleterm/v1/tshd_events_service.proto index de3f127c62a2d..82e6501ed2363 100644 --- a/proto/teleport/lib/teleterm/v1/tshd_events_service.proto +++ b/proto/teleport/lib/teleterm/v1/tshd_events_service.proto @@ -164,9 +164,14 @@ message PromptMFAResponse { // Request for PromptHardwareKeyPIN. message PromptHardwareKeyPINRequest { - string root_cluster_uri = 1; - // Specifies if a PIN is optional, allowing the user to set it up if left empty. + reserved 1; + reserved "root_cluster_uri"; + // PinOptional specified if a PIN is optional, allowing the user to set it up if left empty. bool pin_optional = 2; + // ProxyHostname is the proxy hostname of the client key. + string proxy_hostname = 3; + // Command is an optional command string to provide context for the prompt. + string command = 4; } // Response for PromptHardwareKeyPIN. @@ -177,7 +182,12 @@ message PromptHardwareKeyPINResponse { // Request for PromptHardwareKeyTouchRequest. message PromptHardwareKeyTouchRequest { - string root_cluster_uri = 1; + reserved 1; + reserved "root_cluster_uri"; + // ProxyHostname is the proxy hostname of the client key. + string proxy_hostname = 2; + // Command is an optional command string to provide context for the prompt. + string command = 3; } // Response for PromptHardwareKeyTouch. @@ -185,7 +195,10 @@ message PromptHardwareKeyTouchResponse {} // Response for PromptHardwareKeyPINChange. message PromptHardwareKeyPINChangeRequest { - string root_cluster_uri = 1; + reserved 1; + reserved "root_cluster_uri"; + // ProxyHostname is the proxy hostname of the client key. + string proxy_hostname = 2; } // Response for PromptHardwareKeyPINChange. @@ -201,9 +214,12 @@ message PromptHardwareKeyPINChangeResponse { // Request for ConfirmHardwareKeySlotOverwrite. message ConfirmHardwareKeySlotOverwriteRequest { - string root_cluster_uri = 1; + reserved 1; + reserved "root_cluster_uri"; // Message to display in the prompt. string message = 2; + // ProxyHostname is the proxy hostname of the client key. + string proxy_hostname = 3; } // Response for ConfirmHardwareKeySlotOverwrite. diff --git a/tool/tctl/common/config/profile.go b/tool/tctl/common/config/profile.go index ffe062621dbc2..a45934f0b105c 100644 --- a/tool/tctl/common/config/profile.go +++ b/tool/tctl/common/config/profile.go @@ -19,6 +19,7 @@ package config import ( + "context" "errors" "time" @@ -31,22 +32,25 @@ import ( "github.com/gravitational/teleport/lib/auth/authclient" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/client/identityfile" + libhwk "github.com/gravitational/teleport/lib/hardwarekey" "github.com/gravitational/teleport/lib/service/servicecfg" "github.com/gravitational/teleport/lib/utils" ) // LoadConfigFromProfile applies config from ~/.tsh/ profile if it's present func LoadConfigFromProfile(ccf *GlobalCLIFlags, cfg *servicecfg.Config) (*authclient.Config, error) { + ctx := context.Background() + proxyAddr := "" if len(ccf.AuthServerAddr) != 0 { proxyAddr = ccf.AuthServerAddr[0] } - clientStore := client.NewFSClientStore(cfg.TeleportHome) + hwks := libhwk.NewService(ctx, nil /*prompt*/) + clientStore := client.NewFSClientStore(cfg.TeleportHome, client.WithHardwareKeyService(hwks)) if ccf.IdentityFilePath != "" { - var err error - clientStore, err = identityfile.NewClientStoreFromIdentityFile(ccf.IdentityFilePath, proxyAddr, "") - if err != nil { + clientStore = client.NewMemClientStore(client.WithHardwareKeyService(hwks)) + if err := identityfile.LoadIdentityFileIntoClientStore(clientStore, ccf.IdentityFilePath, proxyAddr, ""); err != nil { return nil, trace.Wrap(err) } } @@ -67,14 +71,9 @@ func LoadConfigFromProfile(ccf *GlobalCLIFlags, cfg *servicecfg.Config) (*authcl return nil, trace.BadParameter("your credentials have expired, please login using `tsh login`") } - c := client.MakeDefaultConfig() log.WithFields(log.Fields{"proxy": profile.ProxyURL.String(), "user": profile.Username}).Debugf("Found profile.") - if err := c.LoadProfile(clientStore, proxyAddr); err != nil { - return nil, trace.Wrap(err) - } - webProxyHost, _ := c.WebProxyHostPort() - idx := client.KeyIndex{ProxyHost: webProxyHost, Username: c.Username, ClusterName: profile.Cluster} + idx := client.KeyIndex{ProxyHost: profile.Name, Username: profile.Username, ClusterName: profile.Cluster} key, err := clientStore.GetKey(idx, client.WithSSHCerts{}) if err != nil { return nil, trace.Wrap(err) @@ -102,7 +101,7 @@ func LoadConfigFromProfile(ccf *GlobalCLIFlags, cfg *servicecfg.Config) (*authcl } // Do not override auth servers from command line if len(ccf.AuthServerAddr) == 0 { - webProxyAddr, err := utils.ParseAddr(c.WebProxyAddr) + webProxyAddr, err := utils.ParseAddr(profile.ProxyURL.Host) if err != nil { return nil, trace.Wrap(err) } @@ -113,7 +112,7 @@ func LoadConfigFromProfile(ccf *GlobalCLIFlags, cfg *servicecfg.Config) (*authcl authConfig.Log = cfg.Log authConfig.DialOpts = append(authConfig.DialOpts, metadata.WithUserAgentFromTeleportComponent(teleport.ComponentTCTL)) - if c.TLSRoutingEnabled { + if profile.TLSRoutingEnabled { cfg.Auth.NetworkingConfig.SetProxyListenerMode(types.ProxyListenerMode_Multiplex) } diff --git a/tool/teleport/testenv/test_server.go b/tool/teleport/testenv/test_server.go index 1242b677c3d28..d7fb7fa647f37 100644 --- a/tool/teleport/testenv/test_server.go +++ b/tool/teleport/testenv/test_server.go @@ -45,6 +45,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/accesslist" "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" apisshutils "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/entitlements" "github.com/gravitational/teleport/lib" @@ -545,7 +546,7 @@ func (p *cliModules) IsBoringBinary() bool { } // AttestHardwareKey attests a hardware key. -func (p *cliModules) AttestHardwareKey(_ context.Context, _ interface{}, _ *keys.AttestationStatement, _ crypto.PublicKey, _ time.Duration) (*keys.AttestationData, error) { +func (p *cliModules) AttestHardwareKey(_ context.Context, _ interface{}, _ *hardwarekey.AttestationStatement, _ crypto.PublicKey, _ time.Duration) (*keys.AttestationData, error) { return nil, trace.NotFound("no attestation data for the given key") } diff --git a/tool/tsh/common/app.go b/tool/tsh/common/app.go index 6c14a791ef472..72762e73e3133 100644 --- a/tool/tsh/common/app.go +++ b/tool/tsh/common/app.go @@ -286,7 +286,8 @@ func onAppLogout(cf *CLIConf) error { if err != nil { return trace.Wrap(err) } - activeRoutes, err := profile.AppsForCluster(tc.SiteName) + + activeRoutes, err := profile.AppsForCluster(tc.SiteName, tc.ClientStore) if err != nil { return trace.Wrap(err) } @@ -342,7 +343,7 @@ func onAppConfig(cf *CLIConf) error { if err != nil { return trace.Wrap(err) } - routes, err := profile.AppsForCluster(tc.SiteName) + routes, err := profile.AppsForCluster(tc.SiteName, tc.ClientStore) if err != nil { return trace.Wrap(err) } diff --git a/tool/tsh/common/config.go b/tool/tsh/common/config.go index 295dfa3e24801..5714f3cdc9445 100644 --- a/tool/tsh/common/config.go +++ b/tool/tsh/common/config.go @@ -26,8 +26,8 @@ import ( "github.com/coreos/go-semver/semver" "github.com/gravitational/trace" - "github.com/gravitational/teleport/api/profile" "github.com/gravitational/teleport/api/utils/keypaths" + "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/config/openssh" ) @@ -78,7 +78,21 @@ func onConfig(cf *CLIConf) error { return trace.Wrap(err) } - keysDir := profile.FullProfilePath(tc.Config.KeysDir) + var keysDir string + switch s := tc.ClientStore.KeyStore.(type) { + case *client.FSKeyStore: + keysDir = s.KeyDir + default: + switch { + case cf.IdentityFileIn != "": + return trace.BadParameter("tsh config command is not supported with identity files. You can flatten your identity file into a tsh profile with \"tsh login -i\" and retry") + default: + // Currently there are no paths that lead to this error, since `tsh config` + // requires an active login and does not perform relogin. + return trace.BadParameter("tsh config command is not supported with a tsh profile outside of file storage") + } + } + knownHostsPath := keypaths.KnownHostsPath(keysDir) identityFilePath := keypaths.UserKeyPath(keysDir, proxyHost, tc.Config.Username) diff --git a/tool/tsh/common/daemon.go b/tool/tsh/common/daemon.go index dda2baf01c97c..094d35a80ad62 100644 --- a/tool/tsh/common/daemon.go +++ b/tool/tsh/common/daemon.go @@ -51,6 +51,7 @@ func onDaemonStart(cf *CLIConf) error { AgentsDir: cf.DaemonAgentsDir, InstallationID: cf.DaemonInstallationID, AddKeysToAgent: cf.AddKeysToAgent, + HardwareKeyAgent: cf.HardwareKeyAgentServer, }) if err != nil { return trace.Wrap(err) diff --git a/tool/tsh/common/db.go b/tool/tsh/common/db.go index 45f5a577a22cf..a6596b8c37a27 100644 --- a/tool/tsh/common/db.go +++ b/tool/tsh/common/db.go @@ -93,7 +93,7 @@ func onListDatabases(cf *CLIConf) error { log.Debugf("Failed to fetch user roles: %v.", err) } - activeDatabases, err := profile.DatabasesForCluster(tc.SiteName) + activeDatabases, err := profile.DatabasesForCluster(tc.SiteName, tc.ClientStore) if err != nil { return trace.Wrap(err) } @@ -249,7 +249,7 @@ func onDatabaseLogin(cf *CLIConf) error { if err != nil { return trace.Wrap(err) } - routes, err := profile.DatabasesForCluster(tc.SiteName) + routes, err := profile.DatabasesForCluster(tc.SiteName, tc.ClientStore) if err != nil { return trace.Wrap(err) } @@ -362,7 +362,7 @@ func onDatabaseLogout(cf *CLIConf) error { if err != nil { return trace.Wrap(err) } - activeRoutes, err := profile.DatabasesForCluster(tc.SiteName) + activeRoutes, err := profile.DatabasesForCluster(tc.SiteName, tc.ClientStore) if err != nil { return trace.Wrap(err) } @@ -442,7 +442,7 @@ func onDatabaseEnv(cf *CLIConf) error { if err != nil { return trace.Wrap(err) } - routes, err := profile.DatabasesForCluster(tc.SiteName) + routes, err := profile.DatabasesForCluster(tc.SiteName, tc.ClientStore) if err != nil { return trace.Wrap(err) } @@ -504,7 +504,7 @@ func onDatabaseConfig(cf *CLIConf) error { if err != nil { return trace.Wrap(err) } - routes, err := profile.DatabasesForCluster(tc.SiteName) + routes, err := profile.DatabasesForCluster(tc.SiteName, tc.ClientStore) if err != nil { return trace.Wrap(err) } @@ -738,7 +738,7 @@ func onDatabaseConnect(cf *CLIConf) error { if err != nil { return trace.Wrap(err) } - routes, err := profile.DatabasesForCluster(tc.SiteName) + routes, err := profile.DatabasesForCluster(tc.SiteName, tc.ClientStore) if err != nil { return trace.Wrap(err) } @@ -1319,7 +1319,7 @@ func needDatabaseRelogin(cf *CLIConf, tc *client.TeleportClient, route tlsca.Rou } } found := false - activeDatabases, err := profile.DatabasesForCluster(tc.SiteName) + activeDatabases, err := profile.DatabasesForCluster(tc.SiteName, tc.ClientStore) if err != nil { return false, trace.Wrap(err) } @@ -1766,7 +1766,7 @@ func getDbCmdAlternatives(clusterFlag string, route tlsca.RouteToDatabase) []str func formatAmbiguousDB(cf *CLIConf, selectors resourceSelectors, matchedDBs types.Databases) string { var activeDBs []tlsca.RouteToDatabase if profile, err := cf.ProfileStatus(); err == nil { - if dbs, err := profile.DatabasesForCluster(cf.SiteName); err == nil { + if dbs, err := profile.DatabasesForCluster(cf.SiteName, cf.getClientStore()); err == nil { activeDBs = dbs } } diff --git a/tool/tsh/common/hardware_key_test.go b/tool/tsh/common/hardware_key_test.go index 3a3690da608f2..ee5bf473551bb 100644 --- a/tool/tsh/common/hardware_key_test.go +++ b/tool/tsh/common/hardware_key_test.go @@ -474,7 +474,7 @@ func TestHardwareKeyApp(t *testing.T) { require.NoError(t, json.Unmarshal(confOut.Bytes(), &info)) - clientCert, err = tls.LoadX509KeyPair(info.Cert, info.Key) + clientCert, err = keys.LoadX509KeyPair(info.Cert, info.Key) require.NoError(t, err) resp = testDummyAppConn(t, fmt.Sprintf("https://%v", proxyAddr.Addr), clientCert) diff --git a/tool/tsh/common/kube.go b/tool/tsh/common/kube.go index c2523c3e4a706..406f89bf493a1 100644 --- a/tool/tsh/common/kube.go +++ b/tool/tsh/common/kube.go @@ -216,7 +216,7 @@ func (c *kubeJoinCommand) run(cf *CLIConf) error { KubeProxyAddr: tc.Config.KubeProxyAddr, WebProxyAddr: tc.Config.WebProxyAddr, TLSRoutingConnUpgradeRequired: tc.Config.TLSRoutingConnUpgradeRequired, - EnableEscapeSequences: tc.Config.EnableEscapeSequences, + EnableEscapeSequences: !tc.Config.DisableEscapeSequences, Tracker: meta, TLSConfig: tlsConfig, Mode: types.SessionParticipantMode(c.mode), diff --git a/tool/tsh/common/piv.go b/tool/tsh/common/piv.go new file mode 100644 index 0000000000000..16d6097e6e7c6 --- /dev/null +++ b/tool/tsh/common/piv.go @@ -0,0 +1,59 @@ +/* + * Teleport + * Copyright (C) 2025 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package common + +import ( + "github.com/alecthomas/kingpin/v2" + "github.com/gravitational/trace" + + "github.com/gravitational/teleport/api/utils/keys/piv" + libhwk "github.com/gravitational/teleport/lib/hardwarekey" +) + +type pivCommands struct { + agent *pivAgentCommand +} + +func newPIVCommands(app *kingpin.Application) pivCommands { + piv := app.Command("piv", "PIV commands.").Hidden() + return pivCommands{ + agent: newPIVAgentCommand(piv), + } +} + +// pivAgentCommand implements `tsh piv agent`. +type pivAgentCommand struct { + *kingpin.CmdClause +} + +func newPIVAgentCommand(parent *kingpin.CmdClause) *pivAgentCommand { + cmd := &pivAgentCommand{ + CmdClause: parent.Command("agent", "Start PIV key agent."), + } + return cmd +} + +func (c *pivAgentCommand) run(cf *CLIConf) error { + hwKeyService := piv.NewYubiKeyService(nil /*prompt*/) + s, err := libhwk.NewAgentServer(cf.Context, hwKeyService, libhwk.DefaultAgentDir()) + if err != nil { + return trace.Wrap(err) + } + return s.Serve(cf.Context) +} diff --git a/tool/tsh/common/proxy.go b/tool/tsh/common/proxy.go index 9cd7c48c947b0..d4b0c6379628f 100644 --- a/tool/tsh/common/proxy.go +++ b/tool/tsh/common/proxy.go @@ -141,7 +141,7 @@ func onProxyCommandDB(cf *CLIConf) error { if err != nil { return trace.Wrap(err) } - routes, err := profile.DatabasesForCluster(tc.SiteName) + routes, err := profile.DatabasesForCluster(tc.SiteName, tc.ClientStore) if err != nil { return trace.Wrap(err) } diff --git a/tool/tsh/common/putty_config_windows.go b/tool/tsh/common/putty_config_windows.go index 7d202895d3b47..f8e4e061ca905 100644 --- a/tool/tsh/common/putty_config_windows.go +++ b/tool/tsh/common/putty_config_windows.go @@ -27,7 +27,6 @@ import ( "github.com/gravitational/trace" - "github.com/gravitational/teleport/api/profile" "github.com/gravitational/teleport/api/utils/keypaths" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/puttyhosts" @@ -321,9 +320,23 @@ func onPuttyConfig(cf *CLIConf) error { return trace.Wrap(err) } + var keysDir string + switch s := tc.ClientStore.KeyStore.(type) { + case *client.FSKeyStore: + keysDir = s.KeyDir + default: + switch { + case cf.IdentityFileIn != "": + return trace.BadParameter("tsh puttyconfig command is not supported with identity files. You can flatten your identity file into a tsh profile with \"tsh login -i\" and retry") + default: + // Currently there are no paths that lead to this error, since `tsh puttyconfig` + // requires an active login and does not perform relogin. + return trace.BadParameter("tsh puttyconfig command is not supported with a tsh profile outside of file storage") + } + } + // get root cluster name and set keypaths rootClusterName := clusterClient.RootClusterName() - keysDir := profile.FullProfilePath(tc.Config.KeysDir) ppkFilePath := keypaths.PPKFilePath(keysDir, proxyHost, tc.Config.Username) certificateFilePath := keypaths.SSHCertPath(keysDir, proxyHost, tc.Config.Username, rootClusterName) diff --git a/tool/tsh/common/tsh.go b/tool/tsh/common/tsh.go index b63fea8b26a3c..c98035e8df84c 100644 --- a/tool/tsh/common/tsh.go +++ b/tool/tsh/common/tsh.go @@ -41,6 +41,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "time" "github.com/alecthomas/kingpin/v2" @@ -68,7 +69,8 @@ import ( apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/types/wrappers" apiutils "github.com/gravitational/teleport/api/utils" - "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" + "github.com/gravitational/teleport/api/utils/keys/piv" "github.com/gravitational/teleport/api/utils/prompt" "github.com/gravitational/teleport/lib/asciitable" "github.com/gravitational/teleport/lib/auth/authclient" @@ -82,6 +84,7 @@ import ( "github.com/gravitational/teleport/lib/defaults" dtauthn "github.com/gravitational/teleport/lib/devicetrust/authn" dtenroll "github.com/gravitational/teleport/lib/devicetrust/enroll" + libhwk "github.com/gravitational/teleport/lib/hardwarekey" "github.com/gravitational/teleport/lib/kube/kubeconfig" "github.com/gravitational/teleport/lib/modules" "github.com/gravitational/teleport/lib/observability/tracing" @@ -565,6 +568,24 @@ type CLIConf struct { // allows users with potentially stale credentials preventing access to gain the required access // without having to manually run tsh login and the failed command again. Relogin bool + + // HardwareKeyAgentServer determines whether `tsh daemon` will run the hardware key agent server. + HardwareKeyAgentServer bool + // disableHardwareKeyAgentClient determines whether the client will attempt to connect + // to the hardware key agent. Some commands, like login, are better with the + // direct PIV service so that prompts are not split between processes. + disableHardwareKeyAgentClient bool + + // clientStore is the client identity storage interface. This store must be initialized once + // and only once in order to ensure key (and hardware key) storage is synced across the process. + // + // Use getClientStore instead of using this directly to ensure the client store is initialized, + // instead of performing nil checks. + clientStore *client.Store + // clientStoreSet ensures that the client store is only initialized once. Generally, using an + // atomic here is overkill as the CLIConf is generally consumed sequentially. However, occasionally + // we need concurrency safety, such as for [forEachProfileParallel]. + clientStoreSet int32 } // Stdout returns the stdout writer. @@ -839,6 +860,7 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { daemonStart.Flag("kubeconfigs-dir", "Directory containing kubeconfig for Kubernetes Access").StringVar(&cf.DaemonKubeconfigsDir) daemonStart.Flag("agents-dir", "Directory containing agent config files and data directories for Connect My Computer").StringVar(&cf.DaemonAgentsDir) daemonStart.Flag("installation-id", "Unique ID identifying a specific Teleport Connect installation").StringVar(&cf.DaemonInstallationID) + daemonStart.Flag("hardware-key-agent", "Serve the hardware key agent as part of the daemon process").BoolVar(&cf.HardwareKeyAgentServer) daemonStop := daemon.Command("stop", "Gracefully stops a process on Windows by sending Ctrl-Break to it.").Hidden() daemonStop.Flag("pid", "PID to be stopped").IntVar(&cf.DaemonPid) @@ -1241,6 +1263,8 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { vnetAdminSetupCmd := newVnetAdminSetupCommand(app) vnetDaemonCmd := newVnetDaemonCommand(app) + pivCmd := newPIVCommands(app) + if runtime.GOOS == constants.WindowsOS { bench.Hidden() } @@ -1620,6 +1644,8 @@ func Run(ctx context.Context, args []string, opts ...CliOption) error { err = vnetAdminSetupCmd.run(&cf) case vnetDaemonCmd.FullCommand(): err = vnetDaemonCmd.run(&cf) + case pivCmd.agent.FullCommand(): + err = pivCmd.agent.run(&cf) default: // Handle commands that might not be available. switch { @@ -1849,6 +1875,10 @@ func onLogin(cf *CLIConf, reExecArgs ...string) error { cf.DesiredRoles = "" } + // For login operations, we use the hardware key + // service directly instead of the agent. + cf.disableHardwareKeyAgentClient = true + if cf.IdentityFileIn != "" { err := flattenIdentity(cf) if err != nil { @@ -1877,7 +1907,6 @@ func onLogin(cf *CLIConf, reExecArgs ...string) error { if err != nil { return trace.Wrap(err) } - tc.HomePath = cf.HomePath // The user is not logged in and has typed in `tsh --proxy=... login`, if // the running binary needs to be updated, update and re-exec. @@ -4090,7 +4119,7 @@ func makeClientForProxy(cf *CLIConf, proxy string) (*client.TeleportClient, erro // Load SSH key for the cluster indicated in the profile. // Handle gracefully if the profile is empty, the key cannot // be found, or the key isn't supported as an agent key. - profile, profileError := c.GetProfile(c.ClientStore, proxy) + profile, profileError := c.GetProfile(proxy) if profileError == nil { if err := tc.LoadKeyForCluster(ctx, profile.SiteName); err != nil { if !trace.IsNotFound(err) && !trace.IsConnectionProblem(err) && !trace.IsCompareFailed(err) { @@ -4198,8 +4227,7 @@ func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, err } // 1: start with the defaults - c := client.MakeDefaultConfig() - + c := &client.Config{} c.DialOpts = append(c.DialOpts, metadata.WithUserAgentFromTeleportComponent(teleport.ComponentTSH)) c.Tracer = cf.tracer @@ -4300,20 +4328,25 @@ func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, err } if cf.PIVSlot != "" { - c.PIVSlot = keys.PIVSlot(cf.PIVSlot) + c.PIVSlot = hardwarekey.PIVSlotKeyString(cf.PIVSlot) if err = c.PIVSlot.Validate(); err != nil { return nil, trace.Wrap(err) } } - c.ClientStore, err = initClientStore(cf, proxy) - if err != nil { - return nil, trace.Wrap(err) + c.ClientStore = cf.getClientStore() + + // If the client store was initialized for the identity file, but the wrong (or missing) + // proxy address, re-load the identity file for the provided proxy address. + if cf.IdentityFileIn != "" && cf.Proxy != proxy { + if err = identityfile.LoadIdentityFileIntoClientStore(c.ClientStore, cf.IdentityFileIn, proxy, c.SiteName); err == nil { + return nil, trace.Wrap(err) + } } // load profile. if no --proxy is given the currently active profile is used, otherwise // fetch profile for exact proxy we are trying to connect to. - profileErr := c.LoadProfile(c.ClientStore, proxy) + profileErr := c.LoadProfile(proxy) if profileErr != nil && !trace.IsNotFound(profileErr) { fmt.Printf("WARNING: Failed to load tsh profile for %q: %v\n", proxy, profileErr) } @@ -4322,6 +4355,7 @@ func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, err if cf.Namespace != "" { c.Namespace = cf.Namespace } + if cf.Username != "" { c.Username = cf.Username } @@ -4455,7 +4489,7 @@ func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, err c.AddKeysToAgent = client.AddKeysToAgentNo } - c.EnableEscapeSequences = cf.EnableEscapeSequences + c.DisableEscapeSequences = !cf.EnableEscapeSequences // pass along mock functions if provided (only used in tests) c.MockSSOLogin = cf.MockSSOLogin @@ -4468,13 +4502,6 @@ func loadClientConfigFromCLIConf(cf *CLIConf, proxy string) (*client.Config, err c.OverrideMySQLOptionFilePath = cf.overrideMySQLOptionFilePath c.OverridePostgresServiceFilePath = cf.overridePostgresServiceFilePath - // Set tsh home directory - c.HomePath = cf.HomePath - - if c.KeysDir == "" { - c.KeysDir = c.HomePath - } - if cf.IdentityFileIn != "" { c.NonInteractive = true } @@ -4504,38 +4531,66 @@ func setEnvVariables(c *client.Config, options Options) { } } -func initClientStore(cf *CLIConf, proxy string) (*client.Store, error) { - switch { - case cf.IdentityFileIn != "": - // Import identity file keys to in-memory client store. - clientStore, err := identityfile.NewClientStoreFromIdentityFile(cf.IdentityFileIn, proxy, cf.SiteName) - if err != nil { - return nil, trace.Wrap(err) - } - return clientStore, nil +// setClientStore sets the client store. If the client store was already set, +// it returns an error instead, so this should not be used after initClientStore. +func (c *CLIConf) setClientStore(store *client.Store) error { + if !atomic.CompareAndSwapInt32(&c.clientStoreSet, 0, 1) { + return trace.AlreadyExists("setClientStore: client store is already set; this is a bug") + } + c.clientStore = store + return nil +} + +// getClientStore gets the client store, initializing it if needed. This should be +// preferred over using clientStore directly in cases where it might not be initialized. +func (c *CLIConf) getClientStore() *client.Store { + c.initClientStore() + return c.clientStore +} + +// initClientStore initializes the client identity store which will be used by the +// client to interface with client identity material. After the first call to +// initClientStore, further calls will be a no-op. +func (c *CLIConf) initClientStore() { + if !atomic.CompareAndSwapInt32(&c.clientStoreSet, 0, 1) { + // client store already initialized. + return + } + + var hwks hardwarekey.Service + if c.disableHardwareKeyAgentClient { + hwks = piv.NewYubiKeyService(nil /*prompt*/) + } else { + hwks = libhwk.NewService(c.Context, nil /*prompt*/) + } - case cf.IdentityFileOut != "", cf.AuthConnector == constants.HeadlessConnector: + switch { + case c.IdentityFileIn != "", c.IdentityFileOut != "", c.AuthConnector == constants.HeadlessConnector: // Store client keys in memory, where they can be exported to non-standard // FS formats (e.g. identity file) or used for a single client call in memory. - return client.NewMemClientStore(), nil + c.clientStore = client.NewMemClientStore(client.WithHardwareKeyService(hwks)) - case cf.AddKeysToAgent == client.AddKeysToAgentOnly: + case c.AddKeysToAgent == client.AddKeysToAgentOnly: // Store client keys in memory, but save trusted certs and profile to disk. - clientStore := client.NewFSClientStore(cf.HomePath) - clientStore.KeyStore = client.NewMemKeyStore() - return clientStore, nil + c.clientStore = client.NewFSClientStore(c.HomePath, client.WithHardwareKeyService(hwks)) + c.clientStore.KeyStore = client.NewMemKeyStore() default: - return client.NewFSClientStore(cf.HomePath), nil + c.clientStore = client.NewFSClientStore(c.HomePath, client.WithHardwareKeyService(hwks)) + } + + // If an identity file is provided, opportunistically try to load it into the keystore. It may + // fail if the user did not provide the --proxy flag, but in some cases the proxy, the proxy + // address will be provided later on and the client will attempt to load the identity file then. + if c.IdentityFileIn != "" { + if err := identityfile.LoadIdentityFileIntoClientStore(c.clientStore, c.IdentityFileIn, c.Proxy, c.SiteName); err == nil { + slog.DebugContext(c.Context, "failed to load identity file into client store", "err", err) + } } } func (c *CLIConf) ProfileStatus() (*client.ProfileStatus, error) { - clientStore, err := initClientStore(c, c.Proxy) - if err != nil { - return nil, trace.Wrap(err) - } - profile, err := clientStore.ReadProfileStatus(c.Proxy) + profile, err := c.getClientStore().ReadProfileStatus(c.Proxy) if err != nil { return nil, trace.Wrap(err) } @@ -4543,11 +4598,7 @@ func (c *CLIConf) ProfileStatus() (*client.ProfileStatus, error) { } func (c *CLIConf) FullProfileStatus() (*client.ProfileStatus, []*client.ProfileStatus, error) { - clientStore, err := initClientStore(c, c.Proxy) - if err != nil { - return nil, nil, trace.Wrap(err) - } - currentProfile, profiles, err := clientStore.FullProfileStatus() + currentProfile, profiles, err := c.getClientStore().FullProfileStatus() if err != nil { return nil, nil, trace.Wrap(err) } @@ -4557,19 +4608,14 @@ func (c *CLIConf) FullProfileStatus() (*client.ProfileStatus, []*client.ProfileS // ListProfiles returns a list of profiles the current user has // credentials for. func (c *CLIConf) ListProfiles() ([]*client.ProfileStatus, error) { - clientStore, err := initClientStore(c, c.Proxy) - if err != nil { - return nil, trace.Wrap(err) - } - - profileNames, err := clientStore.ListProfiles() + profileNames, err := c.getClientStore().ListProfiles() if err != nil { return nil, trace.Wrap(err) } profiles := make([]*client.ProfileStatus, 0, len(profileNames)) for _, profileName := range profileNames { - status, err := clientStore.ReadProfileStatus(profileName) + status, err := c.getClientStore().ReadProfileStatus(profileName) if err != nil { return nil, trace.Wrap(err) } @@ -4581,17 +4627,13 @@ func (c *CLIConf) ListProfiles() ([]*client.ProfileStatus, error) { // GetProfile loads user profile. func (c *CLIConf) GetProfile() (*profile.Profile, error) { - store, err := initClientStore(c, c.Proxy) + clientStore := c.getClientStore() + profileName, err := client.ProfileNameFromProxyAddress(clientStore, c.Proxy) if err != nil { return nil, trace.Wrap(err) } - profileName, err := client.ProfileNameFromProxyAddress(store, c.Proxy) - if err != nil { - return nil, trace.Wrap(err) - } - - profile, err := store.GetProfile(profileName) + profile, err := clientStore.GetProfile(profileName) return profile, trace.Wrap(err) } @@ -4718,30 +4760,26 @@ func parseCertificateCompatibilityFlag(compatibility string, certificateFormat s // flattenIdentity reads an identity file and flattens it into a tsh profile on disk. func flattenIdentity(cf *CLIConf) error { - // Save the identity file path for later - identityFile := cf.IdentityFileIn - - // We clear the identity flag so that the client store will be backed - // by the filesystem instead (in ~/.tsh or TELEPORT_HOME). - cf.IdentityFileIn = "" - - // Load client config as normal to parse client inputs and add defaults. - c, err := loadClientConfigFromCLIConf(cf, cf.Proxy) - if err != nil { - return trace.Wrap(err) - } - // Proxy address may be loaded from existing tsh profile or from --proxy flag. - if c.WebProxyAddr == "" { + if cf.Proxy == "" { return trace.BadParameter("No proxy address specified, missed --proxy flag?") } + // Usually, initializing the client store with an identity file would result in + // an in-memory client store with a profile for cf.Proxy pre-loaded. Instead, + // initialize an FS client store and load the identity file into it. + hwks := piv.NewYubiKeyService(nil /*prompt*/) + clientStore := client.NewFSClientStore(cf.HomePath, client.WithHardwareKeyService(hwks)) + if err := cf.setClientStore(clientStore); err != nil { + return trace.Wrap(err) + } + // Load the identity file key and partial profile into the client store. - if err := identityfile.LoadIdentityFileIntoClientStore(c.ClientStore, identityFile, c.WebProxyAddr, c.SiteName); err != nil { + if err := identityfile.LoadIdentityFileIntoClientStore(clientStore, cf.IdentityFileIn, cf.Proxy, cf.SiteName); err != nil { return trace.Wrap(err) } - fmt.Printf("Successfully flattened Identity file %q into a tsh profile.\n", identityFile) + fmt.Printf("Successfully flattened Identity file %q into a tsh profile.\n", cf.IdentityFileIn) // onStatus will ping the proxy to fill in cluster profile information missing in the // client store, then print the login status. diff --git a/tool/tsh/common/tsh_test.go b/tool/tsh/common/tsh_test.go index 82efafbf75738..456d24a255c57 100644 --- a/tool/tsh/common/tsh_test.go +++ b/tool/tsh/common/tsh_test.go @@ -69,6 +69,7 @@ import ( apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/api/utils/keypaths" "github.com/gravitational/teleport/api/utils/keys" + "github.com/gravitational/teleport/api/utils/keys/hardwarekey" "github.com/gravitational/teleport/api/utils/prompt" "github.com/gravitational/teleport/entitlements" "github.com/gravitational/teleport/integration/kube" @@ -266,7 +267,7 @@ func (p *cliModules) IsBoringBinary() bool { } // AttestHardwareKey attests a hardware key. -func (p *cliModules) AttestHardwareKey(_ context.Context, _ interface{}, _ *keys.AttestationStatement, _ crypto.PublicKey, _ time.Duration) (*keys.AttestationData, error) { +func (p *cliModules) AttestHardwareKey(_ context.Context, _ interface{}, _ *hardwarekey.AttestationStatement, _ crypto.PublicKey, _ time.Duration) (*keys.AttestationData, error) { return nil, trace.NotFound("no attestation data for the given key") } @@ -908,13 +909,12 @@ func TestMakeClient(t *testing.T) { conf.HomePath = t.TempDir() // Create a empty profile so we don't ping proxy. - clientStore, err := initClientStore(&conf, conf.Proxy) - require.NoError(t, err) profile := &profile.Profile{ SSHProxyAddr: "proxy:3023", WebProxyAddr: "proxy:3080", } - err = clientStore.SaveProfile(profile, true) + + err = conf.getClientStore().SaveProfile(profile, true) require.NoError(t, err) tc, err = makeClient(&conf) @@ -5995,7 +5995,13 @@ func TestFlatten(t *testing.T) { require.NoError(t, err) // Test execution: validate that flattening succeeds if a profile already exists. - conf.IdentityFileIn = identityPath + conf = CLIConf{ + Proxy: proxyAddr.String(), + InsecureSkipVerify: true, + IdentityFileIn: identityPath, + HomePath: freshHome, + Context: context.Background(), + } require.NoError(t, flattenIdentity(&conf), "unexpected error when overwriting a tsh profile") } @@ -6407,13 +6413,11 @@ func TestProxyTemplatesMakeClient(t *testing.T) { } // Create a empty profile so we don't ping proxy. - clientStore, err := initClientStore(conf, conf.Proxy) - require.NoError(t, err) profile := &profile.Profile{ SSHProxyAddr: "proxy:3023", WebProxyAddr: "proxy:3080", } - err = clientStore.SaveProfile(profile, true) + err := conf.getClientStore().SaveProfile(profile, true) require.NoError(t, err) modify(conf) diff --git a/tool/tsh/common/vnet_common.go b/tool/tsh/common/vnet_common.go index aa688d714ee34..18ea309bef6fa 100644 --- a/tool/tsh/common/vnet_common.go +++ b/tool/tsh/common/vnet_common.go @@ -30,6 +30,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/client/clientcache" + libhwk "github.com/gravitational/teleport/lib/hardwarekey" "github.com/gravitational/teleport/lib/utils" "github.com/gravitational/teleport/lib/vnet" ) @@ -44,7 +45,8 @@ type vnetAppProvider struct { } func newVnetAppProvider(cf *CLIConf) (*vnetAppProvider, error) { - clientStore := client.NewFSClientStore(cf.HomePath) + hwks := libhwk.NewService(cf.Context, nil /*prompt*/) + clientStore := client.NewFSClientStore(cf.HomePath, client.WithHardwareKeyService(hwks)) p := &vnetAppProvider{ cf: cf, @@ -219,7 +221,7 @@ func (p *vnetAppProvider) newTeleportClient(ctx context.Context, profileName, le cfg := &client.Config{ ClientStore: p.clientStore, } - if err := cfg.LoadProfile(p.clientStore, profileName); err != nil { + if err := cfg.LoadProfile(profileName); err != nil { return nil, trace.Wrap(err, "loading client profile") } if leafClusterName != "" { diff --git a/web/packages/teleterm/src/mainProcess/mainProcess.ts b/web/packages/teleterm/src/mainProcess/mainProcess.ts index 34d2195b56b6b..582529e012692 100644 --- a/web/packages/teleterm/src/mainProcess/mainProcess.ts +++ b/web/packages/teleterm/src/mainProcess/mainProcess.ts @@ -228,6 +228,9 @@ export default class MainProcess { `--add-keys-to-agent=${this.configService.get('sshAgent.addKeysToAgent').value}`, ]; + if (this.configService.get('hardwareKeyAgent.enabled').value) { + flags.unshift('--hardware-key-agent'); + } if (settings.insecure) { flags.unshift('--insecure'); } diff --git a/web/packages/teleterm/src/services/config/appConfigSchema.ts b/web/packages/teleterm/src/services/config/appConfigSchema.ts index 76581f0c9d6db..2ff887a1a2c2a 100644 --- a/web/packages/teleterm/src/services/config/appConfigSchema.ts +++ b/web/packages/teleterm/src/services/config/appConfigSchema.ts @@ -206,6 +206,12 @@ export const createAppConfigSchema = (settings: RuntimeSettings) => { "'no' never attempts to add them, 'yes' always attempts to add them, " + "'only' always attempts to add the keys to the agent but it does not save them on disk." ), + // Defaults to true for prod, false for dev. Otherwise dev instances would + // claim the hardware key agent runner over prod instances by default. + 'hardwareKeyAgent.enabled': z + .boolean() + .default(!settings.dev) + .describe('Controls whether the hardware key agent will be started.'), }); }; diff --git a/web/packages/teleterm/src/ui/DocumentGateway/OnlineDocumentGateway.tsx b/web/packages/teleterm/src/ui/DocumentGateway/OnlineDocumentGateway.tsx index cab0d56b79b06..93cd1d36b7f2e 100644 --- a/web/packages/teleterm/src/ui/DocumentGateway/OnlineDocumentGateway.tsx +++ b/web/packages/teleterm/src/ui/DocumentGateway/OnlineDocumentGateway.tsx @@ -23,8 +23,8 @@ import * as Alerts from 'design/Alert'; import Validation from 'shared/components/Validation'; import { debounce } from 'shared/utils/highbar'; +import { CliCommand } from '../components/CliCommand'; import { ConfigFieldInput, PortFieldInput } from '../components/FieldInputs'; -import { CliCommand } from './CliCommand'; import { DocumentGatewayProps } from './DocumentGateway'; type OnlineDocumentGatewayProps = Pick< @@ -89,30 +89,32 @@ export function OnlineDocumentGateway(props: OnlineDocumentGatewayProps) { Connect with CLI - - - handleChangePort(e.target.value)} - mb={2} - /> - handleChangeDbName(e.target.value)} - spellCheck={false} - ml={2} - mb={2} - /> - + + + + handleChangePort(e.target.value)} + mb={0} + /> + handleChangeDbName(e.target.value)} + spellCheck={false} + ml={2} + mb={0} + /> + + + + {$errors} - - {$errors} Connect with GUI diff --git a/web/packages/teleterm/src/ui/ModalsHost/ModalsHost.story.tsx b/web/packages/teleterm/src/ui/ModalsHost/ModalsHost.story.tsx index 2245ab90b5d02..c07b58e2ef852 100644 --- a/web/packages/teleterm/src/ui/ModalsHost/ModalsHost.story.tsx +++ b/web/packages/teleterm/src/ui/ModalsHost/ModalsHost.story.tsx @@ -36,7 +36,8 @@ export default { const hardwareKeyTouchDialog: DialogHardwareKeyTouch = { kind: 'hardware-key-touch', req: { - rootClusterUri: '/clusters/foo', + proxyHostname: 'foo.example.com', + command: '', }, onCancel: () => {}, }; @@ -44,8 +45,9 @@ const hardwareKeyTouchDialog: DialogHardwareKeyTouch = { const hardwareKeyPinDialog: DialogHardwareKeyPin = { kind: 'hardware-key-pin', req: { - rootClusterUri: '/clusters/foo', + proxyHostname: 'foo.example.com', pinOptional: false, + command: '', }, onSuccess: () => {}, onCancel: () => {}, diff --git a/web/packages/teleterm/src/ui/ModalsHost/ModalsHost.test.tsx b/web/packages/teleterm/src/ui/ModalsHost/ModalsHost.test.tsx index 7d27a474a3f19..88f151332384a 100644 --- a/web/packages/teleterm/src/ui/ModalsHost/ModalsHost.test.tsx +++ b/web/packages/teleterm/src/ui/ModalsHost/ModalsHost.test.tsx @@ -44,7 +44,8 @@ const clusterConnectDialog: DialogClusterConnect = { const hardwareKeyTouchDialog: DialogHardwareKeyTouch = { kind: 'hardware-key-touch', req: { - rootClusterUri: '/clusters/foo', + proxyHostname: 'foo.example.com', + command: '', }, onCancel: () => {}, }; diff --git a/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/AskPin.tsx b/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/AskPin.tsx index a476b8f04189c..c690417686e3a 100644 --- a/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/AskPin.tsx +++ b/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/AskPin.tsx @@ -28,6 +28,8 @@ import FieldInput from 'shared/components/FieldInput'; import Validation from 'shared/components/Validation'; import { requiredField } from 'shared/components/Validation/rules'; +import { CliCommand } from 'teleterm/ui/components/CliCommand'; + import { CommonHeader } from './CommonHeader'; export function AskPin(props: { @@ -58,19 +60,20 @@ export function AskPin(props: { > - - - Enter your YubiKey PIV PIN. -
- {props.req.pinOptional && - 'To change the default PIN, leave the field blank.'} + + + Enter your YubiKey PIV PIN to continue + {props.req.command ? ' with command:' : '.'} - + {props.req.command && ( + + )} + + {props.req.pinOptional && + 'To change the default PIN, leave the field blank.'} +
diff --git a/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/ChangePin.tsx b/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/ChangePin.tsx index 97b5b3e25dd4c..efd87cb6f9a5e 100644 --- a/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/ChangePin.tsx +++ b/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/ChangePin.tsx @@ -77,7 +77,7 @@ export function ChangePin(props: { }} > diff --git a/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/CommonHeader.tsx b/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/CommonHeader.tsx index fd41eaddaed6f..eabe3e5dec48f 100644 --- a/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/CommonHeader.tsx +++ b/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/CommonHeader.tsx @@ -20,18 +20,14 @@ import { ButtonIcon, H2 } from 'design'; import { DialogHeader } from 'design/Dialog'; import * as icons from 'design/Icon'; -import { RootClusterUri, routing } from 'teleterm/ui/uri'; - export function CommonHeader(props: { onCancel(): void; - rootClusterUri: RootClusterUri; + proxyHostname: string; }) { - const rootClusterName = routing.parseClusterName(props.rootClusterUri); - return (

- Unlock hardware key to access {rootClusterName} + Verify your identity on {props.proxyHostname}

diff --git a/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/Touch.tsx b/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/Touch.tsx index 48d67007d3067..ce3b98f239302 100644 --- a/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/Touch.tsx +++ b/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/Touch.tsx @@ -21,6 +21,7 @@ import DialogConfirmation, { DialogContent } from 'design/DialogConfirmation'; import { PromptHardwareKeyTouchRequest } from 'gen-proto-ts/teleport/lib/teleterm/v1/tshd_events_service_pb'; import svgHardwareKey from 'teleterm/ui/ClusterConnect/ClusterLogin/FormLogin/PromptWebauthn/hardware.svg'; +import { CliCommand } from 'teleterm/ui/components/CliCommand'; import LinearProgress from 'teleterm/ui/components/LinearProgress'; import { CommonHeader } from './CommonHeader'; @@ -42,20 +43,31 @@ export function Touch(props: { > - - Touch your YubiKey + + Touch your YubiKey to continue + {props.req.command ? ' with command:' : '.'} + + {props.req.command && ( + + )} + diff --git a/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/index.story.tsx b/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/index.story.tsx index 34e7418554c3c..485746c5b9648 100644 --- a/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/index.story.tsx +++ b/web/packages/teleterm/src/ui/ModalsHost/modals/HardwareKeys/index.story.tsx @@ -27,42 +27,63 @@ import { Touch as TouchComponent } from './Touch'; const rootCluster = makeRootCluster(); -export default { +interface StoryProps { + command: boolean; +} + +const meta: Meta = { title: 'Teleterm/ModalsHost/HardwareKeys', -} satisfies Meta; + argTypes: { + command: { + control: { type: 'boolean' }, + description: 'Show a command when asked for pin or touch.', + }, + }, + args: { + command: true, + }, +}; + +export default meta; -export function AskPinOptional() { +const longCommand = + 'tsh ssh -X --forward-agent=yes --proxy=root.example.com --user=testuser'; + +export function AskPinOptional(props: StoryProps) { return ( {}} onCancel={() => {}} req={{ - rootClusterUri: rootCluster.uri, + proxyHostname: rootCluster.proxyHost, pinOptional: true, + command: props.command ? longCommand : '', }} /> ); } -export function AskPinRequired() { +export function AskPinRequired(props: StoryProps) { return ( {}} onCancel={() => {}} req={{ - rootClusterUri: rootCluster.uri, + proxyHostname: rootCluster.proxyHost, pinOptional: false, + command: props.command ? longCommand : '', }} /> ); } -export function Touch() { +export function Touch(props: StoryProps) { return ( {}} req={{ - rootClusterUri: rootCluster.uri, + proxyHostname: rootCluster.proxyHost, + command: props.command ? longCommand : '', }} /> ); @@ -73,7 +94,9 @@ export function ChangePin() { {}} onCancel={() => {}} - req={{ rootClusterUri: rootCluster.uri }} + req={{ + proxyHostname: rootCluster.proxyHost, + }} /> ); } @@ -84,7 +107,7 @@ export function OverwriteSlot() { onConfirm={() => {}} onCancel={() => {}} req={{ - rootClusterUri: rootCluster.uri, + proxyHostname: rootCluster.proxyHost, message: "Would you like to overwrite this slot's private key and certificate?", }} diff --git a/web/packages/teleterm/src/ui/DocumentGateway/CliCommand.tsx b/web/packages/teleterm/src/ui/components/CliCommand.tsx similarity index 77% rename from web/packages/teleterm/src/ui/DocumentGateway/CliCommand.tsx rename to web/packages/teleterm/src/ui/components/CliCommand.tsx index f3df59a4413b2..a8e96f959e76e 100644 --- a/web/packages/teleterm/src/ui/DocumentGateway/CliCommand.tsx +++ b/web/packages/teleterm/src/ui/components/CliCommand.tsx @@ -24,16 +24,19 @@ import { fade } from 'design/theme/utils/colorManipulator'; interface CliCommandProps { cliCommand: string; - onButtonClick(): void; - isLoading: boolean; - buttonText?: string; + button?: { + text?: string; + onClick(): void; + }; + isLoading?: boolean; + wrapContent?: boolean; } export function CliCommand({ cliCommand, - onButtonClick, + button, isLoading, - buttonText = 'Run', + wrapContent, }: CliCommandProps) { const [shouldDisplayIsLoading, setShouldDisplayIsLoading] = useState(false); @@ -57,12 +60,13 @@ export function CliCommand({ justifyContent="space-between" borderRadius={2} bg="bgTerminal" - mb={2} + width="100%" > {`$`} {cliCommand} @@ -78,26 +82,31 @@ export function CliCommand({ /> )} - - {buttonText} - + {button && ( + + {button.text || 'Run'} + + )}
); } -const CommandContainer = styled(Flex)<{ shouldDisplayIsLoading?: boolean }>` +const CommandContainer = styled(Flex)<{ + shouldDisplayIsLoading?: boolean; + wrapContent?: boolean; +}>` overflow: auto; - white-space: pre; + white-space: ${props => (props.wrapContent ? 'wrap' : 'pre')}; word-break: break-all; font-size: 12px; color: ${props => {