From d09c7fa8fc21cdc1ca6aa30821335b26c1612cfe Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Mon, 15 Apr 2024 19:36:35 -0400 Subject: [PATCH 01/54] update README for Cert Warden --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 10ea261..119a724 100644 --- a/README.md +++ b/README.md @@ -86,12 +86,12 @@ for passing the pem content from another application without having to save the pem files to disk. Putting all of this together, you can combine the install binary with -a tool like LeGo CertHub (https://www.legocerthub.com/) to call the +a tool like Cert Warden (https://www.certwarden.com/) to call the install binary, with environment variables, to directly upload new -certificates as they're issued by LeGo, without having to write a +certificates as they're issued by Cert Warden, without having to write a separate script. - + ## Thanks From ecf10f1fdc4f06fe8061018b07ea71aa61f88421 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 4 Jun 2024 18:59:35 -0400 Subject: [PATCH 02/54] go: update to 1.22.3 --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 5d6ca7f..d5bd69a 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module apc-p15-tool -go 1.22.1 +go 1.22.3 require ( github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 From 01be6ca577cf5fb2b1d5ed12006d6f227c01dbc1 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 4 Jun 2024 18:59:36 -0400 Subject: [PATCH 03/54] add p15 key output file The NMC Security Wizard can also produce .p15 files that contain just a private key. Add this ability to this tool. When the `create` function is used, both files will be outputted. --- pkg/app/cmd_create.go | 31 ++++-- pkg/app/cmd_install.go | 2 +- pkg/app/config.go | 6 +- pkg/app/file_header.go | 4 +- pkg/app/pem_to_p15.go | 20 ++-- pkg/pkcs15/pem_to_p15.go | 230 ++++++++++++++++++++++++++++----------- 6 files changed, 206 insertions(+), 87 deletions(-) diff --git a/pkg/app/cmd_create.go b/pkg/app/cmd_create.go index 51cf1cc..bc9ecdf 100644 --- a/pkg/app/cmd_create.go +++ b/pkg/app/cmd_create.go @@ -6,7 +6,10 @@ import ( "os" ) -const createDefaultOutFilePath = "apctool.p15" +const ( + createDefaultOutFilePath = "apctool.p15" + createDefaultOutKeyFilePath = "apctool.key.p15" +) // cmdCreate is the app's command to create an apc p15 file from key and cert // pem files @@ -26,25 +29,35 @@ func (app *app) cmdCreate(_ context.Context, args []string) error { // validation done - // make p15 file - apcFile, err := app.pemToAPCP15(keyPem, certPem, "create") + // make p15 files + apcKeyCertFile, keyFile, err := app.pemToAPCP15s(keyPem, certPem, "create") if err != nil { return err } // determine file name (should already be done by flag parsing, but avoid nil just in case) - fileName := createDefaultOutFilePath + keyCertFileName := createDefaultOutFilePath if app.config.create.outFilePath != nil && *app.config.create.outFilePath != "" { - fileName = *app.config.create.outFilePath + keyCertFileName = *app.config.create.outFilePath } - // write file - err = os.WriteFile(fileName, apcFile, 0777) + keyFileName := createDefaultOutFilePath + if app.config.create.outKeyFilePath != nil && *app.config.create.outKeyFilePath != "" { + keyFileName = *app.config.create.outKeyFilePath + } + + // write files + err = os.WriteFile(keyCertFileName, apcKeyCertFile, 0777) if err != nil { - return fmt.Errorf("create: failed to write apc p15 file (%s)", err) + return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) } + app.stdLogger.Printf("create: apc p15 key+cert file %s written to disk", keyCertFileName) - app.stdLogger.Printf("create: apc p15 file %s written to disk", fileName) + err = os.WriteFile(keyFileName, keyFile, 0777) + if err != nil { + return fmt.Errorf("create: failed to write apc p15 key file (%s)", err) + } + app.stdLogger.Printf("create: apc p15 key file %s written to disk", keyFileName) return nil } diff --git a/pkg/app/cmd_install.go b/pkg/app/cmd_install.go index 5d826ec..26635e7 100644 --- a/pkg/app/cmd_install.go +++ b/pkg/app/cmd_install.go @@ -52,7 +52,7 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { // validation done // make p15 file - apcFile, err := app.pemToAPCP15(keyPem, certPem, "install") + apcFile, _, err := app.pemToAPCP15s(keyPem, certPem, "install") if err != nil { return err } diff --git a/pkg/app/config.go b/pkg/app/config.go index 6514391..bea67aa 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -28,7 +28,8 @@ type config struct { debugLogging *bool create struct { keyCertPemCfg - outFilePath *string + outFilePath *string + outKeyFilePath *string } install struct { keyCertPemCfg @@ -71,7 +72,8 @@ func (app *app) getConfig(args []string) error { cfg.create.certPemFilePath = createFlags.StringLong("certfile", "", "path and filename of the certificate in pem format") cfg.create.keyPem = createFlags.StringLong("keypem", "", "string of the rsa-1024 or rsa-2048 key in pem format") cfg.create.certPem = createFlags.StringLong("certpem", "", "string of the certificate in pem format") - cfg.create.outFilePath = createFlags.StringLong("outfile", createDefaultOutFilePath, "path and filename to write the p15 file to") + cfg.create.outFilePath = createFlags.StringLong("outfile", createDefaultOutFilePath, "path and filename to write the key+cert p15 file to") + cfg.create.outKeyFilePath = createFlags.StringLong("outkeyfile", createDefaultOutKeyFilePath, "path and filename to write the key p15 file to") createCmd := &ff.Command{ Name: "create", diff --git a/pkg/app/file_header.go b/pkg/app/file_header.go index b803d68..20097cd 100644 --- a/pkg/app/file_header.go +++ b/pkg/app/file_header.go @@ -7,6 +7,8 @@ import ( "github.com/sigurn/crc16" ) +const apcHeaderLen = 228 + // makeFileHeader generates the 228 byte header to prepend to the .p15 // as required by APC UPS NMC. Contrary to the apc_tools repo, it does // mot appear the header changes based on key size. @@ -28,7 +30,7 @@ func makeFileHeader(p15File []byte) ([]byte, error) { // *(uint32_t *)(buf + 208) = keySize; // 1 for 1024 key, otherwise (2048 bit) 2 // Unsure why this was in original code but seems irrelevant - header := make([]byte, 228) + header := make([]byte, apcHeaderLen) // always 1 header[0] = 1 diff --git a/pkg/app/pem_to_p15.go b/pkg/app/pem_to_p15.go index eb020aa..b006c9e 100644 --- a/pkg/app/pem_to_p15.go +++ b/pkg/app/pem_to_p15.go @@ -5,34 +5,36 @@ import ( "fmt" ) -// pemToAPCP15 reads the specified pem files and returns the apc p15 bytes -func (app *app) pemToAPCP15(keyPem, certPem []byte, parentCmdName string) ([]byte, error) { +// pemToAPCP15s reads the specified pem files and returns the apc p15 files (both a +// p15 file with just the private key, and also a p15 file with both the private key +// and certificate). The key+cert file includes the required APC header, prepended. +func (app *app) pemToAPCP15s(keyPem, certPem []byte, parentCmdName string) (apcKeyCertFile, keyFile []byte, err error) { app.stdLogger.Printf("%s: making apc p15 file from pem", parentCmdName) // make p15 struct p15, err := pkcs15.ParsePEMToPKCS15(keyPem, certPem) if err != nil { - return nil, fmt.Errorf("%s: failed to parse pem files (%w)", parentCmdName, err) + return nil, nil, fmt.Errorf("%s: failed to parse pem files (%w)", parentCmdName, err) } app.stdLogger.Printf("%s: successfully loaded pem files", parentCmdName) // make file bytes - p15File, err := p15.ToP15File() + keyCertFile, keyFile, err := p15.ToP15Files() if err != nil { - return nil, fmt.Errorf("%s: failed to make p15 file (%w)", parentCmdName, err) + return nil, nil, fmt.Errorf("%s: failed to make p15 file (%w)", parentCmdName, err) } // make header for file bytes - apcHeader, err := makeFileHeader(p15File) + apcHeader, err := makeFileHeader(keyCertFile) if err != nil { - return nil, fmt.Errorf("%s: failed to make p15 file header (%w)", parentCmdName, err) + return nil, nil, fmt.Errorf("%s: failed to make p15 file header (%w)", parentCmdName, err) } // combine header with file - apcFile := append(apcHeader, p15File...) + apcKeyCertFile = append(apcHeader, keyCertFile...) app.stdLogger.Printf("%s: apc p15 file data succesfully generated", parentCmdName) - return apcFile, nil + return apcKeyCertFile, keyFile, nil } diff --git a/pkg/pkcs15/pem_to_p15.go b/pkg/pkcs15/pem_to_p15.go index a7a9fef..19392f2 100644 --- a/pkg/pkcs15/pem_to_p15.go +++ b/pkg/pkcs15/pem_to_p15.go @@ -2,6 +2,7 @@ package pkcs15 import ( "apc-p15-tool/pkg/tools/asn1obj" + "encoding/asn1" "math/big" ) @@ -9,62 +10,11 @@ const ( apcKeyLabel = "Private key" ) -// ToP15File turns the key and cert into a properly formatted and encoded -// p15 file -func (p15 *pkcs15KeyCert) ToP15File() ([]byte, error) { +// toP15KeyCert creates a P15 file with both the private key and certificate, mirroring the +// final p15 file an APC UPS expects (though without the header) +func (p15 *pkcs15KeyCert) toP15KeyCert(keyEnvelope []byte) (keyCert []byte, err error) { // private key object - pkey, err := p15.toP15PrivateKey() - if err != nil { - return nil, err - } - - cert, err := p15.toP15Cert() - if err != nil { - return nil, err - } - - // ContentInfo - p15File := asn1obj.Sequence([][]byte{ - - // contentType: OID: 1.2.840.113549.1.15.3.1 pkcs15content (PKCS #15 content type) - asn1obj.ObjectIdentifier(asn1obj.OIDPkscs15Content), - - // content - asn1obj.ExplicitCompound(0, [][]byte{ - asn1obj.Sequence([][]byte{ - asn1obj.Integer(big.NewInt(0)), - asn1obj.Sequence([][]byte{ - asn1obj.ExplicitCompound(0, [][]byte{ - asn1obj.ExplicitCompound(0, [][]byte{ - pkey, - }), - }), - asn1obj.ExplicitCompound(4, [][]byte{ - asn1obj.ExplicitCompound(0, [][]byte{ - cert, - }), - }), - }), - }), - }), - }) - - return p15File, nil -} - -// toP15PrivateKey creates the encoded private key. it is broken our from the larger p15 -// function for readability -// NOTE: Do not use this to try and turn just a private key into a p15, the format isn't -// quite the same. -func (p15 *pkcs15KeyCert) toP15PrivateKey() ([]byte, error) { - // rsa encrypted key in encrypted envelope - envelope, err := p15.encryptedKeyEnvelope() - if err != nil { - return nil, err - } - - // key object - key := asn1obj.Sequence([][]byte{ + privateKey := asn1obj.Sequence([][]byte{ // commonObjectAttributes - Label asn1obj.Sequence([][]byte{ asn1obj.UTF8String(apcKeyLabel), @@ -87,20 +37,12 @@ func (p15 *pkcs15KeyCert) toP15PrivateKey() ([]byte, error) { asn1obj.Sequence([][]byte{ // AuthEnvelopedData Type ([4]) asn1obj.ExplicitCompound(4, [][]byte{ - envelope, + keyEnvelope, }), }), }), }) - return key, nil -} - -// toP15Cert creates the encoded certificate. it is broken our from the larger p15 -// function for readability -// NOTE: Do not use this to try and turn just a cert into a p15. I don't believe, -// such a thing is permissible under the spec. -func (p15 *pkcs15KeyCert) toP15Cert() ([]byte, error) { // cert object cert := asn1obj.Sequence([][]byte{ // commonObjectAttributes - Label @@ -134,5 +76,163 @@ func (p15 *pkcs15KeyCert) toP15Cert() ([]byte, error) { }), }) - return cert, nil + // build the file + + // ContentInfo + keyCert = asn1obj.Sequence([][]byte{ + + // contentType: OID: 1.2.840.113549.1.15.3.1 pkcs15content (PKCS #15 content type) + asn1obj.ObjectIdentifier(asn1obj.OIDPkscs15Content), + + // content + asn1obj.ExplicitCompound(0, [][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.Integer(big.NewInt(0)), + asn1obj.Sequence([][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + privateKey, + }), + }), + asn1obj.ExplicitCompound(4, [][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + cert, + }), + }), + }), + }), + }), + }) + + return keyCert, nil +} + +// toP15Key creates a P15 file with just the private key, mirroring the p15 format +// the APC tool uses when generating a new private key (Note: no header is used on +// this file) +func (p15 *pkcs15KeyCert) toP15Key(keyEnvelope []byte) (key []byte, err error) { + // private key object (slightly different than the key+cert format) + privateKey := asn1obj.Sequence([][]byte{ + // commonObjectAttributes - Label + asn1obj.Sequence([][]byte{ + asn1obj.UTF8String(apcKeyLabel), + }), + // CommonKeyAttributes + asn1obj.Sequence([][]byte{ + // CommonKeyAttributes - iD - uses keyId that is SHA1( SubjectPublicKeyInfo SEQUENCE ) + asn1obj.OctetString(p15.keyId()), + // CommonKeyAttributes - usage (trailing 0s will drop) + asn1obj.BitString([]byte{byte(0b11100010)}), + // CommonKeyAttributes - accessFlags (trailing 0s will drop) + asn1obj.BitString([]byte{byte(0b10110000)}), + }), + + // + asn1obj.ExplicitCompound(0, [][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + p15.keyIdInt2(), + p15.keyIdInt8(), + p15.keyIdInt9(), + }), + }), + }), + + // ObjectValue - indirect-protected + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.Sequence([][]byte{ + // AuthEnvelopedData Type ([4]) + asn1obj.ExplicitCompound(4, [][]byte{ + keyEnvelope, + }), + }), + }), + }) + + // ContentInfo + key = asn1obj.Sequence([][]byte{ + + // contentType: OID: 1.2.840.113549.1.15.3.1 pkcs15content (PKCS #15 content type) + asn1obj.ObjectIdentifier(asn1obj.OIDPkscs15Content), + + // content + asn1obj.ExplicitCompound(0, [][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.Integer(big.NewInt(0)), + asn1obj.Sequence([][]byte{ + // [0] Private Key + asn1obj.ExplicitCompound(0, [][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + privateKey, + }), + }), + // [1] Public Key + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + asn1obj.Sequence([][]byte{ + // commonObjectAttributes - Label + asn1obj.Sequence([][]byte{ + asn1obj.UTF8String(apcKeyLabel), + }), + // CommonKeyAttributes + asn1obj.Sequence([][]byte{ + asn1obj.OctetString(p15.keyId()), + asn1obj.BitString([]byte{byte(0b10000010)}), + asn1obj.BitString([]byte{byte(0b01000000)}), + }), + + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.ObjectIdentifier(asn1obj.OIDrsaEncryptionPKCS1), + asn1.NullBytes, + }), + // RSAPublicKey SubjectPublicKeyInfo + asn1obj.BitString( + asn1obj.Sequence([][]byte{ + asn1obj.Integer(p15.key.PublicKey.N), + asn1obj.Integer(big.NewInt(int64(p15.key.PublicKey.E))), + }), + ), + }), + }), + // not 100% certain but appears to be rsa key byte len + asn1obj.Integer(big.NewInt(int64(p15.key.PublicKey.N.BitLen() / 8))), + }), + }), + }), + }), + }), + }), + }), + }), + }) + + return key, nil +} + +// ToP15File turns the key and cert into a properly formatted and encoded +// p15 file +func (p15 *pkcs15KeyCert) ToP15Files() (keyCertFile []byte, keyFile []byte, err error) { + // rsa encrypted key in encrypted envelope (will be shared by both output files) + envelope, err := p15.encryptedKeyEnvelope() + if err != nil { + return nil, nil, err + } + + // key + cert file + keyCertFile, err = p15.toP15KeyCert(envelope) + if err != nil { + return nil, nil, err + } + + // key only file + keyFile, err = p15.toP15Key(envelope) + if err != nil { + return nil, nil, err + } + + return keyCertFile, keyFile, nil } From da84a7b0852fbbfc97a73d8a885556ad14fd2d15 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 4 Jun 2024 18:59:36 -0400 Subject: [PATCH 04/54] debug: add base64 encoded debug files When troubleshooting it is helpful to put the generated files into an asn1 decoder. The files can be copy/pasted easily in b64 format. This change creates b64 files when the debug flag is set to make this process easier. --- .gitignore | 1 + pkg/app/cmd_create.go | 27 +++++++++++++++++++++++++++ pkg/app/config.go | 2 +- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9e11ecb..6cd9c9f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # key/cert files *.p15 *.pem +*.b64 # ignore test_data folder /_test_data diff --git a/pkg/app/cmd_create.go b/pkg/app/cmd_create.go index bc9ecdf..46b239c 100644 --- a/pkg/app/cmd_create.go +++ b/pkg/app/cmd_create.go @@ -2,6 +2,7 @@ package app import ( "context" + "encoding/base64" "fmt" "os" ) @@ -59,5 +60,31 @@ func (app *app) cmdCreate(_ context.Context, args []string) error { } app.stdLogger.Printf("create: apc p15 key file %s written to disk", keyFileName) + // if debug, write additional debug files (b64 format to make copy/paste into asn1 decoder + // easy to do e.g., https://lapo.it/asn1js) + if app.config.debugLogging != nil && *app.config.debugLogging { + keyCertFileNameDebug := keyCertFileName + ".noheader.b64" + err = os.WriteFile(keyCertFileNameDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[apcHeaderLen:])), 0777) + if err != nil { + return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) + } + app.debugLogger.Printf("create: apc p15 key+cert file %s written to disk", keyCertFileNameDebug) + + keyCertFileNameHeaderDebug := keyCertFileName + ".header.b64" + err = os.WriteFile(keyCertFileNameHeaderDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[apcHeaderLen:])), 0777) + if err != nil { + return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) + } + app.debugLogger.Printf("create: apc p15 key+cert file header %s written to disk", keyCertFileNameHeaderDebug) + + keyFileNameDebug := keyFileName + ".b64" + err = os.WriteFile(keyFileNameDebug, []byte(base64.StdEncoding.EncodeToString(keyFile)), 0777) + if err != nil { + return fmt.Errorf("create: failed to write apc p15 key file (%s)", err) + } + app.debugLogger.Printf("create: apc p15 key file %s written to disk", keyFileNameDebug) + + } + return nil } diff --git a/pkg/app/config.go b/pkg/app/config.go index bea67aa..e8ff1fc 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -57,7 +57,7 @@ func (app *app) getConfig(args []string) error { // apc-p15-tool -- root command rootFlags := ff.NewFlagSet("apc-p15-tool") - cfg.debugLogging = rootFlags.BoolLong("debug", "set this flag to enable additional debug logging messages") + cfg.debugLogging = rootFlags.BoolLong("debug", "set this flag to enable additional debug logging messages and files") rootCmd := &ff.Command{ Name: "apc-p15-tool", From f0253ccaf219379df5686c5b5491cfe0a45befb3 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 4 Jun 2024 18:59:36 -0400 Subject: [PATCH 05/54] create: set file permissiosns to owner only --- pkg/app/cmd_create.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/app/cmd_create.go b/pkg/app/cmd_create.go index 46b239c..c6b6750 100644 --- a/pkg/app/cmd_create.go +++ b/pkg/app/cmd_create.go @@ -48,13 +48,13 @@ func (app *app) cmdCreate(_ context.Context, args []string) error { } // write files - err = os.WriteFile(keyCertFileName, apcKeyCertFile, 0777) + err = os.WriteFile(keyCertFileName, apcKeyCertFile, 0600) if err != nil { return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) } app.stdLogger.Printf("create: apc p15 key+cert file %s written to disk", keyCertFileName) - err = os.WriteFile(keyFileName, keyFile, 0777) + err = os.WriteFile(keyFileName, keyFile, 0600) if err != nil { return fmt.Errorf("create: failed to write apc p15 key file (%s)", err) } @@ -64,21 +64,21 @@ func (app *app) cmdCreate(_ context.Context, args []string) error { // easy to do e.g., https://lapo.it/asn1js) if app.config.debugLogging != nil && *app.config.debugLogging { keyCertFileNameDebug := keyCertFileName + ".noheader.b64" - err = os.WriteFile(keyCertFileNameDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[apcHeaderLen:])), 0777) + err = os.WriteFile(keyCertFileNameDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[apcHeaderLen:])), 0600) if err != nil { return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) } app.debugLogger.Printf("create: apc p15 key+cert file %s written to disk", keyCertFileNameDebug) keyCertFileNameHeaderDebug := keyCertFileName + ".header.b64" - err = os.WriteFile(keyCertFileNameHeaderDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[apcHeaderLen:])), 0777) + err = os.WriteFile(keyCertFileNameHeaderDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[apcHeaderLen:])), 0600) if err != nil { return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) } app.debugLogger.Printf("create: apc p15 key+cert file header %s written to disk", keyCertFileNameHeaderDebug) keyFileNameDebug := keyFileName + ".b64" - err = os.WriteFile(keyFileNameDebug, []byte(base64.StdEncoding.EncodeToString(keyFile)), 0777) + err = os.WriteFile(keyFileNameDebug, []byte(base64.StdEncoding.EncodeToString(keyFile)), 0600) if err != nil { return fmt.Errorf("create: failed to write apc p15 key file (%s)", err) } From b44b49cd19c3a1d80fc3a7147ece2edb6a25ec7c Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 4 Jun 2024 18:59:36 -0400 Subject: [PATCH 06/54] create: add additional flag to signal creation of additional key.p15 --- pkg/app/cmd_create.go | 26 ++++++++++++++++---------- pkg/app/config.go | 2 ++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/pkg/app/cmd_create.go b/pkg/app/cmd_create.go index c6b6750..ad31003 100644 --- a/pkg/app/cmd_create.go +++ b/pkg/app/cmd_create.go @@ -47,19 +47,13 @@ func (app *app) cmdCreate(_ context.Context, args []string) error { keyFileName = *app.config.create.outKeyFilePath } - // write files + // write file(s) err = os.WriteFile(keyCertFileName, apcKeyCertFile, 0600) if err != nil { return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) } app.stdLogger.Printf("create: apc p15 key+cert file %s written to disk", keyCertFileName) - err = os.WriteFile(keyFileName, keyFile, 0600) - if err != nil { - return fmt.Errorf("create: failed to write apc p15 key file (%s)", err) - } - app.stdLogger.Printf("create: apc p15 key file %s written to disk", keyFileName) - // if debug, write additional debug files (b64 format to make copy/paste into asn1 decoder // easy to do e.g., https://lapo.it/asn1js) if app.config.debugLogging != nil && *app.config.debugLogging { @@ -77,13 +71,25 @@ func (app *app) cmdCreate(_ context.Context, args []string) error { } app.debugLogger.Printf("create: apc p15 key+cert file header %s written to disk", keyCertFileNameHeaderDebug) - keyFileNameDebug := keyFileName + ".b64" - err = os.WriteFile(keyFileNameDebug, []byte(base64.StdEncoding.EncodeToString(keyFile)), 0600) + } + + // make key p15 ? + if app.config.create.makeKeyP15 != nil && *app.config.create.makeKeyP15 { + err = os.WriteFile(keyFileName, keyFile, 0600) if err != nil { return fmt.Errorf("create: failed to write apc p15 key file (%s)", err) } - app.debugLogger.Printf("create: apc p15 key file %s written to disk", keyFileNameDebug) + app.stdLogger.Printf("create: apc p15 key file %s written to disk", keyFileName) + // debug file ? + if app.config.debugLogging != nil && *app.config.debugLogging { + keyFileNameDebug := keyFileName + ".b64" + err = os.WriteFile(keyFileNameDebug, []byte(base64.StdEncoding.EncodeToString(keyFile)), 0600) + if err != nil { + return fmt.Errorf("create: failed to write apc p15 key file (%s)", err) + } + app.debugLogger.Printf("create: apc p15 key file %s written to disk", keyFileNameDebug) + } } return nil diff --git a/pkg/app/config.go b/pkg/app/config.go index e8ff1fc..a3b18c1 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -29,6 +29,7 @@ type config struct { create struct { keyCertPemCfg outFilePath *string + makeKeyP15 *bool outKeyFilePath *string } install struct { @@ -73,6 +74,7 @@ func (app *app) getConfig(args []string) error { cfg.create.keyPem = createFlags.StringLong("keypem", "", "string of the rsa-1024 or rsa-2048 key in pem format") cfg.create.certPem = createFlags.StringLong("certpem", "", "string of the certificate in pem format") cfg.create.outFilePath = createFlags.StringLong("outfile", createDefaultOutFilePath, "path and filename to write the key+cert p15 file to") + cfg.create.makeKeyP15 = createFlags.BoolLong("keyp15", "create a second p15 file with just the private key") cfg.create.outKeyFilePath = createFlags.StringLong("outkeyfile", createDefaultOutKeyFilePath, "path and filename to write the key p15 file to") createCmd := &ff.Command{ From 7dcf0f10b99f592d7cdeb39598d038bddf481e47 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 4 Jun 2024 19:00:56 -0400 Subject: [PATCH 07/54] create: fix header debug file --- pkg/app/cmd_create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/app/cmd_create.go b/pkg/app/cmd_create.go index ad31003..c5d700c 100644 --- a/pkg/app/cmd_create.go +++ b/pkg/app/cmd_create.go @@ -65,7 +65,7 @@ func (app *app) cmdCreate(_ context.Context, args []string) error { app.debugLogger.Printf("create: apc p15 key+cert file %s written to disk", keyCertFileNameDebug) keyCertFileNameHeaderDebug := keyCertFileName + ".header.b64" - err = os.WriteFile(keyCertFileNameHeaderDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[apcHeaderLen:])), 0600) + err = os.WriteFile(keyCertFileNameHeaderDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[:apcHeaderLen])), 0600) if err != nil { return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) } From 7a415f5c85dabc5b479327eeef4894750a698f16 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 4 Jun 2024 19:01:45 -0400 Subject: [PATCH 08/54] v0.5.0-preview1 --- CHANGELOG.md | 26 +++++++++++++++++--------- pkg/app/app.go | 2 +- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecfe84d..f1963c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,47 +1,55 @@ # APC P15 Tool Changelog +## [v0.5.0] - 2024-06-04 + +Add functionality to additionally output a key.p15 file. This file +format matches that of APC's NMC Security Wizard's key file output. +(Use `create` and flag `--keyp15`.) + +Add creation of additional base64 encoded files when the `--debug` +flag is used with `create`. This makes it easier to paste these +encoded files into an ASN1 viewer to analyze them. + +Modify output file permissions to `0666` instead of `0777`. + +Update Go version to 1.22.3. + ## [v0.4.2] - 2024-03-29 Fix usage message. Thanks @k725. - ## [v0.4.1] - 2024-03-06 Update to Go 1.22.1, which includes some security fixes. - ## [v0.4.0] - 2024-02-05 Add `--restartwebui` flag to issue a reboot command to the webui -after a new certificate is installed. This was not needed with +after a new certificate is installed. This was not needed with my NMC2, but I suspect some might need it to get the new certificate to actually load. - ## [v0.3.3] - 2024-02-04 Add `--insecurecipher` flag to enable aes128-cbc and 3des-cbc for older devices/firmwares. These ciphers are considered insecure and -should be avoided. A better alternative is to update the device +should be avoided. A better alternative is to update the device firmware if possible. - ## [v0.3.2] - 2024-02-04 Add support for 1,024 bit RSA keys. These are not recommended! RSA 1024 is generally considered to not be completely secure anymore. Add `diffie-hellman-group-exchange-sha256` key exchange algorithm -which may be needed by some UPSes to connect via SSH to use the +which may be needed by some UPSes to connect via SSH to use the install command. - ## [v0.3.1] - 2024-02-03 Fixes debug logging always being on. App now accurately reflects the state of the --debug flag. - ## [v0.3.0] - 2024-02-03 Initial release. diff --git a/pkg/app/app.go b/pkg/app/app.go index 3119334..5cd9ebe 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -12,7 +12,7 @@ import ( ) const ( - appVersion = "0.4.2" + appVersion = "0.5.0" ) // struct for receivers to use common app pieces From 41efc56c6253c6256e861d0c93019abdb253f61a Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Thu, 6 Jun 2024 22:51:12 -0400 Subject: [PATCH 09/54] ssh: clarify log error msg --- pkg/app/ssh_response.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/app/ssh_response.go b/pkg/app/ssh_response.go index 3b44523..8068704 100644 --- a/pkg/app/ssh_response.go +++ b/pkg/app/ssh_response.go @@ -12,7 +12,7 @@ func sshCheckResponse(remoteOutPipe io.Reader) error { buffer := make([]uint8, 1) _, err := remoteOutPipe.Read(buffer) if err != nil { - return fmt.Errorf("ssh: failed to make read output buffer (%w)", err) + return fmt.Errorf("ssh: failed to read output buffer (%w)", err) } responseType := buffer[0] From 06c9263bc4866bfe9e0bd5ca8ccadcdce906e947 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Thu, 6 Jun 2024 22:51:12 -0400 Subject: [PATCH 10/54] ssh: breakout ups ssh to its own package This was done for clearer separation of function. A subsequent update will (hopefully) make the SSL command more robust so it works for both NMC2 and NMC3. The method for sending shell commands was also updated to use an interactive shell instead. This allows capturing responses of the commands which will be needed to deduce if devices are NMC2 or NMC3. --- go.mod | 2 + pkg/apcssh/client.go | 134 +++++++++++++++++++++++++++++++++ pkg/apcssh/cmd_restartwebui.go | 24 ++++++ pkg/apcssh/scp.go | 129 +++++++++++++++++++++++++++++++ pkg/apcssh/shell.go | 88 ++++++++++++++++++++++ pkg/apcssh/shell_helpers.go | 38 ++++++++++ pkg/apcssh/ssl.go | 15 ++++ pkg/app/cmd_install.go | 114 ++++------------------------ pkg/app/ssh_resetwebui.go | 45 ----------- pkg/app/ssh_response.go | 34 --------- pkg/app/ssh_scp.go | 105 -------------------------- 11 files changed, 444 insertions(+), 284 deletions(-) create mode 100644 pkg/apcssh/client.go create mode 100644 pkg/apcssh/cmd_restartwebui.go create mode 100644 pkg/apcssh/scp.go create mode 100644 pkg/apcssh/shell.go create mode 100644 pkg/apcssh/shell_helpers.go create mode 100644 pkg/apcssh/ssl.go delete mode 100644 pkg/app/ssh_resetwebui.go delete mode 100644 pkg/app/ssh_response.go delete mode 100644 pkg/app/ssh_scp.go diff --git a/go.mod b/go.mod index d5bd69a..2ae4f6a 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,8 @@ replace apc-p15-tool/cmd/install_only => /cmd/install_only replace apc-p15-tool/cmd/tool => /cmd/tool +replace apc-p15-tool/pkg/apcssh => /pkg/apcssh + replace apc-p15-tool/pkg/app => /pkg/app replace apc-p15-tool/pkg/pkcs15 => /pkg/pkcs15 diff --git a/pkg/apcssh/client.go b/pkg/apcssh/client.go new file mode 100644 index 0000000..33b01d5 --- /dev/null +++ b/pkg/apcssh/client.go @@ -0,0 +1,134 @@ +package apcssh + +import ( + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "errors" + "fmt" + "log" + "net" + "runtime" + "strings" + "time" + + "golang.org/x/crypto/ssh" +) + +const ( + apcSSHVer = 1 + + sshTimeout = 90 * time.Second +) + +// APC UPS won't except Go's SSH "Run()" command as the format isn't quite +// the same. Therefore, write a custom implementation instead of relying on +// something like github.com/bramvdbogaerde/go-scp + +type Config struct { + Hostname string + Username string + Password string + ServerFingerprint string + InsecureCipher bool +} + +// Client is an APC UPS SSH client +type Client struct { + hostname string + sshCfg *ssh.ClientConfig +} + +// New creates a new SSH Client for the APC UPS. +func New(cfg *Config) (*Client, error) { + // make host key callback + hk := func(_hostname string, _remote net.Addr, key ssh.PublicKey) error { + // calculate server's key's SHA256 + hasher := sha256.New() + _, err := hasher.Write(key.Marshal()) + if err != nil { + return err + } + actualHash := hasher.Sum(nil) + + // log fingerprint for debugging + actualHashB64 := base64.RawStdEncoding.EncodeToString(actualHash) + actualHashHex := hex.EncodeToString(actualHash) + + // check for fingerprint match (b64 or hex) + if actualHashB64 != cfg.ServerFingerprint && actualHashHex != cfg.ServerFingerprint { + log.Printf("apcssh: remote server key fingerprint (b64): %s", actualHashB64) + log.Printf("apcssh: remote server key fingerprint (hex): %s", actualHashHex) + + return errors.New("apcssh: fingerprint didn't match") + } + + return nil + } + + // kex algos + // see defaults: https://cs.opensource.google/go/x/crypto/+/refs/tags/v0.18.0:ssh/common.go;l=62 + kexAlgos := []string{ + "curve25519-sha256", "curve25519-sha256@libssh.org", + "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", + "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1", + } + // extra for some apc ups + kexAlgos = append(kexAlgos, "diffie-hellman-group-exchange-sha256") + + // ciphers + // see defaults: https://cs.opensource.google/go/x/crypto/+/master:ssh/common.go;l=37 + ciphers := []string{ + "aes128-gcm@openssh.com", "aes256-gcm@openssh.com", + "chacha20-poly1305@openssh.com", + "aes128-ctr", "aes192-ctr", "aes256-ctr", + } + + // insecure cipher options? + if cfg.InsecureCipher { + log.Println("WARNING: insecure ciphers are enabled (--insecurecipher). SSH with an insecure cipher is NOT secure and should NOT be used.") + ciphers = append(ciphers, "aes128-cbc", "3des-cbc") + } + + // install file on UPS + // ssh config + config := &ssh.ClientConfig{ + User: cfg.Username, + Auth: []ssh.AuthMethod{ + ssh.Password(cfg.Password), + }, + // APC seems to require `Client Version` string to start with "SSH-2" and must be at least + // 13 characters long + // working examples from other clients: + // ClientVersion: "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.6", + // ClientVersion: "SSH-2.0-PuTTY_Release_0.80", + ClientVersion: fmt.Sprintf("SSH-2.0-apcssh_v%d %s-%s", apcSSHVer, runtime.GOOS, runtime.GOARCH), + Config: ssh.Config{ + KeyExchanges: kexAlgos, + Ciphers: ciphers, + }, + HostKeyCallback: hk, + + // reasonable timeout for file copy + Timeout: sshTimeout, + } + + // if hostname missing a port, add default + if !strings.Contains(cfg.Hostname, ":") { + cfg.Hostname = cfg.Hostname + ":22" + } + + // connect to ups over SSH (to verify everything works) + sshClient, err := ssh.Dial("tcp", cfg.Hostname, config) + if err != nil { + return nil, err + } + _ = sshClient.Close() + + // return Client (note: new ssh Dial will be done for each action as the UPS + // seems to not do well with more than one Session per Dial) + return &Client{ + hostname: cfg.Hostname, + sshCfg: config, + }, nil +} diff --git a/pkg/apcssh/cmd_restartwebui.go b/pkg/apcssh/cmd_restartwebui.go new file mode 100644 index 0000000..4b904c1 --- /dev/null +++ b/pkg/apcssh/cmd_restartwebui.go @@ -0,0 +1,24 @@ +package apcssh + +import ( + "fmt" + "strings" +) + +// RestartWebUI sends the APC command to restart the web ui +// WARNING: Sending a command directly after this one will cause issues. +// This command will cause SSH to also restart after a slight delay, therefore +// any command right after this will start to run but then get stuck / fail +// somewhere in the middle. +func (cli *Client) RestartWebUI() error { + result, err := cli.cmd("reboot -Y") + if err != nil { + return err + } + + if strings.ToLower(result.code) != "e000" { + return fmt.Errorf("apcssh: failed to restart web ui (%s: %s)", result.code, result.codeText) + } + + return nil +} diff --git a/pkg/apcssh/scp.go b/pkg/apcssh/scp.go new file mode 100644 index 0000000..9db77c0 --- /dev/null +++ b/pkg/apcssh/scp.go @@ -0,0 +1,129 @@ +package apcssh + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "io/fs" + "path" + + "golang.org/x/crypto/ssh" +) + +// UploadSCP uploads a file to the destination specified (e.g., "/ssl/file.key") +// containing the file content specified. An existing file at the destination +// will be overwritten without warning. +func (cli *Client) UploadSCP(destination string, fileContent []byte, filePermissions fs.FileMode) error { + // connect + sshClient, err := ssh.Dial("tcp", cli.hostname, cli.sshCfg) + if err != nil { + return fmt.Errorf("apcssh: scp: failed to dial session (%w)", err) + } + defer sshClient.Close() + + // make session to use for SCP + session, err := sshClient.NewSession() + if err != nil { + return fmt.Errorf("apcssh: scp: failed to create session (%w)", err) + } + defer session.Close() + + // attach pipes + out, err := session.StdoutPipe() + if err != nil { + return err + } + w, err := session.StdinPipe() + if err != nil { + return err + } + defer w.Close() + + // send execute cmd -- + // build cmd to send as request + // Go implementation sends additional 0x22 bytes when using Run() (as + // compared to putty's scp tool). these additional bytes seem to cause the + // apc ups to fail execution of the command + payload := []byte(fmt.Sprintf("scp -q -t %s", destination)) + payloadLen := uint8(len(payload)) + payload = append([]byte{0, 0, 0, payloadLen}, payload...) + + ok, err := session.SendRequest("exec", true, payload) + if err != nil { + return fmt.Errorf("apcssh: scp: failed to execute scp cmd (%w)", err) + } + if !ok { + return errors.New("apcssh: scp: execute scp cmd not ok") + } + + // check remote response + // Note: File upload may not work if the client doesn't actually read from + // the remote output. + err = scpCheckResponse(out) + if err != nil { + return fmt.Errorf("apcssh: scp: failed to send scp cmd (bad remote response 1) (%w)", err) + } + + // just file name (without path) + filename := path.Base(destination) + + // send file header + _, err = fmt.Fprintln(w, "C"+fmt.Sprintf("%04o", filePermissions.Perm()), len(fileContent), filename) + if err != nil { + return fmt.Errorf("apcssh: scp: failed to send file info (%w)", err) + } + + err = scpCheckResponse(out) + if err != nil { + return fmt.Errorf("apcssh: scp: failed to send file info (bad remote response 2) (%w)", err) + } + + // send actual file + _, err = io.Copy(w, bytes.NewReader(fileContent)) + if err != nil { + return fmt.Errorf("apcssh: scp: failed to send file(%w)", err) + } + + // send file end + _, err = fmt.Fprint(w, "\x00") + if err != nil { + return fmt.Errorf("apcssh: scp: failed to send final 00 byte (%w)", err) + } + + err = scpCheckResponse(out) + if err != nil { + return fmt.Errorf("apcssh: scp: failed to send file (bad remote response 3) (%w)", err) + } + + // done + return nil +} + +// scpCheckResponse reads the output from the remote and returns an error +// if the remote output was not 0 +func scpCheckResponse(remoteOutPipe io.Reader) error { + buffer := make([]uint8, 1) + _, err := remoteOutPipe.Read(buffer) + if err != nil { + return fmt.Errorf("apcssh: failed to read output buffer (%w)", err) + } + + responseType := buffer[0] + message := "" + if responseType > 0 { + bufferedReader := bufio.NewReader(remoteOutPipe) + message, err = bufferedReader.ReadString('\n') + if err != nil { + return fmt.Errorf("apcssh: failed to read output buffer (%w)", err) + } + } + + // if not 0 (aka OK) + if responseType != 0 { + return fmt.Errorf("apcssh: remote returned error (%d: %s)", responseType, message) + } + + return nil +} diff --git a/pkg/apcssh/shell.go b/pkg/apcssh/shell.go new file mode 100644 index 0000000..c6f082c --- /dev/null +++ b/pkg/apcssh/shell.go @@ -0,0 +1,88 @@ +package apcssh + +import ( + "bufio" + "fmt" + "strings" + + "golang.org/x/crypto/ssh" +) + +// upsCmdResponse is a structure that holds all of a shell commands results +type upsCmdResponse struct { + command string + code string + codeText string + resultText string +} + +// cmd creates an interactive shell and executes the specified command +func (cli *Client) cmd(command string) (*upsCmdResponse, error) { + // connect + sshClient, err := ssh.Dial("tcp", cli.hostname, cli.sshCfg) + if err != nil { + return nil, fmt.Errorf("apcssh: failed to dial session (%w)", err) + } + defer sshClient.Close() + + session, err := sshClient.NewSession() + if err != nil { + return nil, fmt.Errorf("apcssh: failed to create session (%w)", err) + } + defer session.Close() + + // pipes to send shell command to; and to receive repsonse + sshInput, err := session.StdinPipe() + if err != nil { + return nil, fmt.Errorf("apcssh: failed to make shell input pipe (%w)", err) + } + sshOutput, err := session.StdoutPipe() + if err != nil { + return nil, fmt.Errorf("apcssh: failed to make shell output pipe (%w)", err) + } + + // make scanner to read shell continuously + scanner := bufio.NewScanner(sshOutput) + scanner.Split(scanAPCShell) + + // start interactive shell + if err := session.Shell(); err != nil { + return nil, fmt.Errorf("apcssh: failed to start shell (%w)", err) + } + // discard the initial shell response (login message(s) / initial shell prompt) + for { + if token := scanner.Scan(); token { + _ = scanner.Bytes() + break + } + } + + // send command + _, err = fmt.Fprint(sshInput, command+"\n") + if err != nil { + return nil, fmt.Errorf("apcssh: failed to send shell command (%w)", err) + } + + res := &upsCmdResponse{} + for { + if tkn := scanner.Scan(); tkn { + result := string(scanner.Bytes()) + + cmdIndx := strings.Index(result, "\n") + res.command = result[:cmdIndx-1] + result = result[cmdIndx+1:] + + codeIndx := strings.Index(result, ": ") + res.code = result[:codeIndx] + result = result[codeIndx+2:] + + codeTxtIndx := strings.Index(result, "\n") + res.codeText = result[:codeTxtIndx-1] + + res.resultText = result[codeTxtIndx+1 : len(result)-2] + break + } + } + + return res, nil +} diff --git a/pkg/apcssh/shell_helpers.go b/pkg/apcssh/shell_helpers.go new file mode 100644 index 0000000..e2084cb --- /dev/null +++ b/pkg/apcssh/shell_helpers.go @@ -0,0 +1,38 @@ +package apcssh + +import ( + "regexp" +) + +// scanAPCShell is a SplitFunc to capture shell output after each interactive +// shell command is run +func scanAPCShell(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + + // regex for shell prompt (e.g., `apc@apc>`) + re := regexp.MustCompile(`(\r\n|\r|\n)[A-Za-z0-0.]+@[A-Za-z0-0.]+>`) + + // find match for prompt + if index := re.FindStringIndex(string(data)); index != nil { + // advance starts after the prompt; token is everything before the prompt + return index[1], dropCR(data[0:index[0]]), nil + } + + // If we're at EOF, we have a final, non-terminated line. Return it. + if atEOF { + return len(data), dropCR(data), nil + } + + // Request more data. + return 0, nil, nil +} + +// dropCR drops a terminal \r from the data. +func dropCR(data []byte) []byte { + if len(data) > 0 && data[len(data)-1] == '\r' { + return data[0 : len(data)-1] + } + return data +} diff --git a/pkg/apcssh/ssl.go b/pkg/apcssh/ssl.go new file mode 100644 index 0000000..514545d --- /dev/null +++ b/pkg/apcssh/ssl.go @@ -0,0 +1,15 @@ +package apcssh + +import "fmt" + +// InstallSSLCert installs the specified p15 cert file on the UPS. This +// function currently only works on NMC2. +func (cli *Client) InstallSSLCert(keyCertP15 []byte) error { + // install NMC2 P15 file + err := cli.UploadSCP("/ssl/defaultcert.p15", keyCertP15, 0600) + if err != nil { + return fmt.Errorf("apcssh: ssl cert install: failed to send file to ups over scp (%w)", err) + } + + return nil +} diff --git a/pkg/app/cmd_install.go b/pkg/app/cmd_install.go index 26635e7..f59bd7b 100644 --- a/pkg/app/cmd_install.go +++ b/pkg/app/cmd_install.go @@ -1,16 +1,10 @@ package app import ( + "apc-p15-tool/pkg/apcssh" "context" - "crypto/sha256" - "encoding/base64" - "encoding/hex" "errors" "fmt" - "net" - "runtime" - - "golang.org/x/crypto/ssh" ) // cmdInstall is the app's command to create apc p15 file content from key and cert @@ -52,100 +46,29 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { // validation done // make p15 file - apcFile, _, err := app.pemToAPCP15s(keyPem, certPem, "install") + keyCertP15, _, err := app.pemToAPCP15s(keyPem, certPem, "install") if err != nil { return err } - // make host key callback - hk := func(hostname string, remote net.Addr, key ssh.PublicKey) error { - // calculate server's key's SHA256 - hasher := sha256.New() - _, err := hasher.Write(key.Marshal()) - if err != nil { - return err - } - actualHash := hasher.Sum(nil) - - // log fingerprint for debugging - actualHashB64 := base64.RawStdEncoding.EncodeToString(actualHash) - actualHashHex := hex.EncodeToString(actualHash) - app.debugLogger.Printf("ssh: remote server key fingerprint (b64): %s", actualHashB64) - app.debugLogger.Printf("ssh: remote server key fingerprint (hex): %s", actualHashHex) - - // allow base64 format - if actualHashB64 == *app.config.install.fingerprint { - return nil - } - - // allow hex format - if actualHashHex == *app.config.install.fingerprint { - return nil - } - - return errors.New("ssh: fingerprint didn't match") + // make APC SSH client + cfg := &apcssh.Config{ + Hostname: *app.config.install.hostAndPort, + Username: *app.config.install.username, + Password: *app.config.install.password, + ServerFingerprint: *app.config.install.fingerprint, + InsecureCipher: *app.config.install.insecureCipher, } - // kex algos - // see defaults: https://cs.opensource.google/go/x/crypto/+/refs/tags/v0.18.0:ssh/common.go;l=62 - kexAlgos := []string{ - "curve25519-sha256", "curve25519-sha256@libssh.org", - "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", - "diffie-hellman-group14-sha256", "diffie-hellman-group14-sha1", - } - // extra for some apc ups - kexAlgos = append(kexAlgos, "diffie-hellman-group-exchange-sha256") - - // ciphers - // see defaults: https://cs.opensource.google/go/x/crypto/+/master:ssh/common.go;l=37 - ciphers := []string{ - "aes128-gcm@openssh.com", "aes256-gcm@openssh.com", - "chacha20-poly1305@openssh.com", - "aes128-ctr", "aes192-ctr", "aes256-ctr", - } - - // insecure cipher options? - if app.config.install.insecureCipher != nil && *app.config.install.insecureCipher { - app.stdLogger.Println("WARNING: insecure ciphers are enabled (--insecurecipher). SSH with an insecure cipher is NOT secure and should NOT be used.") - ciphers = append(ciphers, "aes128-cbc", "3des-cbc") - } - - // install file on UPS - // ssh config - config := &ssh.ClientConfig{ - User: *app.config.install.username, - Auth: []ssh.AuthMethod{ - ssh.Password(*app.config.install.password), - }, - // APC seems to require `Client Version` string to start with "SSH-2" and must be at least - // 13 characters long - // working examples from other clients: - // ClientVersion: "SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.6", - // ClientVersion: "SSH-2.0-PuTTY_Release_0.80", - ClientVersion: fmt.Sprintf("SSH-2.0-apc-p15-tool_v%s %s-%s", appVersion, runtime.GOOS, runtime.GOARCH), - Config: ssh.Config{ - KeyExchanges: kexAlgos, - Ciphers: ciphers, - // MACs: []string{"hmac-sha2-256"}, - }, - // HostKeyAlgorithms: []string{"ssh-rsa"}, - HostKeyCallback: hk, - - // reasonable timeout for file copy - Timeout: sshScpTimeout, - } - - // connect to ups over SSH - client, err := ssh.Dial("tcp", *app.config.install.hostAndPort, config) + client, err := apcssh.New(cfg) if err != nil { return fmt.Errorf("install: failed to connect to host (%w)", err) } - defer client.Close() - // send file to UPS - err = sshScpSendFileToUPS(client, apcFile) + // install SSL Cert + err = client.InstallSSLCert(keyCertP15) if err != nil { - return fmt.Errorf("install: failed to send p15 file to ups over scp (%w)", err) + return fmt.Errorf("install: failed to send file to ups over scp (%w)", err) } // installed @@ -155,16 +78,7 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { if app.config.install.restartWebUI != nil && *app.config.install.restartWebUI { app.stdLogger.Println("install: sending restart command") - // connect to ups over SSH - // opening a second session doesn't seem to work with my NMC2 for some reason, so make - // a new connection instead - client, err = ssh.Dial("tcp", *app.config.install.hostAndPort, config) - if err != nil { - return fmt.Errorf("install: failed to reconnect to host to send webui restart command (%w)", err) - } - defer client.Close() - - err = sshResetUPSWebUI(client) + err = client.RestartWebUI() if err != nil { return fmt.Errorf("install: failed to send webui restart command (%w)", err) } diff --git a/pkg/app/ssh_resetwebui.go b/pkg/app/ssh_resetwebui.go deleted file mode 100644 index 52f90d9..0000000 --- a/pkg/app/ssh_resetwebui.go +++ /dev/null @@ -1,45 +0,0 @@ -package app - -import ( - "errors" - "fmt" - - "golang.org/x/crypto/ssh" -) - -// sshResetUPSWebUI sends a command to the UPS to restart the WebUI. This -// command is supposed to be required to load the new cert, but that -// doesn't seem to be true (at least it isn't on my UPS). Adding the -// option though, in case other UPS might need it. -func sshResetUPSWebUI(client *ssh.Client) error { - // make session to use for restart command - session, err := client.NewSession() - if err != nil { - return fmt.Errorf("ssh: restart: failed to create session (%w)", err) - } - defer session.Close() - - // start shell - err = session.Shell() - if err != nil { - return fmt.Errorf("ssh: restart: failed to start shell (%w)", err) - } - - // execure reboot via SendRequest - payload := []byte("reboot -Y") - payloadLen := uint8(len(payload)) - payload = append([]byte{0, 0, 0, payloadLen}, payload...) - - ok, err := session.SendRequest("exec", true, payload) - if err != nil { - return fmt.Errorf("ssh: scp: failed to execute scp cmd (%w)", err) - } - if !ok { - return errors.New("ssh: scp: execute scp cmd not ok") - } - - // don't read remote output, as nothing interesting actually outputs - - // done - return nil -} diff --git a/pkg/app/ssh_response.go b/pkg/app/ssh_response.go deleted file mode 100644 index 8068704..0000000 --- a/pkg/app/ssh_response.go +++ /dev/null @@ -1,34 +0,0 @@ -package app - -import ( - "bufio" - "fmt" - "io" -) - -// sshCheckResponse reads the output from the remote and returns an error -// if the remote output was not 0 -func sshCheckResponse(remoteOutPipe io.Reader) error { - buffer := make([]uint8, 1) - _, err := remoteOutPipe.Read(buffer) - if err != nil { - return fmt.Errorf("ssh: failed to read output buffer (%w)", err) - } - - responseType := buffer[0] - message := "" - if responseType > 0 { - bufferedReader := bufio.NewReader(remoteOutPipe) - message, err = bufferedReader.ReadString('\n') - if err != nil { - return fmt.Errorf("ssh: failed to read output buffer (%w)", err) - } - } - - // if not 0 (aka OK) - if responseType != 0 { - return fmt.Errorf("ssh: remote returned error (%d: %s)", responseType, message) - } - - return nil -} diff --git a/pkg/app/ssh_scp.go b/pkg/app/ssh_scp.go deleted file mode 100644 index 54f216c..0000000 --- a/pkg/app/ssh_scp.go +++ /dev/null @@ -1,105 +0,0 @@ -package app - -import ( - "bytes" - "errors" - "fmt" - "io" - "path" - "time" - - "golang.org/x/crypto/ssh" -) - -// APC UPS won't except Go's SSH "Run()" command as the format isn't quite -// the same. Therefore, write a custom implementation to send the desired -// command instead of relying on something like github.com/bramvdbogaerde/go-scp - -const ( - sshScpP15Destination = "/ssl/defaultcert.p15" - sshScpP15PermissionsStr = "0600" - - sshScpTimeout = 90 * time.Second -) - -// sshScpSendFileToUPS sends the p15File to the APC UPS via the SCP protocol. it is -// automatically placed in the correct directory and will overwrite any existing -// file -func sshScpSendFileToUPS(client *ssh.Client, p15File []byte) error { - // make session to use for SCP - session, err := client.NewSession() - if err != nil { - return fmt.Errorf("ssh: scp: failed to create session (%w)", err) - } - defer session.Close() - - // attach pipes - out, err := session.StdoutPipe() - if err != nil { - return err - } - w, err := session.StdinPipe() - if err != nil { - return err - } - defer w.Close() - - // send execute cmd -- - // build cmd to send as request - // Go implementation sends additional 0x22 bytes when using Run() (as - // compared to putty's scp tool). these additional bytes seem to cause the - // apc ups to fail execution of the command - payload := []byte(fmt.Sprintf("scp -q -t %s", sshScpP15Destination)) - payloadLen := uint8(len(payload)) - payload = append([]byte{0, 0, 0, payloadLen}, payload...) - - ok, err := session.SendRequest("exec", true, payload) - if err != nil { - return fmt.Errorf("ssh: scp: failed to execute scp cmd (%w)", err) - } - if !ok { - return errors.New("ssh: scp: execute scp cmd not ok") - } - - // check remote response - // Note: File upload may not work if the client doesn't actually read from - // the remote output. - err = sshCheckResponse(out) - if err != nil { - return fmt.Errorf("ssh: scp: failed to send scp cmd (bad remote response) (%w)", err) - } - - // just file name (without path) - filename := path.Base(sshScpP15Destination) - - // send file header - _, err = fmt.Fprintln(w, "C"+sshScpP15PermissionsStr, len(p15File), filename) - if err != nil { - return fmt.Errorf("ssh: scp: failed to send file info (%w)", err) - } - - err = sshCheckResponse(out) - if err != nil { - return fmt.Errorf("ssh: scp: failed to send file info (bad remote response) (%w)", err) - } - - // send actual file - _, err = io.Copy(w, bytes.NewReader(p15File)) - if err != nil { - return fmt.Errorf("ssh: scp: failed to send file(%w)", err) - } - - // send file end - _, err = fmt.Fprint(w, "\x00") - if err != nil { - return fmt.Errorf("ssh: scp: failed to send final 00 byte (%w)", err) - } - - err = sshCheckResponse(out) - if err != nil { - return fmt.Errorf("ssh: scp: failed to send file (bad remote response) (%w)", err) - } - - // done - return nil -} From dda11df624f31d50b61c3762e5098cb9479d0d15 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Thu, 6 Jun 2024 22:51:12 -0400 Subject: [PATCH 11/54] install: add support for native ssl command The code should auto-select the native ssl method if the ssl command is available on the UPS. If this fails, install will drop back to the original install method used by this tool (which works on NMC2). --- pkg/apcssh/shell.go | 5 ++- pkg/apcssh/ssl.go | 74 ++++++++++++++++++++++++++++++++++++++---- pkg/app/cmd_create.go | 2 +- pkg/app/cmd_install.go | 4 +-- pkg/app/pem_to_p15.go | 6 ++-- 5 files changed, 77 insertions(+), 14 deletions(-) diff --git a/pkg/apcssh/shell.go b/pkg/apcssh/shell.go index c6f082c..4643621 100644 --- a/pkg/apcssh/shell.go +++ b/pkg/apcssh/shell.go @@ -79,7 +79,10 @@ func (cli *Client) cmd(command string) (*upsCmdResponse, error) { codeTxtIndx := strings.Index(result, "\n") res.codeText = result[:codeTxtIndx-1] - res.resultText = result[codeTxtIndx+1 : len(result)-2] + // avoid out of bounds if no result text + if codeTxtIndx+1 <= len(result)-2 { + res.resultText = result[codeTxtIndx+1 : len(result)-2] + } break } } diff --git a/pkg/apcssh/ssl.go b/pkg/apcssh/ssl.go index 514545d..c3fac28 100644 --- a/pkg/apcssh/ssl.go +++ b/pkg/apcssh/ssl.go @@ -1,14 +1,74 @@ package apcssh -import "fmt" +import ( + "fmt" + "strings" +) -// InstallSSLCert installs the specified p15 cert file on the UPS. This -// function currently only works on NMC2. -func (cli *Client) InstallSSLCert(keyCertP15 []byte) error { - // install NMC2 P15 file - err := cli.UploadSCP("/ssl/defaultcert.p15", keyCertP15, 0600) +// InstallSSLCert installs the specified p15 key and p15 cert files on the +// UPS. It has logic to deduce if the NMC is a newer version (e.g., NMC3 with +// newer firmware) and acts accordingly. +func (cli *Client) InstallSSLCert(keyP15 []byte, certPem []byte, keyCertP15 []byte) error { + // run `ssl` command to check if it exists + result, err := cli.cmd("ssl") if err != nil { - return fmt.Errorf("apcssh: ssl cert install: failed to send file to ups over scp (%w)", err) + return fmt.Errorf("apcssh: ssl cert install: failed to send ssl cmd (%w)", err) + } + // E101 is the code for "Command Not Found" + supportsSSLCmd := strings.ToLower(result.code) != "e101" + + // if SSL is supported, use that method + if supportsSSLCmd { + return cli.installSSLCertModern(keyP15, certPem) + } + + // fallback to legacy + return cli.installSSLCertLegacy(keyCertP15) +} + +// installSSLCertModern installs the SSL key and certificate using the UPS built-in +// command `ssl`. This command is not present on older devices (e.g., NMC2) or firmwares. +func (cli *Client) installSSLCertModern(keyP15 []byte, certPem []byte) error { + // upload the key P15 file + err := cli.UploadSCP("/ssl/nmc.key", keyP15, 0600) + if err != nil { + return fmt.Errorf("apcssh: ssl cert install: failed to send nmc.key file to ups over scp (%w)", err) + } + + // upload the cert PEM file + err = cli.UploadSCP("/ssl/nmc.crt", certPem, 0666) + if err != nil { + return fmt.Errorf("apcssh: ssl cert install: failed to send nmc.key file to ups over scp (%w)", err) + } + + // run `ssl` install commands + result, err := cli.cmd("ssl key -i /ssl/nmc.key") + if err != nil { + return fmt.Errorf("apcssh: ssl cert install: failed to send ssl key install cmd (%w)", err) + } + if strings.ToLower(result.code) != "e000" { + return fmt.Errorf("apcssh: ssl cert install: ssl key install cmd returned error code (%s: %s)", result.code, result.codeText) + } + + result, err = cli.cmd("ssl cert -i /ssl/nmc.crt") + if err != nil { + return fmt.Errorf("apcssh: ssl cert install: failed to send ssl cert install cmd (%w)", err) + } + if strings.ToLower(result.code) != "e000" { + return fmt.Errorf("apcssh: ssl cert install: ssl cert install cmd returned error code (%s: %s)", result.code, result.codeText) + } + + return nil +} + +// installSSLCertLegacy installs the SSL key and certificate by directly uploading +// them to a .p15 file on the UPS. This is used for older devices (e.g., NMC2) and +// firmwares that do not support the `ssl` command. +func (cli *Client) installSSLCertLegacy(keyCertP15 []byte) error { + // upload/install keyCert P15 file + err := cli.UploadSCP("/ssl/defaultcert.p15", keyCertP15, 0600) + if err != nil { + return fmt.Errorf("apcssh: ssl cert install: failed to send defaultcert.p15 file to ups over scp (%w)", err) } return nil diff --git a/pkg/app/cmd_create.go b/pkg/app/cmd_create.go index c5d700c..de9512d 100644 --- a/pkg/app/cmd_create.go +++ b/pkg/app/cmd_create.go @@ -31,7 +31,7 @@ func (app *app) cmdCreate(_ context.Context, args []string) error { // validation done // make p15 files - apcKeyCertFile, keyFile, err := app.pemToAPCP15s(keyPem, certPem, "create") + keyFile, apcKeyCertFile, err := app.pemToAPCP15(keyPem, certPem, "create") if err != nil { return err } diff --git a/pkg/app/cmd_install.go b/pkg/app/cmd_install.go index f59bd7b..f4b8a22 100644 --- a/pkg/app/cmd_install.go +++ b/pkg/app/cmd_install.go @@ -46,7 +46,7 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { // validation done // make p15 file - keyCertP15, _, err := app.pemToAPCP15s(keyPem, certPem, "install") + keyP15, keyCertP15, err := app.pemToAPCP15(keyPem, certPem, "install") if err != nil { return err } @@ -66,7 +66,7 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { } // install SSL Cert - err = client.InstallSSLCert(keyCertP15) + err = client.InstallSSLCert(keyP15, certPem, keyCertP15) if err != nil { return fmt.Errorf("install: failed to send file to ups over scp (%w)", err) } diff --git a/pkg/app/pem_to_p15.go b/pkg/app/pem_to_p15.go index b006c9e..e376bc6 100644 --- a/pkg/app/pem_to_p15.go +++ b/pkg/app/pem_to_p15.go @@ -5,10 +5,10 @@ import ( "fmt" ) -// pemToAPCP15s reads the specified pem files and returns the apc p15 files (both a +// pemToAPCP15 reads the specified pem files and returns the apc p15 files (both a // p15 file with just the private key, and also a p15 file with both the private key // and certificate). The key+cert file includes the required APC header, prepended. -func (app *app) pemToAPCP15s(keyPem, certPem []byte, parentCmdName string) (apcKeyCertFile, keyFile []byte, err error) { +func (app *app) pemToAPCP15(keyPem, certPem []byte, parentCmdName string) (keyFile []byte, apcKeyCertFile []byte, err error) { app.stdLogger.Printf("%s: making apc p15 file from pem", parentCmdName) // make p15 struct @@ -36,5 +36,5 @@ func (app *app) pemToAPCP15s(keyPem, certPem []byte, parentCmdName string) (apcK app.stdLogger.Printf("%s: apc p15 file data succesfully generated", parentCmdName) - return apcKeyCertFile, keyFile, nil + return keyFile, apcKeyCertFile, nil } From ce9958e42275b35bac267ee0bdc84d93fd8d486f Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Thu, 6 Jun 2024 22:51:13 -0400 Subject: [PATCH 12/54] create: always produce both p15 files --- pkg/app/cmd_create.go | 32 +++++++++++++------------------- pkg/app/config.go | 2 -- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/pkg/app/cmd_create.go b/pkg/app/cmd_create.go index de9512d..1a6c88a 100644 --- a/pkg/app/cmd_create.go +++ b/pkg/app/cmd_create.go @@ -48,6 +48,12 @@ func (app *app) cmdCreate(_ context.Context, args []string) error { } // write file(s) + err = os.WriteFile(keyFileName, keyFile, 0600) + if err != nil { + return fmt.Errorf("create: failed to write apc p15 key file (%s)", err) + } + app.stdLogger.Printf("create: apc p15 key file %s written to disk", keyFileName) + err = os.WriteFile(keyCertFileName, apcKeyCertFile, 0600) if err != nil { return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) @@ -57,6 +63,13 @@ func (app *app) cmdCreate(_ context.Context, args []string) error { // if debug, write additional debug files (b64 format to make copy/paste into asn1 decoder // easy to do e.g., https://lapo.it/asn1js) if app.config.debugLogging != nil && *app.config.debugLogging { + keyFileNameDebug := keyFileName + ".b64" + err = os.WriteFile(keyFileNameDebug, []byte(base64.StdEncoding.EncodeToString(keyFile)), 0600) + if err != nil { + return fmt.Errorf("create: failed to write apc p15 key file (%s)", err) + } + app.debugLogger.Printf("create: apc p15 key file %s written to disk", keyFileNameDebug) + keyCertFileNameDebug := keyCertFileName + ".noheader.b64" err = os.WriteFile(keyCertFileNameDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[apcHeaderLen:])), 0600) if err != nil { @@ -73,24 +86,5 @@ func (app *app) cmdCreate(_ context.Context, args []string) error { } - // make key p15 ? - if app.config.create.makeKeyP15 != nil && *app.config.create.makeKeyP15 { - err = os.WriteFile(keyFileName, keyFile, 0600) - if err != nil { - return fmt.Errorf("create: failed to write apc p15 key file (%s)", err) - } - app.stdLogger.Printf("create: apc p15 key file %s written to disk", keyFileName) - - // debug file ? - if app.config.debugLogging != nil && *app.config.debugLogging { - keyFileNameDebug := keyFileName + ".b64" - err = os.WriteFile(keyFileNameDebug, []byte(base64.StdEncoding.EncodeToString(keyFile)), 0600) - if err != nil { - return fmt.Errorf("create: failed to write apc p15 key file (%s)", err) - } - app.debugLogger.Printf("create: apc p15 key file %s written to disk", keyFileNameDebug) - } - } - return nil } diff --git a/pkg/app/config.go b/pkg/app/config.go index a3b18c1..e8ff1fc 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -29,7 +29,6 @@ type config struct { create struct { keyCertPemCfg outFilePath *string - makeKeyP15 *bool outKeyFilePath *string } install struct { @@ -74,7 +73,6 @@ func (app *app) getConfig(args []string) error { cfg.create.keyPem = createFlags.StringLong("keypem", "", "string of the rsa-1024 or rsa-2048 key in pem format") cfg.create.certPem = createFlags.StringLong("certpem", "", "string of the certificate in pem format") cfg.create.outFilePath = createFlags.StringLong("outfile", createDefaultOutFilePath, "path and filename to write the key+cert p15 file to") - cfg.create.makeKeyP15 = createFlags.BoolLong("keyp15", "create a second p15 file with just the private key") cfg.create.outKeyFilePath = createFlags.StringLong("outkeyfile", createDefaultOutKeyFilePath, "path and filename to write the key p15 file to") createCmd := &ff.Command{ From 12c613f3b4371d7975b65049dc5b5db054ae32e0 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Thu, 6 Jun 2024 22:51:13 -0400 Subject: [PATCH 13/54] apcssh: remove logging For sanity and consistency, centralize logging in the app with the app's loggers. --- pkg/apcssh/client.go | 8 +------- pkg/app/cmd_install.go | 5 +++++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pkg/apcssh/client.go b/pkg/apcssh/client.go index 33b01d5..4e7f1d3 100644 --- a/pkg/apcssh/client.go +++ b/pkg/apcssh/client.go @@ -4,9 +4,7 @@ import ( "crypto/sha256" "encoding/base64" "encoding/hex" - "errors" "fmt" - "log" "net" "runtime" "strings" @@ -57,10 +55,7 @@ func New(cfg *Config) (*Client, error) { // check for fingerprint match (b64 or hex) if actualHashB64 != cfg.ServerFingerprint && actualHashHex != cfg.ServerFingerprint { - log.Printf("apcssh: remote server key fingerprint (b64): %s", actualHashB64) - log.Printf("apcssh: remote server key fingerprint (hex): %s", actualHashHex) - - return errors.New("apcssh: fingerprint didn't match") + return fmt.Errorf("apcssh: server returned wrong fingerprint (b64: %s ; hex: %s)", actualHashB64, actualHashHex) } return nil @@ -86,7 +81,6 @@ func New(cfg *Config) (*Client, error) { // insecure cipher options? if cfg.InsecureCipher { - log.Println("WARNING: insecure ciphers are enabled (--insecurecipher). SSH with an insecure cipher is NOT secure and should NOT be used.") ciphers = append(ciphers, "aes128-cbc", "3des-cbc") } diff --git a/pkg/app/cmd_install.go b/pkg/app/cmd_install.go index f4b8a22..3cdf2cf 100644 --- a/pkg/app/cmd_install.go +++ b/pkg/app/cmd_install.go @@ -51,6 +51,11 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { return err } + // log warning if insecure cipher + if app.config.install.insecureCipher != nil && *app.config.install.insecureCipher { + app.stdLogger.Println("WARNING: install: insecure ciphers are enabled (--insecurecipher). SSH with an insecure cipher is NOT secure and should NOT be used.") + } + // make APC SSH client cfg := &apcssh.Config{ Hostname: *app.config.install.hostAndPort, From 579419ae31ce264bfb470f3aa8894516fdca8c3e Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Thu, 6 Jun 2024 22:51:13 -0400 Subject: [PATCH 14/54] cmd: remove cmd done log msgs remove these unncessary log messages because it says done before any returned error (which could imply it didn't error) --- pkg/app/cmd_create.go | 3 --- pkg/app/cmd_install.go | 3 --- 2 files changed, 6 deletions(-) diff --git a/pkg/app/cmd_create.go b/pkg/app/cmd_create.go index 1a6c88a..77f13ee 100644 --- a/pkg/app/cmd_create.go +++ b/pkg/app/cmd_create.go @@ -15,9 +15,6 @@ const ( // cmdCreate is the app's command to create an apc p15 file from key and cert // pem files func (app *app) cmdCreate(_ context.Context, args []string) error { - // done - defer app.stdLogger.Println("create: done") - // extra args == error if len(args) != 0 { return fmt.Errorf("create: failed, %w (%d)", ErrExtraArgs, len(args)) diff --git a/pkg/app/cmd_install.go b/pkg/app/cmd_install.go index 3cdf2cf..fa310b7 100644 --- a/pkg/app/cmd_install.go +++ b/pkg/app/cmd_install.go @@ -10,9 +10,6 @@ import ( // cmdInstall is the app's command to create apc p15 file content from key and cert // pem files and upload the p15 to the specified APC UPS func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { - // done - defer app.stdLogger.Println("install: done") - // extra args == error if len(args) != 0 { return fmt.Errorf("install: failed, %w (%d)", ErrExtraArgs, len(args)) From 67503e663672f786573462f5b02c2566dba5b9de Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Thu, 6 Jun 2024 22:51:13 -0400 Subject: [PATCH 15/54] v0.5.0-preview2 --- CHANGELOG.md | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1963c3..7217ba9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,27 +1,39 @@ # APC P15 Tool Changelog -## [v0.5.0] - 2024-06-04 +## [v0.5.0] - 2024-06-06 -Add functionality to additionally output a key.p15 file. This file -format matches that of APC's NMC Security Wizard's key file output. -(Use `create` and flag `--keyp15`.) +-- Preview Build 2 -- + +Add additional output of a key.p15 file. This file format matches +that of APC's NMC Security Wizard's key file output. + +Add functionality to `install` command to leverage the native `ssl` +command if the UPS device supports it. This should be applicable +to newer devices such as NMC3 on newer firmwares. The tool auto +selects the correct install method. Note: There may still be some +devices that don't work with the install function. I can only test +the one piece of hardware I have. If you have issues, try updating +your device's firmware first. Add creation of additional base64 encoded files when the `--debug` flag is used with `create`. This makes it easier to paste these encoded files into an ASN1 viewer to analyze them. -Modify output file permissions to `0666` instead of `0777`. +Modify output file permissions to `0600` instead of `0777`. Update Go version to 1.22.3. + ## [v0.4.2] - 2024-03-29 Fix usage message. Thanks @k725. + ## [v0.4.1] - 2024-03-06 Update to Go 1.22.1, which includes some security fixes. + ## [v0.4.0] - 2024-02-05 Add `--restartwebui` flag to issue a reboot command to the webui @@ -29,6 +41,7 @@ after a new certificate is installed. This was not needed with my NMC2, but I suspect some might need it to get the new certificate to actually load. + ## [v0.3.3] - 2024-02-04 Add `--insecurecipher` flag to enable aes128-cbc and 3des-cbc for @@ -36,6 +49,7 @@ older devices/firmwares. These ciphers are considered insecure and should be avoided. A better alternative is to update the device firmware if possible. + ## [v0.3.2] - 2024-02-04 Add support for 1,024 bit RSA keys. These are not recommended! RSA @@ -45,11 +59,13 @@ Add `diffie-hellman-group-exchange-sha256` key exchange algorithm which may be needed by some UPSes to connect via SSH to use the install command. + ## [v0.3.1] - 2024-02-03 Fixes debug logging always being on. App now accurately reflects the state of the --debug flag. + ## [v0.3.0] - 2024-02-03 Initial release. From a47dd3fb68a28962a3ffcc17c674882295b06052 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Thu, 6 Jun 2024 22:51:14 -0400 Subject: [PATCH 16/54] go: update to 1.22.4 --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 2ae4f6a..ad1bc05 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module apc-p15-tool -go 1.22.3 +go 1.22.4 require ( github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 From c669621bd3f275a864fb7f7444697d837d1016d3 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 18 Jun 2024 21:30:38 -0400 Subject: [PATCH 17/54] install: add ssh connect log message --- pkg/app/cmd_install.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/app/cmd_install.go b/pkg/app/cmd_install.go index fa310b7..b02d112 100644 --- a/pkg/app/cmd_install.go +++ b/pkg/app/cmd_install.go @@ -66,6 +66,7 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { if err != nil { return fmt.Errorf("install: failed to connect to host (%w)", err) } + app.stdLogger.Println("install: connected to ups ssh, installing ssl key and cert...") // install SSL Cert err = client.InstallSSLCert(keyP15, certPem, keyCertP15) From 7bf70c4d7152f5e20ffa89698e90570a9d23023f Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 18 Jun 2024 21:30:39 -0400 Subject: [PATCH 18/54] ssh: switch string comps to EqualFold func --- pkg/apcssh/cmd_restartwebui.go | 2 +- pkg/apcssh/ssl.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/apcssh/cmd_restartwebui.go b/pkg/apcssh/cmd_restartwebui.go index 4b904c1..bde8d3a 100644 --- a/pkg/apcssh/cmd_restartwebui.go +++ b/pkg/apcssh/cmd_restartwebui.go @@ -16,7 +16,7 @@ func (cli *Client) RestartWebUI() error { return err } - if strings.ToLower(result.code) != "e000" { + if !strings.EqualFold(result.code, "e000") { return fmt.Errorf("apcssh: failed to restart web ui (%s: %s)", result.code, result.codeText) } diff --git a/pkg/apcssh/ssl.go b/pkg/apcssh/ssl.go index c3fac28..233ea38 100644 --- a/pkg/apcssh/ssl.go +++ b/pkg/apcssh/ssl.go @@ -15,7 +15,7 @@ func (cli *Client) InstallSSLCert(keyP15 []byte, certPem []byte, keyCertP15 []by return fmt.Errorf("apcssh: ssl cert install: failed to send ssl cmd (%w)", err) } // E101 is the code for "Command Not Found" - supportsSSLCmd := strings.ToLower(result.code) != "e101" + supportsSSLCmd := !strings.EqualFold(result.code, "e101") // if SSL is supported, use that method if supportsSSLCmd { @@ -46,7 +46,7 @@ func (cli *Client) installSSLCertModern(keyP15 []byte, certPem []byte) error { if err != nil { return fmt.Errorf("apcssh: ssl cert install: failed to send ssl key install cmd (%w)", err) } - if strings.ToLower(result.code) != "e000" { + if !strings.EqualFold(result.code, "e000") { return fmt.Errorf("apcssh: ssl cert install: ssl key install cmd returned error code (%s: %s)", result.code, result.codeText) } @@ -54,7 +54,7 @@ func (cli *Client) installSSLCertModern(keyP15 []byte, certPem []byte) error { if err != nil { return fmt.Errorf("apcssh: ssl cert install: failed to send ssl cert install cmd (%w)", err) } - if strings.ToLower(result.code) != "e000" { + if !strings.EqualFold(result.code, "e000") { return fmt.Errorf("apcssh: ssl cert install: ssl cert install cmd returned error code (%s: %s)", result.code, result.codeText) } From 208827f6360e7874f84355524af37c06f064e7b6 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 18 Jun 2024 21:30:40 -0400 Subject: [PATCH 19/54] ssh: fix shell regex * from ssh videos I found on youtube, the @ symbol might not be present in prompt, so make it optional * fix typo of 0-0 instead of 0-9 (all numbers are possible in the prompt) --- pkg/apcssh/shell_helpers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/apcssh/shell_helpers.go b/pkg/apcssh/shell_helpers.go index e2084cb..7c36c60 100644 --- a/pkg/apcssh/shell_helpers.go +++ b/pkg/apcssh/shell_helpers.go @@ -11,8 +11,8 @@ func scanAPCShell(data []byte, atEOF bool) (advance int, token []byte, err error return 0, nil, nil } - // regex for shell prompt (e.g., `apc@apc>`) - re := regexp.MustCompile(`(\r\n|\r|\n)[A-Za-z0-0.]+@[A-Za-z0-0.]+>`) + // regex for shell prompt (e.g., `apc@apc>`, `apc>`, `some@dev>`, `other123>`, etc.) + re := regexp.MustCompile(`(\r\n|\r|\n)([A-Za-z0-9.]+@?)?[A-Za-z0-9.]+>`) // find match for prompt if index := re.FindStringIndex(string(data)); index != nil { From b94e17e8f33e499989f021a976960111e372c192 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 18 Jun 2024 21:30:41 -0400 Subject: [PATCH 20/54] readme: update info regarding insecure ssh ciphers --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 119a724..fb1cae5 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,12 @@ setup) is: If you have problems you can post the log in an issue and I can try to fix it but it may be difficult without your particular hardware to test with. -In particular, if you are experiencing `ssh: handshake failed:` please run -`ssh -vv myups.example.com` and include the `peer server KEXINIT proposal` -in your issue. For example: +In particular, if you are experiencing `ssh: handshake failed:` first try +using the `--insecurecipher` flag. If this works, you should upgrade your +NMC to a newer firmware which includes secure ciphers. You should NOT automate +your environment using this flag as SSH over these ciphers is broken and +exploitable. If this also does not work, please run `ssh -vv myups.example.com` +and include the `peer server KEXINIT proposal` in your issue. For example: ``` debug2: peer server KEXINIT proposal From d3ad01da0c2f3b6483bf86cb0bbf777844e96104 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 18 Jun 2024 21:30:42 -0400 Subject: [PATCH 21/54] build: fix typo for windows install only file --- build.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.ps1 b/build.ps1 index 3fee1f4..7147a33 100644 --- a/build.ps1 +++ b/build.ps1 @@ -11,7 +11,7 @@ go build -o $outDir/apc-p15-tool-amd64.exe ./cmd/tool $env:GOARCH = "amd64" $env:GOOS = "windows" $env:CGO_ENABLED = 0 -go build -o $outDir/apc-p15-install-amd64.exe ./cmd/tool +go build -o $outDir/apc-p15-install-amd64.exe ./cmd/install_only # Linux x64 $env:GOARCH = "amd64" From 04307eff17a9722a4f2f11da2a2bfe919fcf3e58 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 18 Jun 2024 21:30:43 -0400 Subject: [PATCH 22/54] readme: update general info about tool and compatibility --- README.md | 91 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index fb1cae5..b41e8f3 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,78 @@ # APC P15 Tool -A tool to create APC p15 formatted certificates from pem files, without -having to use APC's closed-source tool, APC generated keys, or other -proprietary tools (such as cryptlib). + +APC P15 Tool is a completely open source application designed to make +creating and installing SSL certificates on APC (Schneider Electric) +Network Management Cards (2 & 3) simple and easy to do. It is also +designed to simplify automation of the certificate management lifecycle. + +## Background + +When APC created the NMC2 (Network Management Card 2), they chose to use +the p15 file format for their SSL keys and certificates, which is a +relatively obscure file format. In addition to this, they designed the +device to require an APC specific header be prepended to the p15 file +or the file would be rejected by the device. Accordingly, they created +a proprietary tool (the `NMC Security Wizard CLI Utility`) to generate +the required format. + +Unfortunately, the proprietary tool has a number of shortcomings: +- It can be difficult to find the right version to use. APC has released + a number of versions (in both a CLI and GUI form). Not all of the + versions worked correctly (or at all). +- User provided private keys are not supported. Private keys must be + generated by the proprietary tool and are only outputted in the p15 + format. APC's proprietary tool is closed source and as such there is + no way to audit the key generation process. +- Since the generated keys are in the p15 format, they can't be loaded + easily into other management tools (such as Cert Warden + https://www.certwarden.com/), nor can CSRs be generated easily + outside of the proprietary tool. The proprietary tool is generally + required to generate the CSR. +- The CSR generation function in the proprietary tool is fairly rigid, + making customization (e.g., multiple DNS names) difficult, if not + impossible. +- After the user generates a key, generates a CSR, sends that CSR to + their CA, and receives a certificate back, they're still not done. + The tool must be used again to generate the final p15 file for the + NMC. +- To install the final file on the NMC, the user must use an SCP + program such as `pscp` to install the file, or the NMC's web UI. + +Due to all of this, others have tried to recreate the proprietary +functionality. The only implementations I have found rely on a closed +source library called `cryptlib`. This library has evolved over time +and more recent versions do not work for the NMC (it appears at some +point cryptlib switched from 3DES to AES and NMC does not support +AES within the p15 file). It was also near impossible to find an old +enough version of cryptlib that would work. Even if one gets this +working, it does not resolve the obscurity of a closed source +implementation and would continue to be subject to potential future +breakage as the cryptlib library continues to evolve. + +This project aims to solve all of these problems by accepting the most +common key and cert file format (PEM) and by being 100% open source +and licensed under the GPL-3.0 license. ## Compatibility Notice -This tool's create functionality is modeled from the APC NMCSecurityWizardCLI -aka `NMC Security Wizard CLI Utility`. The files it generates should be -comaptible with any UPS that accepts p15 files from that tool. Only RSA 1,024 -and 2,048 bit keys are accepted. 1,024 bit RSA is no longer considered -completely secure; avoid keys of this size if possible. Most (all?) public -ACME services won't accept keys of this size anyway. +Both NMC2 and NMC3 devices should be fully supported. However, I have one +NMC2 device in a home lab and have no way to guarantee success in all cases. -The install functionality is a custom creation of mine so it may or may not -work depending on your exact setup. My setup (and therefore the testing -setup) is: +Only RSA 1,024 and 2,048 bit keys are accepted. 1,024 bit RSA is no longer +considered completely secure; avoid keys of this size if possible. Most +(all?) public ACME services won't accept keys of this size anyway. + +Even though later versions of the NMC3 firmware supports RSA 4,096 and +ECDSA keys, this tool does not. These options were not available in APC's +proprietary tool, and as such I have no way to generate files to reverse +engineer. + +My setup (and therefore the testing setup) is: - APC Smart-UPS 1500VA RM 2U SUA1500RM2U (Firmware Revision 667.18.D) - AP9631 NMC2 Hardware Revision 05 running AOS v7.0.4 and Boot Monitor v1.0.9. -If you have problems you can post the log in an issue and I can try to fix it +If you have problems, please post the log in an issue and I can try to fix it but it may be difficult without your particular hardware to test with. In particular, if you are experiencing `ssh: handshake failed:` first try @@ -62,14 +115,16 @@ content. e.g. `./apc-p15-tool create --keyfile ./apckey.pem --certfile ./apccert.pem` -The command outputs ./apctool.p15 by default. This file can be -directly loaded on to an APC NMC2 (Network Management Card 2). +The command creates and outputs ./apctool.p15 and ./apctool.key.p15 by +default. These files are equivelant to the key and final p15 files +generated by APC's proprietary tool. ### Install -Install works similarly to create except it doesn't save the p15 file -to disk. It instead uploads the p15 file directly to the specified -remote host, via scp. +Install generates the necessary p15 file(s) but does NOT save them to +disk. It instead installs the files directly on the NMC. Logic +automatically deduces if the device is an NMC2 or NMC3 and performs +the appropriate installation steps. e.g. `./apc-p15-tool install --keyfile ./apckey.pem --certfile ./apccert.pem --apchost myapc.example.com:22 --username apc --password someSecret --fingerprint 123abc` From f1dd079632d8d134948ce969f81269f255ca4b8b Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 18 Jun 2024 21:38:00 -0400 Subject: [PATCH 23/54] v0.5.1 --- CHANGELOG.md | 48 +++++++++++++++++++++++++++++++----------------- pkg/app/app.go | 2 +- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7217ba9..b2ab32c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,27 +1,41 @@ # APC P15 Tool Changelog -## [v0.5.0] - 2024-06-06 +## [v0.5.1] - 2024-06-18 --- Preview Build 2 -- +Both NMC2 and NMC3 should now be fully supported. -Add additional output of a key.p15 file. This file format matches -that of APC's NMC Security Wizard's key file output. +### Added +- Add proper NMC3 support. +- The `create` function now also generates a .p15 formatted key file. + The format of this file matches that of what is generated by the NMC + Security Wizard. +- Add additional b64 formatted output files when using the `--debug` + flag with `create`. These files can easily be pasted into an ASN1 + decoder for inspection (except for the header file, as the header is + not ASN1 encoded). -Add functionality to `install` command to leverage the native `ssl` -command if the UPS device supports it. This should be applicable -to newer devices such as NMC3 on newer firmwares. The tool auto -selects the correct install method. Note: There may still be some -devices that don't work with the install function. I can only test -the one piece of hardware I have. If you have issues, try updating -your device's firmware first. +### Fixed +- Fix `install` function for NMC3 on newer firmware version by + leveraging the native `ssl` command to install the key and cert, if + it is available. If not available, fallback to the 'old' way of + installing the SSL cert. +- Fix PowerShell build script in repo. Posted builds were not impacted + by this as the script is not used by the GitHub Action. -Add creation of additional base64 encoded files when the `--debug` -flag is used with `create`. This makes it easier to paste these -encoded files into an ASN1 viewer to analyze them. +### Changed +- Move APC SSH functions to a separate package and change how commands + are sent. In particular, leverage the interactive shell to send + commands and read back the result of those commands. +- Set output file permissions to `0600` instead of `0777`. +- Minor logging updates. +- Leverage `strings.EqualFold` as a more robust alternative to using + `strings.ToLower` for string comparisons. +- Update Go version to 1.22.4. +- Update readme to clarify tool's purpose, current state, and + compatibility. -Modify output file permissions to `0600` instead of `0777`. - -Update Go version to 1.22.3. +### Removed +N/A ## [v0.4.2] - 2024-03-29 diff --git a/pkg/app/app.go b/pkg/app/app.go index 5cd9ebe..b02d805 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -12,7 +12,7 @@ import ( ) const ( - appVersion = "0.5.0" + appVersion = "0.5.1" ) // struct for receivers to use common app pieces From 841a459dcad3dbc6bd9e207cbd02797a3fa290d3 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Wed, 19 Jun 2024 19:56:16 -0400 Subject: [PATCH 24/54] apcssh: minor log and logic clarity --- pkg/apcssh/cmd_restartwebui.go | 6 ++---- pkg/apcssh/scp.go | 2 +- pkg/apcssh/ssl.go | 8 +++----- pkg/app/cmd_install.go | 2 +- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/pkg/apcssh/cmd_restartwebui.go b/pkg/apcssh/cmd_restartwebui.go index bde8d3a..5196212 100644 --- a/pkg/apcssh/cmd_restartwebui.go +++ b/pkg/apcssh/cmd_restartwebui.go @@ -13,10 +13,8 @@ import ( func (cli *Client) RestartWebUI() error { result, err := cli.cmd("reboot -Y") if err != nil { - return err - } - - if !strings.EqualFold(result.code, "e000") { + return fmt.Errorf("apcssh: failed to restart web ui (%w)", err) + } else if !strings.EqualFold(result.code, "e000") { return fmt.Errorf("apcssh: failed to restart web ui (%s: %s)", result.code, result.codeText) } diff --git a/pkg/apcssh/scp.go b/pkg/apcssh/scp.go index 9db77c0..a37d154 100644 --- a/pkg/apcssh/scp.go +++ b/pkg/apcssh/scp.go @@ -19,7 +19,7 @@ func (cli *Client) UploadSCP(destination string, fileContent []byte, filePermiss // connect sshClient, err := ssh.Dial("tcp", cli.hostname, cli.sshCfg) if err != nil { - return fmt.Errorf("apcssh: scp: failed to dial session (%w)", err) + return fmt.Errorf("apcssh: scp: failed to dial client (%w)", err) } defer sshClient.Close() diff --git a/pkg/apcssh/ssl.go b/pkg/apcssh/ssl.go index 233ea38..2eb3bce 100644 --- a/pkg/apcssh/ssl.go +++ b/pkg/apcssh/ssl.go @@ -12,7 +12,7 @@ func (cli *Client) InstallSSLCert(keyP15 []byte, certPem []byte, keyCertP15 []by // run `ssl` command to check if it exists result, err := cli.cmd("ssl") if err != nil { - return fmt.Errorf("apcssh: ssl cert install: failed to send ssl cmd (%w)", err) + return fmt.Errorf("apcssh: ssl cert install: failed to test ssl cmd (%w)", err) } // E101 is the code for "Command Not Found" supportsSSLCmd := !strings.EqualFold(result.code, "e101") @@ -45,16 +45,14 @@ func (cli *Client) installSSLCertModern(keyP15 []byte, certPem []byte) error { result, err := cli.cmd("ssl key -i /ssl/nmc.key") if err != nil { return fmt.Errorf("apcssh: ssl cert install: failed to send ssl key install cmd (%w)", err) - } - if !strings.EqualFold(result.code, "e000") { + } else if !strings.EqualFold(result.code, "e000") { return fmt.Errorf("apcssh: ssl cert install: ssl key install cmd returned error code (%s: %s)", result.code, result.codeText) } result, err = cli.cmd("ssl cert -i /ssl/nmc.crt") if err != nil { return fmt.Errorf("apcssh: ssl cert install: failed to send ssl cert install cmd (%w)", err) - } - if !strings.EqualFold(result.code, "e000") { + } else if !strings.EqualFold(result.code, "e000") { return fmt.Errorf("apcssh: ssl cert install: ssl cert install cmd returned error code (%s: %s)", result.code, result.codeText) } diff --git a/pkg/app/cmd_install.go b/pkg/app/cmd_install.go index b02d112..d3270a4 100644 --- a/pkg/app/cmd_install.go +++ b/pkg/app/cmd_install.go @@ -71,7 +71,7 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { // install SSL Cert err = client.InstallSSLCert(keyP15, certPem, keyCertP15) if err != nil { - return fmt.Errorf("install: failed to send file to ups over scp (%w)", err) + return fmt.Errorf("install: %w", err) } // installed From 703c26bd271a37135198dc9583770d5338fe0045 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Wed, 19 Jun 2024 19:56:17 -0400 Subject: [PATCH 25/54] apcssh: add shell cmd timeout It was possible for scanner.Scan() to block indefinitely if the UPS never returned the expected prompt regex pattern. This could occur with a UPS using a prompt format I'm not aware of, or if the UPS responds in a non-standard way. This change ensures that Scan() is aborted after a fixed amount of blocking time and the shell cmd function accordingly returns an error. Some error messages, comments, and var names are also updated for clarity. --- pkg/apcssh/shell.go | 120 +++++++++++++++++++++++++----------- pkg/apcssh/shell_helpers.go | 16 ++--- 2 files changed, 92 insertions(+), 44 deletions(-) diff --git a/pkg/apcssh/shell.go b/pkg/apcssh/shell.go index 4643621..bb05639 100644 --- a/pkg/apcssh/shell.go +++ b/pkg/apcssh/shell.go @@ -2,14 +2,25 @@ package apcssh import ( "bufio" + "errors" "fmt" "strings" + "time" "golang.org/x/crypto/ssh" ) -// upsCmdResponse is a structure that holds all of a shell commands results -type upsCmdResponse struct { +// Abort shell connection if UPS doesn't send a recognizable response within +// the specified timeouts; Cmd timeout is very long as it is unlikely to be +// needed but still exists to avoid an indefinite hang in the unlikely event +// something does go wrong at that part of the app +const ( + shellTimeoutLogin = 20 * time.Second + shellTimeoutCmd = 5 * time.Minute +) + +// upsCmdResult is a structure that holds all of a shell commands results +type upsCmdResult struct { command string code string codeText string @@ -17,75 +28,112 @@ type upsCmdResponse struct { } // cmd creates an interactive shell and executes the specified command -func (cli *Client) cmd(command string) (*upsCmdResponse, error) { +func (cli *Client) cmd(command string) (*upsCmdResult, error) { // connect sshClient, err := ssh.Dial("tcp", cli.hostname, cli.sshCfg) if err != nil { - return nil, fmt.Errorf("apcssh: failed to dial session (%w)", err) + return nil, fmt.Errorf("failed to dial client (%w)", err) } defer sshClient.Close() session, err := sshClient.NewSession() if err != nil { - return nil, fmt.Errorf("apcssh: failed to create session (%w)", err) + return nil, fmt.Errorf("failed to create session (%w)", err) } defer session.Close() // pipes to send shell command to; and to receive repsonse sshInput, err := session.StdinPipe() if err != nil { - return nil, fmt.Errorf("apcssh: failed to make shell input pipe (%w)", err) + return nil, fmt.Errorf("failed to make shell input pipe (%w)", err) } sshOutput, err := session.StdoutPipe() if err != nil { - return nil, fmt.Errorf("apcssh: failed to make shell output pipe (%w)", err) + return nil, fmt.Errorf("failed to make shell output pipe (%w)", err) } - // make scanner to read shell continuously + // make scanner to read shell output continuously scanner := bufio.NewScanner(sshOutput) scanner.Split(scanAPCShell) // start interactive shell if err := session.Shell(); err != nil { - return nil, fmt.Errorf("apcssh: failed to start shell (%w)", err) + return nil, fmt.Errorf("failed to start shell (%w)", err) } - // discard the initial shell response (login message(s) / initial shell prompt) - for { - if token := scanner.Scan(); token { - _ = scanner.Bytes() - break + + // use a timer to close the session early in case Scan() hangs (which can + // happen if the UPS provides output this app does not understand) + cancelAbort := make(chan struct{}) + defer close(cancelAbort) + go func() { + select { + case <-time.After(shellTimeoutLogin): + _ = session.Close() + + case <-cancelAbort: + // aborted cancel (i.e., succesful Scan()) } + }() + + // check shell response after connect + scannedOk := scanner.Scan() + // if failed to scan (e.g., timer closed the session after timeout) + if !scannedOk { + return nil, errors.New("shell did not return parsable login response") } + // success; cancel abort timer + cancelAbort <- struct{}{} + // discard the initial shell response (login message(s) / initial shell prompt) + _ = scanner.Bytes() // send command _, err = fmt.Fprint(sshInput, command+"\n") if err != nil { - return nil, fmt.Errorf("apcssh: failed to send shell command (%w)", err) + return nil, fmt.Errorf("failed to send shell command (%w)", err) } - res := &upsCmdResponse{} - for { - if tkn := scanner.Scan(); tkn { - result := string(scanner.Bytes()) + // use a timer to close the session early in case Scan() hangs (which can + // happen if the UPS provides output this app does not understand); + // since initial login message Scan() was okay, it is relatively unlikely this + // will hang + go func() { + select { + case <-time.After(shellTimeoutCmd): + _ = session.Close() - cmdIndx := strings.Index(result, "\n") - res.command = result[:cmdIndx-1] - result = result[cmdIndx+1:] - - codeIndx := strings.Index(result, ": ") - res.code = result[:codeIndx] - result = result[codeIndx+2:] - - codeTxtIndx := strings.Index(result, "\n") - res.codeText = result[:codeTxtIndx-1] - - // avoid out of bounds if no result text - if codeTxtIndx+1 <= len(result)-2 { - res.resultText = result[codeTxtIndx+1 : len(result)-2] - } - break + case <-cancelAbort: + // aborted cancel (i.e., succesful Scan()) } + }() + + // check shell response to command + scannedOk = scanner.Scan() + // if failed to scan (e.g., timer closed the session after timeout) + if !scannedOk { + return nil, fmt.Errorf("shell did not return parsable response to cmd '%s'", command) + } + // success; cancel abort timer + cancelAbort <- struct{}{} + + // parse the UPS response into result struct and return + upsRawResponse := string(scanner.Bytes()) + result := &upsCmdResult{} + + cmdIndx := strings.Index(upsRawResponse, "\n") + result.command = upsRawResponse[:cmdIndx-1] + upsRawResponse = upsRawResponse[cmdIndx+1:] + + codeIndx := strings.Index(upsRawResponse, ": ") + result.code = upsRawResponse[:codeIndx] + upsRawResponse = upsRawResponse[codeIndx+2:] + + codeTxtIndx := strings.Index(upsRawResponse, "\n") + result.codeText = upsRawResponse[:codeTxtIndx-1] + + // avoid out of bounds if no result text + if codeTxtIndx+1 <= len(upsRawResponse)-2 { + result.resultText = upsRawResponse[codeTxtIndx+1 : len(upsRawResponse)-2] } - return res, nil + return result, nil } diff --git a/pkg/apcssh/shell_helpers.go b/pkg/apcssh/shell_helpers.go index 7c36c60..02c5e81 100644 --- a/pkg/apcssh/shell_helpers.go +++ b/pkg/apcssh/shell_helpers.go @@ -1,31 +1,31 @@ package apcssh import ( + "io" "regexp" ) // scanAPCShell is a SplitFunc to capture shell output after each interactive // shell command is run func scanAPCShell(data []byte, atEOF bool) (advance int, token []byte, err error) { - if atEOF && len(data) == 0 { + // EOF is not an expected response and should error (e.g., when the output pipe + // gets closed by timeout) + if atEOF { + return len(data), dropCR(data), io.ErrUnexpectedEOF + } else if len(data) == 0 { + // no data to process, request more data return 0, nil, nil } // regex for shell prompt (e.g., `apc@apc>`, `apc>`, `some@dev>`, `other123>`, etc.) re := regexp.MustCompile(`(\r\n|\r|\n)([A-Za-z0-9.]+@?)?[A-Za-z0-9.]+>`) - // find match for prompt if index := re.FindStringIndex(string(data)); index != nil { // advance starts after the prompt; token is everything before the prompt return index[1], dropCR(data[0:index[0]]), nil } - // If we're at EOF, we have a final, non-terminated line. Return it. - if atEOF { - return len(data), dropCR(data), nil - } - - // Request more data. + // no match, request more data return 0, nil, nil } From b7026ff9067f450e9685fd96722a20a90087e800 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Wed, 19 Jun 2024 19:57:56 -0400 Subject: [PATCH 26/54] v0.5.2 --- CHANGELOG.md | 6 ++++++ pkg/app/app.go | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2ab32c..39c9104 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # APC P15 Tool Changelog +## [v0.5.2] - 2024-06-19 + +Minor tweak to the previous version. Add timeout for shell +commands that don't execute as expected. + + ## [v0.5.1] - 2024-06-18 Both NMC2 and NMC3 should now be fully supported. diff --git a/pkg/app/app.go b/pkg/app/app.go index b02d805..d1f4d6d 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -12,7 +12,7 @@ import ( ) const ( - appVersion = "0.5.1" + appVersion = "0.5.2" ) // struct for receivers to use common app pieces From 06f9892501b9fb9c14b73fdd95a4d9ff8eae9301 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Mon, 24 Jun 2024 18:23:02 -0400 Subject: [PATCH 27/54] add rsa 3,072 bit support --- README.md | 13 +++++++------ pkg/pkcs15/pem_decode.go | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b41e8f3..541a046 100644 --- a/README.md +++ b/README.md @@ -58,14 +58,15 @@ and licensed under the GPL-3.0 license. Both NMC2 and NMC3 devices should be fully supported. However, I have one NMC2 device in a home lab and have no way to guarantee success in all cases. -Only RSA 1,024 and 2,048 bit keys are accepted. 1,024 bit RSA is no longer -considered completely secure; avoid keys of this size if possible. Most +Only RSA 1,024, 2,048, and 3,072 bit keys are accepted. 1,024 bit RSA is no +longer considered completely secure; avoid keys of this size if possible. Most (all?) public ACME services won't accept keys of this size anyway. -Even though later versions of the NMC3 firmware supports RSA 4,096 and -ECDSA keys, this tool does not. These options were not available in APC's -proprietary tool, and as such I have no way to generate files to reverse -engineer. +NMC2 does not officially support the 3,072 bit key size, however, it works fine +on my NMC2. If you use this size and it doesn't work on your NMC2, try a 2,048 +bit key instead. Later versions of the NMC3 firmware support RSA 4,096 and +ECDSA keys, but this tool does not. ECDSA was not included in APC's proprietary +tool, and as such I have no way to generate files to reverse engineer. My setup (and therefore the testing setup) is: - APC Smart-UPS 1500VA RM 2U SUA1500RM2U (Firmware Revision 667.18.D) diff --git a/pkg/pkcs15/pem_decode.go b/pkg/pkcs15/pem_decode.go index d4b8764..1fab7d0 100644 --- a/pkg/pkcs15/pem_decode.go +++ b/pkg/pkcs15/pem_decode.go @@ -13,7 +13,7 @@ var ( errPemKeyBadBlock = errors.New("pkcs15: pem key: failed to decode pem block") errPemKeyFailedToParse = errors.New("pkcs15: pem key: failed to parse key") errPemKeyWrongBlockType = errors.New("pkcs15: pem key: unsupported pem block type (only pkcs1 and pkcs8 supported)") - errPemKeyWrongType = errors.New("pkcs15: pem key: unsupported key type (only rsa 1,024 or 2,048 supported)") + errPemKeyWrongType = errors.New("pkcs15: pem key: unsupported key type (only rsa 1,024, 2,048, and 3,072 supported)") errPemCertBadBlock = errors.New("pkcs15: pem cert: failed to decode pem block") errPemCertFailedToParse = errors.New("pkcs15: pem cert: failed to parse cert") @@ -48,7 +48,7 @@ func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) { } // verify proper bitlen - if rsaKey.N.BitLen() != 1024 && rsaKey.N.BitLen() != 2048 { + if rsaKey.N.BitLen() != 1024 && rsaKey.N.BitLen() != 2048 && rsaKey.N.BitLen() != 3072 { return nil, errPemKeyWrongType } @@ -71,7 +71,7 @@ func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) { } // verify proper bitlen - if rsaKey.N.BitLen() != 1024 && rsaKey.N.BitLen() != 2048 { + if rsaKey.N.BitLen() != 1024 && rsaKey.N.BitLen() != 2048 && rsaKey.N.BitLen() != 3072 { return nil, errPemKeyWrongType } From 7c1ad8ef437840c387fe3bdc3e2ce97a138df529 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Mon, 24 Jun 2024 18:23:05 -0400 Subject: [PATCH 28/54] pkcs15: add some prep for maybe ec key support later --- pkg/pkcs15/keyid.go | 98 +++++++++++++++++++++++++-------------- pkg/pkcs15/pem_decode.go | 46 +++++++++++++----- pkg/pkcs15/pem_parse.go | 4 +- pkg/pkcs15/pem_to_p15.go | 55 +++++++++++++--------- pkg/pkcs15/private_key.go | 50 +++++++++++++------- 5 files changed, 167 insertions(+), 86 deletions(-) diff --git a/pkg/pkcs15/keyid.go b/pkg/pkcs15/keyid.go index 76f4297..68a3051 100644 --- a/pkg/pkcs15/keyid.go +++ b/pkg/pkcs15/keyid.go @@ -2,6 +2,7 @@ package pkcs15 import ( "apc-p15-tool/pkg/tools/asn1obj" + "crypto/rsa" "crypto/sha1" "encoding/binary" "math/big" @@ -11,22 +12,6 @@ import ( func (p15 *pkcs15KeyCert) keyId() []byte { // object to hash is just the RawSubjectPublicKeyInfo - // Create Object to hash - // hashObj := asn1obj.Sequence([][]byte{ - // asn1obj.Sequence([][]byte{ - // // Key is RSA - // asn1obj.ObjectIdentifier(asn1obj.OIDrsaEncryptionPKCS1), - // asn1.NullBytes, - // }), - // // BIT STRING of rsa key public key - // asn1obj.BitString( - // asn1obj.Sequence([][]byte{ - // asn1obj.Integer(p15.key.N), - // asn1obj.Integer((big.NewInt(int64(p15.key.E)))), - // }), - // ), - // }) - // SHA-1 Hash hasher := sha1.New() _, err := hasher.Write(p15.cert.RawSubjectPublicKeyInfo) @@ -124,18 +109,28 @@ func (p15 *pkcs15KeyCert) keyIdInt7() []byte { } // keyIdInt8 returns the sequence for keyId with INT val of 8; This value is equivelant -// to "pgp", which is PGP v3 key Id. This value is just the last 8 bytes of the public -// key N value +// to "pgp", which is PGP v3 key Id. func (p15 *pkcs15KeyCert) keyIdInt8() []byte { - nBytes := p15.key.N.Bytes() + var keyIdVal []byte + + switch privKey := p15.key.(type) { + case *rsa.PrivateKey: + // RSA: The ID value is just the last 8 bytes of the public key N value + nBytes := privKey.N.Bytes() + keyIdVal = nBytes[len(nBytes)-8:] + + default: + // panic if non-RSA key + panic("key id 8 for non-rsa key is unexpected and unsupported") + } // object to return - obj := asn1obj.Sequence([][]byte{ + idObj := asn1obj.Sequence([][]byte{ asn1obj.Integer(big.NewInt(8)), - asn1obj.OctetString(nBytes[len(nBytes)-8:]), + asn1obj.OctetString(keyIdVal), }) - return obj + return idObj } // bigIntToMpi returns the MPI (as defined in RFC 4880 s 3.2) from a given @@ -156,33 +151,64 @@ func (p15 *pkcs15KeyCert) keyIdInt9() []byte { // Public-Key packet starting with the version field. The Key ID is the // low-order 64 bits of the fingerprint. - // the entire Public-Key packet + // first make the public key packet publicKeyPacket := []byte{} // starting with the version field (A one-octet version number (4)). publicKeyPacket = append(publicKeyPacket, byte(4)) // A four-octet number denoting the time that the key was created. - time := make([]byte, 4) - // NOTE: use cert validity start as proxy for key creation since key pem // doesn't actually contain a created at time -- in reality notBefore tends // to be ~ 1 hour ish BEFORE the cert was even created. Key would also // obviously have to be created prior to the cert creation. + time := make([]byte, 4) binary.BigEndian.PutUint32(time, uint32(p15.cert.NotBefore.Unix())) publicKeyPacket = append(publicKeyPacket, time...) - // A one-octet number denoting the public-key algorithm of this key. - // 1 - RSA (Encrypt or Sign) [HAC] - publicKeyPacket = append(publicKeyPacket, byte(1)) + // the next part is key type specific + switch privKey := p15.key.(type) { + case *rsa.PrivateKey: + // A one-octet number denoting the public-key algorithm of this key. + // 1 - RSA (Encrypt or Sign) [HAC] + publicKeyPacket = append(publicKeyPacket, byte(1)) - // Algorithm-Specific Fields for RSA public keys: - // multiprecision integer (MPI) of RSA public modulus n - publicKeyPacket = append(publicKeyPacket, bigIntToMpi(p15.key.N)...) + // Algorithm-Specific Fields for RSA public keys: + // multiprecision integer (MPI) of RSA public modulus n + publicKeyPacket = append(publicKeyPacket, bigIntToMpi(privKey.N)...) - // MPI of RSA public encryption exponent e - e := big.NewInt(int64(p15.key.PublicKey.E)) - publicKeyPacket = append(publicKeyPacket, bigIntToMpi(e)...) + // MPI of RSA public encryption exponent e + e := big.NewInt(int64(privKey.PublicKey.E)) + publicKeyPacket = append(publicKeyPacket, bigIntToMpi(e)...) + + // case *ecdsa.PrivateKey: + // // A one-octet number denoting the public-key algorithm of this key. + // // 19 - ECDSA public key algorithm (see rfc 6637 s. 5) + // publicKeyPacket = append(publicKeyPacket, uint8(19)) + + // // Algorithm-Specific Fields for ECDSA public keys (see rfc 6637 s. 11 table) + // // This is a length byte followed by the curve ID (length is the number of bytes the curve ID uses) + // switch privKey.Curve.Params().Name { + // case "P-256": + // // 1.2.840.10045.3.1.7 8 2A 86 48 CE 3D 03 01 07 NIST curve P-256 + // publicKeyPacket = append(publicKeyPacket, byte(8)) + // hex, _ := hex.DecodeString("2A8648CE3D030107") + // publicKeyPacket = append(publicKeyPacket, hex...) + + // case "P-384": + // // 1.3.132.0.34 5 2B 81 04 00 22 NIST curve P-384 + // publicKeyPacket = append(publicKeyPacket, byte(5)) + // hex, _ := hex.DecodeString("2B81040022") + // publicKeyPacket = append(publicKeyPacket, hex...) + + // default: + // panic(fmt.Sprintf("key id 9 for ecdsa key curve %s is unexpected and unsupported", privKey.Curve.Params().Name)) + // } + + default: + // panic if non-RSA key + panic("key id 9 for non-rsa key is unexpected and unsupported") + } // Assemble the V4 byte array that will be hashed // 0x99 (1 octet) @@ -205,10 +231,10 @@ func (p15 *pkcs15KeyCert) keyIdInt9() []byte { keyId := sha1Hash[len(sha1Hash)-8:] // object to return - obj := asn1obj.Sequence([][]byte{ + idObj := asn1obj.Sequence([][]byte{ asn1obj.Integer(big.NewInt(9)), asn1obj.OctetString(keyId), }) - return obj + return idObj } diff --git a/pkg/pkcs15/pem_decode.go b/pkg/pkcs15/pem_decode.go index 1fab7d0..66a34b7 100644 --- a/pkg/pkcs15/pem_decode.go +++ b/pkg/pkcs15/pem_decode.go @@ -1,12 +1,14 @@ package pkcs15 import ( + "crypto" "crypto/rsa" "crypto/tls" "crypto/x509" "encoding/pem" "errors" "fmt" + "reflect" ) var ( @@ -22,7 +24,7 @@ var ( // pemKeyDecode attempts to decode a pem encoded byte slice and then attempts // to parse an RSA private key from the decoded pem block. an error is returned // if any of these steps fail OR if the key is not RSA and of bitlen 1,024 or 2,048 -func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) { +func pemKeyDecode(keyPem []byte) (crypto.PrivateKey, error) { // decode pemBlock, _ := pem.Decode([]byte(keyPem)) if pemBlock == nil { @@ -30,13 +32,11 @@ func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) { } // parsing depends on block type - var rsaKey *rsa.PrivateKey + var privateKey crypto.PrivateKey switch pemBlock.Type { case "RSA PRIVATE KEY": // PKCS1 - var err error - - rsaKey, err = x509.ParsePKCS1PrivateKey(pemBlock.Bytes) + rsaKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes) if err != nil { return nil, errPemKeyFailedToParse } @@ -53,6 +53,22 @@ func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) { } // good to go + privateKey = rsaKey + + // case "EC PRIVATE KEY": // SEC1, ASN.1 + // var ecdKey *ecdsa.PrivateKey + // ecdKey, err := x509.ParseECPrivateKey(pemBlock.Bytes) + // if err != nil { + // return nil, errPemKeyFailedToParse + // } + + // // verify acceptable curve name + // if ecdKey.Curve.Params().Name != "P-256" && ecdKey.Curve.Params().Name != "P-384" { + // return nil, errPemKeyWrongType + // } + + // // good to go + // privateKey = ecdKey case "PRIVATE KEY": // PKCS8 pkcs8Key, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes) @@ -62,20 +78,28 @@ func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) { switch pkcs8Key := pkcs8Key.(type) { case *rsa.PrivateKey: - rsaKey = pkcs8Key - // basic sanity check - err = rsaKey.Validate() + err = pkcs8Key.Validate() if err != nil { return nil, fmt.Errorf("pkcs15: pem key: failed sanity check (%s)", err) } // verify proper bitlen - if rsaKey.N.BitLen() != 1024 && rsaKey.N.BitLen() != 2048 && rsaKey.N.BitLen() != 3072 { + if pkcs8Key.N.BitLen() != 1024 && pkcs8Key.N.BitLen() != 2048 && pkcs8Key.N.BitLen() != 3072 { return nil, errPemKeyWrongType } // good to go + privateKey = pkcs8Key + + // case *ecdsa.PrivateKey: + // // verify acceptable curve name + // if pkcs8Key.Curve.Params().Name != "P-256" && pkcs8Key.Curve.Params().Name != "P-384" { + // return nil, errPemKeyWrongType + // } + + // // good to go + // privateKey = pkcs8Key default: return nil, errPemKeyWrongType @@ -86,12 +110,12 @@ func pemKeyDecode(keyPem []byte) (*rsa.PrivateKey, error) { } // if rsaKey is nil somehow, error - if rsaKey == nil { + if reflect.ValueOf(privateKey).IsNil() { return nil, errors.New("pkcs15: pem key: rsa key unexpectedly nil (report bug to project repo)") } // success! - return rsaKey, nil + return privateKey, nil } // pemCertDecode attempts to decode a pem encoded byte slice and then attempts diff --git a/pkg/pkcs15/pem_parse.go b/pkg/pkcs15/pem_parse.go index dcd899a..2cd1fea 100644 --- a/pkg/pkcs15/pem_parse.go +++ b/pkg/pkcs15/pem_parse.go @@ -1,14 +1,14 @@ package pkcs15 import ( - "crypto/rsa" + "crypto" "crypto/x509" ) // pkcs15KeyCert holds the data for a key and certificate pair; it provides // various methods to transform pkcs15 data type pkcs15KeyCert struct { - key *rsa.PrivateKey + key crypto.PrivateKey cert *x509.Certificate } diff --git a/pkg/pkcs15/pem_to_p15.go b/pkg/pkcs15/pem_to_p15.go index 19392f2..c213ddb 100644 --- a/pkg/pkcs15/pem_to_p15.go +++ b/pkg/pkcs15/pem_to_p15.go @@ -2,6 +2,7 @@ package pkcs15 import ( "apc-p15-tool/pkg/tools/asn1obj" + "crypto/rsa" "encoding/asn1" "math/big" ) @@ -111,6 +112,38 @@ func (p15 *pkcs15KeyCert) toP15KeyCert(keyEnvelope []byte) (keyCert []byte, err // the APC tool uses when generating a new private key (Note: no header is used on // this file) func (p15 *pkcs15KeyCert) toP15Key(keyEnvelope []byte) (key []byte, err error) { + // create public key object + var pubKeyObj []byte + + switch privKey := p15.key.(type) { + case *rsa.PrivateKey: + pubKeyObj = asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.ObjectIdentifier(asn1obj.OIDrsaEncryptionPKCS1), + asn1.NullBytes, + }), + // RSAPublicKey SubjectPublicKeyInfo + asn1obj.BitString( + asn1obj.Sequence([][]byte{ + asn1obj.Integer(privKey.PublicKey.N), + asn1obj.Integer(big.NewInt(int64(privKey.PublicKey.E))), + }), + ), + }), + }), + // not 100% certain but appears to be rsa key byte len + asn1obj.Integer(big.NewInt(int64(privKey.PublicKey.N.BitLen() / 8))), + }), + }) + + default: + // panic if non-RSA key + panic("p15 key file for non-rsa key is unexpected and unsupported") + } + // private key object (slightly different than the key+cert format) privateKey := asn1obj.Sequence([][]byte{ // commonObjectAttributes - Label @@ -181,27 +214,7 @@ func (p15 *pkcs15KeyCert) toP15Key(keyEnvelope []byte) (key []byte, err error) { asn1obj.BitString([]byte{byte(0b01000000)}), }), - asn1obj.ExplicitCompound(1, [][]byte{ - asn1obj.Sequence([][]byte{ - asn1obj.ExplicitCompound(0, [][]byte{ - asn1obj.ExplicitCompound(1, [][]byte{ - asn1obj.Sequence([][]byte{ - asn1obj.ObjectIdentifier(asn1obj.OIDrsaEncryptionPKCS1), - asn1.NullBytes, - }), - // RSAPublicKey SubjectPublicKeyInfo - asn1obj.BitString( - asn1obj.Sequence([][]byte{ - asn1obj.Integer(p15.key.PublicKey.N), - asn1obj.Integer(big.NewInt(int64(p15.key.PublicKey.E))), - }), - ), - }), - }), - // not 100% certain but appears to be rsa key byte len - asn1obj.Integer(big.NewInt(int64(p15.key.PublicKey.N.BitLen() / 8))), - }), - }), + pubKeyObj, }), }), }), diff --git a/pkg/pkcs15/private_key.go b/pkg/pkcs15/private_key.go index 8c6b0fb..0321551 100644 --- a/pkg/pkcs15/private_key.go +++ b/pkg/pkcs15/private_key.go @@ -1,24 +1,42 @@ package pkcs15 -import "apc-p15-tool/pkg/tools/asn1obj" +import ( + "apc-p15-tool/pkg/tools/asn1obj" + "crypto/rsa" +) // privateKeyObject returns the ASN.1 representation of a private key func (p15 *pkcs15KeyCert) privateKeyObject() []byte { - // ensure all expected vals are available - p15.key.Precompute() + var privKeyObj []byte - pkey := asn1obj.Sequence([][]byte{ - // P - asn1obj.IntegerExplicitValue(3, p15.key.Primes[0]), - // Q - asn1obj.IntegerExplicitValue(4, p15.key.Primes[1]), - // Dp - asn1obj.IntegerExplicitValue(5, p15.key.Precomputed.Dp), - // Dq - asn1obj.IntegerExplicitValue(6, p15.key.Precomputed.Dq), - // Qinv - asn1obj.IntegerExplicitValue(7, p15.key.Precomputed.Qinv), - }) + switch privKey := p15.key.(type) { + case *rsa.PrivateKey: + privKey.Precompute() - return pkey + // ensure all expected vals are available + privKeyObj = asn1obj.Sequence([][]byte{ + // P + asn1obj.IntegerExplicitValue(3, privKey.Primes[0]), + // Q + asn1obj.IntegerExplicitValue(4, privKey.Primes[1]), + // Dp + asn1obj.IntegerExplicitValue(5, privKey.Precomputed.Dp), + // Dq + asn1obj.IntegerExplicitValue(6, privKey.Precomputed.Dq), + // Qinv + asn1obj.IntegerExplicitValue(7, privKey.Precomputed.Qinv), + }) + + // case *ecdsa.PrivateKey: + // // Only private piece is the integer D + // privKeyObj = asn1obj.Sequence([][]byte{ + // asn1obj.Integer(privKey.D), + // }) + + default: + // panic if non-RSA key + panic("private key object for non-rsa key is unexpected and unsupported") + } + + return privKeyObj } From 6363282a756e259b4ffcbfabbbfe02ad3a4f4080 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Mon, 24 Jun 2024 18:24:35 -0400 Subject: [PATCH 29/54] v0.5.3 --- CHANGELOG.md | 5 +++++ pkg/app/app.go | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39c9104..f205de6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # APC P15 Tool Changelog +## [v0.5.3] - 2024-06-24 + +Add 3,072 bit RSA key support. + + ## [v0.5.2] - 2024-06-19 Minor tweak to the previous version. Add timeout for shell diff --git a/pkg/app/app.go b/pkg/app/app.go index d1f4d6d..c228b20 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -12,7 +12,7 @@ import ( ) const ( - appVersion = "0.5.2" + appVersion = "0.5.3" ) // struct for receivers to use common app pieces From 371a2ecc30b5c1a90777bd89b9b686fd39f8e73e Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Wed, 26 Jun 2024 21:34:47 -0400 Subject: [PATCH 30/54] README: update testing setup Update firmware on my UPS, --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 541a046..5e47692 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ tool, and as such I have no way to generate files to reverse engineer. My setup (and therefore the testing setup) is: - APC Smart-UPS 1500VA RM 2U SUA1500RM2U (Firmware Revision 667.18.D) -- AP9631 NMC2 Hardware Revision 05 running AOS v7.0.4 and Boot Monitor +- AP9631 NMC2 Hardware Revision 05 running AOS v7.1.2 and Boot Monitor v1.0.9. If you have problems, please post the log in an issue and I can try to fix it From 9578fa26ce358ae7b663fbc7a440bb8008d9b684 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Mon, 1 Jul 2024 22:35:21 -0400 Subject: [PATCH 31/54] fix go build ver 1.22.4 --- .github/workflows/build_releases.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_releases.yml b/.github/workflows/build_releases.yml index d1a0d90..5114c97 100644 --- a/.github/workflows/build_releases.yml +++ b/.github/workflows/build_releases.yml @@ -8,7 +8,7 @@ on: env: GITHUB_REF: ${{ github.ref }} - GO_VERSION: '1.22.1' + GO_VERSION: '1.22.4' jobs: build-common: From b8e9a2338651d4c5878a1957dbdbe9a965330631 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Mon, 1 Jul 2024 22:35:26 -0400 Subject: [PATCH 32/54] v1.0.0 --- CHANGELOG.md | 7 +++++++ pkg/app/app.go | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f205de6..5e9c752 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # APC P15 Tool Changelog +## [v1.0.0] - 2024-07-01 + +First official stable release. + +Fixes Go version in Github action. + + ## [v0.5.3] - 2024-06-24 Add 3,072 bit RSA key support. diff --git a/pkg/app/app.go b/pkg/app/app.go index c228b20..d2d9767 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -12,7 +12,7 @@ import ( ) const ( - appVersion = "0.5.3" + appVersion = "1.0.0" ) // struct for receivers to use common app pieces From e2b5abc6249da55cae12b47ee332a4cefeb8c4f6 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 9 Jul 2024 19:16:59 -0400 Subject: [PATCH 33/54] readme: add beta notice --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 5e47692..42b8b30 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,14 @@ creating and installing SSL certificates on APC (Schneider Electric) Network Management Cards (2 & 3) simple and easy to do. It is also designed to simplify automation of the certificate management lifecycle. +## Help Needed from NMC3 Users! + +If you have an NMC3, please test the beta release (1.1.0-b). In particular, +please provide feedback if 4,092 bit RSA keys and EC keys of curve types +P-256, P-384, and P-521 work using the beta and your NMC3. + +see: https://github.com/gregtwallace/apc-p15-tool/issues/6 + ## Background When APC created the NMC2 (Network Management Card 2), they chose to use From 51e584740931de5d6b3c312c3cca0362bdc98d08 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 17 Sep 2024 18:44:33 -0400 Subject: [PATCH 34/54] go: update to 1.23.1 --- .github/workflows/build_releases.yml | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_releases.yml b/.github/workflows/build_releases.yml index 5114c97..5ff7f19 100644 --- a/.github/workflows/build_releases.yml +++ b/.github/workflows/build_releases.yml @@ -8,7 +8,7 @@ on: env: GITHUB_REF: ${{ github.ref }} - GO_VERSION: '1.22.4' + GO_VERSION: '1.23.1' jobs: build-common: diff --git a/go.mod b/go.mod index ad1bc05..11730be 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module apc-p15-tool -go 1.22.4 +go 1.23.1 require ( github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 From cbb831e0092942d074f7f1b66486a135654b202f Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 17 Sep 2024 18:44:33 -0400 Subject: [PATCH 35/54] add ecdsa key support and enable 4,092 RSA * apcssh: add descriptive error when required file(s) not passed * create: dont create key+cert file when key isn't supported by NMC2 * config: fix usage messages re: key types * p15 files: dont generate key+cert when it isn't needed (aka NMC2 doesn't support key) * pkcs15: pre-calculate envelope when making the p15 struct * pkcs15: omit key ID 8 & 9 from EC keys * pkcs15: update key decode logic * pkcs15: add key type value for easy determination of compatibility * pkcs15: add ec key support * pkcs15: separate functions for key and key+cert p15 files * update README see: https://github.com/gregtwallace/apc-p15-tool/issues/6 --- README.md | 33 ++- pkg/apcssh/ssl.go | 13 + pkg/app/cmd_create.go | 36 +-- pkg/app/config.go | 8 +- pkg/app/pem_to_p15.go | 57 +++-- pkg/pkcs15/encrypted_envelope.go | 31 ++- pkg/pkcs15/keyid.go | 39 +-- pkg/pkcs15/pem_decode.go | 66 ++--- pkg/pkcs15/pem_parse.go | 62 +++++ pkg/pkcs15/pem_to_p15.go | 412 ++++++++++++++++++++----------- pkg/pkcs15/private_key.go | 13 +- pkg/tools/asn1obj/oid.go | 4 + 12 files changed, 508 insertions(+), 266 deletions(-) diff --git a/README.md b/README.md index 42b8b30..27ff526 100644 --- a/README.md +++ b/README.md @@ -66,23 +66,36 @@ and licensed under the GPL-3.0 license. Both NMC2 and NMC3 devices should be fully supported. However, I have one NMC2 device in a home lab and have no way to guarantee success in all cases. -Only RSA 1,024, 2,048, and 3,072 bit keys are accepted. 1,024 bit RSA is no -longer considered completely secure; avoid keys of this size if possible. Most -(all?) public ACME services won't accept keys of this size anyway. +### Key Types and Sizes -NMC2 does not officially support the 3,072 bit key size, however, it works fine -on my NMC2. If you use this size and it doesn't work on your NMC2, try a 2,048 -bit key instead. Later versions of the NMC3 firmware support RSA 4,096 and -ECDSA keys, but this tool does not. ECDSA was not included in APC's proprietary -tool, and as such I have no way to generate files to reverse engineer. +NMC2: +- RSA 1,024, 2,048, 3,072* bit lengths. + +NMC3: +- RSA 1,024, 2,048, 3,072, and 4,092 bit lengths. +- ECDSA curves P-256, P-384, and P-521. + +* 3,072 bit length is not officially supported by my NMC2, but appears to work + fine. + +1,024 bit RSA is no longer considered completely secure; avoid keys of +this size if possible. Most (all?) public ACME services won't accept keys +of this size anyway. + +### General Troubleshooting My setup (and therefore the testing setup) is: - APC Smart-UPS 1500VA RM 2U SUA1500RM2U (Firmware Revision 667.18.D) - AP9631 NMC2 Hardware Revision 05 running AOS v7.1.2 and Boot Monitor v1.0.9. -If you have problems, please post the log in an issue and I can try to fix it -but it may be difficult without your particular hardware to test with. +If you have trouble, your first step should be to update your NMC's firmware. +Many issues with this tool will be resolved simply by updating to the newest +firmware. + +If you have a problem after that, please post the log in an issue and I can +try to fix it but it may be difficult without your particular hardware to +test with. In particular, if you are experiencing `ssh: handshake failed:` first try using the `--insecurecipher` flag. If this works, you should upgrade your diff --git a/pkg/apcssh/ssl.go b/pkg/apcssh/ssl.go index 2eb3bce..18bff70 100644 --- a/pkg/apcssh/ssl.go +++ b/pkg/apcssh/ssl.go @@ -1,10 +1,13 @@ package apcssh import ( + "errors" "fmt" "strings" ) +var errSSLMissingData = errors.New("apcssh: ssl cert install: cant install nil data (unsupported key/nmc version/nmc firmware combo?)") + // InstallSSLCert installs the specified p15 key and p15 cert files on the // UPS. It has logic to deduce if the NMC is a newer version (e.g., NMC3 with // newer firmware) and acts accordingly. @@ -29,6 +32,11 @@ func (cli *Client) InstallSSLCert(keyP15 []byte, certPem []byte, keyCertP15 []by // installSSLCertModern installs the SSL key and certificate using the UPS built-in // command `ssl`. This command is not present on older devices (e.g., NMC2) or firmwares. func (cli *Client) installSSLCertModern(keyP15 []byte, certPem []byte) error { + // fail if required data isn't present + if keyP15 == nil || len(keyP15) <= 0 || certPem == nil || len(certPem) <= 0 { + return errSSLMissingData + } + // upload the key P15 file err := cli.UploadSCP("/ssl/nmc.key", keyP15, 0600) if err != nil { @@ -63,6 +71,11 @@ func (cli *Client) installSSLCertModern(keyP15 []byte, certPem []byte) error { // them to a .p15 file on the UPS. This is used for older devices (e.g., NMC2) and // firmwares that do not support the `ssl` command. func (cli *Client) installSSLCertLegacy(keyCertP15 []byte) error { + // fail if required data isn't present + if keyCertP15 == nil || len(keyCertP15) <= 0 { + return errSSLMissingData + } + // upload/install keyCert P15 file err := cli.UploadSCP("/ssl/defaultcert.p15", keyCertP15, 0600) if err != nil { diff --git a/pkg/app/cmd_create.go b/pkg/app/cmd_create.go index 77f13ee..8791a81 100644 --- a/pkg/app/cmd_create.go +++ b/pkg/app/cmd_create.go @@ -51,11 +51,14 @@ func (app *app) cmdCreate(_ context.Context, args []string) error { } app.stdLogger.Printf("create: apc p15 key file %s written to disk", keyFileName) - err = os.WriteFile(keyCertFileName, apcKeyCertFile, 0600) - if err != nil { - return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) + // skip key+cert if it wasn't generated + if len(apcKeyCertFile) > 0 { + err = os.WriteFile(keyCertFileName, apcKeyCertFile, 0600) + if err != nil { + return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) + } + app.stdLogger.Printf("create: apc p15 key+cert file %s written to disk", keyCertFileName) } - app.stdLogger.Printf("create: apc p15 key+cert file %s written to disk", keyCertFileName) // if debug, write additional debug files (b64 format to make copy/paste into asn1 decoder // easy to do e.g., https://lapo.it/asn1js) @@ -67,19 +70,22 @@ func (app *app) cmdCreate(_ context.Context, args []string) error { } app.debugLogger.Printf("create: apc p15 key file %s written to disk", keyFileNameDebug) - keyCertFileNameDebug := keyCertFileName + ".noheader.b64" - err = os.WriteFile(keyCertFileNameDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[apcHeaderLen:])), 0600) - if err != nil { - return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) - } - app.debugLogger.Printf("create: apc p15 key+cert file %s written to disk", keyCertFileNameDebug) + // skip key+cert if it wasn't generated + if len(apcKeyCertFile) > 0 { + keyCertFileNameDebug := keyCertFileName + ".noheader.b64" + err = os.WriteFile(keyCertFileNameDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[apcHeaderLen:])), 0600) + if err != nil { + return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) + } + app.debugLogger.Printf("create: apc p15 key+cert file %s written to disk", keyCertFileNameDebug) - keyCertFileNameHeaderDebug := keyCertFileName + ".header.b64" - err = os.WriteFile(keyCertFileNameHeaderDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[:apcHeaderLen])), 0600) - if err != nil { - return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) + keyCertFileNameHeaderDebug := keyCertFileName + ".header.b64" + err = os.WriteFile(keyCertFileNameHeaderDebug, []byte(base64.StdEncoding.EncodeToString(apcKeyCertFile[:apcHeaderLen])), 0600) + if err != nil { + return fmt.Errorf("create: failed to write apc p15 key+cert file (%s)", err) + } + app.debugLogger.Printf("create: apc p15 key+cert file header %s written to disk", keyCertFileNameHeaderDebug) } - app.debugLogger.Printf("create: apc p15 key+cert file header %s written to disk", keyCertFileNameHeaderDebug) } diff --git a/pkg/app/config.go b/pkg/app/config.go index e8ff1fc..6ef840b 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -68,9 +68,9 @@ func (app *app) getConfig(args []string) error { // create -- subcommand createFlags := ff.NewFlagSet("create").SetParent(rootFlags) - cfg.create.keyPemFilePath = createFlags.StringLong("keyfile", "", "path and filename of the rsa-1024 or rsa-2048 key in pem format") + cfg.create.keyPemFilePath = createFlags.StringLong("keyfile", "", "path and filename of the key in pem format") cfg.create.certPemFilePath = createFlags.StringLong("certfile", "", "path and filename of the certificate in pem format") - cfg.create.keyPem = createFlags.StringLong("keypem", "", "string of the rsa-1024 or rsa-2048 key in pem format") + cfg.create.keyPem = createFlags.StringLong("keypem", "", "string of the key in pem format") cfg.create.certPem = createFlags.StringLong("certpem", "", "string of the certificate in pem format") cfg.create.outFilePath = createFlags.StringLong("outfile", createDefaultOutFilePath, "path and filename to write the key+cert p15 file to") cfg.create.outKeyFilePath = createFlags.StringLong("outkeyfile", createDefaultOutKeyFilePath, "path and filename to write the key p15 file to") @@ -88,9 +88,9 @@ func (app *app) getConfig(args []string) error { // install -- subcommand installFlags := ff.NewFlagSet("install").SetParent(rootFlags) - cfg.install.keyPemFilePath = installFlags.StringLong("keyfile", "", "path and filename of the rsa-1024 or rsa-2048 key in pem format") + cfg.install.keyPemFilePath = installFlags.StringLong("keyfile", "", "path and filename of the key in pem format") cfg.install.certPemFilePath = installFlags.StringLong("certfile", "", "path and filename of the certificate in pem format") - cfg.install.keyPem = installFlags.StringLong("keypem", "", "string of the rsa-1024 or rsa-2048 key in pem format") + cfg.install.keyPem = installFlags.StringLong("keypem", "", "string of the key in pem format") cfg.install.certPem = installFlags.StringLong("certpem", "", "string of the certificate in pem format") cfg.install.hostAndPort = installFlags.StringLong("apchost", "", "hostname:port of the apc ups to install the certificate on") cfg.install.fingerprint = installFlags.StringLong("fingerprint", "", "the SHA256 fingerprint value of the ups' ssh server") diff --git a/pkg/app/pem_to_p15.go b/pkg/app/pem_to_p15.go index e376bc6..d48d4a8 100644 --- a/pkg/app/pem_to_p15.go +++ b/pkg/app/pem_to_p15.go @@ -3,13 +3,22 @@ package app import ( "apc-p15-tool/pkg/pkcs15" "fmt" + "slices" ) -// pemToAPCP15 reads the specified pem files and returns the apc p15 files (both a -// p15 file with just the private key, and also a p15 file with both the private key -// and certificate). The key+cert file includes the required APC header, prepended. +// list of keys supported by the NMC2 +var nmc2SupportedKeyTypes = []pkcs15.KeyType{ + pkcs15.KeyTypeRSA1024, + pkcs15.KeyTypeRSA2048, + pkcs15.KeyTypeRSA3072, // officially not supported but works +} + +// pemToAPCP15 reads the specified pem files and returns the apc p15 file(s). If the +// key type of the key is not supported by NMC2, the combined key+cert file is not +// generated and nil is returned instead for that file. If the key IS supported by +// NMC2, the key+cert file is generated and the proper header is prepended. func (app *app) pemToAPCP15(keyPem, certPem []byte, parentCmdName string) (keyFile []byte, apcKeyCertFile []byte, err error) { - app.stdLogger.Printf("%s: making apc p15 file from pem", parentCmdName) + app.stdLogger.Printf("%s: making apc p15 file(s) content from pem", parentCmdName) // make p15 struct p15, err := pkcs15.ParsePEMToPKCS15(keyPem, certPem) @@ -17,24 +26,40 @@ func (app *app) pemToAPCP15(keyPem, certPem []byte, parentCmdName string) (keyFi return nil, nil, fmt.Errorf("%s: failed to parse pem files (%w)", parentCmdName, err) } - app.stdLogger.Printf("%s: successfully loaded pem files", parentCmdName) + app.stdLogger.Printf("%s: successfully parsed pem files", parentCmdName) - // make file bytes - keyCertFile, keyFile, err := p15.ToP15Files() + // make key file (always) + keyFile, err = p15.ToP15Key() if err != nil { - return nil, nil, fmt.Errorf("%s: failed to make p15 file (%w)", parentCmdName, err) + return nil, nil, fmt.Errorf("%s: failed to make p15 key file (%w)", parentCmdName, err) } - // make header for file bytes - apcHeader, err := makeFileHeader(keyCertFile) - if err != nil { - return nil, nil, fmt.Errorf("%s: failed to make p15 file header (%w)", parentCmdName, err) + app.stdLogger.Printf("%s: successfully generated p15 key file content", parentCmdName) + + // check key type for compat with NMC2 + if slices.Contains(nmc2SupportedKeyTypes, p15.KeyType()) { + app.stdLogger.Printf("%s: key type is supported by NMC2, generating p15 key+cert file content...", parentCmdName) + + // make file bytes + keyCertFile, err := p15.ToP15KeyCert() + if err != nil { + return nil, nil, fmt.Errorf("%s: failed to make p15 key+cert file content (%w)", parentCmdName, err) + } + + // make header for file bytes + apcHeader, err := makeFileHeader(keyCertFile) + if err != nil { + return nil, nil, fmt.Errorf("%s: failed to make p15 key+cert file header (%w)", parentCmdName, err) + } + + // combine header with file + apcKeyCertFile = append(apcHeader, keyCertFile...) + } else { + // NMC2 unsupported + app.stdLogger.Printf("%s: key type is not supported by NMC2, skipping p15 key+cert file content", parentCmdName) } - // combine header with file - apcKeyCertFile = append(apcHeader, keyCertFile...) - - app.stdLogger.Printf("%s: apc p15 file data succesfully generated", parentCmdName) + app.stdLogger.Printf("%s: apc p15 file(s) data succesfully generated", parentCmdName) return keyFile, apcKeyCertFile, nil } diff --git a/pkg/pkcs15/encrypted_envelope.go b/pkg/pkcs15/encrypted_envelope.go index cead3c8..71433d1 100644 --- a/pkg/pkcs15/encrypted_envelope.go +++ b/pkg/pkcs15/encrypted_envelope.go @@ -21,14 +21,19 @@ const ( apcKEKIterations = 5000 ) -// encryptedKeyEnvelope encrypts p15's rsa private key using the algorithms and -// params expected in the APC file. Salt values are always random. -func (p15 *pkcs15KeyCert) encryptedKeyEnvelope() ([]byte, error) { +// encryptedKeyEnvelope encrypts p15's private key using the algorithms and +// params expected in the APC file. +func (p15 *pkcs15KeyCert) computeEncryptedKeyEnvelope() error { + // if computation already performed, this is a no-op (keep existing envelope) + if p15.envelopedPrivateKey != nil && len(p15.envelopedPrivateKey) != 0 { + return nil + } + // calculate values for the object kekSalt := make([]byte, 8) _, err := rand.Read(kekSalt) if err != nil { - return nil, err + return err } // kek hash alg @@ -42,7 +47,7 @@ func (p15 *pkcs15KeyCert) encryptedKeyEnvelope() ([]byte, error) { // make DES cipher from KEK for CEK cekDesCipher, err := des.NewTripleDESCipher(kek) if err != nil { - return nil, err + return err } // cek (16 bytes for authEnc128) -- see: rfc3211 @@ -50,7 +55,7 @@ func (p15 *pkcs15KeyCert) encryptedKeyEnvelope() ([]byte, error) { cek := make([]byte, cekLen) _, err = rand.Read(cek) if err != nil { - return nil, err + return err } // LEN + Check Val [3] @@ -71,7 +76,7 @@ func (p15 *pkcs15KeyCert) encryptedKeyEnvelope() ([]byte, error) { cekPadding := make([]byte, cekPadLen) _, err = rand.Read(cekPadding) if err != nil { - return nil, err + return err } wrappedCEK = append(wrappedCEK, cekPadding...) @@ -80,7 +85,7 @@ func (p15 *pkcs15KeyCert) encryptedKeyEnvelope() ([]byte, error) { cekEncryptSalt := make([]byte, 8) _, err = rand.Read(cekEncryptSalt) if err != nil { - return nil, err + return err } cekEncrypter := cipher.NewCBCEncrypter(cekDesCipher, cekEncryptSalt) @@ -94,13 +99,13 @@ func (p15 *pkcs15KeyCert) encryptedKeyEnvelope() ([]byte, error) { contentEncSalt := make([]byte, 8) _, err = rand.Read(contentEncSalt) if err != nil { - return nil, err + return err } contentEncryptKey := pbkdf2.Key(cek, []byte("encryption"), 1, 24, sha1.New) contentDesCipher, err := des.NewTripleDESCipher(contentEncryptKey) if err != nil { - return nil, err + return err } // envelope content (that will be encrypted) @@ -151,7 +156,7 @@ func (p15 *pkcs15KeyCert) encryptedKeyEnvelope() ([]byte, error) { // make MAC _, err = macHasher.Write(hashMe) if err != nil { - return nil, err + return err } mac := macHasher.Sum(nil) @@ -218,5 +223,7 @@ func (p15 *pkcs15KeyCert) encryptedKeyEnvelope() ([]byte, error) { finalEnv = append(finalEnv, envelope[i]...) } - return finalEnv, nil + // set p15 struct envelope + p15.envelopedPrivateKey = finalEnv + return nil } diff --git a/pkg/pkcs15/keyid.go b/pkg/pkcs15/keyid.go index 68a3051..08a3ce4 100644 --- a/pkg/pkcs15/keyid.go +++ b/pkg/pkcs15/keyid.go @@ -2,6 +2,7 @@ package pkcs15 import ( "apc-p15-tool/pkg/tools/asn1obj" + "crypto/ecdsa" "crypto/rsa" "crypto/sha1" "encoding/binary" @@ -119,9 +120,13 @@ func (p15 *pkcs15KeyCert) keyIdInt8() []byte { nBytes := privKey.N.Bytes() keyIdVal = nBytes[len(nBytes)-8:] + case *ecdsa.PrivateKey: + // don't use this key id, leave empty + return nil + default: - // panic if non-RSA key - panic("key id 8 for non-rsa key is unexpected and unsupported") + // panic if unexpected key type + panic("key id 8 for key is unexpected and unsupported") } // object to return @@ -181,33 +186,13 @@ func (p15 *pkcs15KeyCert) keyIdInt9() []byte { e := big.NewInt(int64(privKey.PublicKey.E)) publicKeyPacket = append(publicKeyPacket, bigIntToMpi(e)...) - // case *ecdsa.PrivateKey: - // // A one-octet number denoting the public-key algorithm of this key. - // // 19 - ECDSA public key algorithm (see rfc 6637 s. 5) - // publicKeyPacket = append(publicKeyPacket, uint8(19)) - - // // Algorithm-Specific Fields for ECDSA public keys (see rfc 6637 s. 11 table) - // // This is a length byte followed by the curve ID (length is the number of bytes the curve ID uses) - // switch privKey.Curve.Params().Name { - // case "P-256": - // // 1.2.840.10045.3.1.7 8 2A 86 48 CE 3D 03 01 07 NIST curve P-256 - // publicKeyPacket = append(publicKeyPacket, byte(8)) - // hex, _ := hex.DecodeString("2A8648CE3D030107") - // publicKeyPacket = append(publicKeyPacket, hex...) - - // case "P-384": - // // 1.3.132.0.34 5 2B 81 04 00 22 NIST curve P-384 - // publicKeyPacket = append(publicKeyPacket, byte(5)) - // hex, _ := hex.DecodeString("2B81040022") - // publicKeyPacket = append(publicKeyPacket, hex...) - - // default: - // panic(fmt.Sprintf("key id 9 for ecdsa key curve %s is unexpected and unsupported", privKey.Curve.Params().Name)) - // } + case *ecdsa.PrivateKey: + // don't use this key id, leave empty + return nil default: - // panic if non-RSA key - panic("key id 9 for non-rsa key is unexpected and unsupported") + // panic if unexpected key type + panic("key id 9 for key is unexpected and unsupported") } // Assemble the V4 byte array that will be hashed diff --git a/pkg/pkcs15/pem_decode.go b/pkg/pkcs15/pem_decode.go index 66a34b7..2d51837 100644 --- a/pkg/pkcs15/pem_decode.go +++ b/pkg/pkcs15/pem_decode.go @@ -2,6 +2,7 @@ package pkcs15 import ( "crypto" + "crypto/ecdsa" "crypto/rsa" "crypto/tls" "crypto/x509" @@ -9,21 +10,27 @@ import ( "errors" "fmt" "reflect" + "slices" ) var ( errPemKeyBadBlock = errors.New("pkcs15: pem key: failed to decode pem block") errPemKeyFailedToParse = errors.New("pkcs15: pem key: failed to parse key") - errPemKeyWrongBlockType = errors.New("pkcs15: pem key: unsupported pem block type (only pkcs1 and pkcs8 supported)") - errPemKeyWrongType = errors.New("pkcs15: pem key: unsupported key type (only rsa 1,024, 2,048, and 3,072 supported)") + errPemKeyWrongBlockType = errors.New("pkcs15: pem key: unsupported pem block type") + errKeyWrongType = errors.New("pkcs15: pem key: unsupported key type") errPemCertBadBlock = errors.New("pkcs15: pem cert: failed to decode pem block") errPemCertFailedToParse = errors.New("pkcs15: pem cert: failed to parse cert") ) +var ( + supportedRSASizes = []int{1024, 2048, 3072, 4096} + supportedECDSACurves = []string{"P-256", "P-384", "P-521"} +) + // pemKeyDecode attempts to decode a pem encoded byte slice and then attempts -// to parse an RSA private key from the decoded pem block. an error is returned -// if any of these steps fail OR if the key is not RSA and of bitlen 1,024 or 2,048 +// to parse a private key from the decoded pem block. an error is returned +// if any of these steps fail OR if the key is not supported. func pemKeyDecode(keyPem []byte) (crypto.PrivateKey, error) { // decode pemBlock, _ := pem.Decode([]byte(keyPem)) @@ -47,28 +54,27 @@ func pemKeyDecode(keyPem []byte) (crypto.PrivateKey, error) { return nil, fmt.Errorf("pkcs15: pem key: failed sanity check (%s)", err) } - // verify proper bitlen - if rsaKey.N.BitLen() != 1024 && rsaKey.N.BitLen() != 2048 && rsaKey.N.BitLen() != 3072 { - return nil, errPemKeyWrongType + // verify supported rsa bitlen + if !slices.Contains(supportedRSASizes, rsaKey.N.BitLen()) { + return nil, errKeyWrongType } // good to go privateKey = rsaKey - // case "EC PRIVATE KEY": // SEC1, ASN.1 - // var ecdKey *ecdsa.PrivateKey - // ecdKey, err := x509.ParseECPrivateKey(pemBlock.Bytes) - // if err != nil { - // return nil, errPemKeyFailedToParse - // } + case "EC PRIVATE KEY": // SEC1, ASN.1 + ecdKey, err := x509.ParseECPrivateKey(pemBlock.Bytes) + if err != nil { + return nil, errPemKeyFailedToParse + } - // // verify acceptable curve name - // if ecdKey.Curve.Params().Name != "P-256" && ecdKey.Curve.Params().Name != "P-384" { - // return nil, errPemKeyWrongType - // } + // verify supported curve name + if !slices.Contains(supportedECDSACurves, ecdKey.Curve.Params().Name) { + return nil, errKeyWrongType + } - // // good to go - // privateKey = ecdKey + // good to go + privateKey = ecdKey case "PRIVATE KEY": // PKCS8 pkcs8Key, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes) @@ -84,25 +90,25 @@ func pemKeyDecode(keyPem []byte) (crypto.PrivateKey, error) { return nil, fmt.Errorf("pkcs15: pem key: failed sanity check (%s)", err) } - // verify proper bitlen - if pkcs8Key.N.BitLen() != 1024 && pkcs8Key.N.BitLen() != 2048 && pkcs8Key.N.BitLen() != 3072 { - return nil, errPemKeyWrongType + // verify supported rsa bitlen + if !slices.Contains(supportedRSASizes, pkcs8Key.N.BitLen()) { + return nil, errKeyWrongType } // good to go privateKey = pkcs8Key - // case *ecdsa.PrivateKey: - // // verify acceptable curve name - // if pkcs8Key.Curve.Params().Name != "P-256" && pkcs8Key.Curve.Params().Name != "P-384" { - // return nil, errPemKeyWrongType - // } + case *ecdsa.PrivateKey: + // verify supported curve name + if !slices.Contains(supportedECDSACurves, pkcs8Key.Curve.Params().Name) { + return nil, errKeyWrongType + } - // // good to go - // privateKey = pkcs8Key + // good to go + privateKey = pkcs8Key default: - return nil, errPemKeyWrongType + return nil, errKeyWrongType } default: diff --git a/pkg/pkcs15/pem_parse.go b/pkg/pkcs15/pem_parse.go index 2cd1fea..19e44f1 100644 --- a/pkg/pkcs15/pem_parse.go +++ b/pkg/pkcs15/pem_parse.go @@ -2,6 +2,8 @@ package pkcs15 import ( "crypto" + "crypto/ecdsa" + "crypto/rsa" "crypto/x509" ) @@ -10,6 +12,59 @@ import ( type pkcs15KeyCert struct { key crypto.PrivateKey cert *x509.Certificate + // store the encrypted enveloped Private Key for re-use + envelopedPrivateKey []byte +} + +// KeyType is used by consumers to check for compatibility +type KeyType int + +const ( + KeyTypeRSA1024 KeyType = iota + KeyTypeRSA2048 + KeyTypeRSA3072 + KeyTypeRSA4096 + + KeyTypeECP256 + KeyTypeECP384 + KeyTypeECP521 + + KeyTypeUnknown +) + +// KeyType returns the private key type +func (p15 *pkcs15KeyCert) KeyType() KeyType { + switch pKey := p15.key.(type) { + case *rsa.PrivateKey: + switch pKey.N.BitLen() { + case 1024: + return KeyTypeRSA1024 + case 2048: + return KeyTypeRSA2048 + case 3072: + return KeyTypeRSA3072 + case 4096: + return KeyTypeRSA4096 + + default: + } + + case *ecdsa.PrivateKey: + switch pKey.Curve.Params().Name { + case "P-256": + return KeyTypeECP256 + case "P-384": + return KeyTypeECP384 + case "P-521": + return KeyTypeECP521 + + default: + } + + default: + } + + return KeyTypeUnknown } // ParsePEMToPKCS15 parses the provide pem files to a pkcs15 struct; it also does some @@ -27,10 +82,17 @@ func ParsePEMToPKCS15(keyPem, certPem []byte) (*pkcs15KeyCert, error) { return nil, err } + // create p15 struct p15 := &pkcs15KeyCert{ key: key, cert: cert, } + // pre-calculate encrypted envelope + err = p15.computeEncryptedKeyEnvelope() + if err != nil { + return nil, err + } + return p15, nil } diff --git a/pkg/pkcs15/pem_to_p15.go b/pkg/pkcs15/pem_to_p15.go index c213ddb..0c2214d 100644 --- a/pkg/pkcs15/pem_to_p15.go +++ b/pkg/pkcs15/pem_to_p15.go @@ -2,8 +2,10 @@ package pkcs15 import ( "apc-p15-tool/pkg/tools/asn1obj" + "crypto/ecdsa" "crypto/rsa" "encoding/asn1" + "fmt" "math/big" ) @@ -13,39 +15,87 @@ const ( // toP15KeyCert creates a P15 file with both the private key and certificate, mirroring the // final p15 file an APC UPS expects (though without the header) -func (p15 *pkcs15KeyCert) toP15KeyCert(keyEnvelope []byte) (keyCert []byte, err error) { - // private key object - privateKey := asn1obj.Sequence([][]byte{ - // commonObjectAttributes - Label - asn1obj.Sequence([][]byte{ - asn1obj.UTF8String(apcKeyLabel), - }), - // CommonKeyAttributes - asn1obj.Sequence([][]byte{ - // CommonKeyAttributes - iD - uses keyId that is SHA1( SubjectPublicKeyInfo SEQUENCE ) - asn1obj.OctetString(p15.keyId()), - // CommonKeyAttributes - usage (trailing 0s will drop) - asn1obj.BitString([]byte{byte(0b11100010)}), - // CommonKeyAttributes - accessFlags (trailing 0s will drop) - asn1obj.BitString([]byte{byte(0b10110000)}), - // CommonKeyAttributes - startDate - asn1obj.GeneralizedTime(p15.cert.NotBefore), - // CommonKeyAttributes - [0] endDate - asn1obj.GeneralizedTimeExplicitValue(0, p15.cert.NotAfter), - }), - // ObjectValue - indirect-protected - asn1obj.ExplicitCompound(1, [][]byte{ +func (p15 *pkcs15KeyCert) ToP15KeyCert() (keyCert []byte, err error) { + // encrypted envelope is required + err = p15.computeEncryptedKeyEnvelope() + if err != nil { + return nil, err + } + + // create private key object + var privKeyObj []byte + + switch p15.key.(type) { + case *rsa.PrivateKey: + // private key object + privKeyObj = asn1obj.Sequence([][]byte{ - // AuthEnvelopedData Type ([4]) - asn1obj.ExplicitCompound(4, [][]byte{ - keyEnvelope, + // commonObjectAttributes - Label + asn1obj.Sequence([][]byte{ + asn1obj.UTF8String(apcKeyLabel), }), - }), - }), - }) + // CommonKeyAttributes + asn1obj.Sequence([][]byte{ + // CommonKeyAttributes - iD - uses keyId that is SHA1( SubjectPublicKeyInfo SEQUENCE ) + asn1obj.OctetString(p15.keyId()), + // CommonKeyAttributes - usage (trailing 0s will drop) + asn1obj.BitString([]byte{byte(0b11100010)}), + // CommonKeyAttributes - accessFlags (trailing 0s will drop) + asn1obj.BitString([]byte{byte(0b10110000)}), + // CommonKeyAttributes - startDate + asn1obj.GeneralizedTime(p15.cert.NotBefore), + // CommonKeyAttributes - [0] endDate + asn1obj.GeneralizedTimeExplicitValue(0, p15.cert.NotAfter), + }), + // ObjectValue - indirect-protected + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.Sequence([][]byte{ + // AuthEnvelopedData Type ([4]) + asn1obj.ExplicitCompound(4, [][]byte{ + p15.envelopedPrivateKey, + }), + }), + }), + }) + + case *ecdsa.PrivateKey: + privKeyObj = + asn1obj.ExplicitCompound(0, [][]byte{ + // commonObjectAttributes - Label + asn1obj.Sequence([][]byte{ + asn1obj.UTF8String(apcKeyLabel), + }), + // CommonKeyAttributes + asn1obj.Sequence([][]byte{ + // CommonKeyAttributes - iD - uses keyId that is SHA1( SubjectPublicKeyInfo SEQUENCE ) + asn1obj.OctetString(p15.keyId()), + // CommonKeyAttributes - usage (trailing 0s will drop) + asn1obj.BitString([]byte{byte(0b00100010)}), + // CommonKeyAttributes - accessFlags (trailing 0s will drop) + asn1obj.BitString([]byte{byte(0b10110000)}), + // CommonKeyAttributes - startDate + asn1obj.GeneralizedTime(p15.cert.NotBefore), + // CommonKeyAttributes - [0] endDate + asn1obj.GeneralizedTimeExplicitValue(0, p15.cert.NotAfter), + }), + // ObjectValue - indirect-protected + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.Sequence([][]byte{ + // AuthEnvelopedData Type ([4]) + asn1obj.ExplicitCompound(4, [][]byte{ + p15.envelopedPrivateKey, + }), + }), + }), + }) + + default: + // bad key type + return nil, errKeyWrongType + } // cert object - cert := asn1obj.Sequence([][]byte{ + certObj := asn1obj.Sequence([][]byte{ // commonObjectAttributes - Label asn1obj.Sequence([][]byte{ asn1obj.UTF8String(apcKeyLabel), @@ -59,6 +109,7 @@ func (p15 *pkcs15KeyCert) toP15KeyCert(keyEnvelope []byte) (keyCert []byte, err p15.keyIdInt3(), p15.keyIdInt6(), p15.keyIdInt7(), + // 8 & 9 will return nil for EC keys (effectively omitting them) p15.keyIdInt8(), p15.keyIdInt9(), }), @@ -77,7 +128,7 @@ func (p15 *pkcs15KeyCert) toP15KeyCert(keyEnvelope []byte) (keyCert []byte, err }), }) - // build the file + // build the object // ContentInfo keyCert = asn1obj.Sequence([][]byte{ @@ -92,12 +143,12 @@ func (p15 *pkcs15KeyCert) toP15KeyCert(keyEnvelope []byte) (keyCert []byte, err asn1obj.Sequence([][]byte{ asn1obj.ExplicitCompound(0, [][]byte{ asn1obj.ExplicitCompound(0, [][]byte{ - privateKey, + privKeyObj, }), }), asn1obj.ExplicitCompound(4, [][]byte{ asn1obj.ExplicitCompound(0, [][]byte{ - cert, + certObj, }), }), }), @@ -111,141 +162,212 @@ func (p15 *pkcs15KeyCert) toP15KeyCert(keyEnvelope []byte) (keyCert []byte, err // toP15Key creates a P15 file with just the private key, mirroring the p15 format // the APC tool uses when generating a new private key (Note: no header is used on // this file) -func (p15 *pkcs15KeyCert) toP15Key(keyEnvelope []byte) (key []byte, err error) { - // create public key object - var pubKeyObj []byte +func (p15 *pkcs15KeyCert) ToP15Key() (key []byte, err error) { + // encrypted envelope is required + err = p15.computeEncryptedKeyEnvelope() + if err != nil { + return nil, err + } + + // create private and public key objects + var pubKeyObj, privKeyObj []byte switch privKey := p15.key.(type) { case *rsa.PrivateKey: - pubKeyObj = asn1obj.ExplicitCompound(1, [][]byte{ + // private key object (slightly different than the key+cert format) + privKeyObj = asn1obj.Sequence([][]byte{ + // commonObjectAttributes - Label + asn1obj.Sequence([][]byte{ + asn1obj.UTF8String(apcKeyLabel), + }), + // CommonKeyAttributes + asn1obj.Sequence([][]byte{ + // CommonKeyAttributes - iD - uses keyId that is SHA1( SubjectPublicKeyInfo SEQUENCE ) + asn1obj.OctetString(p15.keyId()), + // CommonKeyAttributes - usage (trailing 0s will drop) + asn1obj.BitString([]byte{byte(0b11100010)}), + // CommonKeyAttributes - accessFlags (trailing 0s will drop) + asn1obj.BitString([]byte{byte(0b10110000)}), + }), + + // Key IDs asn1obj.ExplicitCompound(0, [][]byte{ - asn1obj.ExplicitCompound(1, [][]byte{ - asn1obj.Sequence([][]byte{ - asn1obj.ObjectIdentifier(asn1obj.OIDrsaEncryptionPKCS1), - asn1.NullBytes, + asn1obj.Sequence([][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + p15.keyIdInt2(), + p15.keyIdInt8(), + p15.keyIdInt9(), }), - // RSAPublicKey SubjectPublicKeyInfo - asn1obj.BitString( - asn1obj.Sequence([][]byte{ - asn1obj.Integer(privKey.PublicKey.N), - asn1obj.Integer(big.NewInt(int64(privKey.PublicKey.E))), - }), - ), }), }), - // not 100% certain but appears to be rsa key byte len - asn1obj.Integer(big.NewInt(int64(privKey.PublicKey.N.BitLen() / 8))), - }), - }) + + // ObjectValue - indirect-protected + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.Sequence([][]byte{ + // AuthEnvelopedData Type ([4]) + asn1obj.ExplicitCompound(4, [][]byte{ + p15.envelopedPrivateKey, + }), + }), + }), + }) + + // pub key stub + pubKeyObj = + asn1obj.Sequence([][]byte{ + // commonObjectAttributes - Label + asn1obj.Sequence([][]byte{ + asn1obj.UTF8String(apcKeyLabel), + }), + // CommonKeyAttributes + asn1obj.Sequence([][]byte{ + asn1obj.OctetString(p15.keyId()), + asn1obj.BitString([]byte{byte(0b10000010)}), + asn1obj.BitString([]byte{byte(0b01000000)}), + }), + + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.ObjectIdentifier(asn1obj.OIDrsaEncryptionPKCS1), + asn1.NullBytes, + }), + // RSAPublicKey SubjectPublicKeyInfo + asn1obj.BitString( + asn1obj.Sequence([][]byte{ + asn1obj.Integer(privKey.PublicKey.N), + asn1obj.Integer(big.NewInt(int64(privKey.PublicKey.E))), + }), + ), + }), + }), + // not 100% certain but appears to be rsa key byte len + asn1obj.Integer(big.NewInt(int64(privKey.PublicKey.N.BitLen() / 8))), + }), + }), + }) + + case *ecdsa.PrivateKey: + // private key object (slightly different than the key+cert format) + privKeyObj = + asn1obj.ExplicitCompound(0, [][]byte{ + // commonObjectAttributes - Label + asn1obj.Sequence([][]byte{ + asn1obj.UTF8String(apcKeyLabel), + }), + // CommonKeyAttributes + asn1obj.Sequence([][]byte{ + // CommonKeyAttributes - iD - uses keyId that is SHA1( SubjectPublicKeyInfo SEQUENCE ) + asn1obj.OctetString(p15.keyId()), + // CommonKeyAttributes - usage (trailing 0s will drop) + asn1obj.BitString([]byte{byte(0b00100010)}), + // CommonKeyAttributes - accessFlags (trailing 0s will drop) + asn1obj.BitString([]byte{byte(0b10110000)}), + }), + + // Key IDs + asn1obj.ExplicitCompound(0, [][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + p15.keyIdInt2(), + }), + }), + }), + + // ObjectValue - indirect-protected + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.Sequence([][]byte{ + // AuthEnvelopedData Type ([4]) + asn1obj.ExplicitCompound(4, [][]byte{ + p15.envelopedPrivateKey, + }), + }), + }), + }) + + // convert ec pub key to a form that provides a public key bytes function + ecdhKey, err := privKey.PublicKey.ECDH() + if err != nil { + return nil, fmt.Errorf("failed to parse ec public key (%s)", err) + } + + // select correct OID for curve + var curveOID asn1.ObjectIdentifier + switch privKey.Curve.Params().Name { + case "P-256": + curveOID = asn1obj.OIDprime256v1 + case "P-384": + curveOID = asn1obj.OIDsecp384r1 + case "P-521": + curveOID = asn1obj.OIDsecp521r1 + default: + // bad curve name + return nil, errKeyWrongType + } + + // pub key stub + pubKeyObj = + asn1obj.ExplicitCompound(0, [][]byte{ + // commonObjectAttributes - Label + asn1obj.Sequence([][]byte{ + asn1obj.UTF8String(apcKeyLabel), + }), + // CommonKeyAttributes + asn1obj.Sequence([][]byte{ + asn1obj.OctetString(p15.keyId()), + asn1obj.BitString([]byte{byte(0b00000010)}), + asn1obj.BitString([]byte{byte(0b01000000)}), + }), + + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.ObjectIdentifier(asn1obj.OIDecPublicKey), + asn1obj.ObjectIdentifier(curveOID), + }), + asn1obj.BitString(ecdhKey.Bytes()), + }), + }), + }), + }), + }) default: - // panic if non-RSA key - panic("p15 key file for non-rsa key is unexpected and unsupported") + // bad key type + return nil, errKeyWrongType } - // private key object (slightly different than the key+cert format) - privateKey := asn1obj.Sequence([][]byte{ - // commonObjectAttributes - Label + // assemble complete object + key = asn1obj.Sequence([][]byte{ - asn1obj.UTF8String(apcKeyLabel), - }), - // CommonKeyAttributes - asn1obj.Sequence([][]byte{ - // CommonKeyAttributes - iD - uses keyId that is SHA1( SubjectPublicKeyInfo SEQUENCE ) - asn1obj.OctetString(p15.keyId()), - // CommonKeyAttributes - usage (trailing 0s will drop) - asn1obj.BitString([]byte{byte(0b11100010)}), - // CommonKeyAttributes - accessFlags (trailing 0s will drop) - asn1obj.BitString([]byte{byte(0b10110000)}), - }), - - // - asn1obj.ExplicitCompound(0, [][]byte{ - asn1obj.Sequence([][]byte{ - asn1obj.ExplicitCompound(0, [][]byte{ - p15.keyIdInt2(), - p15.keyIdInt8(), - p15.keyIdInt9(), - }), - }), - }), - - // ObjectValue - indirect-protected - asn1obj.ExplicitCompound(1, [][]byte{ - asn1obj.Sequence([][]byte{ - // AuthEnvelopedData Type ([4]) - asn1obj.ExplicitCompound(4, [][]byte{ - keyEnvelope, - }), - }), - }), - }) - - // ContentInfo - key = asn1obj.Sequence([][]byte{ - - // contentType: OID: 1.2.840.113549.1.15.3.1 pkcs15content (PKCS #15 content type) - asn1obj.ObjectIdentifier(asn1obj.OIDPkscs15Content), - - // content - asn1obj.ExplicitCompound(0, [][]byte{ - asn1obj.Sequence([][]byte{ - asn1obj.Integer(big.NewInt(0)), + // contentType: OID: 1.2.840.113549.1.15.3.1 pkcs15content (PKCS #15 content type) + asn1obj.ObjectIdentifier(asn1obj.OIDPkscs15Content), + // content + asn1obj.ExplicitCompound(0, [][]byte{ asn1obj.Sequence([][]byte{ - // [0] Private Key - asn1obj.ExplicitCompound(0, [][]byte{ + asn1obj.Integer(big.NewInt(0)), + asn1obj.Sequence([][]byte{ + // [0] Private Keys asn1obj.ExplicitCompound(0, [][]byte{ - privateKey, + asn1obj.ExplicitCompound(0, [][]byte{ + privKeyObj, + }), }), - }), - // [1] Public Key - asn1obj.ExplicitCompound(1, [][]byte{ - asn1obj.ExplicitCompound(0, [][]byte{ - asn1obj.Sequence([][]byte{ - // commonObjectAttributes - Label - asn1obj.Sequence([][]byte{ - asn1obj.UTF8String(apcKeyLabel), - }), - // CommonKeyAttributes - asn1obj.Sequence([][]byte{ - asn1obj.OctetString(p15.keyId()), - asn1obj.BitString([]byte{byte(0b10000010)}), - asn1obj.BitString([]byte{byte(0b01000000)}), - }), - + // [1] Public Keys + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ pubKeyObj, }), }), }), }), }), - }), - }) + }) return key, nil } - -// ToP15File turns the key and cert into a properly formatted and encoded -// p15 file -func (p15 *pkcs15KeyCert) ToP15Files() (keyCertFile []byte, keyFile []byte, err error) { - // rsa encrypted key in encrypted envelope (will be shared by both output files) - envelope, err := p15.encryptedKeyEnvelope() - if err != nil { - return nil, nil, err - } - - // key + cert file - keyCertFile, err = p15.toP15KeyCert(envelope) - if err != nil { - return nil, nil, err - } - - // key only file - keyFile, err = p15.toP15Key(envelope) - if err != nil { - return nil, nil, err - } - - return keyCertFile, keyFile, nil -} diff --git a/pkg/pkcs15/private_key.go b/pkg/pkcs15/private_key.go index 0321551..59b538e 100644 --- a/pkg/pkcs15/private_key.go +++ b/pkg/pkcs15/private_key.go @@ -2,6 +2,7 @@ package pkcs15 import ( "apc-p15-tool/pkg/tools/asn1obj" + "crypto/ecdsa" "crypto/rsa" ) @@ -27,15 +28,13 @@ func (p15 *pkcs15KeyCert) privateKeyObject() []byte { asn1obj.IntegerExplicitValue(7, privKey.Precomputed.Qinv), }) - // case *ecdsa.PrivateKey: - // // Only private piece is the integer D - // privKeyObj = asn1obj.Sequence([][]byte{ - // asn1obj.Integer(privKey.D), - // }) + case *ecdsa.PrivateKey: + // Only private piece is the integer D + privKeyObj = asn1obj.Integer(privKey.D) default: - // panic if non-RSA key - panic("private key object for non-rsa key is unexpected and unsupported") + // panic if unsupported key + panic("private key type is unexpected and unsupported") } return privKeyObj diff --git a/pkg/tools/asn1obj/oid.go b/pkg/tools/asn1obj/oid.go index 3975ded..70009c2 100644 --- a/pkg/tools/asn1obj/oid.go +++ b/pkg/tools/asn1obj/oid.go @@ -11,6 +11,10 @@ var ( OIDdesEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7} // des-EDE3-CBC (RSADSI encryptionAlgorithm) OIDpkcs7Data = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1} // data (PKCS #7) OIDauthEnc128 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 3, 15} // authEnc128 (S/MIME Algorithms) + OIDecPublicKey = asn1.ObjectIdentifier{1, 2, 840, 10045, 2, 1} // ecPublicKey (ANSI X9.62 public key type) + OIDprime256v1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 3, 1, 7} // prime256v1 (ANSI X9.62 named elliptic curve) + OIDsecp384r1 = asn1.ObjectIdentifier{1, 3, 132, 0, 34} // secp384r1 (SECG (Certicom) named elliptic curve) + OIDsecp521r1 = asn1.ObjectIdentifier{1, 3, 132, 0, 35} // secp521r1 (SECG (Certicom) named elliptic curve) ) // ObjectIdentifier returns an ASN.1 OBJECT IDENTIFIER with the oidValue bytes From c22447b0c2d5be0d956674a4d2760af0eb43c743 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 17 Sep 2024 18:44:33 -0400 Subject: [PATCH 36/54] readme: update info re: modern key support --- README.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 27ff526..fa53ea0 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,6 @@ creating and installing SSL certificates on APC (Schneider Electric) Network Management Cards (2 & 3) simple and easy to do. It is also designed to simplify automation of the certificate management lifecycle. -## Help Needed from NMC3 Users! - -If you have an NMC3, please test the beta release (1.1.0-b). In particular, -please provide feedback if 4,092 bit RSA keys and EC keys of curve types -P-256, P-384, and P-521 work using the beta and your NMC3. - -see: https://github.com/gregtwallace/apc-p15-tool/issues/6 - ## Background When APC created the NMC2 (Network Management Card 2), they chose to use @@ -71,12 +63,17 @@ NMC2 device in a home lab and have no way to guarantee success in all cases. NMC2: - RSA 1,024, 2,048, 3,072* bit lengths. -NMC3: +NMC3*: - RSA 1,024, 2,048, 3,072, and 4,092 bit lengths. - ECDSA curves P-256, P-384, and P-521. * 3,072 bit length is not officially supported by my NMC2, but appears to work fine. +* The additional key types supported by NMC3 require newer firmware on the + device. I am unsure what the version cutoff is, but you can check support + by connecting to the UPS via SSH and typing `ssl`. If `Command Not Found` + is returned, the firmware is too old and only the key types listed under + NMC2 will work. 1,024 bit RSA is no longer considered completely secure; avoid keys of this size if possible. Most (all?) public ACME services won't accept keys From 1cd9916a17fef26d18143d7f661813b3933fb06f Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 17 Sep 2024 18:44:34 -0400 Subject: [PATCH 37/54] install: add web ui cert verification * connect to the ups web ui after install and verify the proper certificate is being served * rename `apchost` flag to `hostname` * separate ports to additional flags (`sshport` `sslport`) with sane defaults --- README.md | 2 +- pkg/app/cmd_install.go | 56 +++++++++++++++++++++++++++++++++++++++--- pkg/app/config.go | 12 ++++++--- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fa53ea0..d339961 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ disk. It instead installs the files directly on the NMC. Logic automatically deduces if the device is an NMC2 or NMC3 and performs the appropriate installation steps. -e.g. `./apc-p15-tool install --keyfile ./apckey.pem --certfile ./apccert.pem --apchost myapc.example.com:22 --username apc --password someSecret --fingerprint 123abc` +e.g. `./apc-p15-tool install --keyfile ./apckey.pem --certfile ./apccert.pem --hostname myapc.example.com --username apc --password someSecret --fingerprint 123abc` ## Note About Install Automation diff --git a/pkg/app/cmd_install.go b/pkg/app/cmd_install.go index d3270a4..a431eb8 100644 --- a/pkg/app/cmd_install.go +++ b/pkg/app/cmd_install.go @@ -2,9 +2,14 @@ package app import ( "apc-p15-tool/pkg/apcssh" + "bytes" "context" + "crypto/tls" + "encoding/pem" "errors" "fmt" + "strconv" + "time" ) // cmdInstall is the app's command to create apc p15 file content from key and cert @@ -36,7 +41,9 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { } // host to install on must be specified - if app.config.install.hostAndPort == nil || *app.config.install.hostAndPort == "" { + if app.config.install.hostname == nil || *app.config.install.hostname == "" || + app.config.install.sshport == nil || *app.config.install.sshport == 0 { + return errors.New("install: failed, apc host not specified") } @@ -55,7 +62,7 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { // make APC SSH client cfg := &apcssh.Config{ - Hostname: *app.config.install.hostAndPort, + Hostname: *app.config.install.hostname + ":" + strconv.Itoa(*app.config.install.sshport), Username: *app.config.install.username, Password: *app.config.install.password, ServerFingerprint: *app.config.install.fingerprint, @@ -75,7 +82,7 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { } // installed - app.stdLogger.Printf("install: apc p15 file installed on %s", *app.config.install.hostAndPort) + app.stdLogger.Printf("install: apc p15 file installed on %s", *app.config.install.hostname) // restart UPS webUI if app.config.install.restartWebUI != nil && *app.config.install.restartWebUI { @@ -89,5 +96,48 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { app.stdLogger.Println("install: sent webui restart command") } + // check the new certificate is installed + if app.config.install.skipVerify != nil && !*app.config.install.skipVerify && + app.config.install.webUISSLPort != nil && *app.config.install.webUISSLPort != 0 { + + app.stdLogger.Println("install: attempting to verify certificate install...") + + // sleep for UPS to finish anything it might be doing + time.Sleep(5 * time.Second) + + // if UPS web UI was restarted, sleep longer + if app.config.install.restartWebUI != nil && *app.config.install.restartWebUI { + app.stdLogger.Println("install: waiting for ups webui restart...") + time.Sleep(25 * time.Second) + } + + // connect to the web UI to get the current certificate + conf := &tls.Config{ + InsecureSkipVerify: true, + } + conn, err := tls.Dial("tcp", *app.config.install.hostname+":"+strconv.Itoa(*app.config.install.webUISSLPort), conf) + if err != nil { + return fmt.Errorf("install: failed to dial webui for verification (%s)", err) + } + defer conn.Close() + + // get top cert + leafCert := conn.ConnectionState().PeerCertificates[0] + if leafCert == nil { + return fmt.Errorf("install: failed to get web ui leaf cert for verification (%s)", err) + } + + // convert pem to DER for comparison + pemBlock, _ := pem.Decode(certPem) + + // verify cert is the correct one + certVerified := bytes.Equal(leafCert.Raw, pemBlock.Bytes) + if !certVerified { + return errors.New("install: web ui leaf cert does not match new cert") + } + + app.stdLogger.Println("install: ups web ui cert verified") + } + return nil } diff --git a/pkg/app/config.go b/pkg/app/config.go index 6ef840b..7edf216 100644 --- a/pkg/app/config.go +++ b/pkg/app/config.go @@ -33,11 +33,14 @@ type config struct { } install struct { keyCertPemCfg - hostAndPort *string + hostname *string + sshport *int fingerprint *string username *string password *string restartWebUI *bool + webUISSLPort *int + skipVerify *bool insecureCipher *bool } } @@ -92,16 +95,19 @@ func (app *app) getConfig(args []string) error { cfg.install.certPemFilePath = installFlags.StringLong("certfile", "", "path and filename of the certificate in pem format") cfg.install.keyPem = installFlags.StringLong("keypem", "", "string of the key in pem format") cfg.install.certPem = installFlags.StringLong("certpem", "", "string of the certificate in pem format") - cfg.install.hostAndPort = installFlags.StringLong("apchost", "", "hostname:port of the apc ups to install the certificate on") + cfg.install.hostname = installFlags.StringLong("hostname", "", "hostname of the apc ups to install the certificate on") + cfg.install.sshport = installFlags.IntLong("sshport", 22, "apc ups ssh port number") cfg.install.fingerprint = installFlags.StringLong("fingerprint", "", "the SHA256 fingerprint value of the ups' ssh server") cfg.install.username = installFlags.StringLong("username", "", "username to login to the apc ups") cfg.install.password = installFlags.StringLong("password", "", "password to login to the apc ups") cfg.install.restartWebUI = installFlags.BoolLong("restartwebui", "some devices may need a webui restart to begin using the new cert, enabling this option sends the restart command after the p15 is installed") + cfg.install.webUISSLPort = installFlags.IntLong("sslport", 443, "apc ups ssl webui port number") + cfg.install.skipVerify = installFlags.BoolLong("skipverify", "the tool will try to connect to the UPS web UI to verify install success; this flag disables that check") cfg.install.insecureCipher = installFlags.BoolLong("insecurecipher", "allows the use of insecure ssh ciphers (NOT recommended)") installCmd := &ff.Command{ Name: "install", - Usage: "apc-p15-tool install --keyfile key.pem --certfile cert.pem --apchost example.com:22 --fingerprint 123abc --username apc --password test", + Usage: "apc-p15-tool install --keyfile key.pem --certfile cert.pem --hostname example.com --fingerprint 123abc --username apc --password test", ShortHelp: "install the specified key and cert pem files on an apc ups (they will be converted to a comaptible p15 file)", Flags: installFlags, Exec: app.cmdInstall, From 94a76b93dececcd60e75aa3385c84560738ef0e9 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 17 Sep 2024 18:44:35 -0400 Subject: [PATCH 38/54] v1.1.0 --- CHANGELOG.md | 19 +++++++++++++++++++ pkg/app/app.go | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e9c752..4936f32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # APC P15 Tool Changelog +## [v1.1.0] - 2024-09-17 + +> [!IMPORTANT] +> The flag `apchost` on the `install` command has been renamed to +> `hostname`. This flag should contain the hostname only. If a non- +> default SSH port is needed, specify it in the `sshport` flag. + +This version brings support for for RSA 4,092 bit and EC keys. These +keys are only compatible with NMC3 running newer firmwares. To know +if your firmware is new enough, SSH into your UPS and type `ssh` and enter. +If the UPS responds `Command Not Found` the firmware is too old or +otherwise incompatible. + +This version also adds a post `install` check that connects to the web +ui and verifies the certificate served is the expected one. You can +specify a non standard ssl port with the `sslport` flag or skip the check +entirely with the `skipverify` flag. + + ## [v1.0.0] - 2024-07-01 First official stable release. diff --git a/pkg/app/app.go b/pkg/app/app.go index d2d9767..9a3f542 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -12,7 +12,7 @@ import ( ) const ( - appVersion = "1.0.0" + appVersion = "1.1.0" ) // struct for receivers to use common app pieces From 1cfd35c4e2155457631984f1b92990d511cf93e9 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 17 Sep 2024 18:50:06 -0400 Subject: [PATCH 39/54] readme: escape asterisks --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d339961..1d494ed 100644 --- a/README.md +++ b/README.md @@ -67,9 +67,10 @@ NMC3*: - RSA 1,024, 2,048, 3,072, and 4,092 bit lengths. - ECDSA curves P-256, P-384, and P-521. -* 3,072 bit length is not officially supported by my NMC2, but appears to work +\* 3,072 bit length is not officially supported by my NMC2, but appears to work fine. -* The additional key types supported by NMC3 require newer firmware on the + +\* The additional key types supported by NMC3 require newer firmware on the device. I am unsure what the version cutoff is, but you can check support by connecting to the UPS via SSH and typing `ssl`. If `Command Not Found` is returned, the firmware is too old and only the key types listed under From 47b964d6eef8e42bc46ca2d5ed15af3c089e6fc8 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Mon, 27 Jan 2025 19:11:06 -0500 Subject: [PATCH 40/54] install: add time check and warning Clock skew can cause problems with SSL and certificates. Check the UPS clock and log a warning for the user if the UPS clock is more than 1 hour different than the clock of the system this tool is running on see: https://github.com/gregtwallace/apc-p15-tool/issues/11#issuecomment-2609010943 --- pkg/apcssh/cmd_gettime.go | 56 +++++++++++++++++++++++++++++++++++++++ pkg/app/cmd_install.go | 12 +++++++++ 2 files changed, 68 insertions(+) create mode 100644 pkg/apcssh/cmd_gettime.go diff --git a/pkg/apcssh/cmd_gettime.go b/pkg/apcssh/cmd_gettime.go new file mode 100644 index 0000000..7da6397 --- /dev/null +++ b/pkg/apcssh/cmd_gettime.go @@ -0,0 +1,56 @@ +package apcssh + +import ( + "fmt" + "regexp" + "strings" + "time" +) + +// GetTime sends the APC `system` command and then attempts to parse the +// response to determine the UPS current date/time. +func (cli *Client) GetTime() (time.Time, error) { + result, err := cli.cmd("date") + if err != nil { + return time.Time{}, fmt.Errorf("apcssh: failed to get time (%s)", err) + } else if !strings.EqualFold(result.code, "e000") { + return time.Time{}, fmt.Errorf("apcssh: failed to get time (%s: %s)", result.code, result.codeText) + } + + // capture each portion of the date information + regex := regexp.MustCompile(`Date:\s*(\S*)\s*[\r\n]Time:\s*(\S*)\s*[\r\n]Format:\s*(\S*)\s*[\r\n]Time Zone:\s*(\S*)\s*[\r\n]?`) + datePieces := regex.FindStringSubmatch(result.resultText) + if len(datePieces) != 5 { + return time.Time{}, fmt.Errorf("apcssh: failed to get time (length of datetime value pieces was %d (expected: 5))", len(datePieces)) + } + dateVal := datePieces[1] + timeVal := datePieces[2] + formatUPSVal := datePieces[3] + timeZoneVal := datePieces[4] + + // known APC UPS format strings + dateFormatVal := "" + switch formatUPSVal { + case "mm/dd/yyyy": + dateFormatVal = "01/02/2006" + case "dd.mm.yyyy": + dateFormatVal = "02.01.2006" + case "mmm-dd-yy": + dateFormatVal = "Jan-02-06" + case "dd-mmm-yy": + dateFormatVal = "02-Jan-06" + case "yyyy-mm-dd": + dateFormatVal = "2006-01-02" + + default: + return time.Time{}, fmt.Errorf("apcssh: failed to get time (ups returned unknown format string (%s)", formatUPSVal) + } + + // convert to time.Time + t, err := time.Parse(dateFormatVal+" 15:04:05 -07:00", dateVal+" "+timeVal+" "+timeZoneVal) + if err != nil { + return time.Time{}, fmt.Errorf("apcssh: failed to get time (time parse failed: %s)", err) + } + + return t, nil +} diff --git a/pkg/app/cmd_install.go b/pkg/app/cmd_install.go index a431eb8..eacda53 100644 --- a/pkg/app/cmd_install.go +++ b/pkg/app/cmd_install.go @@ -12,6 +12,8 @@ import ( "time" ) +const timeLoggingFormat = time.RFC1123Z + // cmdInstall is the app's command to create apc p15 file content from key and cert // pem files and upload the p15 to the specified APC UPS func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { @@ -75,6 +77,16 @@ func (app *app) cmdInstall(cmdCtx context.Context, args []string) error { } app.stdLogger.Println("install: connected to ups ssh, installing ssl key and cert...") + // check time - don't fail it time is no good, just do logging here + upsT, err := client.GetTime() + if err != nil { + app.errLogger.Printf("warn: install: failed to fetch UPS time (%s), you should manually verify the time is correct on the UPS", err) + } else if upsT.After(time.Now().Add(1*time.Hour)) || upsT.Before(time.Now().Add(-1*time.Hour)) { + app.errLogger.Printf("warn: install: UPS clock skew detected (this system's time is %s vs. UPS time %s", time.Now().Format(timeLoggingFormat), upsT.Format(timeLoggingFormat)) + } else { + app.stdLogger.Printf("install: UPS clock appears correct (%s)", upsT.Format(timeLoggingFormat)) + } + // install SSL Cert err = client.InstallSSLCert(keyP15, certPem, keyCertP15) if err != nil { From eedbdfcc2aa0e6f7b66bf143cf86aefbe11c3ef5 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Mon, 27 Jan 2025 19:22:45 -0500 Subject: [PATCH 41/54] dep: go 1.23.5 --- .github/workflows/build_releases.yml | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_releases.yml b/.github/workflows/build_releases.yml index 5ff7f19..0f351d0 100644 --- a/.github/workflows/build_releases.yml +++ b/.github/workflows/build_releases.yml @@ -8,7 +8,7 @@ on: env: GITHUB_REF: ${{ github.ref }} - GO_VERSION: '1.23.1' + GO_VERSION: '1.23.5' jobs: build-common: diff --git a/go.mod b/go.mod index 11730be..0b139fe 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module apc-p15-tool -go 1.23.1 +go 1.23.5 require ( github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 From 7f377fc5da6d9d6c64afa73f5a730de1b2efbcd0 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Mon, 27 Jan 2025 19:23:18 -0500 Subject: [PATCH 42/54] dep: build with ubuntu-24.04 --- .github/workflows/build_releases.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_releases.yml b/.github/workflows/build_releases.yml index 0f351d0..54c4b74 100644 --- a/.github/workflows/build_releases.yml +++ b/.github/workflows/build_releases.yml @@ -12,7 +12,7 @@ env: jobs: build-common: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout Main Repo @@ -41,7 +41,7 @@ jobs: path: ./CHANGELOG.md build-linux-arm64: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout Repo uses: actions/checkout@v4 @@ -90,7 +90,7 @@ jobs: path: ./apc-p15-install build-linux-amd64: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Checkout Backend Repo uses: actions/checkout@v4 @@ -173,7 +173,7 @@ jobs: release-file-linux-arm64: needs: [build-common, build-linux-arm64] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Make release directory @@ -217,7 +217,7 @@ jobs: release-file-linux-amd64: needs: [build-common, build-linux-amd64] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Make release directory @@ -261,7 +261,7 @@ jobs: release-file-windows-amd64: needs: [build-common, build-windows-amd64] - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - name: Make release directory From 06b76700c4568eef6e7ce6bb21a01f4351aa4baf Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Mon, 27 Jan 2025 19:47:13 -0500 Subject: [PATCH 43/54] dep: update all --- go.mod | 6 +++--- go.sum | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 0b139fe..f500bf1 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,11 @@ go 1.23.5 require ( github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 - github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 - golang.org/x/crypto v0.18.0 + github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1 + golang.org/x/crypto v0.32.0 ) -require golang.org/x/sys v0.16.0 // indirect +require golang.org/x/sys v0.29.0 // indirect replace apc-p15-tool/cmd/install_only => /cmd/install_only diff --git a/go.sum b/go.sum index 96676f0..3b8cd0d 100644 --- a/go.sum +++ b/go.sum @@ -2,13 +2,13 @@ github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 h1:aiqS8aBlF9PsAKeMddMSfbwp3smONCn3UO8QfUg0Z7Y= github.com/peterbourgon/ff/v4 v4.0.0-alpha.4/go.mod h1:H/13DK46DKXy7EaIxPhk2Y0EC8aubKm35nBjBe8AAGc= -github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97McgDGT5zcMrQoi0EICZs8Pgchs= -github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1 h1:NVK+OqnavpyFmUiKfUMHrpvbCi2VFoWTrcpI7aDaJ2I= +github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= From 2e082a30cf5788fb1cb6331f1586def72f9b5ac0 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Mon, 27 Jan 2025 19:54:04 -0500 Subject: [PATCH 44/54] v1.2.0 --- CHANGELOG.md | 10 ++++++++++ pkg/app/app.go | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4936f32..9260307 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # APC P15 Tool Changelog +## [v1.2.0] - 2025-01-27 + +Add a new feature to `install` that checks the time of the UPS to confirm +it is accurate. A log message is added that advises either way. Even if +the check fails, the install still proceeds with attempting to install +the new certificate. + +Dependencies were also all updated. + + ## [v1.1.0] - 2024-09-17 > [!IMPORTANT] diff --git a/pkg/app/app.go b/pkg/app/app.go index 9a3f542..2392056 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -12,7 +12,7 @@ import ( ) const ( - appVersion = "1.1.0" + appVersion = "1.2.0" ) // struct for receivers to use common app pieces From c5bb8edbec46584412dc6d88c9a2efdd88e9cbcf Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 28 Jan 2025 21:04:06 -0500 Subject: [PATCH 45/54] update screenshot --- img/apc-p15-tool.png | Bin 104761 -> 95457 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/img/apc-p15-tool.png b/img/apc-p15-tool.png index 807fb84cf18608689d111ae028769c0ae639f44f..c537585bbaecbccab5c6f73dba3c48be1b5f99ff 100644 GIT binary patch literal 95457 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 literal 104761 zcmeFZ2{@K(+ctbRQJP03Q=&ws%rcauh|ClrnaNBULX%XIA_*b$OofoSNfMcbGGwNt zOqsv^RBNs0`M>x7{_Wki@7uO-```C|)_NAV>%NBbJkI0T_x;$<`-bxIBdclXXeboQ zYPq8_suaqyN(yC(&vGifV=lPv75-D|s(!*z)!;m{t-Xzj`B`ITM;BXTW@Be_6AHz- z*-BYu%}eIxv%Q=4FJ8>SSo(~+Y4y38$f_8t@eNE$W)YNI-+Nc5uaIBtpL}1FhAu`f z+FH80ns)cS*DI%s>6>&I9S`Z<+$$K^rtDMd*wg9FA7{;>?>o+Zc0ziiU(DKQ=j*0N zjJ_<7-Xg1@chsQDeeYhyL8q%$?F<9dy8;;+nfH8(IG^~eU}wvI1pyI0I(}N4i@odH z3tt&oT6h?V@oZ`6d$r}KjneLRYCd;uk0Oy(dKAi{tLBFeDa#!?^w%TdK_0r_l{otH z0R3xywOE$GHRsmZy;Tm|d;O-!ea*d>y=pYg>Pxz6c+6_<#N>$3q%EVC+LFA6yYjhM z4$b9u>gyR-0-}8G{VIy7VtI3=(<47`BFBw-c`BV+Md8**7WePUy?K4XKx0+D^hKr} z3Oktj)Rc^A9fs-kCx(BFalgyR|1c|h?Os$FCE_T(;d6uaHtRlEX^Zu`f8k9&O7lbX zN6bN5R*$adUw>}zefo0ohgI8i6)uY??7i=HV#q*~PyNfuFV~|i?(uZ#YKqv{X<Dvs z$ldK}@HipWDEGL-D!IKoyaZ(4nmxKnw^zoz{wUww$k`lk&$=J+Wo!Y-G_tps)kR9% z&$N3E=jmh!@PFI2tn<LUkCo@GC6{y`ea`&$-LS<vf-+~?E<ZRm$aQ)yVAMp*hkj9{ zRr{Cc$z^WE-D`M%#vHrWb<`t7I&o=!M78kdiyb<rZ7aT|X86@oGau3AYNJql7s~`c zZIo!0sA_2`G8LPhrMPUfPmEe(u^6Fu&Kx0jLQz4?(8h|}z{uu|F}JgoEn<&C5tnkd zH88X^c4R(dY-(;T!SX$?h=tkQNP<O6P?1;B_K>le`B7JUV>Q>~>V~eChN4C+Qj#>{ z&SE%$m9e7%v$NG%YX>oB36_QPis5JSHV+H)!Xb{95-cYam6;FO*c&qoa0_travgRy zKevxXl7?B_-pE8uRYvyDDe#*Fi<zUNtr!o_`Sa(w&+~KJ*qie3iHeHy@b2T;w~q@) za5=bGI~q81Sv%|`r}%vi8Dj@SdvjYya~o@Ba!!LYHcpNbEG+mt^I!L8Wvi(8_vx)2 z{)_^~gU8vxmWPj<m&eMA=U+eJ;CT2PPV(o5{=-i=sAHw_s2V%iIN2K-A3kSn?YQ$_ zPhn*E_vdY$?9VPdj*%gc@mXUl9O{6p^8MQ-kH{%1|NRLv1g7RzwhN!aVE@~Zj^-x+ z4A#Hh8~Mw^<NWK5;NyRv_ur2G>)96$#!-rjVlp;{PUQCFWF%O~^~H>A49$(i7Jd{G z7T^;w77^qU5f$0TB_JXs$|YiC#LLAebY`EhfB+xw83Vq5IhCBXgQJ19p)olXPR?zP z^B9;23+)p$;pY<G$BR=4@(XjF5iu~~+Q-KyVq_r1FCr=^`Y)$YvNuOm8l3&tyCSDD z!l?xL`3-rE1Vy=w1w;+F1dIg)xJ1qf@^cx93K{V8^PMp;;Xku*DkDQNSsQyR15Bs6 zm4T@-kFB-o!XL<hiyc&!lVI7$&HK+g%4ZE6P4EG%0ds33oAVC;{7K#1%2>_OfQ%=f zFt3oPu%Lh-FCYHl<^Sg(O=Eip#3DH>A1^n*(86EH#E9W>Fti3FQgMQX_wh7hhwP0F z9Bu5?ZEVg;u#lT#CZGKK&x%-3Mh1=sG6s&uI4SQw0WscvV!Zt7e7s_UykY_(T)cu} zy#I2%jgh&D%m3HWWce_Q|DN=t<_@^N%fef~*OZ#E-S6Li|9aMZVKFf?FDwf&1H<2s z;9zjh*l6KCajxHgGBh)=HZ?~6_!F>yy>I>>1cQ($pP;CKupt+(@fi^=0U==_E>QtP zUam8OLVRaL`2`I4MSc(7-)DEQF>yR^U~hcT6yu1oLVzxe6*K#vsoL}JcXQs%n9Ku) zj0@rR_mBzlvhe(QSRRrw{(4$*p8sh)#1{_umlT7~{eBOr3z?AT?^O6_yl}7oCtrW& z;s4|%n3@0kBLAuJ{qN!W@8SASjlh5E;Qx-U{~oUY)Cl~i4*u`x`rkHOH2;t(V{0Iw z^Po!IcS`nwW>K9vc0`6UPyY9)AU*)^tgt<L(t$$R!bbjGB&W(cj5nzr<rEK7_bpzs zb{}8A%%^M$g_$BJb5Py6`RiwwQ-Q{F^FJmQ9bS4^X4z(1@g29e(j>66gt(cThZy_h zHJKTk_`K{=ReW|!P;{fFrXhDC*YUWMtk+g<W#4n1llyv9RQu(1m-m)T&UW~XNbFv7 zGp~Pe(s}sEL$}<a!NSUt)(I!cpdC_DZLE}>6$-lo@Xxz<@6=UQqa1oG6J17nvK8EZ z{`}cBG*syeR!Ond5p($&W6_wFI#8cnURimZ>dHZ?D;;rJyCo!avn=YL8#ga$uQWMA zi(?mF!=B447ybG9@d4X4e}25U>@o)8_iOLEhfDtac=}R1{a<gdT-y8Bk0+Q#sQ>(2 zytqX4ueZ1SUwZ0`$@atT2WQ99J?l%ZP}@vC+r`S-=PTT*tf|?cXjA^ZXg)0=fj#xa z6KOU!wpFYPk7K=~zHc!lXO<O*j(m_xA9iD6Vsh%MI=ExUjwHQ;DX#jSr%(6I4!>O6 zoMGx0e&MHFeY*a9Tvqqjri`1nZ#Pti?GFqLWLvv-t<%t_laY~;NxFICg8P;9^ulRY zuGG-feDi1-xlsESX-q=)9#GBCp7XOwI@xJ4D@z<LERr-+PDI|iwQ4n^_z6Lax||fl zxnG@rV$Q>l-rQ#3G&SwDY;Mgk{b2FEwLr|V@6H-Vap&60BO7-gH|u!eWp8h<TI4a? z)zcHlZ(3>g^4jX=Cx+g#zI1Ho&rc-JFFBPOa&6T%``6R@AKVtAp*ZX|X4+b9&6+g` zFMn@u@5%Fpg@yL#&o>IUU5G9&J}}$O<C$yR?AG;meN$6YqE5DTbKd#%#fukn@Bdh& zZES4Z{M6Woo}OOd%&X<O&!30rTh=E9O3u$XSheP#oM`hb`ua8D9Fg28#oun?T0_hT zRY^%ni@JonxOuZRl|DCbX1a`+ST$$Ld9Ptm_1VHzmtm@uW!ZTA+_`g`<%{x%U#z6# zJ$-R%;=9e9uASYJhE$#S)!T*Gg@v^a9z00?<nm_v^`_06%}Z}H{QU7lWw0SN%eF(> z*u*3WOQ*+5US58*Lb&ayznIfN3{Gq@G4we}D?NND@4KXfPI-54?_h6)r{>wSDfA2s z^-l~-k5#_s=Hk-8q=}1*bDSOg@+Arv9USei<>25@tq2ihlQXS+Sgj;A8xs@5DyQ1= z%*v_FeInR2mPy=MIm4u)=GTwTEVl`3tEQ)iKS)eG3=y<6DGWct=ps{}tfeI_efi9p zGXiE+%xtWz?0fde-M)Q$peZB%`MGZvtv#v}WQ9{Grx>s{b6%>a>wQitbWu^cd!I)? z?WOnH<n!NK?d|MT5G#dqQ<ie77`08CHpS$9FVvBmpUGImAQrPb+*#Ubpzev-ke}3C z!WJ&=z8|GbTIbF^Ye>^ge*5;Q1N#L&Jb{X<>+|Q1{VYmKN?B&r($s6{$;}(_ZcTZn zsHli&y^mW;di<Ezs^ytt`F*Z{<m6=SXJ^d|<FI)2_Uzf?I$T2CJv5XyJ3WDU*P!KE zWN2fP-kfF0iKjZ^#~4^`CQxKzY8qeYKCNw~uh$UkF*`*kZ2gdR4>$Mm2+z4(+1vDq z2+-hc-DhWmZ^=eajSZf!7VF=(ZQIn(avmoPUV{7dcyp0wQQ`cYTX%Q2dGW<X8qa?x zw)O$!tYU^;hy(;D&GO}USi{6!RO63_)kOPACgHh{`s17k?M%x??#b!t^6Khp`ka=Q zmhz^iT<L9_Jv}|ghCa854^AUg8+nRmqOi8~p6>b;F5!k`n}$0ptFBf-ypk}-f>YkH zDNQB%8l&4+J_q*C2`W3oMC_y!6BB#dFU-%)$}1?;J>WC!>h0a#FC83$aAOw|dRBAp zV^dRqxZ6-QLQBB3k{&Z7S5i`9Q4@0`Nh4z};G|x|>({RjAM#!!=-p8fD%==#*eBDf zMM&`M$EtkyZ0pY{Q}Hn|uS~XM(L^GMUd>oh^!4>c_wTpteB(>AW{oEP!{frU5H`j2 zJ4C22?!Q`QR1?f^x@p_CJx7ilao}yqG*^?D_~g*wNuQ*lma3g~FEB8+rly92p$)^4 zq%I#V>#MG<%@I)MSUpQaLvt@Agu}+h1`1%X-{y@Qqmg0Mmsf@gSy>2Lekv=I8yXto z;N;}j@9lW$U7vE2U(mAQeSTQU={wQUoowkZ@i>O1x9fW<Ldl1*=H4|moeUFmQbpdX zh&A<?{wB;IVtW8_l@)SusH-dH(h}+c%t7|^aj9`7V^lUPjn+4(c@vxl8*cje)c)u! zt<SMDq~c$A#>Q=^KA+-lGwhGs#&4qjs~{*S=-L_v3&EA*JUmKvc6QCrosw@eN?6Rf zBNwFqn$ge|A9;IsCn=g#tG1q=(=Bivb8OT-Z{AxO_H%qZjkl%6zvT2|u|`r9+B~Ke z#|G;2z65_n23E%S*JW91g^4=I-nemt+zf(Q;KDCk7Z;brgoLBZmMs%1PdjnnEO!fT zn@dYetGla<_who0%~^!n*uLfN-Mf*s@k*RhQu9$6nTHM?N_G_glle6>f(Gl8<DWYX zYA>t6Xlv)#Mm`J+!_@N%npGX}A4L@!#H}}ug_OU0m+3k0VQgg7H`b`1KJ1EAc`qQq zq564OV?fDi<=LsR`fMBhWz^J`W&Uiq(SZ2)-U$0!h^-&8DD64RwymN<&?I9GdNzAn zE-wCdjb#6)FJ4H0Yspr^6sqHt>dBg^93L7Aw^CD6;}OeVzU+)@iH(aJ$Zqqf#j>)< z`WCxoG=Iy}jEpSP4~(m~@)n<p`1DktW#`T;o3_Gon#yF2<XyD1w6XE=$uTiqPt!V) z4Yi+~@sd7tsQ#IiPIBWwLuxWfO6?_AIJmj*2LuEVC=(SGMN_&53HId4lY#Ot)ej^8 zqM~&T47h5PCJr1TYyQ&-lpL}1Ll5`y^JlqFpPQVT(#!BXwwRK3i$Rw{5!Q0y{o2{N zla@Bfazlvz?Vn;@_cK1B!Z?n9Z5s3#iaz3(6cZzZrw#J+^Q-^x`FvYy(M&gwyrN>( zw&PkP;Ec=fi+RlC)ZcOmD2vI@&-WS9(K~01IN4lz^2CXRfZa+-=e{*R&Cb?B9Uzha zttsOOVnDNnOZT}t67UGyU|x%VP>^Z;#I682l2cEf+%{H$c(7JOMMLVoi)#-wrl%vZ zRqvtLW=Mth19Q0S+qX|0={I!B>3y7HXi`dwLfeHAUw?mpQXbuZmhEoF3h=Vh)zu9Q z4o+m)f3B~uI=W|>Xu1rF!?~{?cSKjCCJKknkF*aCCV$R5Kltj##vrG!80oidZTiQ8 zxHVAX#)m#*el*QLKX;=2oQ_8s_?(wL{=F?-NpeyqRM@6rZgx5g$%jij^C-D_GqVI# zMU7N#L9eS<*>~?gLe3*Ee^XUeb+kK3A4%XCgP3DZls9v3ZZ3*L@R!ccEQIOd!-xA( zlGKhKy-p`|_C$X{j{hA-%wRSnstv|X4JqD{it<SY_|kS`n526%$Et#Ds0}H|lJ81N z4n|z~c|+*z$47VWtn=N<CylkCnURS!wo^(<%6+U}t2xs=K>paV^k4ZT-$x$u?)vsk z*IYD^<m{I(4<RWBZC=%wpmJyl)hcCW<vRpe9roE$X0*?|uPQR=Q#NMWb#Ah^``fqF zzCN?rLH+rfr^e-7eSHb>5gyG+F$zeD0@j~IM8(7gGCoMwnnt+C0d8A<%CXbRKVTNH zOHsgi_;hS+tohKV+?&3>8mIvE(MQ%MGtX(at|Y;#jVDjQ!mnMAN8I*vWF$C>sy&XK zXRWHHW}LtOhOqtTk0aj~88-(61?>h#8+5Jw^~)Lcq5oQtX-Vpy9Xk$MHl$RmDM>h$ zHIL$yIqra$<|9271D|q7oFtT7_PqFa2B3Q$HQk~<$*DY(x;V@?hKgd%5>O^%Mi77T z&9E?r*k6=(6VENR3osOKQf8R`g#)NZ<HmCY9lw55L4d{VBnMD(${yJLmtYJFPfMZv zuXv7@QTMyReRRGzuU~(}pLVIHM)g~B*0Je{??GioQHP4RPR+Bhuowx@W!ZPX<CvX0 zcjv(a_UP&a^V+z?$B*xnti~liEhUudb!lliR;7xHO7@u3mQ9<?-rd_1!fSBZ-oe2p z@!7YJPY(G>j6cwG?Pp^UweM1@jVH-~09Yii5m|queZh_cb$i_BXD3{d8Ui0YaIXG# z{#$eG@i4J6GXc#hwffV)K8Rhj?XG+%8VzKliuE0H`SN9MPBR`Zu6noc1xF67#oef1 zCaByN6i7SUIt&YVkCaqVXv6L8d~O0<T#8SgJh5miEI4)Qly%~C`_K?KAQLHkNF1Iw zB`eCy)f5zNEncz&$jqs<(EHuXpX6GAsagQzV>2TkERkri*ia~UD~0D9msWLu31t_S zH8)QzoE_Hz`qEL0KVFae*VW&@2kC3us%sn!J0&IcR?>3E@FQzplqW&iUU?r$4`|!V z%PY~Y^G)_Qrx1l*z|U_1HFWdNrP}8>MmxK@TJ+b%dL~IK9y_LX=1i2HbH}p!M77@A z?rV{xjuH@q)OAW%cMJ?Jbh<|s5Mq~Hh^Rvxqvy<z{no2Geg@vZZ_%3Xg897zI;7{) zz4<f7DhVkw(C;oVQ?5fVhm=&r8nS*kN^TK|PN++%ACs0JqlA%-jc1EqZ=za!rol@p zr~aD5Sl_*G^Z~V0Q+2YB`)(K3JbLsk!)My@@$rGW#DM$Ux=C2_1#?q_KgY&WYYMwd zVly)Muh3{bMR9fi@tW46>1piT>~~Mgw|8~~Z4L>#e}BM-r?3ilKH3$aR1=B&IKW0= z#+v(~p~+)|jhq4k>aDG<oArc)73AgQs_*SlBmCgeBW4LyCxG+D!?a9$xw#vVIRsrt z%?K_w6Bu|>%2?~qc1&Ga`E)Bc@;tJif3;a1h%K8Os@}7c4%zu!9UUFuu>sX)pd|&P zJ)t%srr-45KeKAtL`xgbDB=3Hq$KjiAPRU0zv*q)u>m&3KKO%|w>KvnTWpP`#JR5z zT0Q68M<fHY;vYX&u8mV9Cj%qWOx2DBO($ytbSl8nVEi2NDF7Hd;M9W&E`3&B78Ys1 z{E+?U(<U!mp<dg7yG@zux1Y+)%v495(n)#D9R41_g{f$K<GX$Hh7D&%LTNM@mUO>6 zb|K|dt|}t3_I)sa#e~bp#|AfV-c&^pRy+d`CV7^p@K+RB9)n_gb*=S-EL-z)<CG%O z$a1Q#{_(W+3i#4pRzdTcuB5gP8ru$`<ZC}Qx<R#;@hoEoRGO^jR#gBN6kDTKNp|+5 zA0($!gshrlo<4m_)9LoJtIR*9DZ>;4t!d`N>ad+Kh;6>I7<<dnzH03P*DL_G^y%K# z?(q3Dh7QB+CE8gQfq1W?&0>$I6gh6@*AGp1<3awI8RzbA*L{4F9jCv1IpaR@`TV=L zZ>xY2o$#an_*hVo*)~!87}xQyWlYkDjz=5rSuDD^{{#r5$7DCHVuWPDi(6ViH%uPC z4yG4QB@vL--=7Gu&dJB8V%eA$2!K*y7%u6qon9~=0$|sTKVQ5^LG9VY#&&qciWSY- zHW4pgzQok90w$7G3~*nQUNn~kX5Lpq&2$f^5SNgs;BV(Tw(+QcL_`FL7!ZztO{+ND zu3Z)lDJO+qdiF~{&bI56^}2Sg`RoLV=!v3vUGPea&(E`7ym)ahEG*Tga8j1kz`^vQ zwCV3fZAtm%<T6@%E{Dy{%{8arhYBkVHl{b~*c8OJx3|;uPL0+oWqHiHj(BQqxihPx zs_Ng6uAjbydd=Gp9~LmKz5P@FfPnM_rIjmJ3WAQgk5(VCcXHAIxnV845J4ubQO`{g zD=9rw0uZR`7*BzEM<`7YKh+$&n3(qWf{Ad{a`0WwdccbxKYrZw^J~N^BVZwUq={}j z<P@<BzaArgJjw=hLJAADkzU_bR0IYEHLeb}D?I;QPceAkVbB7aRjbrbpMHb|6n8Si zq#r<ea~gsBA(HOTg{(iBc|33&`XqW-R+eMeE?K0nx{pr`jX|+7R!MRJy9h5PxW;WL z_Xrpz#HMHUSCTR_1!lk5c=CHs8W<RG?A==j@d7l=aD-9+n3lsF5&#F55&#;Av8pef z;Jt?rQ*g5W*dRUs=;&yZ58+%8u$C=d+Be+sQai)M_sf?rqYXMX{g<d2AB%-|n;xBX z8wU2Ilf00FbYhw@OAr{Oug!FHu7}%3*?FpK;uN>9W|GX8U&}yJ`2Nj=utmNz-JX56 zsm6p75FVa3InoPyAoD$c<Tm4l;e(<gA_-W>$y(_R`@iQKjy%x6@WTtChZV<)tXWC_ z3drC5&8=<P`OZ%WlK|BLChWhlJN%C04w+An4X&Vw#DPa@GYDfnuV23&%(x*ry)en7 zB1G;v@3cMAx+|7QmSevv5>m@UMUT-wmD#DmbUHD|1Udn;(_`8P4_?~BqnEPt$nCz_ ziMGazi<THcy@2G0DX{2y9~>g(SqSl~4s2Sk2Gn<~a{u=P)Ha8n_tgaxt(L8(V2-ZS z-?BHaUmxiSObID1TdWP3dhCPeoSKB&_&tm;#3#MjX~K*#?B+kdyv7Rj2Vut$jx}4R zlXsp^59N)$-RY;Es}ukR06B{+viV=7Qe7P#JNl)0g&lenK;z{vUAjcaZ=&GkI*g2j ze4!S^ty_nYNTyrjNoo+5`)Bc1sL7jfIk4rvmusce5$bih4yGgH2tiaj0*_7JKZwj5 zdVZ|NYXduz`{-TCxnG8#of>srhjM$9=-+5&rKi7kdwhXv`h(Pbis#%&1mW1Mdr)gI zSn5cv#-``33YmY^DoL4FJvxXbp#}Z1qLWWt{1mjYFse25W^2xt(?$S3N9%b^8v8Qr z*Th2Ehav^pgfl%q3THkA)TxgAUlBR{E|4?p%xjuuR8$toe4F1?e9Cpm!pcehu&Mmq z-V=`xK$5j+dG<|k{(WBaNV<Me;fs$mbJO1i-KQKe6>dLAN9zzt(Wu^9D7GM)4Pdaz zD9s%E_Nf9g2&ON)g9L*(<mBS27;q<`{M9R&g7Iccq4XU)cOC-WCy1;vLMoknN=Qf( zoCUx;<J#)&n*G@pdyi#aK<@ZCJp5Z2=f14UMX~kf;K@>Hih2a)K@F*f2wdKbu5amI zeQaq-)-UqdEX^ke8PPv4FAp>??g*26oyB+(um+>YF9SJR6K5^Wb1&~lMX^YXyythF z>W{C8%w@&uFoW;~njA(598@aJkBvtA_TZlU&vMha0t$ZnoTs&D(V~IotYp!c%!~}R z58)E^1#aircJChSDhuf8Ox4ZPEGsK>o%vBp7EiMK)cCh#tQU;T@%`t%CRByf>R(Vf zbZyn$(9qC_4<9DcKU_;MqHOr)7T-i7^aTWsfWeEUM0SGIfOM8>A7R~As5?11Ina`w zE-DZdL$#Xj!IcRJSrA4NUKA4k2+H{!vu2=DmCUdN5s^V>0~Y+eH{4CY<o#A?ubg6H z&8Lp<gocM;v_E&Qf&YWJ;hKurm4Yvi+}aum`Uj}Y$<Lqh&fOt(`uRQpKrx_*HB6Ez zD5#vsi@1SzA3iifWF4(jQ|d38pZ#uJpv4{MHZerAcCD~?kAK}$<6}^g6mg`yl9KTJ z9AXKXE9io|dwskT)5t9E)>UL|p)sce>kJ_8X-IlbW-mvLgTOD9Y};5`dW6W?$HT=F zL9O+JKIA7$6s10C$~$i})?ZsHZvF1i$j=W110?{^irIHv&)w_i*ioXPu6_?7fw0<; z(vYyQFsO)+g7iI_Otv3?D6*sf#gaIKm)H71IaNXaH*MXT05~WyJ6cOnX~l-a_t~`f zGD#wdQXczm16B4Wymt+~$RmWL=7Sq<b2BbPN%OD6?=<P#S5bxfRG+Lo_bk@>^K*ah zrQxVN8sUpRArPFtw|?%itgNgggN&o!ujJC^(BC8=BJxq(@RPrfj}HK0{JnddVq;<w zfM|Mb3y?WUG8-Fju?fxQVP~)Uql-R%{FtWkH0lt3PXclinkEcq^NZ;uTZqito@SfZ z%FfNrks^3JLQ;3f+cbsAm;PKj&k~>zX~s%lzH$XP+r**h=i!4y{u=+*qCdjd-qmH~ zkPn&RZF%`IQh0&2NiTzRH`)r_$piruX>^>jwzhV~!UG&}I_d5~f`T+K#>X{1JPM(+ zijv+7S@I}-H#c(<m`V!Xd88r16=K^3En+bDT75b^JUm!${6Wc3bm{Oe!Oo(hC!u`s zN-^)-x++A#+<%v1h~vp#o2o~bWc?U*`v;NU16Y$UPHI1I9%-QqJ%0RnkGBgREM?dg zm9(q9{SngP1EyyW6m~g?Fm2joG~)2ZZzAy%1WYzv_C&IPB*yC0dhAX}T6!SQlZUF$ zR+u{x)qPHHIUS}sM{@zp-wO(gfAC-{D8y?M-C?8yqBJAnKNa0Rs|ta36nWeX5)_&n zNeHuY6=W~mM_-Fgkp{FGuhWg1wj8<-n&<({5nxTHa;od=H3)1bB@v}78B<KU4v0r) zef>6?MHX^$Hwak-Y}e54eV*?z>Z=@ooB=2m%GT)9DN*ElEd1I=ea}>VkIBPOll@4^ z1$aq<q!>t55gZ(hyf56CS*T+82p0v)3kHCWefW?b;t+_XzV})tO=xLWRG-lZ$xh-7 zyMI6K#>U+oY;3PB^01t>*o_^yFrOIL5FsmVueSXVUpl;*8t}I<^lJbNod&8RuK*ZS z_}FOQp1yv4`8MA-mSSlTsRU0KQ%Kth4D{*!y(goFKIf5#gc?$>QSbbrva;c4cX<|S z;)pO)nip=0@Sz@?2lqn7U7qeb{?J*s(uQ{{wD8^N#O8+|gsg_UXOjalD@YlcMQ!nq zLAqA|AU<-d-hKGTk80%AF~Chuq+uw^{uvn=6&tuPhZJsK!m1Adp_1aZk|*D`KEuR{ zA@9PE0u*@y`FoW4`T5nLw?6-B2>RcrKR*@J^C5x<gh{W!wG4Ar<F#&}l*uzQS07C; zsA@o0+U9qxc(*!&^q04^=#2D!&~kLl#GlyY!o$N|zdk7)MYAyULr_?l)tZ92_e5j_ ztlY!ODg#kFm5d47vHM5;T}S&O!J+qvh~!9LEJSOB)Q$y?cVyDlcaaWvwxk2P$dEBO z_il3K+A@{j@b%j_QcMtG3ZzR1oP-LoY0DOqtS}^wB#k`D`G+fkw=OSRnR&WoF~Frc zB-aoLw`XA6P_mgNCc6UAzo<55$hdIueM-t+fPRY?S6B43=Uy<V<}~KrT8DN@sJr9N zO3$56Uq4p&<c$6Jakr?b=r+A*6ebIGM=gr5?i^_%8G7&G$*P7R8tQXdZ*}?WiM_PV zdxM~Y9FFW?D>d^7Recc9P}$izN8{vOd>pNY3X=z60G^25*F8~>?ysV{f^-s#0fMd| z)8F4OfAnY-6u{9|x9_Ae;5t$v+}#s6STXpQ7oZ27CSg2G`?KXA%FC0)(V9gWtjCOI znN-k`PBN&G>tJejipg18NIH;)glLx)v>F!Q_o;BsCYJ)*`c?ah38aZ=pb!s%e~1F> zIx*yasHG}KZlhwTkfP`Obeq$cS1U;WkW(wIZf0_XXtD+KbI>e!g6w~G$sLxFA;2@j zV=57<kkQ!bI5LbHSg^@Y<%OqagfGX($H&ZbHvK%(9cutp356);)2CBhCmt8)pBO?M z8#xrk9t$FZiGK{V&|umdWORp&_O79!oPU7KeC6`xlr1YV)%Ds$!206BhJQZc)G-^e zAidVcG+qA~YDVW`4uK+auqRqNn6@W1H9JwE-<6f|@x*BrxH`GGxsi#eLQjiyJP{b+ zcV#oKqyy9Qep~3u;U=-3F{loYMn@eX=ZBcS)|WqdG8lIp2t5P33Az^OEF+mK5cvDP z<0r}#n3QR2`URCLbUW3j6uk!dsA5(+#b-Qyih^NMc-+*~6e?9e#0!o+d!C3=f$5-C zVmLCky}FTBO>*TgB(bbXS1c@`J03T;Y0e}cPm)V<RCIp6c(B5*CcrhobMS&YlCz^~ ziVt`X0zE+_(+%_sMneb1is(ClT*5OU*5i|0Qu^ggT_?HUYHvn!tY_j%DxWTf926Zi zh4eUk+(Bv2&fW9eSXx`F25$hA6#nnTQSqsNA>{|$yB7nVnt;Zs(4h1&+AXioLi=GQ zAP<3}nI5XJEA)~pa*CG~*Tf!u+fjQ`Wvou?pc!ISS@^!JtQx5=Dor;}FLTZTnE`sn zD3Yh9SsvD?i;$HIgVFBYy8(jwk+=};cgl>Cs#=i}*xA|po=U$yCh9qtUs3qEz%3gL z*;s(CBC<KQtGm13i&hVq{tCyZjaNz@0Td)6k^#AdBWj_hqxp!YMxruJr}phj=p}`A zTA^1=6av7jij}*CEb5NJRDeX7nfsvMrf4>9k6N7BtLy6nE2|1x-QD?+yU^N*2FpAf zZUZJkV1st1ncd6svab+VL1JCMWi@h$Q|xVR?~uODbQA{&4aeug%*@f7l1$vC6_Q2W zZYeX{w}XtMJ9Ns+io@-6e`jZORh9DVH*W^eXxgmT2C{r^=u;vjngoEOo|C^IF=kGV zyyHwi>Q4$8BpU_1Rfn!n1(Q7H<p4=EIvkKo0T{->Y@p#*%}iguycj(--OM`KTuJw- z1W*amwIg!oW?I@%KZb4F5|^#qqW&=+R{{UiI(6!yX4)z9*7!${4xoF2g`dIQG>*Q2 zj#o7VF5VBf^r0l>%8;=4m>tssWz$9bV)Jb<zbL4NNv0pdX;(AM&dk(7b0Cs0x(!)0 z{k65V(0$p_Np!eqjr<AQ1W3DC$>n9uV8bpEpsiLPA0J$~Wv?>2FsSpk6+`_5B?BRU z_!M8`b>}Q{H0Dt&Lel;HrJu1=nZR;H?j_(Fy^=lq_qWVwW07pwu;HG+KTCAA*-#$P zEz!$#pF66c55Z~GTYvk~ch~`-WcNr41}ik@yJSet3?ItP%S%>@knEwBL_b3Xwv>Aj z5$S6se;qh-oBj#iV0Tt--nnnCCyjN};?jA%k>{nQr86%~{-kNlH!NL4Iy{@xCS}b5 zA;=$)UuRL<G~MrlvgPE6$nB%<^F&wgWU~9aQdzVtGa>na;WS(*z;Z*Dt$N|LGJR0+ z^Vbm_0vO;ci0>{ZvYtzi_r`7?W=~ibgeEKj><H+ut5dHCm(YQ5x%o{3E2#GL{glr{ z9-j$T?nICD5C~EgF$B%bTu64!>W&pkddj}g0<!xeqZlTp0b!I7X^co9N(|}^>6Y!> z`O+c+k+pzefN{s(Y_}o0f);v5nKo^bC~2Ii%`hbhnT|e#;|1WHRGrO;Ab4*IMr)4Q zJ37`aSu6esP>`^FJg9$mb~Zq@m9;m1K=!g(GXUDZnSmko;>C;dw9B|)A6Q9R{e(ck zG?WbWr@W#<5m}jdmY`e-8o%3=eDZ;wo*w8?BbfJ@EG5A9bKhH2G5`IqY4z`UdzT94 z#Y1;#z%Y*Cowo?KBzMB!PEJpqaYryIlb$fJWDU|W(ZZmRD}o=RC8u`az(wfkb|Z@) zfyxsSyi4JMa(~n#t^LT-88g5lR_<V-Nb$9R_{8cUR|EZwqfT$AF+BpbC=#%4?0cKO zhldB&d==1YATsX3D^%w1AMl~~T9oGQ&IWIe0K}n!pYJ$b$D2$mIum9HzY?_j@+wr- z<~dLl6ybbmj++=6)8N%nN1mC&=m%m|VS*r3ro6~rJ2Nwb#$hTJtQoQvIy<hjV~yPd z14)oWEg{2BL6S@bTRqNi@}9KV17VCHei_UbH0K^0hEtUi$u~pYJ&qyFtC1s8J4^ir zrRFF7ws4<Hz<BOKhZH#*J?~JUYc&HYq|il6sQQX8Eg@<W&>Ki_e4FR|=K=Q`v=8Uj z5&w_Y4bqxF1Tdfh0|GH?AcBug75&YZwE9Jm7>U>^=s9=c@R1{y;QNI9%ih|$1LAI9 zUmwl%CvYB%>L@Rl2)Hp4u^Q<h43nnh`}Yc@Ap$vFbL<@<|AaJ8p<P2@2HN<or>^9{ zJ2iz41F=r^0gyzfs;O;Wx9$<djHB@TG(wC`f9*rd3CE%5WV!x(pUZ2u^0r6$T$ZQ3 zoB)eN5(qcRi$r}zAiV>k1$eLBs3aZ<^-?pTo`Ie|1;c*go1p+52}y~cZ+9Xj^5P~k zp&$^K(0wkQIPiqfm*2=m*Gi1u0m|q6>hMI;384d~liFI~HV9?70Wltk^n(9r<y-?A zY00)p$JpyWP1O>kV(kZ_Bi&g+h|eeo9ME>qdTjv9$Jx_q4B`q33W%4FsO-c#0qyGe z@!KdQDWJ(oD95y`r_wd_^%L9MPm{iQc@R${h-D_EYZq77fxAZ-t9SZ|M`8V0YVMx1 zhY;6KS~SE$$K*MqvR&Bv9;g8-VmjK?&0lq3tRZWgbTUw9>+@Y4Mjm8jXE#DqC9&YO zl9p&P09*}sb{rBwizZCUv-Q-uUZ@s97jn+$0&UC_HJj<e@TJMn^_V~9>G5xZR?PzB zb|!lxvU4L~s+b$kE=t5u_DooVwnE1!l9x(HY*9{09s*OoVNJ2+CkSG8;CC;2D2k2Q z+L0Jd)8rUBeQrQZG&Ad4om&Xo1k6fuFjxUk|1$Qz;TdN)hRY0+w)pOd3sInebBXD@ z(LDh6eoWRr0{2_UwPzZ{q_JSZ%(3f);PYVcX~GvMdx9=MjTFPhPlDR#8%~4vw#&{H zy(kOVt&w-mN+VrQ7a9>5xUkhCtA6RkSHS6?_kCMrYijrgI`R%s!z3j0w(&-D-mL=X zzMg=Vct6y`7nwv87KXUrZR_)^3QJdz$HnXa)#yrxR_v!2DDUy;BDKtkL3T)hCRWq9 z@F|_e{Qu=_vv4Y`qJp$iy`BugdjKsM0{fOza&8cix{HWMNZNP<FB8zD0JJM1@(Zsg z62A;I6fF~zT}1V#6z>JY#T)TEDeb#h;7SWc6;bQ`5TPY2OBF_rIDmp72F!_0QU5Aj zA*p&9#nu5V03gJj0234()D^C#rXQy^;`kgjWWs(dgLm)W*MZ`74-OuSrxYv7!b~e1 z3}wCfnN>2}vn00x=7L)}uENJ`y`k^n#x;?@yE{7%c~gqHE|UDZ9wp%d135FX<Q`a{ z?hC*C|IQ4(@Kpb6Ys2qD{tLOor$)^Q!3I%ulw|`M3k3gydRMhAC$f_bxlA@%1^sC? z1A`8yHi88y*0dl5!@D&=Rte%oTD%Y3ipD0RbNix#$(~U7$O6lZo)1~Eb8ys9GhKLu zd~X4jPdG~o3}8=mwR2endO_$ne7p9H3%x|B?F*ev5Ob>UTwh32P2BnMADf51{WSU! zq$mN0h`ElkcP^NAtPP1r{nv=xv17GubK}Bjr^@>i3kGbIFkTRdI|X1i3jZu&-ggQL zW}Pg(uUxs3Ur<2Ix&Su%8J3|Uc2VHAkBHQO?q@;Dtgmb|)*+|+XE-YAlRkfx@&rNz zAR+()%1sHS@HieK5lPw-?d%W{yDx0n6T`#GKzpcSSx$qRS&)6Hu9WmV16?JCC=?lB z_d2LFZ-^g?^63ppAxgXU@7MWWIGwg(<Hm*V9?$^PCVy69Z9*M$?erBMh1Qn@`sCVv zi8@V32T~1SP2~EWvY+<N^4qo-b9luG*AVv}oLd;UH}H!|xQ<p4^$GW}dfPr3ps^>p z-(Z2gckdo6<N(M`dX3bow-%hkIg{-5iz%M_+{VAEK%Xa#5S3`z;6n~_a5bu-Axj!m zq|q_?vs=t<d~X<)`U^Awz}ctZd4o4ryZ;;*n*8zOnz&(te#I6HQq~R&0fKJeHaS%& z92{Jo#ES<U1BLT2lAUHc>7oB)H-N-8_R-MK9H@hXo&9mM&~M{~D!>XcS3;~gfYa2V z4|y*<oLdeQ;|@F-D4?n1T<M~P_TBPmm?^;QuYTgh0|X*EQ*qeT0C8ROV<EbX@PDYF z1C#6C&1Rm4mbx_Ra>c0$aomksoPrAaelxSPf*v!@aMGNp_wl=VlXyO0k{}v##{<&9 z0XHNNWaN;~yDi##i+TXfiRF>_8}QjN7|V4SG6=K1zqi`ZPhuAYOky>e{+dBL6@;f} zmOT9q{38fr4jgg}%j%DNgJbWn6CUYDeCcAL2d3&3Xd~V8M|=;|(j!*s_bu9t8F&T) zlQ1n-;~8Z}sxUFc9S^f$ToUM;TVAlcyJfqtECCY!0&e4HAyq?!mj?l)WmvMBPCyxA z0)d5i=%!QKAECVrZ!jq<=#g^VTRYV``(t8)psp;}F$-|_mV8Ov{ne{W3Dv?qlGP1p zbFTV_YHJW=E7DDxF#gN=^8LeW$^B=2y`);NB)x_9lJ7DyTD)YfdgAKtSnD@_Og4G} zkx%(Z+*7g_%V$xFBNQ-~tP8swtgVS)&3U#HL_^e}=dIDcGGOtUX~<nL3@M>`?avCI zBieqX2fyG3_{WxzE!K+KN5~{QBFUeK0?Ad=XQHB`#{hJR%#Qmfjr~nbOzG$*5w8J6 zngF0&uz#Yyki8S6DGOUyU6H3JYz!|iR5X@jwPd0>4vj8&nhm&~_%0x#ld~p*K={YN z12S^PA4aYeWhHm;4dlCe#Aec|h*`9>s^OA)&mZnNKYM<}62TtAXLt?hp9gdat1l@h z=L8=g-_2XM>SE*qMjMOf>U%_BW2uEGkIYLRmq?<}*vPVil_vK)7fdDc#DW5AFzJjz z>@&kvNp{ObPmwf8p%dMmd_brdCQ87vVXqZBlJJbgefsq2eJOvi!W0CsQG9^-enBSz zZl8BONFI=Q0tYX`qaKRux3(gEPR*1mkmhJ0{Q5Lq9rTlEzqx^_!Mwz!mw$rjx?OjV zU{8Q`Rz-hpJfTTM;J~P8I#qEsZs(=*MFl~vh<s-KISFh%nQ!h05oa*~Hgh&QYY*;i za-FC1m4$uV3>liV1)u{F&#FNiFgh_`)Iho8xa9>~@E$bH(E`z+OTXXv>A907xI45M z9jFVnpn+rXVnDn9nD36u!IS_~_SDg|v^0y_IA7L32A#m$1Yc2df)_IG8;}K@mh>XA zdWdr*5$x<~#&i)6+1AsPQcn-b0V^BSg?KEU52od@$^k)5^+o%QVu|DTp&Ew|5~iC+ z86+>9fK|MX*kPe*Bm<iZUD4JdR$UCtAo2d7)?nTL2ZjSQ^Er5Vl|hF|dALHgTEM>R zZMh!(sQg3h+kk>Z1PL_nIo;gcupUV%TX1Yx)N^3OHL*`7Nx!HNwT`5n!K%nZnk6n| z$j>&hKn24nX*{p$Vj@KyUF&Mx;V!&AlG2MV5NStf7kLywig-l)y)(>R@YxWuKNc}R zbA*wR5iSrjuMs%y=4-F6*f@#@NkIDt7_u3PR-8g`6p$$4&?t$T)7bcvfDRWC+OVc} z$>DixZf)Zyc70+HCaST=+@vXd{L21F-Dz6-?izSLvSEfa-$#DH*G<9|)m>|=!~En2 z&(XH|IW2SyY7<nVQ<^<RCt~t%&;Pp#LZfgV$rHz@A*X|mb1v=Oe}1SA1zphS?YiVS zl#nWbW8#u17^&Rf!^bTLXQ~!@AY1mHAVz8o;<Fbn6L5PI2M6i?Aq#0BEBQR*|2NN! zMWNLF*{8paGFr~3ADh?if?lEk$5<p;SZ#|RKwN@TW-nH4s9~SqyT%i50m&bVIyk}} z^$t4{;npEb9i;?Yi&%)TFLi}@+5ewTmKTRYkap0c(|{L>?4C)Q2`8a@anKiTis9Xz zXaE<%&NNUR?G1V#4|0~|IX_1fKFA0hVJpJXCBT*@RO`WD({MiO2mW1Ozoq~xutQpd z9t^6G+1&W~vo67M$R4Ccf`heO>|T+^d!#qxK5Cc$$&&C~TGS_5MKA7UgObbSS{=O$ zKMsS*e7K;C%rH<J?+0lcLZ3DFZUc0|p)wP&fD35Ox)Un~>LYwP$Tdm!p)k{^6krb< zT&1~ah?<8_=0V>?j!lFa#C0GsKBZ8i3i}8OW`35VhuwfFC#|2*Qum%<)Ax(w;Jg6B z!ZxiawC2O$3wt36T;|&0Mf*N7h1JE`+3eSFhd(Pcg$0{b$T3x<3F2yyqkYj%w1_D< zoKViAX!V>UUp&|j(-s^|nbx1hkX;9EelM#hwlSiAL)#=U;BzhH0MbYGuPbzSOFr4u z-4?Zc?OH8JxG66zupg4_SR`9%AQ<ZQKiIy{=sIGetE<bgtUVtsDbgMd_n0zIp6*0D z98woi$jPq33a8cREyp7J1Q)G74Z#|&4AK@P%nUAKYzoRgHm-rb1=76$jGw@F?mg6) znsa2tiGoY=s%2}Tdmi!MVHr7?aqF@NjJ(>Ybo4pz&ccL*>ua4!+RI*Lf{kGjcd##r z<VWN>b8L?!d$^FXOxO5Af?hD?aCGgr`*IcU#lz3sbm~(TbW$YK1ei8C-cO}qkDx}o zlPEf!99O?$-y$4Gn9~}BAaUg-|76s92|p)$glgpiWNw<PZOk;Ul~13B8H04dva8Xh zMS}>eG?YlTL!l(M!(sppJ<!MJSYXR1EOr{>eR~JVp2U_O;pNzIvI)8v8gpnnC}Ktn zyX;Vg&08xqKk8+eD56BxnrwG>ckk}#Fn}AFbM2TCI_&TV5HBJc@Xcu65PO4xNuJ{l zk+G2xH4G?RD<=)&Cb~ZbByZljl^wAHTS!0FX3sMt6BD$^<ZzcXD^?^{%~eMq(Q<wN zbHkzT!42xh?{yV$WA(H1PtNxmDC$mbFK%P~YM{10_Q~19>pw+Z{HnUm_Q-%_J$JNK zR)dA5?(>9HUGvh!)Yz=(k&GyfBXt(3oZRdji*M4=E?u^M(<#dOtIR%f9-gQ7$-H36 zoAL<vju*-m&J~>qKj!>Bw~gQSQDyI|Gko|5O5l#5@?;4cE2~tej>D<wf!LluUy6aT z9qCaFD-G5*H{XNKYkY>$YUOS16aBT+Kut&G<SzO7t)-aV&PG4`46)I+R2O;9)A5+# zuXttcqUx2AQGztTbJcdCRp4JG09<yzh7D%0sY=A<C#&T2SAKfQ3tNf*aF@*e^6s~9 z-(E!5sq${>6tb8<EmtJU$>XdntDh7B0f9{b-)9@Fw3OI$kuGE6;-WCQA$Q9Jy>9v? z=jWf|Z!!Vev=FDNn-<=^JN~3p#sTIk4r=v{v8suC1RW-ZbXj-r-YF`&4O}GAsi1+; z>a3|LHPWh#j7&SAQAqpDm)Ab%)IHc|e6w;T)dAy4fAoaD5Lj4UUvW55{R^6TXpKnO zPL1|kL}sM<oXO%761pbCa!^4*A*@ALTl*&9HP+{8RJ<&1ov|z=FFtHAlaZDE0zauU zOy=v?t-FX`R`I)cH@i<>C$9v>Gq%TNMz3z9@w#)A^?`u-2EvJ0+1L!4o*G}jefteu zxHq9xzIgj~Ioc*?Q+BC3NrM|Iy=ZCK4&;#I{DTG8>1c1S>YHHa<g68CnNb<2&Ij7C zdRD-?VFeromaAJ=uU@U=w0-reRqg%#Hvm9hy?$MS6VP}q2IN}j^Ljaof=fw1b~62f z7HvB{ZuM(w>I|MV<U?Y3N@gP1T|1QMQZykt`ueVec~sC-P<uXN3HW3Np4fXWIoUFG zgkHU%^ToS&H1jjPQXSvE`AW_GS^|G>DU!+}3R1N*n)X#d!c=`2b{UzvI9`$6EUuT? z1Le`{UPMt)R7?&$us*o+RqkJ202;5p`ea%XEZFWzcDNx-4q&ZV(q}2G#x7VtNZ=Oo zZU_h#Nl8Y1_p!iA|E+%IRaNVRgoK>9u~kEmGS9iPW(#TAakpn0t0rzjUI(jQ3f}95 z-W~R!AK(alQC61N{1cQ0DfkF>l7WJ+fN?2xSWDuhxmr`M!*0!VJzwP14#2<~u}WBj zY(YQ6vc80dq^5GA<?MLBEfH466|gd0h1VwP@#A&S;7W0S!`SxBvSY_0Y%NjrY5gI+ z@a*ce$JfZqj4sVR|D6%QUt$%b_*Q^&L2gqM6DkJ>2O|Jj`CZpvpMjsT6YKykgoJ0e z<zuB)RMz9-j*@M4*bj9D3VbxOU5HAC(gql<ot>Qz><Z|>G?6uijN0DOQO%le1FpSC zU#<4@Vn_x#?$bgPGsP`UnhAcna09?z@@&%G!q|8j?8pzroM><xM->!g?CtlFdW=m< zPnv%sp0GHEM@Fuwb67R3uf^Qw*mf+YFtf78$Z=Ov0;4~|XoD)P-)lKAI2aAAef0Qo z><<tg7@Lxh-2l+YzIX3c>Bx%*m@mf0vVqfLJ?Jxwi_=pc14vaIK8PJXEG$vaE|!R{ zuihxLcMEp6F2jU~T=>PiiN>q48(Rr%zqd-H@+wH=cA{h8c>E3aXiL10kYWOP(Jnaj zWQv`OEA?3zN1!}^WFqv^_wV0_znw*5^29jeF_s{^d8u=x+MTH>W!rEAn!(B^CvHJs zipt5!Y2F7eMdAYL43p)k&9S>jS#9m?4qmt*j+VJV)`w&y@PokAp&#|`9=(c!dtmPt zLxke>8#gXxWeI-z{MqT+7x~zyZ=SbfT|u`{d}hTFNJHM~wfX#9!?LKrWhAchwAh|K zEA|DiLKD)dwS#$!4@$vWN-?GwCi<um2QBvql$i@auv-N!)U(FdEnkW(=!GEbvUl<A z<^MfQ4aMybulKEpN>BIWGkk*pKJ)tK=3n2k+cFnx1j>71cR>f%tGB|gE6CHx0gKdc z-(eIwyS2++)MKiDx8jYpkM`x?x_<qDy80&U)vby#*2Uf!9vK*lq@{2I@L2#Qp2yFg zy@4aavEepx(}yx$8fb4Po1)Kte7p$N{Zd}ueq_IpuVXNQ`eI*+;b24R3GArQRCdA2 z+k5iqQViJg)vHsrP6pjKF)_Ior02Tg%#*F?!%2|tw-Y^9F3B?w_)U)o+~1n(Ho^U_ zynK6RT0p=?^bE<;IRi8~JT_K;GHw|KC6^j|Ug^agms780th?|GCV{Ko-iC1KDUJuP zTD@8@ARtY@NUv58F}M5p!(#Yh?m(sKoPwR|1#v6|+iJ$DcGSck`vo&7b5J>-ZTo=} zCr;GmGJq#t4h`Lo{Q%cV{?aZrGBDVIjYBt~S?xe_IbbYDzDNQK^E9|Y$OJXV?qbEG zM-O0~0N#gYeqXmx`UJOFKHRuo!T^i!F(CN`?0sCma^;~DC$_>5b_pP8CnzR=$ORP5 z2Y{KHy8Xb%9UvFTJf$c%9RmY2OO`CbWtjKx-;V7;FVM41sy}YC3R&T*msbZoHHRUG zshm1>3q_Gaft_tF3{$DyASt(Cj6ID8>jM!x7Btpw;d=?TLXA&uoCaZMKzhpJ=0|sK z6po$h2t};1+S-k19$)eCS&8@!b<__H2{{7_mxFnLoOTUHwBpuQMr=DHg$BXX>e9<_ zi(d3;#qpG_Ten_E^U?sKnlrRSfzqTY5!A2~Ikr1eQc~z<^}v;F?d_57mI4^9!2WnL z7JeEuZ(qDv4BP7Un>QCzu$Qdi#EVyzm20pJ-{5wy!}HRC{;pSG;AU)`XTE#1y{G3I z^kTE2=Vl5;8<sC!zG4MR?ka4>y#!URxU6giq-9uZE@o#7W6*8q=iI|+SHlQ@9d?79 zyu4_bVcB_jj;N|`ptM8uD!IOX=Rg(%oE0VT{oR5A!<-=i;exb>vlPS3eg*x`B?<Je zrC=kCiH$XYs`D}LJP%}?M{s+I>dVEdDm)ZfPenx~Ub2x)x=Lut!g8zwSSCvYu_=Rv zb(=}*HulmTW!<ofNs@l2?5zWMWtD9`%(A`}wP19hekG}nFs(u&qyX~6Z%@jkBBLI( z)um9GOQH4H;!8oWJ>q3)DK)kcalmbq<aJ}$u3gT(Vds@qRWCny@Zg=krt8?e6Y)f3 zY*N&K!`Y(7y`!sZsglwMli-UQEO$ww#YDdZlv}@j`^sa-jxAlf^fbDS!=s~Dl|P=d zVcvK{#u;u9I4mwfcVHG0+9D?>XY1(rYP(JTCB$4oS#t%(8ldcT(fq6pv^0F*f?(g} z%iiA981_f$>H34O*C8_koxDUBdZY;;o)=w1g>dn0;KGgfWYKO-YrH<xj1L+hU46_n z-v~|NGB(m2ja3D$ry@^`rTGM`34l2YNkw~59)lesAeWIQ_L!01WEEtSkrNxU#|InN z7R}G?$2Ov?;63KI<uH6<rAAm7>$hyV27L*zi-nJGy?rh{B<o^W`}m;0DoS|%DKpYo z`x<AdSS(hfT&QJdM@130(c_E^Oa2x$b@io|mX<>aRLzC%`>|2S7rt0SP@J>t7HX8x zuHCz-k<F?a8@-j;uO47FtKYwkls^lLW|L>$*Tbz+?2a!~`QN2}*%gO{tNy+;$5!Nr zzh4?8a{hiL&`1a^TEAY<8(jD5wQEmz{P|1YjhE{IsQ-Rx5a9IpE9o+7=HD-s&8sSb z@&0-xod&@C`=$P$xfg=r|9<^ndC3Hoq1FN)#A`dgHO4PX@9^PePy+p+Aw+}SHW;U$ z%DoKBd<WS6S=&G3WpR#-PdL7hmG4BR*?N#rN6Gp2k|hMEBO%DZ?-AXml&H?a!?PAJ zM@fBuqB;k}rX|>!A?@lawwC_SiE640H?CVRp^I;Cxe2_pgN<z&l1k$Eg@XqdrRf#i zL>77Z=~I67_r}OW)L?<VH_Rh$-lRG8+;Ii2EpmQ{o>E*<;Y+>d&(xXQz5?`7i-X$P z*PO+fhI=QvlG_~MGkgf%$;q5c7nkriO2V@61_r-tP-f0R{YTOjH~TZ)M^_#p)yH^r zYz&3u=KS155k&J#Fzoc?l56KXuZO&cvfd!Br>=hW<Hr;9QXcf#Hf^#%GJLQbi9lg_ zF$Qt|YfJW8q+OX)r|3Zv`J4vSDa9B@KDbMfv==Arz2YAh<_r1$3yKNlBCP2KP)N*R zv~L(sQs)5O>VzK<Oz|K*tmIQjcgdBf=aGRvW|*!8QC|&qh*Pn!voAmMB<GFO!?3U) zaKRLV>fnYvcJVO&uBhu(zKMJR+SxM-=L|u+N1=ziic3i`!A)%I<m3g^O=-ty|AOtv zuKV3)l1i|5M;46mF%$=jtfG;hKi6&BwgPMP*RNm2c<SEicmNM1Aznej*U%AuK)vb2 ztzEx$i+s;R!&4wMlEn`kppcEU;xKv<ToZRoUeUTW{{{gF>u9_PpW#K3AeaDU7GE-y zXeWW(x^>&OAIv*`he;Hj-NLkzFWBj8E!dA~T?_=JSR8iu?jkSgMX-uiJcXjL?uHBi z-#wE}&jIxaw1@BfWl6~*>5lodj6(QWhKGl%Zq<2Y(!a-`+xFM6<>cm)Ax9RXrU1g6 zMi=evyLXpiLN#$g`pU-+D|`jR5=wD(^?KAdh;)6#9c5dHTn95;Urp>vWYZ=1q7+Kt zSi}7H`MI?~t$nreYu7IQYptxiO&A)T(vb8H>@T_vL(`{1_w5IaKR&#Fp9=PB#iOe! z5H?>xkja$K?i(C@`TqSX<Xjpr>5&|i@ULILT5#D!#m9R?7tJSboGN<CEni;)U_qpJ zb1t5{M>BWr5fl)RK>*$j2vD;+DLD3a61FmPX@^JqM6#beIjE_*W$og>?ud8cj^vv@ zek^WorUSf0&OT&q%?+V;Jsn-rW}zj4SufyGu)~%e#i}(Fd@qGrm0Pkp$Em^#+pzA4 zc5#Z^Gi9tL%hDT+9b2lSp(WIZ^Kf&^;!5QEpB!enb)OGYnE%Wml_64Soh*v*PUHkU z9Y<i&n{LqV!7<mKo}Rv@jNO1L?VX)h;Bz8O9b4@38|=Z?PZYQcl5xY9xb0iEYypQW z1_InZDyq)0nV^FLw}~^b{B9MtreC#c)t%5#A2{BG%tI9cFF;`H8HM@zEgRf7Z{J?N z<UW?THj2$fux*Sixc&C45D>gbDX=gm#LlsQ1BMn_07XqrEj8qe@8r&MlxzU?5}Xz| z_!`8?MfdOD*Di<Ei4ixnRm_nctb39AeW&VL%a7^Tk#IVZs)onMuVFKBT*@)D@>1FK zk^ssNVL1u3jIbkKPUz?$v(dw7X8FTiv(1N=R0-c$bq)eb@#SS5ND0`Y)FVR*2-ihH z<Xi0~XiBGzjlDp-OOO@iq#d%L`yXVv+YZnRRa*uPzoca#P#aM3E$>ak{AP!cpel%$ zkG-s#SeN9xh628K81jBrH%wNy;UBM<4+;$2At<={*|TT$(jJ1GoSYw<o4-KD&^A7e zA%-haSzG(v5+4j*_-(0XFX@WYi*OK9@C{!8)4IhZu_SCZ2E6+CF{jBoPf$om7JTE$ zxo_IwgnfH0f4*4Ba~d`oB!;W;@w=gj+Rgm3!-5w`c$53&2_LwXU0>CX<x3Zfh=?%b zRxsMtCkMq5C=e76Dl4yp$_Do#%!0i51_O(%kM*~1-C74B2r^8Hei379Yb#c8DU4Qj z*lz%+vjQ96dKbxJ<dinwkXf92>h{;KXYX4fomiNV%B8KMr^krzf(sdw!2h2%G>qO> zJ;#pTyF!E{1317dMBgpYNki=PK&E^h$f<=vpmBGfbuEfeW`Bi#&W<fUcKFghnIlJ{ zp^iJFm+%;!HfUF7zSiGcvU#vg!WR~KW>V4e&b>x$?fClj)Znz^V8dz{G>ak4K1MH> zasXXxJe9!Q4monWsYsxm$nrCVhcLG9m)hr^dTqiS)$m9)QNUONyD^ndom#bjf1Xf= z1cZ4MA?t<-agP}hsIeiL;wwoRlqk3-&xYaZ=<Iy?=8-JI_fl~1HdM16n`m~dTRtGc zqvC*Ixtf@09@@mn;`)p)aR-aVVUEC?kqZ#0e%{YHQh9I)UyjdW5!aS`_OHBw-=pRY z{x?CPz9%dG)Y}9}gxU7UZJ2+d#p_kJzx*4`Ii68&75ny{IqZ7E7~kJ$jmwWJcU6_n zH8EbTY~dx2EKv>re1cL(|A1e_=H#j>Ofu8on6ZYYlxg+*ZI6W0LoWYFS|s+;fhFqx z>%Xqkw@ph&Ic+X+>R0r9T&b4ea;<qC=5>9m-Y#3bh#a})%|AzK>YPYZO}sNd{hhtJ zh^bpE?SO_#36&+)Vt4ng`NNCfU%pa3SFe?|cr+<ebm1!ha&KndJ+Z;TIa#(l-l?j7 z<gNHTw0Sf8F}Z^$<!Nq?e?QWjUU(Uv@gA3J-B|HTZ+HF4ovuRl0`E>mD)S21uo>^! zLq5NW=5W@ms#64L(+Ckif4?Y?R#c35&WA<1aB|?ZbKi?BUjQ{*xcm)f!w)`}4do1! zXWvA#iAkiG%EI_H^_l~b$_EnF?dwF>t8o4UbhS%dCV}|wXli<N-_#`6;PUwkB3JVd zR9E*P&1~Mhx%dk8*@q3|?TZkb0@K~qD#%xL`TyW^ika*~wU$SW1kH2#{d0z%YL#-0 zBm53B4lT_}g5PL|cC$qF{5>VtDKqJnqFJj`Y)~~^|2Z4N`(Mz$$kX8IYuB&;3p}S3 z)AfF0l93r*_j~Rw)<Z|aR=zhNFLsb*!^&gg<2wVCBul^;n@-Cp8o|Hv3n~ry!jzFo zP)WnNnMw5e4XrFmxO@M;v`PlPCa?r9*(<Ol^sVYaz!L5Z>&F*J-s@;41p^cIU{EOs zAayOM&#bKKI&lCA=w1*-n%I#}dL!WbPQEERicGp5Uqm2{bUloQKMU0M2IG}9G{b(L zGfRLi#XP}Khl#c)Dw^A%b0konjr&y2MSStcVtkJz2&D`<T*ELPvFzGK1!2K=?i4tu zpmqFI0ZOA*uiKtYG(;2vHhUmwxdmFMh3*_Oj$mQ>R4<U4->l0sn>M}PLCL8MbUE+g zi)0FjE(-s<X3O;Sv?03L*Zus`wb=1RA-4g#I?&)cjBB<-xCRK>tNVNt5NWbq8@Rk7 z@Y7bw3nDeOwV^X7G>hh>=6?9mO{mGqtsuLRM8w3-z)cSDo!<CFcWgIEG!#7;jju*W z??j9LCg}_3=;A#T{%d%zr!ve3YN6PKIfjQ9?%T7?nJX}iexGTXKDQoec?&3p9Vth^ zF$$WYvt$gTdHEy9;ueh3XW-g#?he!<WA_M%MEgU0Z0rtP5_@ca!N;+ThbMiPC{umI z9LDh%S`l_|)`c0&5%YA`Yfv6O_=lWbTuAX9lbpa8^U*cJ6rR=gAwo*O*G}2Vvtc6M zV&3iH7|?ZV&vdMGWO32lSh_1iJf`6nw(pV=BHyO4n&#^ETJP=6<BCn*&?>Nv_{rI( zeGr1z!_C5gqMF%&eNV68zE{e}wt!zSYdyx-aC+F0s51>`483^0uRmUyohTP?yX{}S z`5)YUc|4YD+x1;Z(x9S9p^;EX2xW*OREA8+lzC2uM5YF86)IE7kj!)DOlgvkF+=7` zGKI{^^sTdc_Vd2qpWmOK-}An|y=`vyeO>2up2xA)I@Wso1r!VR`*eG;OwG0M?n8lW z&U*F7r)7||;jy>|a)|WT1V`Gj$v1iB9%#SI@jJ^Xn|!G-)kJ_99BG;OD|eU$X)$Xf ziB5mp!q%(smKb(~Zb@*InECD3EoM?zSJxW8k75T$*9BA%VRwm{rQ`D`ZJvuEZpH=% z%RM|i(#jjHb(NGn0nfhzOf)o$wI{t}gV|zfP7cG;jK`ACW^Q#eYtze@g)n|dhcOB^ zQlw$x<~4J>IDk1&4V%$o=CCTt%S!=T0%`68iP0zM8m{KLb#L*XLCIx+3W;X@`nPB+ zwTJBoMf2E<ZEV(Glk_C`gsf~2mNgA6?JLk4jyzGp!Ct_#2&jy?)u!n{ET=u2e47oy z@&IBckeATKt~*flt;bU~HF&!)Pa_ln;Evt9SGfI}Ov>(rGHIyzY4Zl0tpt-UDG|f8 zEe$<=phXoh5C)XWKk>j~{JSx>Z}*hQyRI(XL-%z`Y>bTdV9@s<Rx2t$*}gnAcsX$Z zkpaZBhn@WiiY~2%jClJD;A<HBF0iNq8$|FS3S77T0n!|eLcUG!<J2V9Pom?Z%FG9_ z3K8WY1qC9LlR2e>=e4w6m6gS{^#nxTXmcrLK%*n(A^?@qO_*#M!X${y_!hVVM<o!n zu!zV`0f8LlzLd9<pWo)Vot^qjs#XFG0fq9z*vIRxuIFuY0ci5D23})oWg`<4O{V61 zUS-vU^z-?zl*%t<sO(d^G_-|wX5_@k#nNN5KaTkJ{<HDQNKe4d)ZU*eYN|V&rYyeh z*IPfgnOlEw;GFZwbNf}TQEuK(49gwTXIwRz_Is~gMzwrjuU!Yt8tIoc5n5F_+XQJ@ z*iU&z-&jHA?XAQatn{@nrvKf>uc?~XZu!K$5q&;JotkjQ>qXs$@ec`?94i?*-G)k+ zyfSaEQ8hMxcdlSlL5h0QM|QQ%9D8;cwh6FG9zSl)gsuLG=?m~Awphv4OF&6upu6DX zgD0oh`Sa%i_^Pd0*64VPkyrQ5j~})lKYj$|a(oEKn}z`zz8^C)<<O!K4uQr?DiK{y zsXGg%-^!rV(npU)X!hpEvEl@81fXQGR3E3Z^UxuBOy}-}uwWgE;k21H&|5&c)=^Ni zzlCb-Dc*rgNjDZSg@XI*6&0lyIGlntO9j(QF?BpP@MvHoZ3U(hmLEQxuXQ63)hyXr zd3bmpNO*|5y1D`or1O%x2K;j#(=#87oeu$?AFG#TQ(I#RWRUD~`Z&LsMC|sE4jXm5 z)INqMv9VO-rjimC2*(zHY=C={4hkcl#cMgnvR+~Ia(R;KEdbAIYqnV)$#Wb)iwmB~ z2h(a+z_`J%H_0e)GthqTD4<6ZkI@Hm;3SuvF*Dm|QWJh|cnniX^VmK=KD)FVv)b3e zuoh}P{wds^GBkz0u^z~Qmh@}cTKdy4N@D~m1p4I!6!<A(dRkh^1-8B4zjxpP%7sdA zzxFewA?+AdF47a=R$agrPM{kyh&423rLXUa`7+cuI{-ui)d30SRbxDg-X3RwG|=~! z77bGZM0|O7Z#Tsgih697uVCS7xy{PjI(KBB(i(6AU@eq-GN%#&aP(j!xfvJNOW)1Q zvVA)NS6yd&Lq8Tea-(9`88%P@Sx+_ze8OTpOX~(DPB-~%`!J3FzjFDtcn?@kJWfqr z>+S8WRRwLuR%|5b5a0mHdc)p=;o3>66~0s~38$`Qrf{cMN2IK--6n5zVHy45!x1kF zq9a)DXlr|F)qK7Wbl&(R4b`t-OYLW(H@K*)n;mu$IQ46~%rB$V`{==q<AEGw`rkGC z+dsajt66X2?%veZvP+nWnvVCD`q!_Yx~{G07(=_TF~nl$E*ENoN#RBTw0;Gd#@)Mj zm3pWyO#O;Eu}4Z@e+N#56=;FbC_jPdWx`cTYB?s3R$>VNxQ`~fRprR#*C=QJ9~^Pw zfkK9`iRkZ=oEP=!X<(iaKsXRAOM>8|OJAH>6%xHu{O6|?1mMDK<SX#{^$?JY>S;tP znaQtU+O{pfz^w#xj%Wb&!NSwf(0DGsmROIIkn}}lS;7QL6BEk0cTeb;x{xAlu6xP< zw}n<vI>0DmC+G^v+tZvJQryP)$Hm90Bt3-#0Jy3cJW)U4^x^2QZ9@Zz2qAvaWD5g( z%e}t?K+s1#jcD$%hJI)=UQbO+I|$9~+U>#{(D@3(btf>_7c>>XvP;-8h|N#4anF}8 zI|3`f?VrnV{~-*8<R!3g*eX=l3{uDqKYX$4W_I?3;i}t?5djtpyY@4@s;#BN6<@9- zGN_|tkRXJE>PAKeFA&9e$1~2bC}uX|-wI0TGfcTB9-IapeCEu0Lc@~+18N4xkXtVI z1vE+-XsDP#D?;<War0&`qL4oS4f~O>?I#NE#fD9r8myekkIK1eE*w!vQXSlHZDeLf z19}VfX0u2_x%$1p8!2ZKzFb$mwMK>0#&u#H>(_6~moGc@Yw}v}kIzlbqU-0Q{-Xup zJae*czupu_#p@L!bfa30gL5t}d=t~nB3hkC_t8)b2?^Th`S{XbIOwnsYg37VVTWlM zZLB}5pQU}Dl=~MdRr$SZS+?toIda`JI4EBeAze{mA6#n5N<AI0si;WW$b>pk^_j=# z<(2%0EUg2RCAM3mexD}2n@c66v!4KGKF!ZB$FLYtQD86~r=yT)4sbb7+z^ySz;Dja zu97;j5+^ZKHDj1*EP3mB9PG=E9TY-kgMrA-0~pJK>jkXq222OcSlRfEXu8~Hba3#A zH6Oo;wuCU{VB|n{wwO%kqD(`-4G$$h?D9QiM!X;#_z$VnDKAjJ<Wv@58h@9lD06dj z^8>yMQhsqOaOV!fD*l~OmdUobd0~|NutI-oRIy1@OX~|PO^##Q5v(oj#|bL}I0wR( zMaj#T^VEw#Rc{6r2GZ6mC1uF4)E)BO&tS?QXXP9}JAlm%-uBBMp7O@i!5qrHfBzdi zJE$pcg2imosw&xu{vA9aK6Fie2MS|s+fva_k2&!+I*7Z36JOq!C{+a=^M~5>Fcx&T zs+Wk9w@Pv`&|>qG;t72X5pCbfYZ%oa@*rq6m;}r=96fpz#m2|j@)sXEc#k*^-b4o^ z%@evC7E>wfuUxuxknlj*^?>}wYxxGpPv^{Bc;fx8J7@LEtJ9KkN|I&<%MDo=H?R#k z$(eJi(y>Y|74+$+$D~I3s?uJIzR+tY$}_LIo^5?}=E0zu(cPxK@?YnFaaT5cxHS4= znNdxo-yEH^T~DN#;Lg(dM$UZ-@;mB2-0=0clY0xQP-<t;-o5+M;{rBqtPk`>n^=E# z1UC;Nikl$Fv6a_@7sT!T4CrMl5*i-B-EGz#f8{$skm`$^MwweS(@^zy79EB(;u1Ja zj9>|#-v&grMN>X{CmK{+Z1I43bCTLWj&>ILfQWku02J3Sm-;UDH*^xOFerLysQEy3 z4+?+6{bN0*8Lz{=VBdrJucIT~F!qaD06a#B58NeC#{n3WIXrun5OCW?HleY5g>ML6 zFt@PaLP<=>c5uptFJrVGxda_n+8PguX>)7ehlU2*?;j6Nj&`#_sVN$oG`h!UU4(X4 zrb9%)phBF=u_V=3b#+ghnH$w=^}zL__LqVCWX)r7;9{_NOv;te1ssdfUV>`xU!{6R zyn`W-BR?;sKNOwfE(}<BLxmU)GoFc`5Uy^(<^s_X>W}=BB1w5&KE7Jg1HDyBg7wEg z(2aCld-8O{u0w|&h;0ggG&}q9W7B3OF6FTBwMY3sXP>a;TU+R`j1pvQ^r^;somMS3 zYPut?OqE&uG^bDPa2GDrj=bx6%V&a`$zfpmrQiw8CjJAxEg3NY$VO~;JxN$gO0<Lr zXfds<_TwpsJ?naUditYqwdwCJEj7u?e2Z1w#9jfj&$qztcHTQV81CtSo<lR+WF0wa zB6_!l-Ez5k<*f2P2K*>gDko5LlLr_L<`W=rq+~)T<mfMHZ9_0l?6o8kz<$%K<oDIp z7felkP|}$Pdl7hFn@1K8Z*tQb_aHyNb>tX;5Y?pQhjJ)*R96`9kzNUnJ)Zx6OjcI_ zR^~%rh}Cxi4>Ddr%Gp^2;`v+n={qb(@|3Ts+27d^T-EzDGxM4%R~cY|yBN@#xT3D8 zKQKC~XE?iaiYgelFn3X)<?H=*Xh3I<Is>UBG#2jt)brbK(A2^!3i=??s@!k@%U~Lq zkQY&NTM3ONL~Pzbtbv^#-?b4GI=Ot<KHBLx60WM`s+Q(Fefk1Ia0&&u@d+p{aC&V7 zRYO6$rJYUHrf9&%Zm~|Lv+$U{5mln9lGr8_38A=6aiKiRmM^<AH+k(+0Bcyn`}u|F zmUI?|6DO|N&ZMiav!~RBp5)43Avycn*@9K=@>Fs>b<yJMSz+w$J0+YqDRGe>e={-? z*6Vk5>Uv!x0~U<kqHD}hKK(}J_SY51zz`Y?@>4J#&<~AOFB_kX45_uTFb4%&HBUEQ z_<+8f4~I5sizR!HhX5Uy!sEl5GA~EwCJAqbv)gjN-ncM8W0<|&#)*NJs=5ba;g4Cy zYv3;CNr-~h>rcWGhfX>{pj4B`NJAC2;120!b<ZGk;S}dXfSx-TXzxWu1wdoACCYX~ z;`U*dEnBE@386;20P<kUVgj#BwQ^<C&v~@|j%St=RUT=RF)M`5<;dsWUN2Z8zJTBl zlRuU}7Hg`jPhc%m`cu6+>xNfy$r>!d$nrG=AE>?_uQpE^nSdJORZ9ya;1hx@I6gmf znqIY87KNf`<=wMO_xM&&{>ZUPsJqLQKg=eIyM4n6@&cb{t?%*ckR&x8`3deq8vGex zj<abx++186*x2YOVYfpHYZR~)AtY-G%E`*fxfvD3Mj|$#N&Jjy=Zg)Cm{!;rLEn;` z#KG*=@GA}i11V|gOgKudg(?Ed2XwbDaTr5k*lAFD(Q@dupZQ$;;A=_aq;xo??Gh7X zL80CB&5#fJOps}CHo6YXM!-c%&E@jyG6RPP!W)NW{BKI`xek>czo+}nqy!EZ^;w>{ z%TDb=Mu^Lpo>?y~{jN6r8cv6W^(^{>y<T&2avN@y1skj0lnT-<SnlQja+4H2Jq@e- zVU>`e{tlDgg*hcLsG@@%tJt_i+?coASS4*_ldhIt7OTicb9~p)ceO1d=Qt_-piVCI zR$2sv#wF}_oHS$Ihc0mYpDo74pb)%&8s%+M(`M)t;jmi{Xn?I~+7izNc4Hq*x)Ajg z<h0k$1xxU?;U3Up7=XHTetZq~k1@Df3Rc8A;76dn>2FM;C)x!_f+W#dJ$uGP`X@Lq zZD3{f2be+%(F4=YO^&f_8cnM|u^yejF?OltqMzJX!aU<Bu^e2zgd0N6Uz|xpC3Kcy zZ$98$?(5lgj|L)bB2Pt|xq*en4-Ccoh$D=Ux?3o<gNh;|r7{!-e6Vhh_^c(JuaO27 z4pdxas9_GGZ%LcD<|6eHz66ia7v*}}1mMshL=I^;A*t2Z*Uw$!h`UHAT~H@K&^NxU ztV}*vBfCw=>V>zgO|=FfC@Uq-jgA8;=%xsF8&+gRG@Yck;tFnN>WIXZ0qB7aGnTy{ z4;F;YJ`n1m62fy^$1awy^#_=EYbS8}Qof|+qyh-(QxEu-5oG|#-fy`WFVpLbIOM0+ zE#C1l?_#oeN1OuVS|&aUfv3=Pj$t^=Ecae?^iG12fkH{uEn$WR041KzZC$E(4vo`0 zGqY0H&OQ=~hIUb=R2GL;;=77#(IsN1+}!RJS!~|#L+5iX?Enyheg1w;K}P0Ws8}Aq zw#<0>v17aU`t=)TM(I$LtYw{kopom|L)jZbOS;hhu1e+M&qOe!u=DvT)EjZ}ud8yw zu!;(JtCFFuZcD!exM<>?i4t8i#iNU;SnxCoic7#k_kEaP%3H~Am*8){8XHB@^s}TS zPFQ7n;|~9p_XV(8*MBOGX4VQU8vNzw=I8ycrbY&M82onE;~B@u7Qiq%92-d!%YFZ7 zV3`HNRK)ma0Z}ib5ZyP9a)XE=lH>Ff^I;aQ;(UA?Fx;{e9u3{G?Q2xeX#f0jn^%2X zg|?&AOQNU*0@r!1F3~n7hk*cWn0BJzPeK2zzJ>w6lRl6SBir?`3lC-vh<h6u@JHY* z=wt{On4sB2EKDSOHFOuNJho$`0g6>{QB*v7&r?^|@ah)RVBaJ#xT}t15#W{A@WJK| z%E`}{1;uv>GbKRSV*>mu`k9k)4*vw|_h@S5Zu4L$o+nhqWxpXwG<xi@D&~Ric?6-# z144BHvL{SsNyn)W*|uh$)mC)jYx8}k!k4|kE=o{S?A{xgm^6o24jgw>W?<}TZistU z<d{;^cbk6G-GkOzC3=d^OX6Q4?5=Cx;(hrtAEdU=F6z<v`6;_ku;1$GsdxYWad0R& zNHR`U%CoY*uaM@ZRCS`m3FUj!m*1AZPPEiGU9ctk-p1owwqPUjyq|4q+0k_?e7n&B zZ5rob5hlR*6~9$#oS2P;Z4MPmzGsDn7GkFQ`b2w%MwN6pg3Wu=%_pP4Y(fWn@XC)* z7P&cK%1I%Jo^9W@^Ht*qflo@CY62)xoX5FA6v6iz4$8>LFrkydEh9h%sa`D?roHn@ z<l`FdYyyj4f7Hg(675mE%IaWwwV{2|Q#Wq`@3d;D&|oV^4~{cqEk#mZo|Yh~nEPhc z^H|8O>51k9wMM#MbOM0&%h7udP0g~fusG_*0``3kb!r_1z7Qh{v<uPU(S*Hem~tk# z?UVBIuTahKVXZ@Orslm;utV%_WTZctgOb_Qa$LtwYR87hIN=B<0bLIaag>L(4Gj#g zqpA7=DD>pXlP@r}YPt6GHaqcTF7$B3wklN*95;5y-CSI>5O`lyvbDhl#PB7-d3bo% zgU?mk$AI4jPcH-Abxni7pzn6LN)aQ^sc<i^m1t&A&o(96X{99z{)#)f=M@x*1VE=q znwgme&LnV@(4}-jW&r_f-DHxihb#t-zkvE64<Ov{2a998PNrF$>~OBx?r>$+_@6fh z4^~k%GKC*Lu6%0k`xPrlZL^=6&b#9EZKIjm$g+jlJ^iI~HdEGBHR<88jddHS=}L!G z=hnBB9Jxt1I-Zose@T=Q6fU~5?A^V(p}J*nd3ZKFd?dYNQ}mZ_rx{p8-$W=LpA7mv zs1QUZDyg)AvgQ63*E=9-sLg)e)&-6&w?S%L!$ANVxsmyP;%k09xjUu3``11g9D=OJ z>V8}C>0@FCABno!a6*FsuTe2*HeitGr`5g)aEs8om|t$DIqCS1`|P+egc&Vb>|daa zCBg<gaJO}~CsyKd0s~z~l}Yd-;Ku}Zz(~cXwEQlMychL2Kk4r_<p;^&N~ZX?sRsCi z4myO1IMT9X(NiSh!W?le%Ehj(P(CSf#s_;hDbb4;rrQZBlL=ZI5-3A>A%_`bz;Yu4 z!%eJGeYk!7L_6<?8$VQhx8<YqOaQ;2;0a)gAOW5tIc5^{+p)lt&@K>!1TEGx$Du74 zR}^48;OO@>UG$zI1flp@3)f*xZ$Mj?am?6LOTltl0cc3|Y`zHlJJ*IeJ|z(;H&_J{ z+0>OQ!eoMs=PuM#(54Z3=z+MKD24?}Op2H!74ySAmbMujKFm~9`07pdy5aHw*~R$N zr^nk)l>0{u99S108OgUzcmqnOeYb8YIeb0YyIho7o?)b$E(J{8+bvu8a`I(?mt0Da zpXG_xTziO(4O?9Fe$H#?a63m^qgULyI(_rgXXjMB*xtUHOFj^m#Su<X%&RfqrbUfc z2ofxaME~*g^UBV)4{YSK#=;|V7pTX>5xQc!BV)1WD3}5QNMmlBd2?7kocj5RvizCr zEI-(?ltWBKK$~Bew%<IU#n=pGFmzPXC{E#pR)->#xqAtFt}vQcm>$OpHZ((x3_i#) z#1{`)SOwixT$TMh)pXF~HtSG<`gtIBRfvf10C2BFtl4%rBWOOF*@=sc3i$$BCA`}* zjM(~+3Kt+4#G#4y3*KkeTj0URIHNw0Ff!SI%a+TNgT3eH?CdeF;9ZbHz+5%#UUoIU z)<N!9hH@D@`ai4b*^f?-8T=Wa0KP|tjL_L*`k}e;k$aHKuJS?)U8pk<w!sRR6WT3z z1OG$QRe9|`*DBXU0@r`zxoibQ&g#|@WXuLmVCa_L0xH-VcJ>BmuN}kpj@8nuR{eA8 zbp1$NsM1qQ#-Mc%Zl7SB^`+mS5Vt`rwO;R1FYQRujR|Ik?`<okWo2<_TmZ^-{M)x@ zMb_Ky6khgfLOt=OlSR^)W$L<4k%g;b$>m`#3Bz;eay=tJ=<*PfJyMDcK-hm<8>_-e zQs7Q^cFIPGUEP9t%iko+rcEk6cIy^iqdTbvV{?p=B~Son7-Y`_@PzL;8?-fJ@CMKJ z$NY<I7#~hq*$4OsbL><~Tcz^9p0vU-i7ws>2oq`cFmG9-F9ARRLjyOodCGAaFb*MD z3xCUvP(kBHkv92vBRqN0z+e|>-}N|#@O<SyT<H1!{SF2f89@cUfJxM+!NImY^smGZ zKxDrRV_j$_qKk{S<AP()wwhKsGPv=rV5b*Rk!<qSa(!|R9Xyev5h)^J!-24gPxss4 z4WbPB1i990GLWIJP7NU)G~`2TeVV3f(Y2iK>35+(x&8{87|R0*Q?dG`R!7`40Y!m^ zLx7zqucn5o>L*mAklwwfcYCkgZfp9Q`oT)77mXk2Wps+3^aOD}TX?rp^Q@xcWKe}e zy2sz1qo!|!MVL}`cLP&fyY$qjWw*Y38C=^tiV8^j$dR0@1AG73f8YXksi&}YOEa!a z*DV{4@@SsbDjOR!J8ZIcEo;ci6vc-jVeF??dS*>J0Qan3;q_1AI{GYVRU&zCBcS<% z7W5@Nvl8v!#DuwQ5=AitKPL8eYRokh<Ea#O9Av-@vnQ^Z*(?4(b0j<W?_Udin(#E} z-Xjx*rpLyvVLAiR5;wq7n~OhENqTc{x9n3mf&de2w0!k0{u`vH=uUUyQGm9H6;yQc z>Rk{5!0m#}M11cqWy>fYr$y16+l<1TDEe(}`N-r2;&Z6L&VO)y3y%g&qg=qoO8^b% z3qN7T&1Bk7a>ok@#z?gQiK!um4`5^6<NJ9hrXz5pq}c;6V8|JB2iHqWmkxEZlkEh% zG)Q@zup`vL8VCWnj*fG2enJu5@+xd5qBlNc6nFd4D;p?3@=F)en)Mt0G+<agn0r!m z*p{uAMfV|lXe7^CiD8S>;|#jnJEip-ikt$AZO)mn%Ny}a#=W}H(c#jvwRGba@9(^& zZ*b)9Q&1TDQN_vnwfFN#bK1KrbJp+Z=^fLrN^q{8sf}t$JsTE&C+q#G5hI{WOn+3n z;KghKt(>m#YlH6~fDsh;<bZ<~>qTd0iJVQ95Fg(Q8~{FO{NQUq4b!xyQ^_H~hC#Vf zUchAVx)Vk;e2X@Bw6<;q9RCK>`QA8VqtSjr>e>|aJToWf4Fo0U_bP4iJr1YW#cR;H z>O*aD1&oCw&tXgi7sCKo^u`fzgRU^fhtBmU%-?>XrBgdE19}=KDl`*U{K|ndwERGg z+@i_#4st;33m;CMYzwj`#1v^uAtv+z*iE*1$MB|HYf2sFrC}`?qc)f?VROFO>1xUu zz6TJJA`58?_w5E^{C@${lY~7Wg8^`QD^HKHxo}|_82i&Db||vaj))|Wl*6wFyTGwu zw){n5M4Ag^Mi$2<8vW&vqsW9F7J;}LHfYL*&P)`{Hl~ghzjSZ?WnC+N%39evCVmZg z;m=(qS^>e~M|`eF92r{pwe>^%fa}q#V+KaNy4s_?O|+LsTvKN<27mdmeZl_;C7=Fp z0lho00$4@>ipys1yOWSgqU{1N1tocy)fXrn$V|xv<OmUz2U<45(_jXKsFDc@hztVt z105V*SjedRK7ILe9qjKeRFl9(uYvUMy3(_bbfLV(KcrHcehBjOzrwHb!Fqrw)NwAH zR8;gizUx|OC?gIjGIp!QtBKd&e?E^qsDx=H(o#%Dp-x|crSk^t;?VpZsPWF`$zTIz zJc^v#b8VPHrNu2H+Sx_$S5Gh;N4VCRyk#$7uh9J@u&6nnGhajO<tL~!hQy-v4bP!p z#<BvNbR62&T>ZvBW0qvs0BU!>*ey<llOVU?g2|(c;H3buYXrXeGao`X<Kt+TqEb6_ z_ujp0K$=83&OzXa+jAV-JeVhMU;{CouL}#;!TQM)dx=(PeeUr0P`{QS>uzSZ2fr0a zdjOzv39Z<eOC2n~690=+@x`-%S}}hxF-bN*0TNp|3-=awR2_J<k20}()1sLW_isFy zNlzhDJJ{P&9aVQpv;^)1P=rSWgN}9)RW*@f+wd<&rq0W8sOkSv{a{gD$z9`sy;EXg zk{&4qW8hA92nYoEHQ&N%<d25B#j+Kb=QVWvQNybVMg>lI1OCPvGPNZZb^ktjAXfpO zC)FiX&u`b)%LtY^y-PNpZ0x><AX`rqL_|>G@sUr+VOyR1j12~QUf{~QxVVH>^fldJ zWIm#_``>|+jOn&CLg2^8$6K3R{inb6Eg1}HWJjd}!mlw!-4gtH)C1l+#i2fnEZ$u% zE@*F3?I#Z?oon-oJT)k13b06laGw`Trb3$k8A+yZ#|*_bT&yWk_C#m=@x~&X=|FY< zUh_pO#{vwCLPEb1D)op!yT7zL9@7V+j$9B9w{9gjrL~5W=xDVtdteW(yVwy>3&f{) z9_SI7MC)=HI*?|ojTvfi?XNJBM~ZPA-uX46pN^{@#w|o;PD&>fSxBoDAtqFg(bE`v z&@8aqO9nbHQ)Gx+l6yv1AuV8cO%%)aOEvi1jV|Qx;wvn|rE3LL^;mYtz~YMWGSET{ zPK&ZDRV$a)t{~h`rjhEV6!8@xlrXX7fc#w{*O-V>vDh$=$b!aA@$39<!q}en+g;<E z*o1iuvM)oZIOOW{^`8lj6@NZpF6;WefAx(}Jni})<2TDFvOn!SHf*ey{#Um6&$0tE z>;9MWFK1vNbKNsKoXY0^{uIrNCHWgP!{0wLrJXV}Emt{k{CJu(s|n^yet(IbXI*E= zw$ALt^XFEv>gtYc{j_yk6X`b!^8P!cL-m;d&SqLKJ|;n0TU-4bIa^k(V!Fe;GFs`K zCnV0n{|*ef$Rx?~TgvCqcwyooSYotf)n0|X9O1YKmgA!(39WR4KZ*zGFoGmeBXEpd z3w&w4EO`E0d~(mGAlF}<@<JEA@13HK>CmQOpcPU*q~v>i-|)wcc=S)b^}CS?&Ck?S z?sX#FJooRXi#!I39&=Xom6!{FPNjcf;Fz8s8z2KRm{)&y=ez0gR}g$ht0Yk*C-f}l z#g&#?K7M3EDapsrfBoJ)<=(P2h`Dl45^W1VCJNywQRM&mf^f73gYA!1x$tz6O+idd z4A0Z!Cr_?_cBW5azCMJZgg0-f$ZH)sbOZe!C=mgo<;*?$@5QU#rxR1N?8S>0;F>r2 zV#;rop`jtNA*7R3rIJ)FnoX~C{kW`gHelajK0Z%iBapI0L*KGu#SII=8aAC!tU$Uy zqD`!a<v&VA`zQi0(;_2Tq4ma~=Zc79BCjXPFB<9uU}VL-ZLZ!I1Vdu)zq()-Z#5Rj z^u-L(r)r8@(C^_w<Ve|3fAhOIlf0y!?_@1lz4ZzG#@y}yeaFva74j0<&iWLjT6WD3 z>*ZEEuIyb~4dw8bf8R*M&Avg8A(!wl54NLii-n6(c$?4_KUp#zef~OzYhp3L!=z!` z>J8WV0KDDbAaCZqewX9MyLrvI>H5y9Pt%I7e@W*VDg09SY?1YZhVrr9%jwd0?A@_^ z*{%cE8yTd0C24kEJ4IXZetyw&D~Hjh<*^&PGKMPm)`*x^x{Zh&nh=?p?IJ!Q6uDCB zob=@mjoNdeGfNcDFliV`k}vt4k{R2E=_rX}R4@p|*NZrX725K7ji-*RpjfcM>XLjV zGA!4nyzqNfx~nMtKU#p3f7~STUw?4_H&`<qLDiB6;Aonql_o%i|HxHlk*mV9lH$oM zi9eYOA*UjSMgRP#y$CUeRTcS{raQ2yv{1ISeF_lhm^6Ozu?q1=hUAHPImOe07+>as zyGeop8#X@ylC;hh1g~Tqvhc8HUV)$ac7~oJIZO<5&`THzJ#zeT`fvr{MVZ}wf^gcg zjgEJQ+ZK6M{1Q;wx_uN%BL(k0(HJ|=U_;z(aErjDcRs8(X|)n!CVvB;1l-YCye}Z8 ziZ>_}C21H<J@)5&cuM>7Z?8X!(ki7t|9~H-aQwZD_(x&nQ<HDu>&5E77ZU$`2EYIN zn|Mc3fA0cZfaw4CHT?S%pKju^{Qdg><E6C&amEv4ur?HLo2}s)p9+sROUS0lY^rT1 zq@cvc4P#IO!oNRw;^h&6Wt2u<iTNKlP|6VlWf(0zc|VShfp`>?6vP{J;)&QgEM1Pm zp&JuLP;lrl4jX2-I-u|)9(QOwg3An);SfVkHsA)8`h0c1zhD2^%Y$FGQwASG+6Q>S z99klt(7vx<(L72Mhj<uV*;WU%z}k^G{UO0(#<5-;>I`DmM{EHRUDgk31Of1kON*mR zFthPnPu4w!Le|N1b7Y^RYJr({GDO+@RR9l@fzUbj*M0Whrla%MU=#ROBLFMB?Kqek zV)RHE`X*c;9|Se@AOHPo9iNDk<J2A0uJ9y(3|wCdN+sHL>m2uUBk>F9-R3P@;-RiC zABIxyEU12x?+2iZgn(g+Q1$rno4d6YfgNI?nIuvX<0G)84FG=k$`ONQ)Q$0&%adcQ zjK**ciQGZ!5w%ASa162PYJ7U(wMuP^NwztFdJ>jLvXpVrh+P(lk3y@b9#BJ*lt%K# zA+N#6R0_~XJn^-?zfjt$nV7_b`T07sI3)WRqfNxz7!!6?CfW7>>rRl{lZ=Ufxa09S zUWFnCnz2UAZ$kGVU$}wY0~YASjt`8r00v*M7NkJRnLljUi;9@!!U5EPoxyGZ>Y)Kq zNc@9Rk_aHekbN^!y5t@q?gpp-OC&uZ$&p0P!Et{b;=j;ItDzX5!p$U^ADf@Qh0xU! zV}Qge9hP_w<t$G7;1x*1XyEr*Wli2ca^{4WyC1n-_5|NffdD6Nx#REKWox*Lh2rUg z>m~ztFjcrY!h3Uw<{8}OjWabH({+xN7h<pgNv4m$V)Ze3b-~<_yfx@h)G_92B(Ufb zN#dw9Fbl;J^OE<#s<SYsAgLKx%Ls7_0*1Xu4j_#S`g<_o2cbC@X{KAfx*oS60k5fW zAB2Zdu2!yb)fQrOZY&JgJQe+7GI}Et_lB9DJvxc`i{OR0VAVthXC1DBIpl;N6EHFp zJEh;B<i$VOAVXI<#V7;U{~dG{d6aWN>SH+E<*~$sr7Yw~zAJp=RFWNTV|fBH#pa7o zxjlc?s#OSS&3R?p3R^P3ea*1P7@?`&TTxyv3jiCAqgF{%r`gXY$;p?%4+_?NFbv@z zYHO9PaT;u_*chh3czpsf90TASf^AZW;ryeE&z13vHo;O$^tV?nMgNTmrICb`lL&6$ z8w{91#LXo3Q)u=MY!*^rwD8J6CLAv9Wvb!(0{SmJQ(UYbRmbB~4*IaU*FkS%Ec7VD zY>sFP@TV|pcNUmi)qaHEe84tEf^rHvp5wX&yM#lWY5~jxPhJzS0R_L*Kb!nv9&(UC z4#f}RWp8$?_GhYKFdJ!&3nbni4>$?r#kwTEEEVnTMeh>_@$Lqo`cgMCie-g&8axAu zbtI75lH`^P*{uLv*h$n56ayb12`0&<_*esQW=LxgDne``$=Be|UqwZweYqX)Y7jkR zD8lw&4XXCdNa_Ue+y19GM`f7SQUeZ!k)kGhpfB|@%46@}hcuL9jW{U3_VmQyJ|a22 zR9;3LxoTJ<X8@(Q08v5~2C+Qn+P5#U(S@u`uqnv-;{+IhZK6<nc+W#fDIi`>fG#r) z1IQwkg@{p6D|El2t{wu*%FkECLs9Xo;LZ>aUNGo-WA^*u84MD9j~x0k(<GIH!OE?H zj96<el@_^NC<zGZf*6BXG_eTP`M`(l=0oXnt30(Xe|oWO<*HRFxULqET?B`$F>lLB zI>jDH{PG}AhmP@#@$yS4O<XBWS{6Z95#EHB#a=DIJi8T7f<lu_Xe>^je#j$@n<W4{ z125RVq=)bt$T#53n|$isMMjvCLEbt5o$r1%>bEA@oOtFzgZGM;BH@O3w-Uq$n@_s1 z0PcKZJ=?HMj3LaVZJ$C;A{m1UVFeRX#B<@iB@H;T>2hltOtp~Ymq?-+pdjU`3M2mQ zkOHV6FvY;oFc?W6aBjsPT1d7CpZobU5k79ekitc>qL43&U_%l_$YU8{ieyk6oy>5e zgEdPIo@`9Em?KcTH$-s^mU>u_QxzOhW;ji8QsrZdaDax&8rC-?!we$^RaU0h5inW_ zXRR}^j#P#}D`v|T(muepUL`OAi42XK*#qTFvd^K)3bMvx3EN02I`Ake*p`x~4r1Ir z29KnOOpYE2n<L@fq|+?Hf+RkOcpVb6ient-QRVB`F%1Exh&{0TN_@)A@sw&I!<v}z z!JASGthX`*=1KL#m=saNxwRQauINIzVGBxL7^OUyYdBWT?}#f`*J2XZDW^ilV2vw- z>^x3xL7*5uG&b{aDCtJ+B9Nc(4=I@%&mHb`2*iZLag<n+f`=Ydn{2D5JQn(BB_w+; z0Cy6)C}eu()Ete1j1R$Kv07~Qc;+!XsSzAviV96~!T8VSiPS@%w=V>%-oIDH(SmCV zFJs&pE(}6C!W!u*QD(fKSY~UuJ87AxmQi*qO*MX9X_jqF#xN;m2CpRO_O0PhO@LKC zf9wCXr2B&UmV9|uK5exUkP?K~yTEcv<H7$_?t8C~x0#z>cc3J?{r%SCAcY&+%O}Gw z(7hiXKJAHB>GSuGpX8F@;}Fg`N7YYJn)_RkN+#LPH@pbPG<h)Nog=IIZ}w4?vj6_K zXD`(lg4&nDFP2VrKeMYj_H3aW2H3`uh~uHDUG~@;i-f#2+Z8GM_UE@+RW5!%6mh;p ztMBYtV{V@Km&w;&zLt)}PNLthB>1ayRDNZDO|-~oY~0)b*1bED9L#jDAV%?2RQi*J zIr9gDHZxxgHV=1a;NKi=rYt%q+bqY|quk-xzsfVrH1<#8!Lx(Ghbub_GB<>^+$TS1 z@XO)c{t*7TGUupelw`)gW%u1ZvSyJBt)@4><tCafxf^>-KiqwE6{Yd$-#>06nJxpN zruw=4TYAgNKS<{8=M~r8a{GjOO0Y66!kT|C(5HD<3$Ar4RW%9mA+)ID1#kbko}Swp z<6^4Q)KgG^#NFS-oRAkBl=$q~$4osQ7RU9JMBTqPMDqUj-8IUGKg!~AS+M?ncZVM+ z$R0QnU}AFIzOzlScbIXSo}$Y8pX)NvBHEX@E%D<2xOuqH|7iw1?^S=__TTPdhg_qu zsRv^DkV1fk7vTgfoA|X8YfK#;UhMe5ekS`Qmd=@EKcrrfUWfHQ6<RtE!ji?l;3(XZ z3AGD0#{skZ%!m|dy!!i{Cw_3BACv6_Yo}FU7l+~w$GiN2T6n?YTM6LvE6d9h-rG?& zXP24+H-16_cHwno+)RSDU~0~aqzsZb3s35V&v;`cq*r_4cL7KlaXu#wy7^r$3XnAQ zrawY58BRJ2vgL*PczKc8F_KV;?F)~cb~@R?FkwL4ig8S7XlaF^vTg*feHtb;m{JHy zNazzRNyjKyKgx~E1@9L%iAgSG3S6%HQK><8iTosW*bvr7EzO(a01Ga1$KJxO7^(f) z{ydUgK%lyx$S?&9j2f?Pv$Z{nT1%YWa4o&uVS<y{W6Ax1_OrVBX>677UTDB^Ktid| zKd4<Pg(RT`8yty~F~g1me@xPM*c9|`A%#8soE!>9q248-_M+2@&=coQ4Plc0fZp@p z`N24E*|S?y;rEVQX>F7|8mLOE+}v@gioP$Df`4L04vO*2X&9j+`aB6N4=1u=Wn#v% zMq?@`A6X7`2M;UYoQazboVd(eGI^iXr9axPyQgMh81sSwx*iKt{KJ>ikflaK!O?RJ zur2-c!8ue_>?DEt0`f+Hd|zjE{<<*~-hRI-oANH4m?JL!<g>CkbwuMmqJh%3*W<*B zvdk!nhm0&pWJFue!>Oxzu`S{Bqi<Iv^pcT#Pr5dOZHxMEggBwFH{`s0$@5Cq8O+5c zl4aA<GLzh$ojzUB^t><QO)u+f_wPi}l7j3q?RwMnLwyUMi*?|cN?fmUty;&kW(bx~ zVq?)75xe-wTEtCb9Fs&_lcNcfYKZ7-?<O|sxi+0`nL5aznnd(jB8mCIJ4we&_oL>0 z{@oN?$crhm9otawuwyZWwpyv9b6U@ORP%zZO&=m^aDoKU8Y#%jlNc+v@d^%-^g|+a zLM6f{CMJku_yByI1NQ)gn8NTZW(>5RyA}`&3vfQf>{<l|V(GJjGP1Iv1yRuDlp8L5 zJ6u{1b<5`M+qIK*P_gD<wv$|Sb93|iv0y*7r^k|07;a`{WFWFjZ&2v!BQ&Im5ax}~ z%<$$Gqo;^_l?0D_H)y(Ckdme#R*M%V%`jp%;V2b2=_4^8-H})O?p+<ZJD5}pDI_-L zBo|A3Y2Kc+vV|~Cwmcxvie?Qw&1afq(7arjhJm6(5!IwRY7^+p($UittR2M@#;|$w zUMHs++L`h=R9uJ$JP=c_X|V*FmlGL2$UA!+={9{1kt*;4N`1fh9x0@df>w3}M1+QF z1W#Tpf?Vz4sdzg*L)g4kTHk;}vpIYsV1}G(s|V9Vd2rAvAq&Nr(SbQ{e`Y!BSv1R) z`z2spOIlgtM}k=!p1Gdf>EIOW*9!ThfJNU+@&nv3gyr93sx}=wt{PveSFsS6kT68- z^-<ApcN4(U1?u<P;~SH*Bsi!A9_VL3AKV2GdH8SoFJ6Y=^@NN}$l}W{Bk50}NnJRC z`VBXPXC@%U%`SIzmizPI!S8bm7-epPCEjnJ>-^5k=N~@*Hh8E-P0xNW%w^G<rK#FJ zhZqHQS%G$Qp7nX+(@*Bx@DWtGghThhCysmz)n5ZP36kj}Xx$dXt9AI<kFwjny<hee zR2wLd`O;VFWJ)D0POzQhb>~=JI4mT%)Wc>xIl(<6Ze3O;3S00>*HN)t_a(Pky`$z@ zcE57?b)8T23E4Ol=v1A{W)L>GX~g%=LTD)TTz1gJaDH5~xv)(*bN3~m;<lW7FRK)U zQq)4eTSc5N)qRM3t1p2Zd*x2Ex@+zX<f!`8yLZB$MRc-GI=%a_p6kx2#C@%|ZUxQ0 zTqTO`xHr!?yZFuVIK_ujtCo9;65P&)0!HKZ?vA^$=p6<z#{B%d*(i!=WZIoGmRWm# z{}xhiqVgub*>HnD1VwCN<2cF4L-QI*a_LC$7}#<2+F7kuhyY21ZZ?esHtyIVNi>XJ z$Br4#!%B(wkbIp<_5k?jUsZaGPvF_XEiC-3(a;T^%Kr7Sfa*!&b`I>9<;A+t!R(O( zIFJM$L&JE^0+<f*K2K^7m)*QLpy-io%TQEiP)~xLz#~B)El>760v^DJ6SV-bw_f_y zQcAqM0a}v4xc&4DaHJZ*rjrdj(ln6ZX6Sy=Is_w>0!Sgc2rg0zlWY$LgouGq)38uA zS8GEiBT1eI64Q?{{g{!$A)fNpUU-N}i${nc-K)cbXl{zEvLRhKBBQ12i{>n3>*tF& z?$8OMa#ldbRwlqCM<5Yq<kIHl2c$bXN~pD#4kQ2W{K!9_t6DLQ27ze|1X-%jy6{RY zpH0=WeLC?}J8gXtiD?dK>07XfhxtEc+<I=8Yu#^A1nhzYfRI2}0UQa$vKxvZwPuY< z``m@jV1P)XQWLVQ&c`tb`LxF<71s<h)kzZaheJ*k4XT2hxHdq9)`JbAXqWIr0fpV? zc*-2t1>=KfP0r7~TdW+3O18Se?REzrdZBx1Aqppv;{t5xe;;s@x#QyY+?M#5gUGM+ z`s{(u1R9t*yHK>G0BeQ2j^KS6pGkya_DR^dkPL>&D&Eo!;W6`zU%#;{24elXoD^pf z?SE%qEAnb0rFRR{zV4Au9qK;a=;(3(Tlcwso-uHlHD2k*df<jnc}DFAv!JWCPnrGM zc)QbermBWAs*0^2y+;hC(U;@S0+#F<bxJEIhcv!jTH&)kxUY;m<kq1>jzZ#*EOeA9 z8bJiSP?JeVJm~{T36Tw?@p<q-bL6XD@UQwW-{RrLIn-(z72ImoOzJ84XqS(JHFtv; z_?EA4JqTs!2|3D7${~D#G@J!}k!LY0pKDM?i=6YvF78jau0;xT?jjTd;K<|=+@K^c zUzs8Cw77Wk{h?Yjh%B`7tRAwu{x~LR)A2KF#xheA=_vc;#>d8z?B`t21VWwO05`=L ztfb1GF9Find`M*Y#o{q<@AWLnWcHzP_2vO7CY+0l8gNi(j&SNqDupFMoJ=81pa%g! zwLp204$Tlb{8}1A(`5jDfxKHHN#!nId^xa9&;>JljUrnRP{|-{=2c{5yn*4V57W0I zs070cii%=WL3_-xPJpbWbV+{UUzpQEY{|(6udL&nbCG`d9Lt#uo^tb?b`ZgRhGBcq zo+KDi^0bJ!&p(S@W4woF-OoV-y|Ky3Mj$j($RBxUB{T%|M(NU}F~<pKgG|jF<S~;R z(3!5Q8SrHvP|@H+MMjo#n5bj&9&;>lq!>Fo>j^zNP4XInM?%NE8TvH^3@4~!(4=IJ zSUoi31Vw+zfCP*FqS5CmSqojj>!vV7LF!F#)m}p**cr(0z<Tvz{nyLWwSoX09#wxE zKSCU|-<xEos&ngQeNqa4m?Ry@G1T{fX<<8kjNalejo@(6y<69g4>z1R79$hf%FcVR zKJ)oRcVwhku=7}QnM#z~$OvtJXM3;N6g9xp?C@GzISULG(QX$}p~kLcXBRZPI4934 zp(~3m^BbE-P_;#{#v>uxheyxJNJ#XKw#Cis6!A>X-M+REH}5ie_q?R1QFhX&z9=<^ zpSE7sU*^lr0928l?{Q?d4*>$2A%b84SPNLi9M`4Ap%fPI7*|ona)hDf2u(h^8Uh-C z7wL5Fiy%?%Rl}#*1L5$Q2tP7BjY(SVAmvq2eXHZDLR89<AHZ}_YH31ZDF)<qFy}R; z&vdWRb$|Ot)-s8tJT%v4khxD>To*tC$*+WW{E%G<_?}M379=hsgCdx#&kllcQmmsf zDD3e+Qpr;A5p(vPm)osT{&CSi*T^T;JnUxi-kUI~q2n(l#I%+f&ku2iz&L>HGp$Z+ znFLt}{>TOEBN^80;zhSKa_9iCBB8#bu5ueby%#sQf<^<6W>Nz{?$+%uwViv#zXvii z`@QAY5N~XHZotR1-5S7s^pM5<6Iac{@wo<^O6PRE%KyQDhZ$bTpI<XHEA;0;Q%WcS z(^39d;CgDPR?WM7`SvaT*bQouP>y^ZHTCfnl%vdtOzxlz$E9tv<0uzHtMrA}-<w6u z#UbLz*zI2&b14}?6P-?3=l1je`c=unH5fKhtEIW`yqxdRu)`WF7>%b~4GoNbj2AwQ zyghTQE>W5P*UxuaN{`>ui7S0un)Be2SNPES^9oC((CL+o(soh)EG#=qo#!r}o0u@R zbqc*0%40n8A;I3X!YoKoLeKfp;p`7s6rim<T}Gf-A#VO8K^?nO5{gf9+kfxKvdh#% zfZxyaY#j>(bcLHvA}|Mmsui>|ucanC3@{uGpl{fJlGD}!GT}KNGb;~?$Pmh2RDAm< zep!CNk&AMHhvdf!3FW;S=S0tfb~JQ)q%#Gc0m3!}p@VL?5r2TTY9+k(k|3eFABzFM zl*f-Bzt!zSj7-qOhe<f)G_H6_0%Iia7FRx`aMu&j)*HHz_|gvTYw2Aap4TG%7l!Z6 zNLMKv*=8j~b_y^kgE$Whrb34U1=J^JNUG87$)3Uvjqji1^YdpmM?6chhHr5LAShzV z5lWxjh9Hv^XeUX`Dd0oxbbBCdC|DE0aQOFxILxMOPd9~>Be*bYKpv<QIZW}kNq&m^ zY%gQz5TM{wXpIr`A?p=YE2+kH^omm6ZH)n)l=~kNj4P^x@xU(USzWW@*HoaQ&-(jK z$*<hzVinT8X|{{1Mly1IJ0P#vGO%)u{DJ(iYSAK_h}07&$nqHd(r;&P@C0YnUb)Bv zTJJ``JyPiIE9?0*8zUQBWU}rENUp6G!1Bbfx`Qxf2ECdXO5*XUDK+>NJ0gyk8&4zZ z<iRfnlkbD)HA@}dXoD~XgbKNpL+k33tJX3pCHV?x1iRh5qL!(-pZ`%c(#loQDPY(l z6^9#oX>+KqNcan)eb4ncmez)g#9)i?^6}ANGLvIuf3OC)ISvPTuV6q9f#|o;*H)d9 za!CTF$O$|Vbe;wlg5Mq8oK`FUlx<jq=Re2Z0D<EIZ(PgCg*>PnaX?C9iKJ$xY)mfn zI8s^)om&Q;B6Dz6%y@hr&P)P9z;T?25mv0tYUR0ZHIE{PFA5q9NnG8B91Ai&a#)4% z1hqbEUd?H#@~w=Lgbio2_(eLp0NNq{C`7_x#UoNH3Vks)Es`}hg_#Rztq}-f6vtjz z+yy)QA!txZU}$icDWE}uY65jIL%+_`ULzjn0SUX~pao~+#jfG@s!ziQTCK8lEX~<c z)Vx-$e^;u=HtR63E2`gY^Y_->a<u*3dL!$$Roab&%h!b816(GQKyS;+{=utn=YXq7 zl-ddK1{Ok9{k?kw9@!Mywts%jf3cG*Lp|+l*VAaS(a)B%?UfUc5UuDR<vo{H*<CUh zHk7}DGRR7ZiSqBEFY^KH17|JyvEps}c|4&lBt;I}I!UvCC7E<|`YvJzhDmbnZYq-V zg{q+uB!uXpTjQv=+Yk-}DR<=oJ-fXa`*1v-J$3s(S^yFSgSHV@$G_d)&TbM36J#yH zEIHYxXgH5~T`jbsj%cwEgZDMd`XYAHxy}LsFSpnyDOrr^BS;s))_fkPb(84l$R63W zv(-Z>1Nk)&9%W#lEV!scd_dp0-M#+F2@;cJ%>Pi}!mFFiYovKwN=e9a2~ZK9e7{?_ z?CwxI?ZrWar~CtC+(<xHVHeI<q$T;j>Q}P90aS0nJe!Bw+#I=eD6gtgj#vpjG8CAI zi@B1Lm7%S$P4|~8TuMoVt{bREQmbJEC1ZBBDHc)^dA8j=Ef<G#^M?195ZEb0Poe}q zAHCkCj=f?Yg5T*aksdIZ*KWi8Az=n_D<D9FiWYDcE5){6h`PE5tul^;57zme>Vr&1 zQ4Be_A)#Uy;3bkuQhq?a;UXBb$4yO7hg2Bd-$P2l%03~<1^vm1vpq$l>d@U9+1dy0 zJ*@E!ao4GRY}-=&&veIqui1a@$sL6=X3WLKMqLGGpC3FpZq}i>SmKhTU(336VV);Q z`5?2}gD|e!w*xC=^z^p!xF606Im=q5rL^O`T!q(+s-$Pn^gPQE24UA{9n1pzFgKa6 zz__)eL-l^duw9ml+&Il@<wtbl`{rgtLgK%U^$hHzD3ud(Kr93_QEcoUgwy7!QsCsV zs_e}h0)71gk~8#knf5t+CZuNtqptL*fB*zO+)ADw=<XC^BvO+Yco92mvVd@J^$G-A zGBA)DMObch85RI_Zu$F%)SIbdFB1RNzndgJg&i{W2OhYYl~va;qs1auSFKvR&*j-( zMh`ySf$83+#<Z$GkcFC!T=4kyQIdN7a&PFBb}%^u#K58YOsHMU<)!s&)^N{=bpacs zRj7m$t_5~4g783S(1b$6*~LYdW>Yx(N|vKnY=eDD@!KSajo7iF$IEzs+*>_G4Tpgm zA#w~1076DtSPlW4xV3x+r;;ViUDfPPCEvLv)x*7dDWwwMBca^r8imgk<ESKC3Q8Li z-9`e!xE8Dm+Ggf+v!Xto!=LGbDGy*JzwIIo2QN*;MI8vGZmMxh+Xb^WW(;>5-dF15 zuKXT&{S0tVBsOk7GuVMd^mR;pzq{(^5E+VLKF~N_lE7{`u%2*(z8FU`;ZM?2?n@`K z_IkZ(ezMEhV=CBABlX#It@n*pYT*w#Gn&(SyYn;7xJ}->F5`FKG(|mY^JY%ooiCN| z$0dJtorz%Xc8YRk7PyPLbG&HQWUHI&SLd-JM`f47nuQ-LSSXaion+HV_#WulT6I8p zX~RX7h`2zLAgB6)p70#rP}tF*kFKHw<A=YhFS?%{%5cJ+2HvVwP{naNEH)w1CAmZL zi##}fRgC+YJZxV;fFr7MXbt2sa!`kJ-a<gQRI7#gN?}2Pj>c}2VG?9bgoPSMRk|P* z!p84^HxVEtU>SsAMG#1^(_w6Uq_Cls3=&+ZxKrip@6RJBmsp~BM3I&mtI6W#OfD?P zi;y9*M-I{4+7DYo{)<PGehT|W71fa1fJOU{h8JR>E6D;z={W_Av9W)3Iqr2;gk+M% zbbT#uEoo+x?hT>JR)?s7ghYd3*EuVane0-l=Ue4xksqZE8&<$U2uVv#5x)kbkCgKl zOVluX=a|G@8P2hi1#BNC0TpzjzYacFM2Kbu01)(P^`x`IU}gyOIo6FEqtPr40NiQ@ z7VYpCuT~r1diz(|{m=yZY}29lU&<hC*~`x5Rv9GU9hLj|g|cWFw}9%hg<lmJmKwN+ z4vRl8^;)++k)h`Z>Ew~n14Klv!{jz{>!q2)9r=81{_Yc9AqU3S(>VoM9(1`{b%0MP z@BXIVrOC4ewfVw_&X&h3It4i`qiE~>ZZ5JEmu~^CPY_hCZy>J$N%UhbwH`5YB#SY0 z_i*L5g>PV=AvyunU3|*f8~2p72LNN6Ez^vhiHi%;o?`g(F`BOof*=KJ$ihe<JU>65 z=y>o{6Ga9}EO5-p_k^C^&ja`5ulxllf_(=R6)9^Z_|nu{t@3CU4u0f#Wrd?U_tcql zx^qC|CSL`#X)*VTBV<)&84O(`VNI0;q?80yHEjxSF<DxVNS*`v9Z$$NaiEa;H&s2Q z8P0{MXgYzGkkSdLcEN%TYNTHO;=FBmEO$3fg75~|N=ORe)QX5E<rKWtt<RSTA9SSb z`LDiPgaoBQsxvzM+@aJXn9gZDRC_cA^%GD_!eI+eYbSFFJil*v+?k-mNMA=2o$-Ek z0KBY78w%5!;&?lQ+_++T20-5^VKZ>$)YD$NVU4i_-h{;MH_s4MHqeq0g^S2Cm+^!< z2pJGbfRO6P#6C6D9)wZ~Tka6*e3It~klMmkm#$H)X6M-HTJe;(PMv!XOGpSZ2|w-l zG!It#^D056qnfBtxNLvg&c1zf@;<HmFF8y~zw7Z>O!#hJ_3jy;TvBN0QC?O+ljZ;1 z2^#ydy4oPY_rqRJ540R_@7%lE^LE_#*8TH0sQI52B>nt(DPnH%>Sb|C<5_S_N{X!< z%K~|*+%~5<{lQ`j*Pcvwn0$nz0E0}ykow`OhhB*4B%A1i2m1mmVlgZhRA99rPk_OU z=C&M-SK@N<Vw%aExF`tny@!GPDL_Q3AE!~AH+_EXvl&8St~Z%J{TR5waiZ(B+aQ2V zA|0xleeCS<Z_lhhS=yX+2HD1C2Aqdgt7>ao?(bC~(2XC{!6$7pdth#9iDHRxU|V>! z6G>^3l<$^iQRIO|a~?zM{&G@#&9S$TTVl9)^Op9&*T+Qrf%5?&z*8OewdPnV1ZdkX z*o=v|DNMVZhHl9POaX!3NGW!zAf&Ka1Em&8qg?<@Nn`@(VKa(07ETL^*?s*7IHOK; zWGGM2Cap{ZFGx<%y$|(sO#3QVn`D#2i2(j{n&e=D45NqY-PB`tnOqIP8thPEC?$!G zO=k(?%pK)+ynhIsAK?CG;yHsi043DZQ~REyDy)VRA(5V7qQ$P2pa0pI5#()^8CRWV z9r||wS8M9eFIAh?&{L~$i)w$S?8V7B9^j|;;z0g8l&Dn&_M%Rg8jK5s?}Zn+<X&gH zA}NlXK#NW-ztQLC6Cx_pQIas!B?R2j)y?|WS5@p*G*oWYp$YrsO53rCSiZd{W{WOo zzD7Gr^3+}4xo@5C9gH(EHMPW(C4cgy5~Rn$ceYr5d>V~9fM}eM2vcRegI9+sEga1a zn#&=;{H5`A#q5OQ0=|_GS|<_UaE-Cs7ZeA#OWW@ViZ16xOeeYqBACM++RVbDGtz^Z z041nhnxS};_q&U`oD1>R)mHx1*qF}eS>4-%7}knQS&!M_Ajyr69v)^57(TB$rD^Vj zL_-p03^x`O(nh)B37BHD#6%5YU<KjD(0QqxWy)gG7)?SGc0qELu^5B?5KqLmEHsXl z$1ykX#%0n*qm0EQ9FpGe@Xv`tzypy>3!!Ww0t#MTGo+<=5NHR2v`Mft+`TPerWz_1 zt<(aeK`xMj<U_!&PsK(gnJ|oCOi_H2OwFEYOlO{ek;5~qk}+^cBuNz2JF~c}Fxz*H zx^e^(eG(oc)vX)&CDg#mm@?*eWFulrP%eV{FDQ=q&il;wz;Hl(KraGhdyjyRf_nBj zk#2YH3?b{U-$paM^Sjf-jjN1GZWU;1A6)twGY<ja<;+F*OBTsBZ8s+DzV1KIp7E3q zrw;?aa~A5(x`%rezOt~Uf0`Q!ycocGpQll|_FP_SN4dkrk3U{nLOVqN6ip4IiS05^ z7qa`~_yr7i2QYICAeDmotHZf144XDR!RfDd&e~wk=sG0EsOc^g(BCySF|ojfCjo%X zatp~}h)u*w$N^?Q{4q1;y&c$LoYYVmO0{5C5@;z2*apXYn~_%?627V_bo`T8R#jF) zmH><5WbO!=HynqIjG$f^a0`lx8XXr0I~q@N4RMlzFioy^(~qL?B6Sp9w!KJlH%@Pe zix4?E*9b^TrPnMOzHb>er3;s#kQu;ZnE^~_sOexCG8)NC1J^(RX(}<dLqUN&*|cVQ zDj;-NVram7L#|~8>~a#!wAT5~J+0h_I{^imnyzk_V-w?d4kTKs5c(oQ!lSF#MPg|_ zo4h*!U%`bI=D34|MTMQ3fYV5aokUcm$D*@b0}kj)vGp%=G09E9TcG*BFfN>nV+hwY zt-&}J_$KUno1R{f4&}&2*g^24N8AIiD(oH~O<>$_d*|)+dOD3jA6Bkxe-EovQTDwB z^M^{t<qKxqgv53G0@zAzxASt=7gRsu-BuZ;`&3e-+jaMO<$E3A7{~i#ZfO>%M2|k_ zE`4+{<fWM^+6mFwks_W^?u%7hHb;ps3|)=hWUTd_KEBY!?X#NZb83o`*zbA+fP{bB zYmke{wgQQ6oAS_+0Ps<5wMYBrL--bwm`wO^cjVQ|>Kj8HjDv>oDvu6dY(^j}7*0o> z+)Hp&C;Cjx6e$85aw-<WNr~%ViFkWb|Dji~m@fbhc#LSO01PD%B@-Sc+eW86t+f9T zJ<t&ifgEmvj97s*yks+9$0DkV=Pqt>GC^Vh2d{q%*#AUq)`Ns6K6A#Ogzus>Y3#Se z2uIVX`^*v4|J9LV=V94#AxS?CwJdIK6KR)7W3cV;8JLeXEfy2x0pL4X+hCua{Xzzk zR!{eoErDhk0D}DiJbAjyrq|m_GCFYf5cwE6CGZLLG!_tmavBu53Qn*o^dW;7<5z)g zM^f!$ptSvdhr90X@ne(dQf5VPwH|8JOF7priqKEC1{mYK!}0;zPt7)>@#4dWvoI5b z?@}ZFBDWlNr5}#=#*BlZhZI8I-G9V=1Ikd@J9jVk)u`E(p75dLmf)1jYE`uxRE-<D zv&3&1!oN7qNT3kQ3(-98B5HE#AvODTQzxOVc5W96ZQlY`>w0GqOFP3zd%}~~KCKlL zr2ysRm5^>j@ePezld@gRllsRsfs1A3<tb3<scm9L12+f}v-+h=nQ$>kz$b#UALwg2 zfX?y?3W0k%G0N!zZ(++liOKR{4_&}=kV@VRrQ%>)ZVJR*EO8gHNlYSzpz+G%{W>(c ztXDoQgBxK#fCS$Bqi;d{2q2p!^rad!fW%S^q=y^Ov|k`XD^q(QB0$HZivo))KY&HV z?zqQ%4_noN&5e(btq_BQnSTX!#&MJkQ}}TUaF2NULP(=5!u}FSX9k`%=y(M%Y>V2U zzSr%0TdsoQ(XrzNal2yITsUj2aOcX^tCkq)ycH18f&tMqpf=6E`pyuqw5gw=(VszL zOKAI<bbrQ%-W#=rPq==`tK~-P0Ie3poGGr9#(ACjLCAvEEZ~neMJ-NpR0B_iC{7K< zWG`49K<k_w9I~jX!jOebO~<M|_<)2J5Z>e<BgX`HfW7mMx!9Z*7e{He(!P|oUjK*p z8)4HUHui=-v?Q!TT+R&DOp@HOYlC->y1N!sq1jX#xwSdYrm8zv0jI_JfjPw}{<7X- z%*%4Yg<3x2VkkIkjme)i{)?aKM1T26XrFyVp`^2sLa}xaG>_Oz!-Ovx#4VSbi}e7U z3Vo$!@Jcsq^(2g)GzTEvwQyJy1%T0Syoid(Z$N7V)n8fAnK$BNa%i{C=foyW9!c^% zzyTa^<|#sQjy&sGPco#NEwqu)P1J{Vgd|~POu#6kE@T0y;|W#`+BF4qcfl+AEm%}= z#_=aeD4H@vl>blVkKfiq-?B35t%QzLHa~;!eQ#E=jPcAdSd>pTY3P|_Yaxny6d-%$ z@DLRu_*~F^{wFQRNd$fsd^hW6WgLo7|D7E?@u>TE7lj}oL72eVam1!_;{Esss`WY} zW0}iQZ@P!r1;??kEJ3)uu78S&zh3d4C6(z0F7&`8d;FnfCRqh}(Vf`K1YCdGrfC-t zl}hrVz-=1p2+<~w3QShj;hGbH<KnLzi99L<(IMI#;pY5W%X<hr1By)u_*ZlJp{u|W zeSq+symL#QH7YElv&jwxhVgr#+sRVV$o|N&8ex5P=%`e%79f$dtr$I%-D(xHmC{K4 zsG1%B!%q8uOEZIy+|yQ*L%|2SabRuX1~UvBs15NR%EYq^rF)pTn~PV>_!XTNA22~A zWSKY{8Mj0TS%(psPS^g!4*)~TLqqT+()rs~vL=nsG-Gg`+*I2j!{~4P=}j0!gbE`^ zKCK8xB`IulOBM-aL>@;Z1hU#G2mZpG1ki~ve8?#xIf9h>%-J=`Uyj7uZxx$!`h;l| z)R~;vBU?*n*R9i8C7|_O8$19y)B6x*8;(2A^?lA=-w2PDzcSLw4kzpXr5`nfkp1x) zs5+6J{Sf?=$1K|hKW3f7dXF(cmyAhLFoSE>6n*bMJ0NAw*l`8#E{0T>sC=0Z8r+26 zrG|yGnOP$7>A>fe!kJWuvv2mApXHaKq4P<Gd9ln9ltT7!M9|~;Ig7GY`%H&*cIzZ| zGE01iY=qompD`BoV(OobeH4pzyKA;W%N#5sbP)w25k~+LNi~ghmcgcQKy7&+E{Y4J zH@U#%#o*FjIFw!lSX{PeSuph0=+Y7i9uB!a!R{ULxCM)@CTize@wwj->x4jr3QT=4 zmSb?l>7-1!9vjo88Vnrd`ryw)U7Zc>Ad!}nk3-ZCq_d5;amB+$W+N=2!xzP*x*xUn z&Y1c{90O2%X?ZNobt^O_IJxtJmQy8e`hu=Me+=0eT!eI%`ve)e@)XS=Ll6fIWeTd? z+}s>tQ&Ok&LJibG4(xq&M#iye3_=yRYTZf-#fC{HjA&cQm#5xep({o1NxZ<^3X1K< zzaq{ZkBz%26f*wy`{x=u9&Yk;qfibD{a44?u;Xo=5_3$*R%vNY%<|+qZ;$^gV$IZM zeyht|S2#7Su)B%B&+QYJB!|)}&%xhs7O%HQhsMxPCgy0lUtn8K^f3zs;BMOwpOJ~V z=ZUGg{|uKM-ZyvBMAmmp3{>4VjEj-(TIv|&6Sg!<?1*%4%VhihxnP2g^7z-m4}ZkS z`!<JPy>jJUr;*BH6)(33G%g%_E9M$Gx9;D<oXq?Y$nVEkUs^+huSd-96SU9AM>Yv+ zpZ{utmqG1?K{?s~!`_<!bN#+yqu(}(ls05ZSt7DjmS~|!(Po#uNVY85Lbj4pLJ^9D zM0RD#7SbkU-$J%zPqHQZxgOR3|IV57&YXAVoS8Fo-tRQOpTu|he4gii?)$p0>$-W( z!H5=N?*8#Z5X2d9_6K2aASrHeeYf+3GTMLu*tsO;eazce{#uy+{Tpr)$J*N3z@{v) z9(~i=`a=KF(Xb6qmq|}>{#DEce50;_h!h0uP4x6z5g6c((cRmdn4SIL=d0U$Z+2|i zvW0*zii&rkHi6=R8^#1jacAS4{t|7!MC$lThH&BA@&VA;z>T*XNE4A8nPoy|Ao-GK zf8n%4R=Pj$<oNh~^z5OA`PSLF4dWVji&hA=GEfv<|FV8Pp6U-iI&QN(>&K5Dc+cwc z^0&$&AN`yyZTVbpf$#bKD!`-}igfwkci%zPvwz^ry#AH5<`FyV@NyT@gJ?=teM-%5 z4PUUZ8oq57y5Y$(>GHqwzz(X*rX>tB7Qci;a(n9A=PN9J@bU79faCn{?z490%(bUj z9-SEWJl@h?zp#vw@K2a|cidchkWcz(d8WJ50#uw5e?NZY#--!A)$<gO=6^y@om`RE zG3Sq$XY8Rw{`$L!JruTete|}1y#s;W-$&shad`1Bi=xFk4$q>kiS-IH_*SGcSPk9E z*p^GkIqU-o0tC88|NIl;9NGE!i()xR#~Hw78ic0pkJ7UnU(u{t6T1z+d|~mIk%VEe z2o#)$49Qay=NrgO;9&Y>7x|e*&0h)>Af(lYnuf6Xk&79L*ys@wJfm4?Z+ADDmW^Km z7O>oRDMgxYE1&|c9;7Uo9Y*9A=-dW0T?{V25Pc56vlX8NzBGRYBqG!liYQT64p0Y{ zyfeze58z=>$^7#L{`>z&b>^QB|9?}i`dSpx{SHYM0CDM5J79WesFZsFgczx)g=re$ zZCwu!hWnwR3ZBczGkciz5{d5IC_Eep^Ft-}gc2C5r8|Xo>_BVJ#||F;2Yz>Wf+6rK zwm4lYnW#>bl&Yb)1Z9WlZos6<0NoS)q<ElPq*aLn>BLINR`9rk+dsV>(h-C|Qihdv zgN=(e(p{tksW=pM#=1b1I)lR}31^C{ZpLX$tRdDYNLmKLH?8IL-~R;UG?M><8DO_{ z@mYy%d4g0(8@FspA+nZ@8}DnnfrN~aWr<i~PzZ~wwqS%eA4+^&2trRn^E;jhIUfig zLV&=*%*cl2hKC}lS6C1vN&TqrGUA0&e-ktFz!}hOlfZKz`6r5ezn04Iea~RYfTTno z_^>J@V$fa1V?P|tevD%k&1zHR{G=y~5pFRsXE+`}Ow@=N6A5I1;C-1AkbhB20DzW~ z4<H31Xv6VDq4+qp{6Z~pEr;-NsJ&31w3Eax!cJnquIcOo+8fgt4U}P6yas`sUrbCL zUZ*g>jVPO-qw+*Io<FdJ>++<&)Kc_gpg`M$>+$hIdaE_h#y^+Kl-vgAIHR)QzYy&4 z*2g&Vk)4sxjEpGYDG{rsR~}RWqymrqNWkLpIF6_BR$;TJZZ?C19BXL@sGk_Cau30| ztoSs{yf98%$Q?E`kN0b$g<FXEmB|=6fvi2HE*Ibx$)HI>fzE#d;d=K)`wQ-yo<VDd zXmvr)B!qSXzbTwP-G~DU-Th%37L1rD9W&(m0`^xhcM{Jk0O{yq+r>|_D1P*Z-16zi zkH=zSV-rB-@blOQ*a=V|@-Ew?E5Co9!*V3lWIv@oXpIRHm<cOEkT&Jyx>9>Jkx4rM zwXa<#tgp2u_UTjEm1|fPQEij@%gNF4SODPdMIJd84gdz5BzB-zXfIao+GPxmKUxk% zQ30SKZ*?!g7DB`#qSp=EPgKE1paM1m!;}8)dz$t%dC<R>77OM|I=5lHSf=;ZFz@Uv z%Vd)|<w#4574Ku-zYNjxF}SoN&YNSC`x)6_`fU#=OV_`XK75$^!B(zq%L7W@oWJ1a zF8yJ(=9x4G_y~MmTCuzLQLjXLYwyF#k)J>B+P!R>CWJTuh5$r{pELti)FCF3y1pt8 z{&4|NR@cL9{nVB}%K^Jbg9FJKwgchlN`lQtbg3`S5atf#!$*$^$m1?60d;xQaM{*2 z5LnnEe=d=8@7mR?RpBC-ADyPFt1Eix>pSLJ$V9)PLk5T0eJ9thS>uo^9}meX$X=w5 z>c`^Er0P}U>_rFh8+Cb1dXW=qp8J7;wnZH-;5JOdKv1CnJM|ovQhU4$-Qru!cE)k| zdK1x%;zUvgk_S^CERIxsOsuu09T$Tu8fZ?VbOG{dj}TQn4q~xh!^<IuJV^c`0i*Dv zZ+{_uDxkrtHr5veK~+d~(f6u*ppP$LiVv~K^M(l^l`cgVnBaF3M$OrP2Q$zPgdXGs zX?rKPEQUym1|t$i6K;s|klk!eZ`zsHs@H1+AU-dy7=i;86emHra#;9ml6J=c{}A7T zS{tG}c;)zlTnH%AA?DP_0!3FbH>vA+%Mhg04TwUbkbOgFcoH!QLY&5x>@HPt77lpz z#imp2%=EOLbt`Bz5^`<uUezTDH%d~{bbZEhvtj#=9k(_xP$(j_I7rcbK{E<xY3){g zhnN?boP(qeTML+K2)Y$Wx4KkSUTxD)Be4g)EdqTIz7;?f<ZO?ETL5n0L`Ov4JN5hb zF-dKi*~6WebP@71uzTKr_&~sHzcfMcYZ+a;%3;|6Vp-D*sot@lKVcYi7ug5FPkq|5 zuo%n%pwO&cs{xU_VJ@bM0k9!P4&gYUt`)nzU=+fgW=0|wkL`yatpzm?YU3S958?9Q zvHb$nWRXVbzVV|Q06)UJD4?US_oxvL0vL%T3PY@|jx}m_{SA&Hv9NN}`&TqIAEKq^ zq}h_uG6dE-94}7Qo<@%ihaYp<0_NKwH?f{AeDZ`OHw1HDH>$A;OB#SKQA|GDE$on| z+tkzq&1>r1$x0KV5+}BQNU5K79l%Dx%Wp;IdrE;qsd6sJVDa@US_;KCF7h)a3dMwq zL|TUX0AI;mvYDo2TzOI~5nesG^qIk;7#5L8I5eQin}gXv$XYn^z}^u`{E$QL)0;Qr z;Odl}73RgUNieY!Cz3K7oRG5<@&Rc2T7Wf3&j8|Os6q6i8Ln)?MI#CZXg491cYTWP zE#hDQ>#NI%?*B(l?-ul9>(5l4-)#!6Bnj$B<AH_>H4QlowdHGL4M2}{Q}D{u0}z6# z(5_h%jOGU{u#B|vnGBvTqf}kPKE!F;_Oyi>_Y^yrm9x1LbAaOUda8t_jDhG;<a~uu z1m3hH%(Or(22IPMx@8ZuFH(X>NH1Y#7l$L1M0X-RAfgR4L7r6)5pp8X1fn%1rxI2= znbCwc(qQW;NKVGlV&8%61fYK~7XiSrzEa10o)(?0f;(^b@Ng=gm3#a4()aI=kOqyR z|A7yT(8^a00HISt&iU0Aejq15ofQik5?SUEd^W3v&FH{R1|NW>(55$={dxx43*fYq zx#iFu0~F3sDoe)#?I;B%O-QRa{HmEu!1LY@&jV1FPs&9rQF(Sg89rXOaSi*yo$Txp z>giIH6iY5qT&(X7`=W?Vi{Sk6SJi#vgSf0!vcoz5&FQ87l~u^Ing5of`1yqY`jh-m zzpe4N^3_?L*LsRmWQk{yG$%!x|6fsIc0{pl_t1&EYbXN#NTtbRuY}Y`ZfUD6YFC}q zu55^0CSCULWda7)d|Fiz#<)b1lJQS?*|E_tg8jjUNb%&{Ya;_oBs(Q2BL9?+DK--; zy4BnhPD?GcXWP|#{!DOZS$#umnGN2A;~GjiQJ&!8k$A@STu!T{2EgkC$G24B5{`-v zK3-JjOR2=gb1o0@wX;~Mw&>5-?2t~~61w5>GU>7ZyrV}lW20|x+Fz_-ReKs!Eqt=| z2ZIk^O^xck;MlKpEOjF>A>TGL4?H-11}|6n*YL*LKEeEcPQ4koh}E`Drlyi&#(qia z=Du$6ZN?Oj*nc$}7t9M7;j<m(M4NEzxC-8z<QrQ!EB&hx`BYm>Z2ur$0lyuY<Gh02 z6_e8RLi(-&R*DwoFREZoQ&Gvf|CndS^F1rqO0MXoc=Y^zh1Y5Wv(-I_MTqkWNl!0n zX&J>}jYaEMutRWaqV=~BTojyEORD;!vKEp0d-GFv_xfw491MN9t~@z0Vqk!AdKD#< z;qOb{9k&g)%~(?>zS1MOOJPxQTmd(`w#W%^Icp;)4(#8rWj6ymHM*IA&gSQ-o8=7O zewZX~(<ZJML`Ic7JUG}AIpK5rwwB#O`2}0ghRKl;)9N`ST2|GKq+ycPHwEVg=9R-A z$^D~1!a7d(-<3%utk0w<l-|Ek3VuS7?(ciU*Y(!6ij-^x0)vwzkdTT1*8@_Bt1!ZA zf<?0vd$U1O3S3gOBng3$DAzoT6bEf^wU7kV0y-to6tc_}%a`+lkq0=5G(`atu-NGk zZV^Ot>VvNmEQq4CQTMWLehY#-DMrvG2DyC#y#NT4A}h!tHvw<;#yl&?GpY~+h58*q zmySXES-(b-7_6Z$$%%+rERxrnf@lM^&ztIBaCaekMa-{EfJ{XHXOBCa2NyLpkQnj8 zxB?-Rz{@Fx^Uj3qjx+vvvMc5j#t_4iX&wxDEU6q$Z~(WmMFd1eWa^DM7whMd&KEXJ zsTDgS(@F${Z-4NVlT!|Xq{$e1zhAfDjf8lgy6;D}42Xxx)d$rolW~yJzAS&AVJI_` zlaZl{(IM058D!e_o&w*Wfcz+Ok}s`hbk;Tb)Vl~2WQbrPWz(EVRwrTbq8O%|$|I?b z6W^O^u=^oV!akFJnU<yuRxTk>WI)nV{$Q8P+5_W?zSg)3s&MD413i;lo?Hmj9!Psh zHIl5EE{B@1ny_JjJB$5hg1N9-*CixFY%RHvF%wcIY%(M>k0VPX1UK><picxmB^jCZ zl?&-P^3$a)E!tohT8FCF_mJy|Yl2~Tk9x8!S{iml3J3``k{lKVhs2OEL`Gz!79n~P zaF?h?BwG%G5<&WeaFys4IUp*k1~VcpxT6peGtoaVuI>oaGBu3{H=RqKu)px)q<2^} zfy8Db8=C;eYd(94-eCkWk`&O*RV6|jGLepxQVCfkkxV1)+gnknm+!m-yjLQEJ2>@i z1A(uJd@VG|mASc+&_dwALpbvC%D_<3i_z^xzn=>1gZK|qnrI!O97PHS9pnkQ=&`lT z*qV;(s66=3Oa}3HE494Wg~kul6wQXTx`BzNg<nafK}Fg)JL11P^o0Gv*j59j`LFLw zwX+q^`7o3if4rL>aqHA0#UHkn@xRfG(oHR!of>7|r@ou%{GKp-yLp#Q$$CQ>C&G59 z)cx?=_if!4K{WNgi-je5dGVb(q1yK3NE)k@puE{al?;vZ?{k}Bl`#Kf9~<c4T3JmK zUH;vh*aWT|@fvM&)1Dg6*DwxK)kt&AOM7;@cllBebrR20mZHkHU+xk>2<q5{zph2+ z%YX^vK0nLMsSABQx}#4~+?m3ZmblJ9M%*W|0GW3KmXj#vZ4rE*>>QMP-yQOZXgz;= zAdxKSZj=pDvz=>Cb<TGGMyo3sMe{!Q{<Ic*RB(ibNa{zFV&N|{;Xq#8_X8DQD{d8- z`)`j}45M8e@RVd$pkP>K6y##?ggrEVk|z@n)=ZvupH|6_zKr*WafVMc^j+1Ee>tNa zgN_joXat(c>L?xuAUA)eo^)m#>i@aUweE4dB=cZrMqqV#_D6!9Lj;pZ6z_rTiuFY@ zH}N({88A4~FzTKZK$7nERjeTVutrD1s2xp6)TJM3f2bgxnIN*l4??uageDf|+ZSqy zxP1=&np)9DovykbP&Y1L?<t%rw<3xy2|-!BDL9J^63!slF_%%Ve(2AsK7*A9n(Lxc z8X>|I@r$Hutc>S@;u2*cqrOXZKNHE^$xWG27HxLNm~&CLDGDXqd9#krw=Jwmzd#dC zeSE<F+btf}&Z%yjk;#zB*H=-RZGjT+j$iNqz)H7Ynk!B`;`Iz`shlwtDHRn~w6M%q zYnz)H?2A~~IaU$Ad-!uyX^Ul`_Onx?)O10^KRZQV4!U>w+7$LM%Li}^oqpEU)$d5j zzNXkk77iji$x_`W^V*3c3L$Z#5))iFj+)sryOWCaC%~5=_9N)wu#J&n7Ep?vMi-ml z18AmnF?pE@GZF3x&tu2KAcfVTW`Y2!O+^3mYAV7UCYKm=I#PmzN-{sYqU@~<;ZbE` zRw0of6E6dB2l8kSrqr9MA@p*UK=4*SF+mb#DEbLioOm~Vsc@CByL3s3+%`}v2BD;^ zLx>L-j5}s5BsPmGwH_S^yWa52148aoKDE3C5N5a9lnG2jc$ehiK@reQdwQr>S%&1) zUvg9+PIX4+ErMZ4r1Ol@GWUD&IHYno{X{UZ5UDZd&K3l`H@GxD2kRZ;97Ec9q@i1z zDh^j^5$qj8g;=!10>w6=eqxMZM3$|Rt#-91bD_ixcYA+Ugqe_*NSs0gl+fT%0F}`0 zphW5gzuM1+JwHdkA)4L5z;0Z~UCGF;02<^xeW?|9*!2y=1BP1I$}|=NB^{rSR?L}I z_os&^Cb}3JnzJSU5O>b_vd=!jcA>e|HER}aj-v9DU?z79Xg$57ZB>7d=keXC7AvEy zCIp?7$eZlJ)#sJ_C)*RpXND^avL4fZuQZ07P!Ql!Yf2h4f$uxtPP~+#kh&wb(xZW- zrYY9o-8MrUi;{<kt_iV+98Vw<vW#bgO#`M4vCkphDIAFD`t&;)pz0wS!-{>ws+jj> zrYi*k7TTm3YW*0@!mtcF-~`0v40$^E;Z?g_LEf7#T$qs}G#NRkTh4_{T04o7k<eH{ zWmm6l1YI2GD^mJ;&@2+LQW|n~;HgB|De^T?DTkN307d5ql86UUybUC6I%rb0l^}`G zjfoy^>H)_n6-7KLu84jN{rDt6U=5muvj#cY*?nlish~OruE=IVY{9U_iCk&ATgYA0 zYGyUAQoGBTZ}f6S^MPJSXdcn6LyeezUjYbX?&aYWV!nec?nvbjvFQWCwYa2Vt^o;d zUrOGO8nTjM$i`fj3R^u8CJ3(|`wT|zLZ<J$*FpDN1wVvDT!Ip_%!Zs~6i{P5ODJ@z zzx$u@O%L!lC97}F&&Ns78d;nk6jG+Axg*Ow<(|HP(ZnkvX8XA}Pe&kiWJy;sk-BJW zJ|w4wlU07q?WCRIe*TS}P3dY5f?SgWtu5<D!!8d6Bxk5nX=pf24r+Am%AfuP9d?t# zakVsi{%{~qOe9fxL!bw8PNUsTb~mVwi<bn9JeK|I8_w@8v1{{=0PssB2;lP)Nm%$G zq36TUfoK&_=wnNTCedM_=C|SDb`yW&7ia8XtpY7r<KFpTJ7RnXo*c?nLZ%uJP+QN& zmW<&`Fc5SFGvR^ZG!UP-`}a2zdSx4nMs9Yt38pRwJSEz>q@<)Hep6U`go}j=ldV`F zJl|;$eNpD|&*+nhx?QyfmVtY596(vMXs=tSE(;(^QrIq|TCIqck~r9#&;_(WF+6n{ zeAS5foCtehXdvcTn4Xp<f)SIrfps&+5DlLi1Z3NX7?bZd)6&kjXO9X(AZTT3&d#|6 z4G+<G%V~fG?fz_Zb2DtZ6^KF)*)&WKogrSHz<#KsuEboU35-Nn^WQ&8ZMB6`MP0g< z_3qr4)N$s7(RZsfPlpHiq^7!5(pI?MTpI!tyMXf8(YYCJ04D_tzT%~GU+4<?)6(q? znsz^%5=+;7CT<as{N%8lmDR%vUqNxJcERI%kGV?nZH*tM<Tp(1s&r-8JJ#hdnR$tm zjoeoV*<Nq@WSK=$Pe3TdpzqtC#!nwcaH+x01t>>-YH;-)Mv(Oyv*j<KZAS>u@UW+C z^8oYf9-}>H#f=2!ZEa@&u>x|$!I|kYWkcMuumCh5=t3_zdF2*IX>caW^(JS~?8eU! zk@g^+JvR}uKy^Vxk}8^k0QpF^jCT8nlgBC>p%ui%9{@8<87XLbNHXb^z>tkFQD7sE zjmis83UJkwab%&oN<qg;=T}!qaPR<nTEutKP^&FRh$sl`{lr2l^pYN;S)Dl43*c%X zHq4TEM=XlKaXkCY(5voZ_VslxDygqm!<IZH-j{rG!XZ;Dc^b7zCKBuNM-nRJ<Yyft zbY~dSs}mX-5*vhe@p{K35>McCNOU6K(8Pr`9zac^UatqaB_8Jhyg4ggBol@ys4Ahm zjcBa4;+F5fGJdzHRwb>)NJ{8a0Zji&YiqkVF!DfH`wcl*sj>>Ud;zE2$IO!~nm#^i zPNPp(TUw@R<azvHV0Cej;tH3z91%LE#wy7b*0eMF=FKmX4*T~F{XF;Z^7p$(k9q5~ zWZW~Y+WHVolz?yb;0hl+h*eaRe}zS-kd0C`jRNuYrlf>BM-ZK%C|F3>KfC_`NdgI` z3fAHPEYNVY2~NELQi~B2Q6!%sIKA}?3^FD{M+}JI4w5oe0JztHY#;kV)mYQ#xDrz9 z*;yCT96`|a>&Gpe2InIhLsbZ43+9Q3({J<wm#RVLPl%kfFY}AeT941b;qBW4(i@UD zmZ_y3$}2j17pdI2b5Us0sC9wiQk_boFN#)H?yD#k2>E7is?wdjxX`}6skuumak!vT z==g1PNUbEm$24LZqZ;&qa~8xIMI%wVVmNLKJZ=K8+CBw-oKU>5_Y`k;Nr&rS49W@J zhQY(GU|T^8f9<%|GhmMh4@516L2APr6E33#gKD=H*~KF11OD`)zorOJ5oQEzwcr>a z#!HeOob$+gMuLz*v|Ix7M$&vkHUxfja`Bcccf{!_CY0!srw*6flf?qJmvnsP7>rjz z#-?N_5wT6CF0a`*o{vXrK_{nDYIyc>Pv^F=AFZQ)x9(}@IgD<WR5gl^M+stF7RThv zC*`Qvphb0@38qqeCm)+8c4;WQOxxYW&(Ga|v31l|p>XEZym%`Yqm<M$8-X^cRXV;t zDSh)s%SxS3-ty2P8uyu@&|xc0&C=`%^-~)uCf#U1chCd_BuBtq1n{LXX5twxIx(he zu`1L>)fqCLP3|9#+`nuddAKnKS!D__C40f%0<@Y%J|4=h>H1VRHRJM$<LCaAwxo-N z2A0E}IstSokwiTM>rNM_fugGFeH@`}7mLl)o6KgHb{MD%X(&1UpzVrD(bvfDizT== zVIYHA=tnC~at`c&oYka0hgpzrO^#_+3sC<^LXSaTR2h6iv?hDAM=lt~OWxdH>Fbtw z5=kW>lt!+(o$@%UNTesF5}*)$xI!r+gcBbTa#$D4{HVoE9c0sNuY+M1;keXE6b@`z zhR=;eFn--5Fxwg*TtLQ)90uk*usetYJdREnkxCGq8X5$Oumx4Z>_aOKiuI}inp+l| zHu24ltAuOiy)0_sm9myb-sg1PtHk)TaChP4<E0<`L#`b2{*IrLSFph>y`4w&U1J;y zr-NS2&XR($HnXu5=lY~=L^8s^8YU(1P$)WA7jKz;&!^2GBocBpGKo`5bb+N3XE4~n z^ug#MZqMGrs<_X{eo66E1?Q13UyJua(*=TTUd0qy)?m!$IeV7+)I)<voJCT1wZdr> z_$_g4h3YsKkwL`V+AW7!GVSyG_c7H+dGtS`Ur0c3ey~yqY`{E+;hwR`5g-62-H3#A zRwV<2Ct$=Y_KLxfaJ)|Cbba2ZZq#Yj=pqg#a{`yCkL%Vm8zewatIJq947!qlB=C^F z0crA**n_hE6buw=XoX%`moPRmYoov;4l>A(27yP|^6j&gASRJ;@Fh$a1Ss4H`!B-v z@a3!)x=(psU#g}2#4xoWA&_6AL9+1tjDykX2)Al~V}5?y`=>_!R7krghJ~U-LMmp* z0~LVUx3pL$=IEKmqefpXxo`TF7*%xjF1}VRo$s$AT3(i<U%7lVhavjeGo9HvqtqnT zew1uKQc+pqwAx#mN1@DTEyh?r&_BtM2%0?{$NEJo5fEUZtCe>C76I8I!>N-@$VgFo z0lpz27hC4`$1;lsx4@1IMGxxCCT6X&(e>lNlJKFDcjaT3OurX$i^8ab3p?1lAPyt! zWen~st*B5xa>NUc0iiK;f-yq~+=k0-NC%)6FhGS{QAX4Gtd>E6qXt<m0Lw}2l}q+= z-Gy{_g7~eWWdw?xZD=o0dLV1&P<BiNbJ9>#E5d?JM_1SU1Uuj+$TC%;XFtVcoHqZ_ zI|jNg;i^2o@}pY!a%&7Q6;h@_V^RejUJNdU$nPgA^<ExCF`f5?_-PU3AbAoYm@E;( zdR(@4e+TtTe~fM?K3Gc46UW#@t(H(2pE)1<GBQ$J{{yr*gWV-ob#aoOV`WO{+Rs{M zA4-Ai>OySTH?|F&@_a4nM-dclW(pS)bF6Z5jIERhI=4;Q4tI+le0$_biQ^9qvtTpv zy5OfZx_RfrMdVY4noIa4wFKSVc4udQ9IZXghXJmV?35HmAivgL#FLdX61Apkei=!B zk-~`f=zfa(JO<)8(pDt-8mJmGZ#1bn47VjC^97Cb3}T=~6>p_JOpi{T(>|V%D}O;P zyIBV(RUKsb#191t&cw?(w73asf)q#6>#bVn0652Jc`92y420W~JWf_9MoeG&EURe> z-~>6Bl-*gD;W*?pdecalLVt;5tA=`iaf_}1vvi_PuW7oQnUPVaH1p~Mqn*zYTwXOG zbQpL3HM>~ayw4bk$yZy>e!zKVI;z6Vz5}(-{BhqTU^PlCjFAXt7Rc$;+^i_emN3Bv zBRpziXH10XP;`E?TXGS1%$MQO%}0z+MKWepWg=$i@_&A=>izc6OuRmil`g-kriw*U zzpv*5qq0kzN$qi>q==ko1vH%DCL^>_eDVN4^OXgbY5uq#m(zL5-Jlolreh^&NB05i z$mTY;JQ}IF45cl3K+selKiq-S@Y@wQ?m0Vt)Y3`%`tDuAhPWQ{(i?O%uNK^#re?}G zcb*vfQJ~EeEHE?mXwSoMEQ~xzg)YX#>>h4bn-Oct4AdBI+x=)?ap|5tYSVL*c6%v0 zQH$|XRqeyq!}j-SX=#bQIOGl_-!JWJ0!GFQS~JpwT5>KJf4-2_LZBjoOJTaxw}lg? z*D<k$_(Fm`>G9a{>n$zw*pQ$giGttaz24`+u^_btC=~=mft8k8@2|CJbfZ`?1L*_~ z$<c<+U?w1ma46c%gPImE703mj)Z$AoB3sh@LC4ujk(WKBD!~c{CP>)vw(lH+Fx3>h z|0FUhgoiL@qZVY(L|sq1>c~SRkB?wzgh_X45Lvsp=QRn_Q)uWDK0FG}agb8TM9|C> zkYV}3<ASzBNB!i)m~KV+AvDIp9EpY51H{s4)o!9TkYTY&x#^%=>gVx%;K?zyd<<vm zPJdX=r<Jd@rL)s!WJD=U+fu1IM8C9wdYC8R{QNKNhY%;({NQ-l-Ek;9g{ERqalAWZ z)Z6=Rb=mPlYo&G~0|}p<wRLhBPI;|Ar_km}B`>LcG`|l3&X?H1oEh`ae0*lhzk_5- zi++X9Ra78^2m&#DAFSw<(6ml2BbH(~j~W37h!8w4ByS_S@ysBe0vL?i#IH{J5#i=J z@+wFKF(f<z8fmlx=PEOj!E?Yy%mCEXYW<c@^qMW$(%>5%lVmw8-Ww;1sY0Y*3vFl} z;f8~Jqvbh=t~tS<|H!lKm_;$u@(THFQfgwVUp;QAR@v|GfRMlyP)3Y|EmU)o>vNbL z;`;W%$DkHd0|#C?b(4YvjG6{*zdmJM+j3FDIL4r|NWA4fFsbA0&-Z~i9|<U8QG_<` z<J;3`43+>605k~*9UdzF6nnc1kYW(#4m|cGk2Z*BPFwCb$MVF0k>P0&2uKsX*ccsC z@xyG0)Q0Ds>W|zTE|@umuD$K@eu_t#ElS2IKW1*Sv6}Mpnx%e6`bAt`Qg8f`e&UpT zy!~7t8ogh7BU0_#`hPnTh|&MKFtSOtSN4M|WTrNh1($G&s9IR&91&cygvR;Xx)ldT zy=!-Gy1AzRH(<EwGu$mN<IC&fZKO0}e;Sl#)Y-pPl~4Fss{A22%jOB1pM)&XQEC8w zGZHUgKR7h(NueKJrrs*Mi<|ohI^4c5*CM&AA?FE-j{b+nkE?NYI(W6w3?@Dp<q=Z) zKxOJ7$wb3oPOAO`&QoG3L)z}B^vf;*Z9R$5Nia6RNA@s95}hd^;@?Hp5v=!t514_o z?blHak7~p?w0jmoLo^cxfG65%L2+yXgGGW=6I19ufYgDQ&-HchhtW|R$lOj<?jO5@ z&I<tuy>8wVZyCWJGJ`NA;Hm!XSH_7Oox}qQXFei3NCqvuEtfA{swRk{b%*q#$6a@j z1`Nmg#%Ej7jC_dlMZN~GRx{T`J4;O^NO;6{3u$)-2F}gRv@a0mtf5=I?Ks&DsC&Va zFsr*~C5SW$o8vCpDMX2Pbm}e!&yf<2*mw@;E?`<-6;>as(P<$1M7@S`XVLK!=vP;x zV55$$FOgv&+0Tb=;?+ZnH0R!_sou25lH8tQVY5i<FSi%?j%IIyLAVI_k+c@IhY{}8 z_8<H!B2vFF>Z@v+C0tbF4*Kq(#;~#PTWL_K+qZQO;Ur++npUS8G}JnH=9$Zan;PrE z6FjIl?|_(Y?X`oKw+en8tlZ)Q{F}Za*?>u!9#B6O01sdq)^+42)HEse0z<oxY65Jd zRD2);qM-Bu`bT8BT>Yf%vciXyYr0^28`jydgc~OcVfY~YZCbI$SYgYPj8oJ+QU`J1 z2<dt9@$-WK#sL#Q*GxfiuyX+1CN^|PfV)Dtjc9!o{QgSoiULkFc-O-dMAAew8IqMR z{<;quhR0k3$VnV%8{!0XBk*k#m~WGoq(MdN>LkJXqc?fBZ%@Dq^;MR9hLyEwwkQQL zvNJI0aUei(?hEsy0GN>{tHD1VNjcHWiGQ({N&Mam#Vmmnoad){(IjxeVsF3X**Z{J z4dWVla4k$c2i5dba7>xtqY_V2J(rOqq)!76DWWWdzYD2JP;O&Ta=qm4RzNMFZa0#A z3=059oh+2C!CVI>>Qso=mr58eWj>*n5uqe9wx2haQW&EOlxm+fq}0b}nuo^*xh>@1 zR!=SHE3SNeFu$QcXRK$(<+fKodIbvA1N9*pa|3QJ)24I3$7|`#;7q)*W)V1i3d1Oz zZMkT(3i92(1B?vY0b|Ef9zA*_$Ip+75W-0v_@1aQpo%Bd4I-q)Jtiw3&NpIoTlQyH zn=y#<z~m2ON(tx_;WBy)nb0$cyZEsn&=kE()JFE$^_qiW8ofw{QDL7zDprpOJwYlH ze*WX6QHt9GkK!a`pfB5K8Souv_<0cN1+kC>ItPqW8Hyr1eAgT`CgXm&^rr8;+A3j) z!B}y6<CoOx-;)L;ASNNlKzW=P<Sc+<FsERuhlMyAnAxTXn#vGL40KKfq&^|y8DOVG zpiG{dL>DM8QZr?h03eep30XVA)yYQ!n+0tyJ|vQ`Ep8+OI&cax(3U6Ofl?7PQ^HV# zvq}&QHDG~4DpVDOb+Bf35iT1VETRgfcW^PuZ<E3rtTAF3hZC`Iwp&OP?q1B?W~gNe z`eFXFqX>H)4-E-VI6Ty@rf6MjwG6?Z@(Y2O^@3mnd|tbJ9z8g5h4SHPA{ztf`e=0k z33`Mlpx5!?RKTKH21NM+Tl#d%i|fxo;!D(a0Dl&)k(qSbaK!E?xkgKXZkUp`M%u<= zVrS9t4mu?@z1WzBR6ZZu9Oh|B2lOLBez5&<<7x<m3fYGS#m5UMbVG$V^zK{s`!r{^ zUhY&@RaF%+nY+;z@ic1Aeb-PsQ^dB!km|(g)jFD*@yN*#ffIiAggMEWu4qot<V4p8 z4AGDE_4Sv=rV@&1>Dc1#g@o|v>+6GZRqyyc*cw?l*mD`7b4#r47xhDG&<~N$C?s%~ zI)&&hTIMTVJPMTk6RQt;XXnDP|Le8r;+S+JadPP>Cc(prAPPl_Vtymx#7d+jo*;VT zooA%M<i|s66X(grFYuq&l}J1P{w3~)vH-r!esThATo{BBW<LsfF}Xkb7mB>G)kU|= z)}%-C@DRW8mm5T1RU-}(_}^#o*rUOhSF@60ZU2v-6yPREXRw+sW|!83T_X4K0#(<t zU5G^HQE^L4n4Ae=4-4eZA=pS>9qYYqEX;&rFnnJBK&1U<u@f&nSDMB#eA|8TO!%CN zC>^EBj?7^o53X=JBA@y4?8Ln{Z%yw8uiqj#xkCju{;NJNiS+u*GqDazU@rO2TWuf$ zVpsR~h=+TOqR$^sPxrb}YSeS$sFbQ#Z~X#=61pWuYw<}vRIGpW^lWeJI2(kZtDuo| z*I429emLtr*D{UUe)jSuC8wEReEzrYO=g&mj*bOFg`xs7tjRwscpa~Vfkzr%Y2bF@ z52o9L<q!z!B9>zGSByK^XBP+bfz`e`vGM59x6_dWR$$P8u<{W?ThKm@h4iv>C8uMi zO>E)#Z{qx4zE4pO^kzsO&@wj4YUv&rNWggtH`_O3a>@sm6N)YDQ*f++N6U{+G=6#? zg5Cc>O;T%#vz52Gd^v;*I5id@N+3N+;MaIg{4sQWGgf9b$*0CyimC`H8#j59oE)Hp z`EtHvV~r~)B5Zg<^4QwnE7=>sT9llam<S6SHBcJN6;C<3^;_t)h=3(=^h$)gA*3_^ zrA4W`);ZO;t6jCIFF$M{7KRgF<=CmS82y$yKb-I@B{f!Jn<Pbq{qOUq*z|^6j1N`c z)T7+^s=uYsIjoW4{)SLcy#C2p{Lsi~&pdgwcCu({NWW!a3FYKJrrNP-iO6ID>7!$r z?z#(LNBw)!j_Z_s`wS^cWcS~d;h`1I-l3cp<J7#FQZ@Q_^}aqrv@<#a|AThs|ID*9 zIRuPPEX?-?uEgsjFPbjiKaQ=o8P6(Bi}oqsjvY~C*H|ML!T{TcCXfh(GNE$Fhy=A3 znf;wVzbJtQ5_k!w|NUoBQgJZu5El>mfZvzSCuCuD^;j}14Rg@8_kgt}U51Q86>uOL z8-SH~(J$KUtJAm~@1!ZT2b@~cmcsf13sycFgu&JLSdm_kODE_Tx{>}Qx>sb1PgaX5 z6JF!vSz!!c+wq4T8+izUV>=dHmBR9sE638o+r+~|-Q&;3Ab(N)#}LHZ6aBk1|M$1C z{r@}`uZahh1Ue!l;U`10LNXpBQzYG0BuK{?d}7*+*21p@NXsC4f`4bBnb<6XJ52zP zNT&{2h*qzLy1E&hZKp9uIEr`&IbK2K03*hPxVYaD7gN#W?L(3oHje}aR4v=3e<C<Z zZ))st&V$GY#(tu`8?+CgtUrZj5)->pi&_A_gcHUSnAr1`nL*Hu06=9b`STHo&hho8 zSg1e;+Vb;=u?mh&<}r8hZGgojz-|Y!7!7-Ea;_7(158^aE-ij`4^Ms^C>ji4#gJ-- zaLgec121;q0h+4BWgp-*=zJe{<%1;)41#diN4koMgDXJt9lLketV@9kLl=%njVPZo zkpBQM9R$NNn2Y!TlBPJ&0}lDLmLal`fuIwIczH4`fb>BL&&AXoz#^Gn1`7q^xrbb= z5e5*%zm1k^hRh!VRuV(XPDEOQXrdD+01wO}2mCa7M$-1j#oocs?-PhY8+aTufO=Qw zUanpL;>09Q&bOw{X#C)^5I!6TddCAu*^4Sr5q>XqK;}gOT@$ZVb{IJO3g>_cR2Cu> z0{H{#A~Hm2@YkN*yGiYY1D*^lXdr{Va63(Kw~-S$Xo;cLgb6X&H_;@WAyqjU21Xh? zjaTkqUTz?nngDonao_-%+knY)#D5weBKfi|2#Kgo<H6HI|5sz~<CVqF(Xm+ItblQC zGQ<RAd=NB6pVnDpkSqA9q$Kb&S_=*`l1I?5-|wP100SIy#Q@{Lj!!Upq<1hW>x+#K zhzZ}R!zzVVsvxz2*C<-wib(82a1L^jsf^$N!gAS<xKov_T(P2G15E;Sn0VwtA-Idx zhD4w6`?cOW!IcG!YjUY@$%6ocJWZcyfHqy1bSx0A>|bJvWC17{U`WY=F>Ui^@sm(^ z2tS^G<bA6N(ysd;PAB2@k4v(M!J?6G|NbxQl#eTe(ogE&XEg$-S0O2dU3YN3nf-}> z(i6^%KhhIf!oKmV(g`3s(Z2!|0d`i_?{<O8IRDTg+yN5)hcvYWxr#{uVHjOz>^X+E zAFLCeRm>$OC8?r`fP@r~%jHv0QQ)DtjFWV#h_Nj8>kc&Hfrvqvp#lGFB>LYsiXAsL z_u}E=GJ)vBFH$!E5-_L@UbYMo*p76KKq!4!-g8{55Di>1X9PL#BChI&FF?wG@yi>n z#$g!qhWoo>b{63`5P(|c`Jc&Yie5;}v~B^IBO<PgVr7<DjX7V?78V>BL48=4ci9U4 zDw2N_B_(nbtnRNFI=E}3&H@4d!01O#%%liG<bv6n;NV`I*SHUa9|gw)Ie<Qa$VFgR zHBq#ZUBoPmDE5(@s`&QX2kB#ML<51rE`AmU{;vPa0mJzAo?AkZPDcuh^ARe*i&edh zI3P_I52v7z5Pk@+kc>d-5du}1%E^%%E+qstfl7s-DtNOA2wQ;rt+gz1&G6PRxUmZp z&N%xoYBFjxmR<4T<>#+OcaLmfbh-AG<^puo89%v~mWHM-$JV%b{!!)Iw=!s&;$4#g z6oSs}s7Of!A<!L}H0V1;C|__gCB7&HZN)jCeefnJbm&JA-46;kQ-nJlzxSU#<Dpwd zk=}s()`KA!M6&ao-nc*iBqhM#a}lu2mw!dX{vRbJ@hFXTM#T`LkZ-A2*b&d!r|2`7 z?_9vVI6#80ciFu9pUFyyr{wm)k{3f{AR69Grd!nNwQC#ki+YCB#7qiQ1=6GfWOaat z8z;Fa00Z1@@Q$8gB|rqzcx8hfQi8qYs;@u+2GT=fiJQY<cXtdn`5=aEqMKruY;+P6 zLl&KJ5RgcaMCT<49oFyPzn3Lk`hlNDQ;}Fs;wT{69IR^GC-Y=`J+wFpFpb>}kJB1# zbTVfSCtYLQd%gEaZ$YxE0dE+QihTzJh}fBA!ILi9l^;1rNh_m)oKCgAN{N_MNgN&# z$n3BG#9c@}iKZ!X9;8G&TA&I3bL~~}ia&q;+}<h&cMCAG{+)6kW<l~i0Hq)%+hh{5 zF7`1Y50*kZiS`i^UpIO2YLWj}BqhB*T&SN6{%J<WaIpTbX-3RKp&08ZHU8h4k^ik3 z`Tvk+<o_b`|EKWyqbACpK7ZkYn29USOR8jjP0gmr33za@WE3=Aw5*z@@{Bs-Ul@fJ zA9IEq7!Ht+jE`Tep5OEE1l<hB5CkVRKzvUox>`hjkeFLS+3U0D6hh9^e^rM_Isk5B ztOqU>G73gIA`j2lecx<}N8&r=Ja(Y!@o(QQm-=e-HiUu$Ixo>}Prp#E46HhNjFW={ zUcASMP7;yEeqN{b@5oyq@rYk2ykY=A0E;C!EogH=d3jen{mel~cZTzvS-3`KGML=` z;9(|S)<$m(H81figtkx5v2z6q__`c?xK~@;es?-gTD-r#2?%BfH(Mnc%Zr(0$F+LV z>1x2BoJVfIEYTT3Ukg2!z0C&!t3ioDh&sea5T$wDO!oh>Y(%NDJE0h!v<oXjlG|3Q zSrGn2@qD;@0R-n72+Ho^z2om$!&)sH5&stHaA?^<|A6xt){%FS`aUCmM-A&&F*>)Y zY0NPVnET^$kbWT~@-=7=L#E&=Z=qS=j<Y5Yj=V1Nl|<)2_^O1^S_eG?Gdz9gs?StL z#qMS2?$2!0s|B?VO*E@MbK8k|;*N=*(r9Bvsp26?Dh;*Qm<+mWmlFC|s0eTvNaq(h zZ9;bmbT~;bdE67C_b1$YTq+Z^_X%a)?D}(Xeu<kiygGU1VLw3(9gzw?#e^y{-WWW# z+r$%y7(Epge}=G-*&i^?;O7QV36h{PK)qWPuS>WEAjzBIH&8GKaG}LGdacFW#G*cz zSZ@)kN^L^(E36?f1X9=K;yi2Eu@#!k|0i@N3ZNk(MIKDvGuYXAN#1{Iq?nzE^072S zGEetrx#HhpQIC@<1pvG`F@S?5DCvn${Aitd5^=c~z0th537m(dT}AYe$is;#jyc_o zxp^6O3dst|M3`Vf-2~!P24Q)e=sQ1eZ$)csYhQa6=$+6$s+*Fm-Z)pyR2g-AT6kz6 z0agUh8iU86RfP{i8C)QD?o2A`b(7HxOT5@#5Z=Km9o<)86SLne@9xFSQk(A&P9^Im z&blvXzqE~zUkdHID7f@@Ch8t$Y40r6e|5%*mXYrwYMs@aZGL6`c(<zi;N+v*g;TK& z6S<!vgce>Hasx|A*F1{p%K+?8m1RKtIRqIKdbzs+ky@sgXq}1s3cy%Ei>qv;E8yv` zwFjOOrZ}ARdtiYVwURx_K1^3?{)}FAb()4M@9<Do&_df4`<L?e++aUknYKH%m16Q9 zNlyo#H7I;Q^52gv;DYJGQIe5$hz-nPOe$}rZU$Wx3EKZuhBC3xlsEQ#d+Mx3$K~}~ zuRq^GePkp3A?j?8t#KaOQ7Ok-6V%Kt29NDaR@WZ<{N8V<ZeZV@`WG+f<(|F#k(^my zeQAa4k#(0m%{Fb4l~!cAdxY9&TglAIfbHwWG<_y}b8RvYIt71w#Kkpf`*;{E$08VK zbPNsAMJZDI%6WpOV+fA#=XB$>J9qAUe!e7N`|0&!2oucFdqtV)u$D()IZKBU^m=k~ za@z}11hYLH=<K`-W!Y9q$=pN5Wu>J{DX{EXfeu4?g9c(SH7#wecI>{=aWZG!c4MAG z%o`-`&(W*{D6~`W6%)wwH(tEhrOLM{MJMMP^eXG&Ep_e8&c@td)&IBvACS?R-8o!Q zR`wRO*xTqy++mm60^XcQVBm)9hqnQRShs1D7y1n}=wV;?@>&5EdDwpWw!prYEFr?= zsCP1wHpG_hL<A~=ih=pSrN?+HfF&|43Sn=)8e{udbQl;I4A8{<4R-ek*6mp+*r<+P zYW!7@c-o=`Yqbly7#>jQZi1q|*~Kz5)5yt5f{Z9MKpOiS|M$ZY!R#_eICQ?uD-%-A z>~;LPm2}jSbn_T?KTMK~Y+XNBT{o#{2+{pHv~BED0z*S@qbs=qJ>P3M4$@i2<PV0~ zLq<V^UVkt9F%Gj`OYc!{_w(x;9Z0<E=(Nqen#00z{;90Gy71W89;Qb=;o;}_0+-mk z@LZbxZFuJ9O_{S7XJ@)L(wsYQ?sIAb&znz;jF*FjZhoD0;;jgm>HbzC>|pKjxoI=n zK%#!;n;579k8a54>$chNed}EDhIRH^lP;Z-9p9MkJi&*8Y!|w4CmskdK|}dtMTOfa zy8y>JGzhMugUCqZwG{MMIke?}&7<LR@k{nPj4eKsYqtZEm}ODV2TuD*ZzcB#wr|V9 zyI?)&!qk|mZ}pJ<;?gCc*Z#yz1r!{e7!h&o`0<&!mBDB3V6gK_L?|3=6f$Ay^XJcX zdHO*LUWhgn4_OY=%dYvbKEUF^1ZyfBYF;DJ>^C{?Q3FqU5Awn~28J7m$^GGqOA=0r z%?`US$k^G5V9DKpkd76IRr5S1G`z*UYkw%)-!?ZhgIQ-*V;+B?Isb|{mh@U*x!SlP z)NLbhKPjrNUI&<I7g(|5ax+@m+TcvKy~ZG#OPz04u);#7y4Z0&h+=O+!=nXGidlp- zP?giQHPqE@gdFkE`SV;TrU?h1H>i}#!rZ{%8lvB)#|Kz&?Ern4{CruCG0<Le(T8+& zSbwzT`=Hif=1B-uag8Xizp<7*Wm5B)Y#r-;8wtzua36hFm!FCLYk#=ROYqs+F1bp% zFgIP~{QWa?%YClxW{r*cx|@Y5qenFa23)iD?7sFs@(|V0MC}_l-n=V{YA80|<0_dX z=YQk%XQqTVhd(x^9tC6kqgDp>?R4WPrNnJNs+T#~_8#U9dNN+aeP1tEbz*9o_qqF{ zx^{LBT3YGoLrtlLPdN6ZSBO>TU&@eJr!-UC@yI*Q6BZWlVdmHQ_3Jel#cUH6rUy}H zFV;=qFMfJzVHp1Yo|@Xm$+-+rznT>NeT8=)J{*oxJc}gA+SYbYc7Aj;C&(i-IXZq= z^I0Y=d-v@##E|a0W!>3MW3diXK&gH<Xj>IQ&d#!0yzgd|@~tONIIsrnfC#jXXDY-T zK5=3-F0?RsF<NE9pPQS%LJhl|B75XWe~@iSdHIox7k8q#6o&p$OXeV0N@y#tLElsK z&7@QsgmTMZTlzWIKC#U_W*eyfw}6h2pTOuCs>WS=_Q=2{>Z$S$LA_Vrpc<$<>4DmM zH6(=bn~hWSHQPwOJ0P4e;rZ_d2RFp+KC<*G1^a;F0mLf`4Zr&7=zQ#*51_iyv$Ea+ z@lebp4D1hCIk_jNBMzdmxJ;E#El?`Vo**ABLb9>~+zZ9*N{)T>B7B>!U$}7h<N1@# z5xK8eLrWEyE)UThUuHD%$;D+Wy-#Apo}W3U2OM@@U%#r($mp)sXUf#V96Mi4?a0>c zR1awxpL|z3CfMJq%`(a_sN<2Fv2iIU=Yxqs4a10l0@o!`^aC}@_qn*9*WU7$WTNVo zm1WaWS7)e*kXnmtv{yo+sIid|HS}JG?@Z`?hGtmBtJ3rva=HQGxCC++dX;sX*RNW> zbbP3V5rP9CBr>RjeKM~iq(ht%YT>RBvmOZ5^R%>tk=d3SM0@N08t%_cO;{$+D@J$h zez+QHxpy#mxHQ~$U}zMF5T}I1W|(o>2h*&(B}+|1vy`G!;IbK`6WPAb?S=y4(<6xu zSYW@=OJ-pm4-N|2CMvp#($U+iVJt8_aIZZAPQ;1%ZYaIQTwDXvj$ukTT`u!m`qnP7 zp5<yP)6qK;cs3^wiXnZ4^^#azE0oyx9zD7POh>%WmRCq<Ex0ixYP13B;7Opf>Py!Q z$;okwF6*IRci`42i+H4yU67uNT3g{-HKld33=Gd5cka!iUUTDJ8LeWX(&K=oarMrV z+Fo8o-;|s(Zr@q`rTvA>uv_Ngt1FBBWptI6L_OU6)L$-yfBlB2`(;Zg+go!FG1EsD zRvtCd+R|3w`y~2kQzvf4YV$`3$7k!KPQoXbOgwz>@L?1NQczP<OJlu5%UM<Vy6@o) zW!{aVauLDz?_;zT#m~=g;*_-H`Hy$0PQGhWt>=6f=A#gE?QoPd&MFv#-$Gi#wSD^= zL_R=<j^N>R^!GOn*cSrD+8R>U2~)Zhf47hIA8KmWS5#E&Me|ON@*q5%&g<}1<l^<( zD$PYjOJJgxePvP*w1H;brcj<~gY)OFgAC2FW5+6t?OlczAwB%^t@PDZRXYHgJx@=6 z3uU%SZ!X8ZIL*`FL}V{q*e<A(O#zgT%`WadgFfSzLU(rTE7S2S+=7B@C>=dL4Sl=0 zPdBrlJZEaEX%+XqqoXRa_-<I34_3@s#0Mq}<n(Nwf=&NBR2;i(dro``6&<{_y<K#? zDGAa#)~o*8n>jhm<YV)l51lomF%hZS&u~FJRLggCu<_hMM`5nH=|f+KeAD<fC=`Zm z+k^+|RwaBjP-WOCefHw&^0zmePx`X4C2m>ianrviK7J%?wm~3n6Akry1170yZM}OB zSG~`%rQat}6sN*-Y;G=U?-d5LEo)aEwzZW`mh0~>JDJjcN>+C0=j6695AV1xv=rW> z9nsm-L#40Zp1>{~8%vEO3}hgB78W1iV^T`O(bXo_7#g6HZM_5-R|Az9s14a}bCTbO zhFA{dX2hWW<=D1u3Bpxou#o8;NKL-=^l)%<uRt(s?#2&8D;Gh(VS^{wn#+;PnAiVA zc|nazvJWcJd<!9Z>Wn$J$sf8yqo8I`DAy04%lFsaN-xd35d&@6v3biS<&CxB8bAwu zkSEfqv)B!N=yD`;-`rwsXjuD=xdFS6nvO1cVjga_4G+B2#jwedCl*29dliurX<c@8 zpr88by*on6c3FPOrt%GFG!a2K{|wSTn29tvDzD$XDOGKJQf0bJ{?+VgnIj~yVMb)2 zuD8E`F(~_IhHt&YqDa!oSq~b`TM(RnV%T{1ExqHJu3}OZEiEl)H#2fP>DhW;;T+C2 zNniH0SC1{ne%uH$+nLJ!!u0v}1;^F+Kc}yhz>wC3e3_j$ICiYMaPXk;%NJLnnd8l~ zS#dZ%0~2->y+^~uqjn3QIc#!)swjF`Za^zVPdOy$kc#Fiikf{{qEv*C-u3#A_vkgX z27=6Y4K}VtN)|LVMa9Fvf?~LD-#A7Id>FkwZuj)mqmPU<>y-8_9*oN%bd#~M*$w7@ z6`=be8+74+|Nh+pW~yVS7h^|X-{-hFhE1Ec@$u2XNSGTZ45T<KXv5i=hP;&HWtElJ z7l4@!Cec}lyUW*S%f~1?Iyn`!wQWHM<a$<?ILUn#6rRIgon%7*Hobrw!;3<DXeyz8 zK(0O_C01K1XS90lhYyzR512odlbwZ9Q`|A)#P-_YFOKUW`FpV6X*Vc1H$gKy3s+PD z$@u7~Mhy#YwVeV2bb_h;)*%Wpjwq~|MXzZ_S8qjplv<eG1BLn8qeqY40dI6)Q&I{7 zxFJcGCu?EgwNp1YLw-2_<#&G`Fubk<O@3QmP6so%_%T^kBO`8@l3hcRwr%Im)o?ib zwIOK4^pkOVP|zk%TMjezOs{tXw`smD{N}v9hTU>>SE<z(4`sodpWm<OIdte_YxcSZ z!{RsZN>+v5rn&#F*!*~!NXQ$Rn`if!GjIh5(@*v-F_os^I6Se%H7n)gM`;X1c3DEd z&R%+T1rE75B&;0Uw^Jeg!g=Mq?NNUI0bnZ6F(!8wX+L!46TU8)FJF?9Ll{$c#oP;U zt>3{Sz&zg_@a_XC_xTO=m)*$k&5!uI*PX%f=m#hm+t2`vq`esNmPplmdO%)oiBy5B z%H#-FQgZUPz>>4)RmTSJpad!E_H{Z1kBIfiB%Y%;u$oDj0)o@&j8t^GK81;|#qM%0 zySaMvwMG9~xq*Es6@c~lu9;(B`=#DY6mkD<V4x>Vs+sW-GH+zrhhIlqR6~R8y9Rqm z6($ZI#j?12|Niw`8+g8^y3d&ho=y@qcFzcg`sgcAAV+xW4i1%pamyho$t)%&_F&&- z!HX6agE|ZT<4q@IPm8O`ulkX!<ptMtx;FiQ2i|u+exw>ct*ag@5YFM?aN~%z@Y8|< zdIc^=$4NnjibscfO9IU;HMD4awdj^4$VISl(9>i_*QmXJKkYyEY4d~JJoB<l>XRq8 zrKMeqO<Ny>!4iXAYNg#D=leJ<oESMcrVY<opOURRger@3*Dg{<{2U#v$*X+#ZaH#> z4@lriVSWAjbtcw)&6bZEw`8w@>2L>>Z8?D<^vVux4?Gm3e5kcfE7%t$tuMscjc3}> zh1!pK<v-yf6I8P+Ha3<?)NCV~0axMA1~f-{CqtG3i%9H{J&%u_wRM%?WgDA>>8tVT zGSSs_5(?_k)zZo8(a<|4>!q{AF_&EAb+i=3lvhV|CPsRt7B-`k$c9o!WmpUWR&=1B zW%K4DkX~4B1c!ul!Aqii-Vyf<#WOj0(LSR<j`9X_-E)8oty`K;9X_M2&B@D44S|(T z_z){bF=-s^0r>S6^O-zRQK3a45;vsvMC$Q7ppoWm2lSI)25zY*^)4G5UA*dTqMx~9 zKqfy3c_cJ{Yj<COk;ndlM}{4!B5!$Xg<2k){ggNSrsqB5wg8255_TrZMN@&i2QMuR z=9GJlbL)&R>#9BM>=fxHp~A@TrJI-vC)CS}g9BAW<>Sw+4m{d!Z{~7yZ2bDq>RWm- z74P~fiq4%&RlADce*8eS{^4ql<HxCGIe3TLj*Pr8{O-c>WX<g&6b38p=VzUUs2g*) z$Lw>T-;Yi<k_^qR0-nuNd_fB5jEua{N&47r<9W^@6XFAskD$^*?`HRzrB9;B!Q<`i z?NI%zKJdKy&C2BWNLH{`<;=|ZzztO~Zjo~QNdZ+FwNe%BLW0aVTG^2ZlNI;gfad=m zx~R^su39smm0H?o&u%3QQDk!BpEFE107v!Gn)y;vvI?Z>kENwG6+0B?4iaOBU+wjr zRobD_TVJCZ-38tJ_m-@+$dHh131?f{MqEb@Bjf0}msn+j^u7Vq`*NGiqcStmJ3<X@ zs=!}lm9(P<Y>YP*H~9by_%}H3KjAN80M#$*oVO8}-JrE>k|&?<s&`!GM+{;(HaEL< zWc1p@2zJTBpG!x<XVCnm>oTyhw6^B#7A9(+&bQk(`b6;w%8f*q#GN}EDXU~Ba?!Z* z#=BA)14hQrCnEf2_BoggGb+S9XWGwn_yrTPd0s&sVV4suEXSNDZ*)}m+B#`5D+uWt z+(ag+xEKL3-j;(EDzi4N5)2K=w&|t;uMz>MdglgB5D`~_wt%E>HB`mth3~FRkbC=~ zxR~HVj^GD`ZVb(e1=r988vxx=D%ADopIh1jh>vc!c%vT9T31?1gY)I6i_3lh-kq?W z862_tl6@8Qgf2{}CP`ql?hh1-&?b-^2YR2rXM0u!3!=gY<C#Js#^eVytEwUIB8T2F z1%)>#+*h-(uy_vMdT%iPqm_k1&(7`-4S<7^XA6!(T=Fw%=T{NG795WC5&=0m)XSIN zVcNA6o!HNLmxa*!*0f@3eKc3S@nkq_M(zm)3bKjVLp-ZtH|q}`>RY6`7-CWcWCtVE zd=OXW4{<(`;zGjyW^RU*#T>S_cFyxo8@KGGOl<h{-oX3ocg<t2E?b?)#ZNkoZQr80 zvNw}=zcs}`LxY-o{pa#<+IunFG{3%IjnCV6(YXBD!$K*K1jzx+?yPe@5e@66$A(9a z_<DqUdrLAbR$Yq7@Nph=bai>cuY4)eJF3PRAjkBZH{SyDfOG%;R}B-)ZxIc#BtN4u z0jotXB)!pLSLH*$*+@Vie%9FdCcd+9u33F20|7RGqA=|;koE1*iBaxEBcGCRxUYfr zmcaU3^vuk+k<l4qVIqf)D>^HMLmgR`=i#W=Fd-P<zU@2mKZ5drM?G}xoXnD+fG=NQ zC>d(*)$k#ZDU1NE{zi^%?^iNSF8C+Rmqi;`_B#rr8%)p+kh{oxRy+Nu?gHvtRB>;h zcScQ6y>p3!jH<sRdho@_cyYa3c1F2k7bRo=c7U!#y=KjgtgOqPFXIc+qCp0FaNvsA z!P%cOv#>9DhmcC=<|Y;r8*FPju&R>h;hpRpsmib?@qrxT6Jt^FL+y3N!>Y$~d>Y8u zQ8A0chMh+zkJ`HzuQ{`tjb`k%G!CNZvc%M{;^F+yDX&SA7#K)rssG$~t<AxycO9;4 z4Ghk*ZCUfehgKvgi1*a7IPv?1g@!BNRa8K#Fh;Mb{r1O1<GIP{LFYl$FS%ar1=FP+ z1?2pDCLefqv@bn<n~|1TnD8bFD)4)fmpJr!k^euqds14q?nRs`9|1lDLkw|6NH(54 z2s4M*sIZZ{1OGCAS==!^?2jaR9iyPmYvjP*Fi2I?PLoB}#m`R*n=lxzLMF@Jo^m+# z1{1C}OeLN>ja|S_IyCd8&P){apXZPbk-LO3+4byUz$lQ!T!p~l73cy;PM(UfDkauu zI}9-cvnNO@siuh<xP^B4Qsm^E$jL|rN5b5BBO}uYLzIn>eq00lh#uvI_nkYkpf*U( z&hT+@El1xb!8RP6{d3r>6b~5po<k0VsZP}k)ou$1!2-Gq7QUH(YemHxFnw=eec@m> zuRCic54pika9S>bd%?xUm0{+dQQvEYq8p1DqlBj98s{0Qmox2i*h$18BvfD|fwZaV zj)mvzs@c_x9v@KLwCNDav_z%ovkh5R>EE+B_H2kLDe;i=EdBC@s&R8+y1@#Y`CsDq zb#s>5J4tMDl{jLgw}r-GSnk7Ms?4SNS49?ODLRLmy33j^N>!8<?>#IzwVh+&_1>=p z(}MZVTP*Jks5F?XFW|reI?x3pC+}Ogt`-)$CkL(+yK(E*sy0lJ<qcA}*!F5Oyq2#g zCi1hgvE4&<hFp$9fI^Zc<Y@Xeh2mHih6ULy!07jIw$9)*O+4O?<KGaj2o~#kePlTZ zDu{N}nL;<$WNscP`8_a4f{FE{rMbBeJV2NZN72Km=q=zQvn0+Sg%JoB&cQY&v)}{H zR|2IrY9+iuC_|jq3-i~yNR9Rjc}|S%JZvMt*+n@E0|R=W_lrO^{1^-{=jG?bgxAA| zoByPg7)fAn{~mG_OW`xgsjZYq5TqP&aI)+-5Zk+#36Y1u*>J(pvN%XVL<ZlU0sJo# zjxchfeP_2Cb2EFgz<~p72tC;HHHo$hT*IAE#a?WANk@(aOkT^&cE0NU@oui%Nk+DV zcQA}+A~;Ad_c{VC%d3SuACGyK*#dp3NHe0-E7a<opYxS0NNI7OqP1M;7CLFR0hwag zS&}IhVGc(ZE-<PW8_%UB5X2W17q9$-Qd_<(=|M3?RyY93PV@}kK=Nb_*Ok4Jk_ON> zkdg<gY)aHm|H~tRIP-Cs!Iu`67g8O#$vAbc;Z_nkJVu6Wf|luGdSn?OT@2FNjuOoa z$;ek&E+0F6`X;bTI7cI0p<a&utkWBk$Y9jbA?#EVqXB3}KYvaSFBT>YNW<RMAGTHH z-PoKadgJ5yG%=z2Dkgx^1ct%jYxcUSW=EJY(bLm=0MG8k0H|XpPApg46b$AXr-;Z# zR8py#7v)`ts3?fvR(0>vN$xIUx}M~dU}+)jW*u|2mVvzT2IB2kd=_k>i|Jm!==%K| zo+`%^WB?U9K2>H#qU(SmmKF)(h^ufh6N16XYiEFfcqGrY=tn7<d|oYE#~{K?hd&4} z_Z7HNS9E;;{>5_3X(UkTb+akQanz#|{@Z|}c;<{ZR_xZ4$G5^MXK|2|1c+Ire@leM z3>Zv)=!$OR=8kEzM|AVXUq;Fg*PRH}qU!P2>v|P0nh^1exfk%z$(dn%L+7&Fy_eH# z;<e>cr}tT-W(3rvsHJs#c9-GrLrrV!4m#2U*TZTS2CMVbLH)VSHFcHib96A3mzIfz zrSbD+ZH!B~4%jZwX}|2aU6;2l2ZtHs@})~ySy`DyE}esDX+c;&a^Jw1*-Ie~IXNtS zujFKl{6Q@(tx?~@ix)vQdF)CMVz=zjRTQUCSr+w9+`nXF^9IRy6aAkbsY{`5TK?zD z#s3x>Vs-xK@zfLP@P8in){w(~@v%J8%wX)9MVig8mFw1Regho+&x5Hd&C1*R*h*Bw ze;zf)=iiB)We4(D&jdK|&%>krxF>aZcoUPwiN5cjlm%pEEv6(sZQuImSDbVAZ~1U= zOBEOjpO0Cb$XdI4Mc?W=R09nEdDf+hn?BHZ9e!})0cE_eh{}}zfb;9~Z<bSg9j5w* zC%e8Peevx)K8%ZO;S8rOLx-gYb|IjM9o|z-XOt*od+(U4{S^d1jno^$djjd|Z`V~+ z2qPL=NU7fJ%@xPRb|UHK;^UamUU4z88~36)6%fZ_@*IjpBsRz9<j}{dUW{XiTz5;c zErzz#)#a?15e;g1VKK1=TKsOD{jR<6t0WI+8i33uBFq-N{2OVuk>1w%<#<yMLPNhI zF!OE^rIA>+@<~$CYGz+5O2$G=?Bt;dckcD8P<x*<Gow*asZ9uEl#^?At7fyP*c$E~ z*ZrTT=Acwj^={w3T`{p9Nu`He^woR!)}g|RQsvwHSbRe({g=<5nQ$4NzkGQEoyB(s zyH1=qkwI>2=E48m&eJ<&qAQlXMt(-wy8hN#ygL-AfEo@btIH&-n`aq2&VL)yOLmsc z!J)ymeifbtk}#Bk%F!RDx?0w+Qz|3%@1F<7E!B37G|LxeRFeogI1{KmZEp1XS{cfe zVVq;e(A@8Lk+5W%{?CFq@ZRodxW+=3<n!dmJ5}+T(bauk-&TJLYx|MApx=slej>)+ z2%66S{f+J|LQTr~Ns^z8bjO0N+*`%OH{RbP!oy*v``_Ef@>pY`Fsi$5)b*$149m1Q zKJxzmeB|XFlO7F<9;cEU=QHQRyuG#7{%3X7yrty3W=P@3+5Yq6=c~Gw2p)f#acbX& zbxJM&-6V8xJVLy^A2S>lTk@ZMvaWjgKW~(Lvwtz--@xWY5Mn_G&D)z<Hd#GghXx=s z|DS(Eyhm*MytpgtwnC+FS0UyiHQY`aK9D9AT@!jjNI1#2ur}`kMlWh;2&OxP>-dJe zSmrjMS@+RQ;NiLHi|@+H+JhE*NK<n&PV^=w{9WLaH_78(;v~18o3`iONV9+J(;i`Q z@tcYt$3n?(sH)Yz`ajrv^KdNJwr}_np+YJd3#p~jM8+snsFXsKNRg5V$t+_^N>WRu zLPE+Ml{r&FGs;lr(jYRGp^Wcuw_2@rKi~6y+xvX~eA~BepY7i6RpD|u&*MCfeg94S z&6_MJtJmZE@O5uc{%NH6QcEl(Ei0>8=siZz4R|57XkFFVsx))@UD&}F=_l}HW@fh6 zB=HHI2n>wBB#D)8y%;~*{q2fh*F{gM$e2Mt=GTYf|MeRE{V9$A?Y=`_@&CDRJR)R{ zkxqZ*0{p#5CfCIO3%{=pVt0I@muY)n8Wdc=HNjvXin2EaUP>3W4@7o0KX9LO;lc&b zmf}KxZ|TMnvk3mTSmW@X1?M!ibf}nXvJ3CtolQt8vA#R^xKG`&V~397b>kGRJ$r<4 z+Ko%vBEiNV^3)omZXjB9dyaAHCtO^$h!!x`^=*DUZ!uhDO-=JrbC*CH#liZS*aGKH zUd(izv3c_|agmC$GFEsIx<wL$@sgFW6Ho^&c41@QZm|nRj{|)Ke{Ip%UAC~H8@+<I zzX-1=Z{Mt;tING;_UYy^q+M!xdn-e|F3Qb*J$Fz~;t~`)C$@-}K(ZC}RPNWQ6O&6@ zYKIy2wRayso`eZh_-M{NP)PM7s2fuE<WC1g3F7Zi81(-Om6e6zm?#7M%ZlV1pvah@ zE?jT62G}3<tt#GY(b5vvgYskw0~x2jNWtMkCIf>3sS3~|#6$2Dj((fLxB)vc^$DcS z9dZNK2G2QRg}d`yI04d7JynSx<*~jCp^a@Qsj}Vr#J1?@%;M({tgNh5+?;~OY^~=H z?%AhLME`wQ3p{$F2DJDFxT{!rm^We^jmh<>s1TI(gup+4&cT3*l=*N|?p$1|M=+D5 zVf-8BGjJlZLJi)j=0Qlb-{d$hhLlFqt+!P$_H7ajuKSSsjvb>M?RX#3JKKOh&==8U zxLTd9TgyPBOCjB4xltMeH@*~_IR{kficxJS_xhkdVM7z(GyFOVL^cMy>^H;OTLMRh zvl8$VjK6Ed@`Dyl3$g=DB|MFHu9mXndnxGa$&Yyuc2v)T8$tjekL=S7yn<?+KGEuV zb|B<5#AeY<mH8*TKYTbr7Yin0++wy94_hkkDI>j%A2Z5b?z<q<usI#hVh8MkVD_mA z!I90%Fxs`t9AXGi-2q@1QtbgqV6uEMmLQ|Gqhor8aWEjB8$0jpVJ&fw(?5;$f4X4k zAtCo+)ityqgE&VG7$1_g4yM^+H%BZM3w{p$47AJ&uHEy<Y<w{+Y@vd}m4#J*FWsY) zxRxkXOjt@0>yGh)QxJl@<c5L7h0B)%U`w)EZ_Q|beS0RMOIi8grg2wp=q|aTpK!l8 zCnaS?L}X+q$K~L&XSs!i_gLg;i23297hdajBdf8hO3(gJ{d6M^yfz_xa8^n3N9+uV z5K-}dASc6?c-)bVjcxzIgLQ%tAf>3C<36oXe_A#F!2>gMb3f3nSTSERX8zi^)_Kvk z?X1hi#YKpsv9Tb2_ysI<v`M&0?I&NT8m9E(+7RiDUnJb+Cm@;Y?Qc-o$lEm*0ntCw zRWG}Q=ZuzZ3+x~G$+0+dfJ)1DV-}K{i;En{oP$S3XphO6D32;MS{_hXsDh-xbI7Uu zeI9RHLx1ej*t(SqCMvoZRaVxfs}P2+HgfO&eGPaZ=;8rUYQyVE;_$+;Hc<NvItEC8 z*l;x-AnFLb{&D?;M>trc+TBr~MJRg-qU<R{`0=Tw+iy`p1kuNvy9YMk%X|yo>s@QB zu0_ttlbW$Z2h7cxHf$(oO+g@K1>bU9o{Z^!stgDWOiUVBeReLu9(RlCgz7T52xzTs zTr?YDFU+4lee#ek4ga?A8AzqSU|e-vM#M`^v$3(sA6U7!fCH<B_7r?6Bhfl=XPDHX z<hyrQ!^Uk6-q>T{;bv`Z!QD!00efM=zE-|o4r~#7Q!(0S%w4vwZp-uhxf%<=7Xnq= zF=vME5}wCTo*b%F7WDW=^+$XHDiN&EBM>csy<7#**Eb<ysZanub#fXt1h$=6#%UQD zny|i`9Xj-Od9=GMQq$0YzQ<T0^d=fK5`^Wwb%vmIhOs@31Q9?8==Jt1xY77W;N<og zrolwsjbxvZL0kB`4o#Tq>G@%yK$%g){xb<R%|k%hK*?&BF@rfif8l~3Zl<D-r`CoI zotJJB(gW^z|7rnnJ3nJ^a>NXNk2PIV&Bcp%MmYJ~zY3IvsJ82Mu@n}~-V|i&vh2df zO0<_G<Xe69x!JBzXl2DfQi3to0H99d55$&Y_gJeJ?08yZ3N~~g${Z+TFaSjrPcU#O zj#|YEU?>>zHA_%%I;aXf`=U^G;B=I=zOWP3P$~A}bntG_e9kBJ8y-t=#eUed%pt?= z?eCY#PJzJ~PLVT+91#OBb@}pT;AHEe<sm(E{%LL60qW<VaO*NWy1W5RTlL+^S}q#u z8Z4Dq%@Ay;GZhpRtTx<Nd0A4z46!OJL|!2z6~rERMbdl<LiruOf6~FO(s@ZHAw+k{ zBYfk6OWF=NLx9)C;CP`-{B%%tU@Y>hy5J-ySs}IDco4Jm@<<fsiwTF3<zG<0K{cZD z=+UEANWz#I{oso68#|Ammx1aGurq-W#4OMj1zR(+3^?#LcZxsmr%JH?ApmkJ4=1}5 zYx5~5)uYX&x%Esctz_!&3WH`xVSB{I#ZEp1gFyHJ6R+@i4v4{`waD*<@%5AR8j36b z+Gx#iKpp0=Uj4f#UcZD#U6lL&7u7$XKi_gCJUlDBd_9;xaak<Zjj>gdDE_fP0wx0a zrVpexL(3k+I~@M(@1~M!j^X*gOMLq4{NJ4d{ahz1S$}`LD*oNl;&cD%XD7c=>+qCc zUug~HX^EB<(-c47U=-Ic|9KIA{)<^UO41uq2Os<WgEm&QsjX!0uhpA>WoMQ=vy)g% zX36g##Hbd&BCTL=%2A2cEd2YezM4@kGV{5>jjZJQ;|@)X$akp5JNNzFZS`MFTDIg) z!IzajqZ0{zPX4_e6967rnUDIZ7WJZWxb(YG^f*&9_cF#+ez0#k^S<3`EzVlOxIZ~- zr?8ZjNq%`dhu7S^ee~6452jo@tC#ex1ONOojNxc^cT4eZul`vQsiqbs_nq>VU5d|k z;hbBS8m*uxSm8M+uq84wV@P^AeqXQO%}~|rgiO6p5|_?Iy9*`Y%ZHSQ*(EL|Y8AE* z3Sh%r{(C!TVboWy_37LZ@X0p*eZ4H+V<Kk-TIhj)-u`p}LEF?FNzwWVy0hn$uv%OE zAtj}oV1T}No-=k?*5B>Kg*`{cdq3&OZ4^4@_Wjl#x>6}Y)bP?r)%K3Qs!!OUuo&Z$ z_qW~8c&ZHq)V6S~atA8je`5@cu0Tg?k;Dx+@ANu<cYOU9-94-Cc3WAVv$ESeW?)j7 z7WJRs)_W`x-)7W;E7<kV6~ub^*P2qed({h7&A&PsMq1gL5zvOh|Mbx~PXD_;XxnKp z`ZplFbNdn(2FX+B?kpg5V8#OwHBf9~Wwk_ktbaS`h-&vQ@y01-AllC$os|dpCqN=i z<a+i&jQ9>oC~k<*2#u;EYSt^FrW^{n#33VzOVeR`0E)Yg=p~?~igh8UPbUmy*JR=6 z$_)Sfifq~DF=g)&z+<`)-)D)SqNNC;%$ZOFA-v=iwo24xH6upC%ASGnN<Kh1QF}|F z3aI^A!oue;!EeE`Wl6OKIE2dqLrNi({O0Z3@*_4mxxuxmrWm1OZ4*U8oV^9RdotD> zd^wM{9B2=Mz4x{^JBgT>HXw104L{})6wn+6pPaJB;2i1jER?;~^z7NQKDg(=W6xC? z|6vi^d~n0Ld53gW)}cfu4`WKtsepo7lMZKGUVIqvEkx(8SAHAdA|D?cRAS(a05p@L za=>o!C2ceVQvn=x-F^C0o1`pob`oNYgW(jlH0VX_8%XG5lEsA-BxVLqNHI|2=wt|O zhCe(I={K!FW;FoWg^$k5&(F8{x)`yN$C9yonju35XIYI+7=GV*3P%{^kPdJpt;vwv z^pi1~!x$^iK~j%ri46~MpRs$R0I$m{#5u#w&QkA#SypYN3a30*;e$8>y~lqZ9L3a! zE66Gy{}xtQT|Wr5_K&akDy&c3+?l$&_|meSX~^q&fI)j#<ZO+D3nmsm5J8${AbK$9 zTCMhm<5C9(2b1U&%Hdu}$%~K}s@4-!z6*uQ?s2yX*v{Kf4k>sI%a9m`w8>E7ux5<` z#wYUG&BH@3htm$>Q&^W%7~oRrCU1;;#cCUV<w_thFK~f3Uk*T&?*I*~$IxdK*r=uy z8(uvQ+`9|=-?E{myag`KDe1do_0`;agrTL;>dtCWA8S`SJ&^6tK0H3cUEAlnA=W@_ zD6dC!Zx+|e&bpGdl4%`ERbwNMe|GNG+x@I1tb5sxA`BaZjN3T6D01#H-E5!2X2nSH zExyP(Be9L25eGCMhTh6&+TM%qEfjv*;?2x(a_^EQ78IW>Ee*;Z=tyD>dHa2&XTgOh zHxlA~Jxf?WV49{)c)P@=EgD+;7wsjCmEY$iP|m4f`2E2ZSx?M|C=T_PhyN<v?PR0$ zDXAe|eLE8j6ud_juxtZxwI4!?2<qBn$o@?l6IRdqjT_lXYmV?6K$)mV8w<RI5Ks3I z1q>DAh=Z*bP?viIixjxX=!w|Pv1(|1#s{+s-+_Cig``xAo-7^B90Nx>8<A)2?d`1d zSAl?Iq+#{}Aa;3^y;z-4FKw~kg?w8i-uPmso+kkO-tXU&oR6Uwg9XeSoH=MwqX%wM z@UwFOtwf)>JLl+Bl6rv}n3`^M7VoDBhq1i~nnmPCZG);K7z#lPIKqw`J$hVsi3S)i zuxP!QP-u>LLjaVju%Yfb($b8v0o)|`LWM*;hc~2*OOkWh@1Pfe8sZV64|{2pM+tg3 zn~2@<U0zk(0mG{x@k6yy?lb;fgcM`AG{^Jvy@G9Dzk1aR=Mp_lvRkjq1pK8(M|L8E zfuRFbbOE{&ygvqw=lQEwr@6Wgv5Lg5#Bp#%-d<QGp47uo-=Pw7avw0<_~60$466%r zYkl;^mMy#T^QQpE%Tl1jm!9}47KGyVNhOz*ERd8NA9I*~Qu1zw=IzsS%s+JoDLo4V zFSix~zUZUh_VX(#sDAy*5!Bi$Se`L`hX1CzR0bn%_c=BxsjqGTK2F3L12>$4c2W}> zJKAfpPly6PK$PdU@mS484+Cc{?8UZ*N3s=cFwzlgo1^gXAh_Wns3OREG*N=zFTVMI z>QV4eyKc0+jHEf(lWDJk3vl}3!-qSX?r6uox;%b<<D#zAJAg~Lu5Jl{QOpd!HA(OO zHr++DqY*n{lymeC90#;@<j;{%HEbm__)JWD!6qVz_Sz9@YO9e7Y**M0)?Qw<mkaI8 z;rjato9@Kb-qOW9z#k3(9ibY3SXZ}mq>)k7*f?(z46v=2Av1#3$o&(bSKE*`E)CHG zDSgpI=F~bv6cY^kt!~%dIA!#&MIiIveg1rk4mChDATAGADx+&Vhb{C`X=zQ4H%98A zU*tkv54~dV`YVC$OK)6!=3vvkd$gm?A1;I=pTAe=T@9bX&LW8l7lORnH@6l^>E#u4 zcRlrGI<Jyzt=E$md12OaR?gK5#!ahbc*ME5GL)6Wzq~e=^^ifuq&t^ay})~&{^G3~ zwK1By5$k4P8*vB`P77D!Q%~TD3tJi8|0*uAM>k~3NcpC*u?2V27&3wv$A*_ltd{OP zJf}$rgTH5G_)PZx**HK55CWA(3NJ+7T=?_P<H&uJKU9dW1fvdoqoPEJ<qQuu8DLgR z(7@KrGkq##Wtj9P@86&VUJ!Q+5>;%;PC=P|_wGp}jE@DDgm8I#>oCl5oo=5$zzf2? z|KV#7AAVq95)#L^h_8pDt6pbs61e?Gk3<(QW<$z`20Z+nsRzNW5tq;4M*PUmmw(91 z^I^O$DWM_2vpK7E=M%_&#F8vnvnI#z;YZ66y*mc)vG>%wNIt?Efb{^0m0_#aCm3Y@ zXamE099%v_R93d8Rgt<FxDC>fUy@(vU}1_)1#d%LRh0ooeHIVTiARq#x{hV9v?x34 z7ZNfPw{p+tVYnYjT8oFGW8T%~Yl)rXt9IRG!O2+vQxJ>IuJ=O)<h=I>I^g>2gKx&f zWTp=?AOUS23$Fi}i$v0NxVF0FoaYAx#xHz2(YJv|Sdk@Idn(lIi&pOn9lX7A)hTHE z%1rYfzEa(_4yrQtNAD8{IG%sp_dhi#2xPC`{3>geD37|S@tHqQu(&8fQ$~e;ygw{v z4{81O^vuMu@MC!RAy5DSpz8q}h7PW{q-|5J`Ld#-9D-Cbq<}52%}r4iRYl6?tl6`V z0|kKa@4k@`NWu~%biW<EZ6JXNNd^ik*1D5+ob-Hv949qRQBjfNv)g}3UbwN(lp^x< z{xdwu!BBevl&aZvMq|<J1t?Zj4YT2!7(C^sf|s?qBLTcH#ksm4xiCli-fNQdB1M0% z$)uc+IX-vxLm)Z<W{|mU+_b3}azC3RQ(g~~JfyswnE^7jGHx?pe@}}DZN1vsiLS>S z9UUQktj_U<7z6f{X=tZ0j``3XH>;J-I_$f>!q*+kY~G)Q(v!jjdog1U4Iy(o#<RGr z^uvC)Z+!G;#^p;nKt4%aN77Lc$GwAtTG%@2_`r_SXr@Bg8S0HPB-l&^e}z18KrZC! z!avs5h5_AeCo%4=bD?L0_1q_v4Ky{mcNrDEPj77$Q{bPyY87Pf?%LCP+wHAolhP!w zICZiz;vYXQ8k?`w-@S7gkK2(^h3dI;xvyPS8@6)F5tEvwoimSN_9{i_rsDVW?UxU^ zyN$@5IVXI8)M}EGPqrj#V-%FY8j4z4L_ou}H|HzCUO?hjaAD$IV5zIGEV_qd4>pq{ zPEN_LQ(n>4g`L$6fs@vpGs4#ou=jn6(N6%#g?jrOk@s*1kyrz=g^<!9zJ;KrH^s?Z zHvrxO{RsrrRRdKsbLx$ZjUT>!D*(${-3)#V&5G7tQVDp~qti-BL7@n60yb9LsyQMr z5)_|b(vF)HRse11LX$3A3xwljMuseqM&mf|yxl9yG1w2Vf=%S;>&K7T5l2&sL#YpS zt*C@VV%42{09Qnz?1yj=VnP3ni)fLEUedmR6AOED?R?*hVGCic0#Ss43bOoyJGZSB zoHwsgEcV5U6)Ui`^*_HTE>^qKf?6>hAu3QK&>A3g8F4RXA)<Yc;W{KohyK;P_h*!X z!xY%(H23WjBXKIp4M;^wT4)?lUwsb0%7I9h<oAeA`hgKpX^?-B&IQa_DHg~{$N<UJ z_-%t4$1J@Kmi0+|EVpj?YB9gCFn5Vfs993zJ)fIh^h@2$+4lBbrvG8DXs?lEV_0k` zPb_C@eHZI)ExgmA^KvF@UY~S@!HQ?kPrPqjqnZ++cYEu3<y<jN#v-p_RVzEL_?jj8 zo;gnj8qn5`z58$j4jCioNOSDp*adcs`AW%ZqWmLuFVNG!XpZ=*ky<Mm0*+xe@dXw{ zVqQj~_}vG;EER_1H~&EN%m2_)u}K=^BB5+L3AUuVVQ`9{YC{i}Ld{Av6<iEhDQZXF zOr636bv~dnJ%HFYHk~SuM)n~A8&m)j8A3fVHqa>FL-xAY;4YFVL(O$0-?@9czCOfx z#+_qm6%Y6B56hjCDZXeCZ57V7EIt2?DdXdLj!hdks!|2?BK)@3b*RyyptGyI8O0zO zG;-ablUayv$=<7JliKI!S6|;)whnQe!Pp;#9&8g2J+$G<&SNlN5FH_(Sg}k(0wI$2 z`-*&-P=UT$DK8~e47_p{YH?JX(Qp`*JxWI1M+)+c13`&*F1rK%>P4(;D>_mBHAff0 z_5~=J7#LFMV9k|4g_6;u8Lb;gk`zcyjI!+J<H{L)?hFiU581Nh(nHH(CnrG_VYn(1 zV`35+!&0C8{Mu8#n<g4V==B)m4aa)s?~~%$v`O-NpI>?~=W{kLjklMUacjpJ9xyne zezE1jv`c=)v(=@g*@eA?lWcPKZ8qJ(s&n8EhC$5=pPT*fHA&}uAueuD4(~nhF;VW* zx>uv@kKEN~NM_z!AHpeQJ#MPKjqH$~(cH5#94EJ)G?YVh@x;WYfPJZ3IrQz@ah#@z z;tJ;5<HiJ|RdBt%p582~&mq-Rm$<YKd0J_eKmDrq01*M${0>?wEp6@TmM^Z=#Gg-> z!?TMBqO`26a`*`#j)n0We!|He7LuW2;0W*F#Y!BAM2uAMGKQdf+;+D6f4MjAtk&Ip zKY#XVjVsDtifAfH_J`3VY`+v?3<gx98$kO2Q{=q9Hon{E^@fFY!UtiT>P)}A<*NQ? zFW9ByGg3V>fM_FlQvlsk3A!k<d3$6rC4N$ZVRJ1=bmX7KtaI3R=+HDc0K4|b59C{> zBqvK^+6+oe7QpS;onB|c*RKc@4`zbJtBt+u=rHa)cs?%fz`cu41t4>RZ*@=S$S#RX zV(7G6M>a@Y5?n4%#t^i;Z1BfEe)h~Cf*Ob{$V5nHz(tF0mTlT}VdDu57~%zLKd`4t zO{0Iqc9V_Csqw1gAu2KLz3;?DVn@q3mwY@LXu8`>nCC=eu#h<_i!BimXew1Vc|OQo zwApd#S|@h7dzNbZ4oyQfd7hO;b;*UI4^^(6@Zs@9DHQWDP)a<SEG0aC^ezC3i(`Qs z27SJ7ZyOp)&=|lF{17Lkv5l2s%VT5I;LA}bp_DrG{c|4r!sRrOeFWF^ILeSZK^M7z z|BV%dbNYe?|HV1)Tl>1gb3<yyx!GI6tD&}dkND+M{Fj8X<WMF_A3ZOy&G3+-vac~B zA|h>H=@~_vqPjBF`l}>gK#Jhup-2Nqw}i{3sO>xys3G<Mlha5bjpmk2woq@@<(dA4 zzQbpVgK(25H=UY5VrUD*6a#~vjeStc9a=n%6S+>84<ugaaROzHLSY%mjQ&(BN#Y2K z7xsrkxY7oa^2g85Q4T0!055emt+M%W(-|x^sRUtKyZ`ZA;^L@`^z?**{Q=0H8=NjG z7ISW^q5kn>Njsv=hV1-@!WM~(EnI!~UDU?|2XrHp*wRDiKuVhit7LT@pZaN*nJ^AK zxhmZb4d06{?agFDR;_7w^l#xjq23kmWayAS-nQ0}^;UOx{m~Wb+qaMU4Bi`@kI1aQ zv;f^}lM|Lri2P8RcFgGbl-iit+$Vfzuunf8XxNx#oRN}ISi3T|DcLwF(>PDbh1XW8 z{$<@(lXSz|D{HEZJCDaN5tEpzxqpM|k!gXVi<U2V(lb+3XG_u4U1xggzW<1SvTjKH zb0hb+g2hn_j~?;OOyzp&`1zXW1Bsk@CwubQrq*SOiVbfk9JzWEf(z*gLCp?9rEGS^ z!-rFV76zU<^AJc4AUnI&9}Zbtrl1@+?(Z*U`J6x2xE}il-~phoxuskU$I+f3A#I=2 zG1RG2$dLIlGLp6F3qSE#P+1bG3{`-&wY71E_sG~_{*I)Y7qdap<#i91t>y(|WSo-u z_UMY>8ODlAN-CA5I~%SA2XkVdz{)<33daKKZrXMs(`82SSnM&wFQXbc2;6VdTKO6C zRJ`Y5V5%TFpV34&I;*&Vn5FKPB#?)Jt$?=9YjXqTIS&mZboE;I-ot3Z4GJA+?af=a z#AIadza5iZnVN&rgJ?D!b-EIl6g+=Of{2yGU@fdy|L<SbcICO)hQ^r+A{mf_=G&xt zo(F%3Lm}O~gw@T>O%yY!T=(keaC*aH!1nEp;4_6;v(}yMU;lms?+W$0tI`W*2bqh^ zs@i|VTSemBeE+e)S(1)UCrYkJr5#%0u*11~UZVbX^YJpFvXGScc<&4Pn>QP^U;4WG z$NZP&Y!3tMbrbt}x3Vz~>=UaFU0jlEv(xC=e(tvo>qTd)IS)ukoSXNq6}6}S<N=U? zvR4cqI0os~zq+8l6z5y?AnIzyzI1-jx6}acUMVkNDUW9dmwF1|SwiVTKXP~1!jyUz z1gKMBK~`oaYww01jXNOw!Yw|I+0j6O;@%b%La2fI0Ox?MZukQ!4Rrn*a5>-m!wq5y zWWaII8hBgkcR4XNRm<6#D#<N-mfr<&he1G$lpR69AxVYGj<CB7fdSx${L+>jRLnv2 z+Vpx2gpOw+dwq|wRhk9{Jl8$HJrU1Zv2vvr-YUZkXc7R41ECFNrKJlL6tdrbUOxLY z-6&`p3|;$h@z^0t=a)Ndl;r(7#=)WTQ)7Vwitl4|qu{fh(evOt*Q0&u-35OUtLx4e z<)OF&Ic4d#Rb%ElvY|in^Gh2XIH_YMn!EPrBJC{OW1=Ies5@)kzD?ug3TUzjQL{QU z%~^A*#6F8oc}Xd~n9<4w3NNm6+OV-1X~(q>KbOu<KM>UI9c{#sSE<XsVnwu`MD)fj zGG5P9P0gFKR<UsQT)>OzURSALLU(d=l3G_su&~0>50B^DNMR_R7|h`~wb1K$i_X}k zsHjNT581G=d+V@%Qyt@Y1MrT(upquwc6wILm+_Pl-nKwmdJg0%nNO~LF*G#fjA}Sx zo2wA-@6SwfUwjKx>qSUAebR<VNHXBUu3NS+WNl2IDb78Rl_W)m;g^xGnbx0BV@=Tz zjLJKERhmkVXIAoi>_O9x*VPfuOW%{>@9*C|ifkeV;)VhVG{BXVb#I2|5lruwJ)1%K z)Esv-F)>+-8O0QhfuNuOv}duUOT+g8*CcHW`g_`~!%eI2r=QSv9C*~((SbaI0CZ1S zZ>46YEZZUBkwj!-Q)w37KZFAoRWX{bhK^(2O`Y#=$jZuQA9;tB0ev)UwEJe1FtI_~ za2C_)fIN)o!~6PjluREi5bNNOnLhRDVE4(mJSC0H%p;zo4us3d45jl1JsJA(`u2js zp;PhEqgN#svt<eTe;23EE?>7+^nPZ*vxqf#+}b{+nx>(gdhJ+IEWRXu{Iip#_|q+( za}xTlSi-+vk$$UeiP>VYn3pF>I=2|y?5UeK7vXszk7@33Nfw%=d5E#KF}R)m2c8Sk zR3VGV0@V<V$(l3g1j#)4cogk)3G%ql9ax|^8&+KOvhx6%Vo;al^q25aA}yQwhVlu! zXII}KKLji-^-A>kK#><(j2z5qi0WYhp@CLgfaNrlOt90iki`PH3p5>9Ifjt&9)#Ku z875T0=#*1Wh(rUr^%WKJ?hdzLLI=FYqFWefVbR+KCIlKNysd>QDlPjbtTK#|Ajy#A zXfg?cLIYx-Q;2RR0lFLJKX(rw-wdaPB-k6`e{#S5kyY1`nvzoULM}@B;MP#-*C<W; zFldqN!$1mLIbPZxJ}il|19}{0Ix)ay012xiKyu)k?eeA77g55$Lr99wIY&doOHYnU zgQ&_C7z|cX!gsd9`UK6<)dy~IHP-1UFRAy#G^av@YG@Rg&(gc?Z1IfujiyGCoC5!j zJ$sIL_i66ln^$Wl)X-|yud`L|?Ktl0lHvA+p+ik)&IRm$wFu8}Wo=EgnDY3!=+WR6 zCT3qgv6h#MoZ2Yn_iWjnB8c8OG81x~rq$J@Nax&ny8U*mg00|k!KXaUI#(~>;@vXU z_gdrb{p<Qzw($V)-ST*)s948t2rp2yfwz-FO{#$KfSeQq2~A*M2&=ge897!YydKIm z%a;+Sfr5bnn+Lgh<)!6S))|NfE<#iq*~c)3tL1x)XV(gd>Y)R8lkKn&eY10g4y-nE za=chxq;Erm-Y}YuSlE4JMccf&1Tpsr*>Ny!zYVbkB<+X{YlVuz4b7YsSQ3A<f(%q} z4=Jz&@iFFr)A2(q*Ohm4SQB6jJZ}SZ9$2|E0HMoyokP1u^8H`n8t5^8mROvy02#_q z6iRxJy7`;6G&GphsnF~Py#~C_*XDd7U|mNX97v^_w93=k!onX3m{{slNlpkBvOB>G zPDOy5wI9ad@l$~C5JR`6kO?84bq^H6kt346!!s7@ytq;@=J|bSs2pEPSEaI2*WX^! zi3K`~-17Ta0;U3UfxnQz00!h2<D*Jo<fy@}uQzV8g#q&iXfl8zk{<n5rkfV}sopTz zPOA|BMNQ;hR3-QW9X;qNj-%}C;|vN^%n#3VQ8V4c<(IQ&UfH>mIuqkF_ZQ0k{HZIx znAvzm$C;p>+?PKt87ArWKUo~Ft#k5Lt{`V&L`>>U7I~;onwQ;Cbr?*3l4+Qb5U;(d zDkEX*=Jh(>%4}By<L9)Ug&=b4EkHq(ZFi~B%I;Gl_G{r|U!~~c18IjT<v7GgK_dRp zcY!jP391J07%6gcXU{_T0e0-E4C=?-5XdL|SgPSqxa^6lB=at&IjCZtK}+KtXOGhy zp>eN1`K{Jf>GZ^O^Z2Bs5^#v&%MVFGoCgJ8(TBrF0Z~4{{A=#%Cn;77UuAJW7#sq$ zqf+J%Pl#8fW6Bcoh&q?2-$h%9e|MhoXY(^hPpB4kcke!nGN~1Bj?4*I<XAdTt1vOJ zg%#mcw@qucv@n5^4qs&kZ#wt~RFw2SQ1Rimh+f#3hN-mvC)f52x-+4pXYPf?y=6e2 zi*PS^)=A()8cM(mj&*+dXvr|8A{emw!nco-^GUFyq$C8XOk&l(f~*-x9WgTEM`GLs zc)Io{T-OQ?Iw8JzvC#7A9;+}2F8H3#37%l2`+BxWzZF`=imi-;UflcVM?TP*F}N+L zZunR4XGf2!XJ~kU`&_3=IbHp75g!8M5|fggKc7wr+J-iniVNcS!uEJ<$~xlER>1~J zMKg%E$$677nvB$(U9Ufm48>x&j(@|&&lRzUdz$l_eVQ9{#O+rN_payh`ti=xz9k<2 zd`z*R;2C<ZZ?EE3bdY`M`=nm{34s#;;?YhIn@;@`d@*cYD$tz5#p1SYhM(Mq<-D}$ zNm)uO9(diCDK8JbzGV&#A-{|j_lbupEn!iRq>FEjV8@#SH9ie-<*Ep6fbhE)Gp*kx z+`g`k8ol9uL5?yCdD6?#%OL>c494b&f)K#y21)>dAB;4jmaB}ib93LLbku;U2D4($ zfd?g11JZEZzNVK;x_|qiq`U!|pk*c&+7?D$Dj=We4pTlCf^*7I?GXnL=$1%Coa2z* zNDA0Bym@2x?%_!?Jz)j<)6a(dwn(J75B+!*G{K=2JzFbgFrc7w-!`Mf7&1->umaDW zD?_=5Zsp1`j}_W+`VW9B8)Y88YHp^t=N#aM^~-tT7W{fHIum3GTK;n=DyWkH(`4^* zc7ShMev76WkZWhY*Vn!QQ_5_YMf6DkRA<zUpm1fwwg((Pgs$qt6Vr_DS#sjU-G8^g z0@JoB?PDtaQlLa2k4&;r0?QTp`3-2=S;%^$b%TR4H|W&n%gc*Hw<*8<`Y?Y|kWj6r zJOY!*iafu-dVlmuHP;Oj4C0fMPk{P4<-QhZ=FFR)Tpe+abMx|+g)*t)sUiyZjneY3 zEDQ{`;-HB@mIp__xsR=(R0lN3*!%O^GARfGBh7y7i<To)7O8h@FW%WK*6~C5QP26| z1O0+NY^g~}>(RWBQK~%T;09NQ>0p;XIx$3Pm|I%jGVv_vd+4%xu{D%rBsT<|WYyBO z0IV#;|MNffaaWZDOOx|uJ){YQbR);EXOt;sbC}ADRh5wg?(5R_YCM15`tBVQvcZ+? zZ0zkTkT){<k@4cQp$j||&MfFnj54g}M%Asq1=hkS&t-)YX%_LJ$g?2N_pd+F|F_Si zKIivGf11GzdZ{W~BGAIapPieOc6)4eDEnI%%@n`)&8WFaazq;rpIiT^b6D@s6M{hJ zA1xNg)%3f|%6$Xd-;w>|uMftko}dQCBfhjcs4g#m*?NJPbwyOk*I!bwm{ZTX%)fRq zr7T%~HnRF<e|eFC?@Lu!b)0`on8X+Z=E=HCPLx(B_~t1o-OSI<E7{j!@XNK-b>g>1 zX?>!D_`Ot(AYFFYlqL?2ds-RYjeBvZu~GDz%3gV{;CDIWB5;9t?LPGD!US?0$4Yg) zl9Q|cb9_YCu7xY<!+xW=eCu!QxEE{ew_}ld#y#o8u`zRL3)bYWE_0mQ+$3vi_(w;^ zJ)51S(mEqocL#KKOL!iF!$a{5fvmc%?FxyqbDDY^*%K2JZ`4>bsSe3!cyb6%;HC#a zYm9vtplQddJy))NDbw`HT;mU2TP;P%{JL(cd=Vx_M(~Ri!AEPH!V4M%%{8=J92Fwm zW87Fp^FZfd`v<$|9~Vazu@ha3jCH^DVKbL*zXyX7fQd<ilBFdps>kZuT6K_8Fbma7 zuPvPLwy#!*ZN!(<*46_2gn4caUg@lLL`(B!NVNX(_e|;C5b4OsZLP)c2s{WUj<mo< zyuy&OoO3wJg49C$z5Ev;XZ$U~i#c^=Jo$TBp%63rO?8O$?;EG6ZG2hu;Nw{8L{Hhc zUFxkh>;FT|*S^5@*ptg;4T2@RYh~@n#_M_<x2vsLj}tz#^!Jr<dgtC&sMEAHMTIqM zs4!vTKZIr)jB$1=ovH2BE@Lg#6L_fpr6yzMs8rITFCY0wE+*b2+<JHa=7xT0Tsf8B zSI%iCfA8#BRVC_Fe*XR#X8x@&q<>pS4^sUFC<(M;&B^CJtID*>N@jT&X;LSZbr}nx z_kaY-g@qomrc3x^VCg>ko8GcXmGe1(ek3UU`wLL37e9G&dOkUno=&P-7ovGEFf@#g zj}MHTbSS-mr$gP)a4vv{w*uJwQl6hRg^02MbFR?NFrjtXAneY9RmMHt-87UIQ-{qp z4E=Zyq&Yq@@i=N5C1^(DIq5;RK8RZhg+BuYBxSTgYJdjCg@fL%8Umu0^Oits>^0nP zehR-|#w@x(^Xc3!blE()@#1|lrSUT8>;L+=ApH9x(ieFA78uf}zx;FI@wuC){(g4p zbN~N0{tqvXUJyAyNcRKVjrX=Lq^T0=2b<FRIDOeXTXcfxIf7uDaRHn*`H`VWHz)Er zrY8exo{l8EYYz%jQ@J2M207|UI&xl-ZLw9zRbc@%;FIW$`fNR_8?s<fL%MO}#?~i) zJ4+Z0X`DTJpWnmzL-hqb;E;k#<`$xQA^$l<ljipJE$`bft8OmllMinn(9_hMN|hDx zFleff7D4HpYu$hCdF%{4Uol(DLBE1Bf%aYvwI|%1oZW>`=7Tvva>NWj)!xd15Uto^ zpsc7UPeM+mDt4jwCaeRKAIy_5o&Yv>Fof@k-XwOnvC(NL0aC07Za`**S*4S~d7*4N zg<|VH3V4JC6~hh=_AF&a-JIF8k6?}o5hiG{At5S*bd14h@%+w-xh_PnVR%uJCRiZM z2ssN$Arph(A%LhwXg6B>Anm`2)LAr=<Q0d4gT4z|ttP2Ag~(6w9_f-Fh2)P;2{dJ5 z3l}cn4GKT(h;0BtZ~IV}bG-O<KtEnoOZKW0DAG=YJVPMFv(`b#UQnSx4#bE3e==|K z9g1o4bfBXPfSe-+9oP>HfPwDCrm_IH61VqcWaND4M1n2v$s?5dA%10ZNL4WUuodtT z7373(KqSF`I2@^nu$&VE2KJ~ZHbnUh(c7OZqU;CPAPjIWsSQMVruR{;wr$-24;Bp( zQ^-0Mr@Y(}99QUO5y-fPmLF7SQN%LcH(s$#_}49ZPd(eqc;H>Q?otwUe};vzWf*fp z+1iwe))iKoD>M(0*8?_+BW-1zXpzaErv6T(ZWoeS9l$_sloxuCnE15yjkB}!V+4M| zp#U2b19zR_i}sHq)UdkM#Y4YjVhU+7SyK?xJB5KVorubg>ZK@y%l9kX`O6*;yAUOl z;s9jP-D^Ka=qVyGkrrT9y%mqc{rfz?MG<jPjPetP1qHk#()yQR)~MCp`M;j7b-Vw9 zB4O78p-(c;)V+mV@V21g#3UV<20GPYNvgXeg3EufvKc2hSQ@xYh!1F`d?HjBFD~mM zd$a`Rva@zTkppnwVCp$=B#(5;VsYrwX{`dI48UEz%JEX@;LB~-*Y4uG#yn8$4!4e; zk&)GE#?GV~ye|2;C`kizo8~iG5%PHL+BM4(k6(Ae!;MaWj+?ozJtX=9G)RGM2&zX! z(nEA791ksT6zp(A=p>ozV8v<bDhc4syJKOKVT^_PcNT(rboT8*@EWiO0?GaZ5iyod zKKKKsd-P!#W%%J1fTWf~J(K0@;--ln@#J5YftXX|c#q+V0qhL*1m5tA+Io2T^ODBE z%Edx+16P?Aq}f>dWC`p4`u4S19{fy1PT&ep=2*72GH`i;$F<abM<s@|lSSn90PPR< zPJpzN4h~Yt&OgOge~<>)+SO(&D=Fn2YYsz*17M|U>w)=M!9hW7)9666g<0a)p@%l@ zD>sNSIr8bpM;h+jxp3h^W>iT>TwPBg3tLU6@3QA(%rqrg8B9FqBO`;+I;|JGz<)UG z&p)TYAVz7jIEWb?Hi6++n_5gXaBL&+M-q9a9P<?ocGkv0;`Mg+s$p;mmGG=IwdF*j zk^^~SW^W&YCbe-a{Q6b$A_L<JzIIK}bZnrkeDUnlu$y;hN9E<aQAJR#56}gEamsgr z5|9l#SeWwCEwHH?Up+rr8$bj{G$dZJsJVIE@5HBI+$L637NnoU6Nfb!r6-zNWrb&h z!!l>5q2W8=A4AStQg+M;*0PXs9Km1X<Krdk$004HsF$(ATeny#V6rADOz&|E_UdWy zP(1`B#rd=dB?OF1(Yd+x`<3AC!n`9M9v<lAHn>t0ICiLO>%{TQ=;e7@@lAIQor)p| zE~etG*ut%uBgn6K4)utct!<O_-aO>;X@MUj(Tu8i=+)&mui#Q5Oc(246Z<C-%JA3V z2R7{tc0_@@F;1UYYoKy)f?_SJA3mJ?#P~B-7$JUtZMV=*F9jH20o}1y#v390BKX-; zfUr?mC6KEr*hpxNQ`U&A4KD;TTQUc5t8pHYQXJScA#VungXpUilzjNw$zSN0BQP8F zXjL2y&oZfCT21F|S3LG$yq6RY4p&!K8lom2JLZCP5Ykgnz!J&ev7WK%Nnk;cSsaj` z|DWQnVm#Anw?WM~4=LJ+UEx<aB1)=PV76*2!-SsO%>+P$bCxv3f7w*ufyFq9ETwzp zxEC`iKvrYJ1`9m63_wAwX846)cu)G0U}b<Y&lhPx^xJ?RK@9uRq>!m}V>Z*ErfjD# zv#H*V*&&Wc)7XW1Rt8(jj-+LsWG30QYnr@#7T0$-RHLo$-@i&MUyCOXgqX0EJ1=c8 z9S;y6f{ebCz!zOuGp_7jc?w(E^A|4=<gkF7z1zbyruKYP)J*vP@G8_Y-V6wgpMNOB zIN%c&wT9D>$orG({`NieewO8bzB}VrkwE_$FZ$gL;BOoMX;SDXPnPAszqtROeifhl zSF=jr7$WeyGwu%9k#u13eaL!>wgPcB`(3wQ*u?R);dYe3O$bl_X%P0e4vK~`c{EsY zJQo@XXM$AmUrhz$8rO4yh^!T??YQ|Nzl&Uf6Kz>+pExQ_c5W}u_V%5$3)%keb3Go* z@?Df4uYyU;=XL(;nGb~~R&cX#Vf_G?)H})&_{U7cZL@ND9iZ*8z6}QR6i?Jg%(F6- za4}lOeI>jk#?}4}!fIAd^h^k_v=2wGDWUG^pB|mTAMmLL5paXe$Kp3_J6L|-r7+0= zf7+nqp(w#M|KY+Y@9E3r<(tMho}sz9PW#8?NB<FP>&rRDDVA2vTdiNccIe6<y0IfE z7_IAbG!y@B*DFtcic=YXT=`&r&CT1f{pBLgc>Dj+mM++S4+G&TTt*@`_N`i1@@hr+ zAq5441MX2r#YC%a@?$moOB=_dZx(bV%PlS!#c72;@X_NPAO6+?{Jjd=7dVd`b_~{H zzN9_1x+dMR-o$jify5=d-F8uMIsMDXG>ggCu567n$6tU;*S{`)qq5B|A35oRGOo!E z*4Dcf{>!)hh<%A~OSZ?}<o$bT(7FGwmL}ru^K@(@|ExcDhEdxjXu|oQ-y&tfN8O^{ z!R-bH)iHzEyH{BEAYdnEurAfqM(-FOUl?x&e7%SCU>+Ue%|IPsr#Au(e^z`xA=&jN zyLM&7=j$gN);o4my((sK>(;GZ9-fYl-+42z!+f5phO1{SMSIUoB19+<3Ni&q&z$c? zYyS;>y)g<MI_BnP2A4H7tE|}LVD;Z(2bP2Ms}y$#=`yhKlNw@bIE2X+0gdH-g$M$d zNV*<6Br9z5?ZXp<9J$>q*H5a!7i&_vuzh<U#*W}5l*#czx)&<i*Iz9f5SIXmsprd2 zA~OK1J-99R3wT@Se?u#&MhyJ}u)n&2eDs&tNhE>&q0(bVJkZ-QoV)K)&JdtZf(6je z(Bi&JFBdNP8_u+df}a`*Lto0uOG?`Ou0IPN*|vGJw7XB61xWD4C<sEthqjxV29p9F zOj$8hM*)k$u$(Kewa&l+Rt(ECC6hI+S%-l!RQ;o$PJ==9?T!QkicH0fiigm10%S+- z_$e^4YcWq8Dal!>DjCKr3WhtD5u=PGE@4Waj2%P2G1_JCQ-U!$AtK&Cn4r9rt-pn0 zhlW__C&;YfT`}KcyTy^)<5r0&-g$`iEJ8YoO7>*&AKye9mj`=%jWrCZurr7v#TPJ( ztE`Y7y7IcZI??kOlS^K5XwlLz9)V(H5i$Fx9y^5TRC={RiN?wRx=hS8VE8v#_98UM z0)7sfT|sGdK~b?$QBhUiIBuQF1rflYjMP2<T~2r!;i&k57fHv2O|%<R7>Jj0DR0Yg zk~7^71R9=syCWtRQDF#A5fv?3qKojrAhqmWxW1@Od9haI-6FuP5Ifz6!3$K386oF8 z)N3!of$0_iH<!#_2cjN)B&Br1!gB(lUBl;?JZ&yak{Xg2+ThmBw`$KLR4ZvP4SQn3 z+Z6?em?ynMC_|{gW{`Ro1897WmfH<F_d{bv12%A6<vJSUrXlkehbH^XndKOi%93DU zT`30u9(@NXU>TyKq71Z95T=#D-F}L|e8>UHa1&8q;aNa1BQpb^C2*Hwkq2SqI)ks6 zLaZZZPFVJ@{djZ@SaK;)VMsOT@(W2%h$2QQcO3eRM=c1V3+3BpCsK%(%~T0|0j5t_ zJU+ibXU~;|AT)|?K5sR;_G8T*!=&h37f#|fevI@)ANmogvD!gQ7y~QW!`&-t5)GC# zr!jrIQyAbh=JAG=t!`qgQyc3Ua8q*^Z@qH3≈9;p}|4GAftwWZ$)(3zA;;O_@W7 zfs<FqQls3w^=5~rrD!obA6g2<#R9ms5(Dg$6$<-j@BZ}Mujtk6Gc7*lf6U=aXSy-u zGcm*FDo6b_!N-L^SxZ~I>h?#qW5Bm6IF_}3K$0aVw-i4FgcCuPJ5m1V5jl=sBf6TB zcCJb`&Um_{ANzoUNB<I9NF)_TWnPYEhNF7|Y(Os-6gB-=B60|gL>C!dW9{(s+W~rT zXao#O1k50f<n>7zgSxVex)2qoLm42}%kiilhhQ1-*ppTx=#gt)bR++O?iM6<(Rqq{ zpyOu9ta&};9Sq+F#mZ*uOb9U$WNzcan}>uIaFV2i!fgO#APNo`NG&=qs^AL82Ud|I z9o-e_$In`_gk8MYiV=uq7;6j$W<O<fpuUAU>jacb$VW)GYf>aRFIhYA>(&3!_!VAF z68Xm3vl{H&+2%7bB0_N-03dqYux^8gw_u9{&5Y-YLDn&nVt}605k{ZmMO!L_8U)Hv zG_<<;*ik7Q3qRmwBAMxXd!G>@yjnuu5<RRzpV2lEWW+rJGu;Qy>B~xQN?}lP<-%?@ zjRLBHL+A||e?vY2%qj?nK+Avjcn?6ANkIw;M1Vwsix4;*digTzNp&Wi8vYL6x)zPq z6@h55CUZBTFr@p{hQeDWx7EUWRASk(bjZZ1TE!z&sxt9&8;ksjKQuJ@KZ|jnHhb4G z{WBEy8FyAqZ}v9fyWX<uf<N=&Da<;opD)|lrTzKD*V22jgV%)DMudN;-kEn^b;*gV zp>yU%xlNH{V#>aI^bekc73vP3I67`^JB3y@&B@WfsWMut+?ii?I&X;bbcXJekJ)qQ zzwkrXs*$a*WLDMsR*<I~5upMv=V=;6fZYHty`hO{%}FO%8m8d_(!{M10R@yh0%WHb zdqgqxkrb>6dz%weEPNus8G12Uf~59{K-r+BrPb+aHisQb{66xMiHDk_#?HOL!@xE$ z=>rBKMVn(EOcytR*}68~!2*L^%lC5KJKNxNut(AeU?>><MoneAb?F%mG4ggqc?}9s zlpvgf0=spYI+#Dg7dREeQXfJ%5R6rL2LV>OefQGSi?R65LF=V1c7dz{nxFM;fEPVP zn1G~f_imoKbKk*2;;$XI`pqXCTMEYpm*Z@mJat!wwc*)BD=a>g0RI>yrnmynBbE4| z70Xp-Vn9$d`f-&{G5ti$RN(LL1aa(u96`J`E5vVd?B`FaFM!)3&8=@AIu9xY5O)Jy zWTDBVxR(zQfmYkahzJg{iebphiWM*7Z)L#^=M|)0cvIixEHt?5=+Z;G(}p$!TWDUA z3mRbx!0&~4B^m%BiA;=lCtU+!5TL!XJZ_ESk>VB+zR@v<k9T}DO&z2lW3?<|Y!u%j zX}$gG`5O&;Cbo58JdC`jO;xL_lC<<2PJw+#GjqN6FIt}KYX7az+3S;l!dW3UXbfm5 z%ALI-$&0V67B|?hcNmtrFUYyPsp;M7gIp)}Nr5|F?>_E1)}zbmH`et?O^y5QTPEWb zi7R)j>UewK^NN``bq@%f;$+fWWCR{~K#g#5kj{@#OB@AA->|LnhTI>1u@lG+M)&#U z_&NX)SFZ5ma<3ihe@$5yZ<}GLMx{p<Gs=h`^a~FcqV^INGa9Sfg?X!`LiP`s0Dbv$ z_n#BtFqA+y25Aw75T%!#&A_Tc0fp1K_tBhfH()mq&vHZ*A=*+VGCj*I`K%AY0Dfq8 zc6KYMXY0FrTp@%3Jsk+UQfzPjLV#(g&lG?u0HClnFkXxk7c+>8h*E`s3L{scY2%+- zJ#66(q|J2VD!6`s1T`~MwYATzOtwE<htYbmN0$YIUx2Io0Jm>LeSHj!^je=+=&DF7 z;bH(#y#j$caKN)GRWVpz430Q-x2!WhX4LR2DJh{?;X9f$n(F_(ENTUy#d54PQl8+K z?_*mtt13$OOQ!7-prTea*6g;2%~phFSovJ*u(s;#FdJ`=O_SH#QBl?+GG+6#;jU`y z*pWZxh^t!Ja_Sh){m|mc3`1ow6hn<?&S{N5JQ;7z{v>5&u;%3GnMWE{7q!`+KR2^@ zZ*r)1Y=l{TfsFPa5^Zg3(3VAV5cIS)6~j|-FEw$^JO(;WLM9K@0&&ck2xDJWP`WQ& zPp7>s_Zid)#BkpwQyI0}FytpDN>o=@-!~RPszyn1@jJh+ZHQb3PJ$_TR+-{1CV7y0 z(oh{dC6oSPWz9*&7z-6NurWJj?M2uRPBW5ny6A0#4jwmyF|fb^3c*uKLr`aJ@%Bd+ z{X!ay0@|7tBejvk%ne~91mM=+=2<s_*4Y6~Sb2y@2vXqZL&Q;6(`|Ry74cExdmU$= zW+A~pxOB)(Q;r4WS!H6tJRV&;g9%Ft#{fo>?1je+xQf7u0R~}wnCe@C4>KMAOhF5D z`^TOS8J~FqdJysx1_L5rfq8ij7hZoFM^6#1GK#0fx8wO1E1`n8I(=t(J5`8$E=11% zYB%F$R{+;ZoCP<kZYZwcthD0jcBO5cb4EbjhmY^dF0Wu6vf-KDsOZ51Tnl$y)b5ID z>c)+}+AHt)*mqPhzc2RZ9D1vJD{k=Hfr}!_&q?aJe-dBa2QrirfKZs>wxJfKgfq-? z<A)e>NM=Sg&q;N2nb0f*tzp{&SJgS6Pn1SAky$^h*(2%6)$Ez-Z5(}Un#fFy*1eIN zD@gnYo+-G>{V=a-9|mH8{I^+cWUE6j0q6oCzOd~N(K;S9WZ5}6G}j6Rb#4QKN68;{ zdz(n?wd$-+koR~@;BTnek>Bf3cu_mfE;FkD<q~NxNGRUle*z{ijFlAS4Z3jtye~9W zv2sur<Ec0WiBQYLxGRC1IQq3307|JLukGc_t*-9~?MDf%28jr4o97T5VP!X>x{7By zARNYNjhJOA%++CQFKEYsagbNogJ5wyjU+rpAPzI2C5k@-P{vuddey25tK|*WXJPn+ zo`J%%@cT4<|3OoYAta(o3&A@aFPo5VWI5qv|0!u05Ti@-aKB&C)9IDg(fM8ZJ=YJ( z#J_yZV3}exlb_KU5cG^;#wW}ry<tQAG6`MR@3+)A9T}3Y&M7|g7}i3y2|1=O;x^|B ze*F}(R^P|OTbT!;?}D7q=gnVW_rp?vVKmgq7M-%<MwRN-KVQ8c?!wROJK~^=$1@pu zHy|@;zzGhd4wWLKT?<e?lFku*CI#MI*yvO-g8q%t>mlYNv<&h<=J>sjSK}Tb=PB>@ zx5X(-A+`XO1Nq#cyPi+-k)EarR2WY8^O!%KmyhE}$>pmmc3Nd98Mj0zr0ej#SiWl2 z29%T$k&!)bh5!r6cSBcE)8zf}>0HKuT6d&y9*Ez+%=(C`3g-QCcw6v{H%~tv#rIdW zhBM*kQDp+{h||5|bx8d|#9IV((12Sp<zjXHXPnXwowX~H_v8zr^;k+BHAY*^29u^? zWn~pQ(3c0{I2<D#Px(t9Jt}Hw5F)z|hLWLjzUIl9o12TmWC8jaAe-28d@&FWwj-1s zU>DesT}HF_0F$B~YOhTZ0{=28BZI7n)#7cvMY4VZu^{x*QIR4{f73nbhHx?7AOZz3 zch7%@6FJf;mRl*aF#nShAe{NpMrTti)-snr30=T_`o_vtcV^zOw-CZ~GmpZ}vbi}n zd}ox0ANQ_%f~Ml;qBlQAN*>tkd=qfuhJ@@~_4Moln!3{wF)v<c%VqBRH>#Drc^9-2 zWPV<eH8g}|lu6aA=^N|Q7N1iP_Cqf$bkQaqlkMOSbB>D>=fl(;M?Su6pYOp{U9vej zk3HT`yN)2M`D0^~{I@#|5m~cF0G>xJSh1b6M&J@god+eZ%#zm*S-3}FJ-O)j#q)-v z=VW9v+u3vK*t3Bk{P+;MjmlztiQF#7u|24Jz#i`V(fjTVCImvVL}6{%UM=9M#k{i; zaOs@9Vzx5`gUi~57_RFJSvH!Mu$3LNy(6c$`TjZp;5>YMZK#lE(#RYD?Vtj-$_JSw z7`z8sj09t7?60g$%goB!fISSaAD;Xf8U4M_r7K#gbD*dzoGTXiO)z{$nW_w=EX(|~ zB_jt0QBvd{3DN-U_oBmY1Kd&$5T_Saqxzxu{tuu&f~^amB~qW^b8-E*zCN`qyCw#M zSy1_Ll#=V#p|vwG`|FZ+RqVT@FDX)GhYlS=%)88z(D!f<Vyvq$;!oVj<=5y~(NtHu z23l$(BctBQ^IF4VW25{UfdU7I_QBy!?oEp-tE+D~HOO7;X=!<HVQ|n={f{u}IvWCx z?)!7v9J8t<zWcJW@rRd`4vJnv29~wZ$}(d#rU<kDDkf%D)tl$6gZRsAVKxYk9|;ge zZ2<jDOl&MRS3(+eEwZsi_YMthKodtJ#BfDJN31wXuOUf>e28YFKl-}HJt<@w^n#rG z1WF9DG&Vb?9N11%3#et%%0lU~4+uC>x)2&twnC_Mn9{Y8X|mv{UwJi#z}P~g4xWs_ z=mMAAY=2t-@-(OmeW2iOjalq0q$UjtngJcTKO}5dlY=l^3;y6$lvM(bn;Zsxpby`L z?e&I@@z@ch1<|JvfFs>8hnzhaVoFsX4ZlNePIf+;qQxfS=?jGFJ#L_!+d8xhj|F+R zZ${J)Y7vT-$BvH-hz`qFL)g$^%vlf17qSdM9Kn)06}n<J{mh#QQg#mltAFIavXY_G z5iA6OQm}}%3}vm{a_Km*d=)<D1AnBZ?D7v+as~IldI6KW<LBmqaT!BHT}b1fA-4{C z?Y!vq3%J;QE9(U_cHW8kR=w2es&Z3yq(0#YOdSWK!ZGQ``QEFVxpM<TzE>|bLe}Ba z4EB^{(O;gafS}r2@gI)z6buRT+}`$mKTK0|{?ANPTtah1#O!943hu&?9OTdA(347x z>uPU@+>$lJ*w&yt2fE>((P%)mvW75#5ZmPmWb=n#JeQ9C2m-@&<h2m{guy?ComnmR zEuE_x<3*AOVK*9QzNN*SvZLW}JG>duRaOW!A64bsn{R?j2?MLM*l&+_VJrgwI*<Em zhi1WSLxRVZVKl4<Py=fh$m9RwerkivQWP^0kf>mU%olQK(9?<0&uHZMle2uUC?lZ~ ztCh?%(AH|;woz9|S#db*<)J!U=U*3r^9_$H(VUR)P$h>g%8zqK7}LN<GfywCStyo) zs8Qr2R(Mg;odRov@=>*O4(MBHs;=o$Uc6Zx+xwtBC*e2H)9z!nZfHo+v%dqyqicN- zxELw7!)=w|27%1~PkR>1fKyrIy}wG#DRRvmv2^~yS+mfs55>NJBlRYPp9u%L!dU@k zAs-)xYE6zKDYJhH+4HMp#>5P3YD3*v)vIjPuM`n7^8{zh4TGk^;4S|TJyxUx$n6=_ z{c0yu`v)cr{<j`0f6ZAvHUHIPCHeOM&SS;BQa+#^s41kNhcj6!@X(P)%ExCyE>Gdc z9iNGDO2F^V&GbMy039%cfm-dboDulo^+%4R*<FUZKo$}hEDG~)Vxb5PrHvW8JJ}>C ze*&@tsDUK}EmIK7DEOkDclt@<_i2#E#mkr9V@x?7L}|BmWUT|aPi6#QfXLWny}ISr zj3OpVbHUpA0XYbrFvkN<R_tGWwMxpw@hqB460vg#tY*QOcMO%?AIitweY5WkRLQdJ z6GD7!wp@H{e~4M{{WdY863EPK-qkrHyhzo_{oAICb`687gQi#siTCcA3(e!mD;M^! zd^H;*)(jq%$ra=XW-L{4V&8i2U`c&3(`Egoxys);><+*ERz8Bl;lFoX1s96!KZw40 zEgR_Y(8;HH+9kwjVT00n$!~7}TnZtTh>|I8Xc+NZzW)oJ*<aIfp&gMIc+5N?y}b=a z8p{`gG_*`J+TA2EA)%OJ1rUcsc^Tq1w?IA1dfLs&{PO+=6s<&eLbH4R{CTtcOKuk6 z%P33?HLcr+3mcrWHpl5>?(+gEsaePy&8>f%wH?@_a|h4l5R2TPsq=S1x`gj<#h|18 z?lHN!iqPdOKq-zuuc{PI8lC`SFE@toh#?Qd`kUumb`~<g=d&70%*Aa_H!Ge36KosF zK>*U}2-iW|f}H?$ot(tBefIV<(Lar1fCM>hF)>{VPP$O(PX|^Dcu$PElF(A((zUfJ zCdbaA7Yu+iuoZ)Iim^$+c4H&cIkwf>#%9)>Im`^W_vNlQyey^oVdY?Tzq|Rz%)fMV zT}W9mPsC^5fx_|WFPJ5Y;}pCE_xbn)3O4UGShFU(hCf0+`fJ@@^Dpn1qBCR(&3)V~ zEbrQN{8AWS1k7P_YHEy*&iJsgVVlr$8AqpNpL4RZT1=?qoL5eYq$n{HEgivOFrNej z1qD6nf`Z@_O@{#40+Wh<XhRcvoEbQK{*JsR6%p@Z76u>;xXge%G^Q`o+CbiMNYGJz z?Zbe3t>T-=(1S9aQO$PIcFi0UB&MuhJsS?2M|gbzBIc_VRzOVy!4izvWKZ+geFZoY zO{VJZ-ScQ#B*rF&N++ZK`IpzKB*etlHpMRUC-hjosMzFknux?fFca)moyQ&VJ;Zk4 z2IGhjA2Cd-Ap?Tym?j74eAD1>INc$5sQ$olgaI)_5jIQ|K%8Z<4!cJ%x*A(A-ahmJ z%s9ta*Kb3)M5CE*-duz`iNG11EVuZAO#U{2K{PN2{6!J)>28Jfql4ZUW2!>heGp_a zBf$cozq9c;P00&n2Vh_F^x2G;s|o@;ZR!LMPnOG1o`^TJG-k}u6!F^st-z$J>im{# zESnQ}>T?wh9+kI9T`WvLFk^XBW9yF}Z78pfU?z6<gKuYw<z&NFz8lVU79TQn@8Fqt zTgP8>{ndwgOJBdrHcMRk@2dF9r0^q$*rP{}R55-Cr7UHL9z_Xy1~3O@94IM`_^k^7 z)<fffU|%yT@LYr9>c+BNF*VkR$ML1iL0n8|<Z4%V|2RmQuILvj8wOvU`}tOt6_rMa zRw0BGKfreU4Nfa5o7&}g&dgCNBlash^>Pk^#o%MdC=EYQVDA6`mclE=QIFw6Tdv7W z162PIgwP2L5<=93>d-tWod}O48yA{vno@{x7NyZEl%EGU5oKUGVp6U(z62~$TwKiP z@VAK5pT>aOvv#19XaW>V{)*pfYHFx>2RjCV0wmw$My8q?Ixl(&bV+?<LoKm)@9Nj3 z_6?*V0hlfdcme%{>UM_#M5aTVNL0oYW@eoNOH6M-<Atrr7b^nIf#3^m`y?qBh|ADF zpiU(L9k9C=)5k~KSW)%UsBuuBtw>mt5g4BXn5*Q4X&#!UQ$>yQxGc&a-tw7XyW4VY z)N@H_C{qfrb7`ro>A}&biH1vtKg=<RyQRL}wCRWC+1x*8Hv)onzw=;9>Dzna4Ljm) z#Yp_2S$7Mt3QD1Q`_U(#CI5ipRm4i*$<W$)E;>hl0k%)_kI*Iu1qf=G_5!|duMfgF ziV_tHQF9E6oxDG5wNNNx_hMqucqW*?GuVzk&-{Qk0Bq_z|C!1~gx7I%6LM_&MF>wh z_>=paZ;*8uLvpHXYRplP>Ri50`W2We%#Pkl$N2YVp9wFB^r^tb`6X4a1#b;<vXQ>4 z9{Sk`oprO<P?PxPsgQcnzq4lKnmogfK{6^F2{l$~4GmLSSVZxp5e*GV8uTmR<)|M~ zLjXS%Emv00UpX_M!C=?hq@iYT;EbKaD{S#3@x-7{lyorO$s1+Ae*Jn5!2TjIfG4n^ zDWNa7^zgvmnJ6ubsg$}~DD2g*2X7}7H6`e>p{<eq`GyUXf^ZgMQ-EsfN<`DRXnFP; z%FaRF#$eaTL|0RVO~Zf)TkpYFi*y<c%fG6gHqI<Q6X>Y4By_q&43E6H#oXc~wwV^s zBVDS}!w>fPa38q7|0(O!R+HQpp%$X=<GeLzXujKgYAm{GOTnoND;siCuVsAs+Q#&U z?Z#^#2Da93yWZp4d}r17r_1g%*7~=C@m%Biqj#TUU9Is~xN`yGK1PEv-gjk{{GsgG zU95vIrX~4UBQJA){@A5KuIx~B{^!Oco6{{enAF*4sc7a6*bn_|NOcK6mNhcEE6B3B zQR!LAwSuFcwxnX&ymogCTXhgdr@0g369wO2{`ncY!mIF1?)3b53tFOy+ODkOpFjFW zJxAS4WSLsW7bvj(2+MK$qK^sbS0H?yJM~W*X$e<YB#lgh4eVKqHA`*;!Z*ndhX{}- z*sU*vK(&No4vaZ?kEf_jue*P`I`Zv>L$vN=K$YJCbYp8*egdZbI|eCQ!rtqSDCY0z z<}6Wi+2j_wCs}7)17da%w*tt?Asi`T*pRBNGmx?>j}#9N1nj&$uy{FyxS%0Rc4r@1 z0Q2`#tb6Ho>msp3tOif6&jo*AT{+OAylmSS`q!erTjw49s9jc8_NuNf3{ap_z!M#G zoiCvAyaF%>BMuHgp0Si?`j@V*7XS*5B5KKV{O9L2AY`y@p8ufrMp;quS~;q=%v>z{ z#UxdQ5VaEtTXHCYDnK%a8K+zZiFp7RvOCC-ZiOe6EiF-qjW`<;vZ^7g6&b8Awr^W^ z3BOJr>#3oAXT!c%uAli4cGI6oWBLY>_6M~4g-d&*u3z7iy!ZP!GG14~tY;0G=f^)5 zJ2M!J+OPQ&P@Aq7@ZAbl@Q{pPLS1JLHaGZ3R>Sgd*Ih9w7attm^AhKGz^PN~1dguL zI5yPe`aLR7#zp&2V5G|+VrZUUvOc799Q-*Fe9nCs=_xGiJP<Np=`#N2mYycomFi~a zs(+_xZn<lC>#m-xmM?QZcgaO`_n-OxX|a@IToeAf^-Si-aGr?ZjytC;9@pCTUn?)d zC42^L@Ksc7qdggQqtd(BGChAD@HlqtbyHI$viaY57OY8c>Q)cN96Z^vu`!I3Mo{fm z8gYFW9k;Wik8kjcGmeZ}2<^_p>abr^hb8Oq+oLV>R3}VC>eeZ7FTHdZ*O-HYqjBKw z=B_$UT$H45-iFf{Ml*#G*h!h4!gZ|Qf3$^shPqjwoi5s3Q;Y-0$BmP%^Ctgb%nn)i zKmV}tXny)nEt?&7YZ*poCclKcQc#HT*H`(5U(&WV<6y|i6tBUzoc~Z(D_UBtR@$ES zzV$|mF+=gEtI;%k!ptH^_H4fFjV?jq9XGjb=KIG@aq?3v>X;qDjL&XLJ(XU%PwJ9C zTc~XV&&}<bwwph;7V`vYGObZPHTiDL{H;^>?`e#QGupCqXWhV^*bUpW4m`V-G?VLA zXM0ttgH^!il)a2(<w$Y7oNxH???-bY^iwY9%vrqJwZkAWY-QYVgZ<LcfiU%y<61j+ zj=#y3_4(Qy*(~3@v4VN#&D|?ocp{k7GFdQpWwaCesh7h0!?f+9IFs`7OlEb@0WAnI zsMN(3yg^Ixzf0P<H3>VCb{QEx?pe6-YGa-}KxHhrO?Tb2M=UDWc76Hs1boQ$lpXlr zoo#zAY6rEw%B|mho-2*D{Ju8cBV*Anj+e_WX?F~G43CVww)$T)3DTNsWNtnk*c#<J z|9;{%fju#L%YhqKfX6ZV0Z%5{wR?B)$`Ig%Rt(qXf#bkz#nQ03>gIMkFJHYn6?jhy za91~Q^U{*I^~>Yd>&L}c{5${p-j$fZxRoKmRT`6lOE9OLehT#0?Qa(QzwTmKBL|Bv zyXT%_&9{LD01Fo2bgr)C6z=o?>w2eKtq;-Cz4rUsu>}lkY{80i64tIhH+%Wn1KV!u zYysMHG2@QNB<?e}*H^#(ezr<~`|_OvvI%dYv9-JO^t5M{XAQ3Jd$v0M|Fv2AqEoof zGG3boc6$WR`PujNOYK$zw=rF-Upb#KqP7XR%YZ@QWN$;+N`|>xSO1?|#|Aa4VK&Ew z>+XLUU&w%6v0+Ixb4>KMHNJBhU)VrIl$hce%(@l<yRblTfER2O149O|kbbcMm{F?s zt`lHjSmUubZhI^j!y3kemJaM+zkUTCgXX5lzz{JJ*hl{I^{XZe!x|%y6fX}?N>-K^ zSPHm42bh;MU{Y7Jww^hEKE0?Y$PlRU8gQEN+4JYXj+Y2CLs+CICxcZsaIqNhHc98S z^z_fbzEL`G=u1=VY&H`^L9PF1pgq8ASW<yk%Q!nbcQG+!I~4F;NCY0t0Bix405=wY z1`dFw0aq;ax;ZB@FnBBs@BntP*x1-M6+ic5Wo$52vS5!;2gXwWT_&JE*D!wrQNYA5 ehU8(21wPi_wUpU+_ejwykWrqlelF{r5}E*M3?;At From 096b50187a7d86c947a8a87f6b972ad5209bfb28 Mon Sep 17 00:00:00 2001 From: FingerlessGloves <me@FingerlessGloves.me> Date: Tue, 18 Mar 2025 02:01:37 +0000 Subject: [PATCH 46/54] Fix GetTime for GMT users (#13) UPS failes to use the required `+` when in the GMT timezone. Account for that. --------- Co-authored-by: Greg T. Wallace <greg@gregtwallace.com> --- pkg/apcssh/cmd_gettime.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/apcssh/cmd_gettime.go b/pkg/apcssh/cmd_gettime.go index 7da6397..139b0ba 100644 --- a/pkg/apcssh/cmd_gettime.go +++ b/pkg/apcssh/cmd_gettime.go @@ -28,6 +28,12 @@ func (cli *Client) GetTime() (time.Time, error) { formatUPSVal := datePieces[3] timeZoneVal := datePieces[4] + // GMT time requires + prefix + // APC UPS fails to use the required +, so add it + if timeZoneVal == "00:00" { + timeZoneVal = "+" + timeZoneVal + } + // known APC UPS format strings dateFormatVal := "" switch formatUPSVal { From ad8c4e88a927b56bf0b055ed8da546983f1111c5 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Mon, 17 Mar 2025 22:05:29 -0400 Subject: [PATCH 47/54] dep: go 1.24.1 --- .github/workflows/build_releases.yml | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_releases.yml b/.github/workflows/build_releases.yml index 54c4b74..7ef37de 100644 --- a/.github/workflows/build_releases.yml +++ b/.github/workflows/build_releases.yml @@ -8,7 +8,7 @@ on: env: GITHUB_REF: ${{ github.ref }} - GO_VERSION: '1.23.5' + GO_VERSION: '1.24.1' jobs: build-common: diff --git a/go.mod b/go.mod index f500bf1..9e40b33 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module apc-p15-tool -go 1.23.5 +go 1.24.1 require ( github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 From c67001f0e4210ada91bc74e1d4539ff5cdb046e5 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Mon, 17 Mar 2025 22:06:46 -0400 Subject: [PATCH 48/54] dep: update all --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 9e40b33..3c00d28 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,10 @@ go 1.24.1 require ( github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1 - golang.org/x/crypto v0.32.0 + golang.org/x/crypto v0.36.0 ) -require golang.org/x/sys v0.29.0 // indirect +require golang.org/x/sys v0.31.0 // indirect replace apc-p15-tool/cmd/install_only => /cmd/install_only diff --git a/go.sum b/go.sum index 3b8cd0d..950e8c5 100644 --- a/go.sum +++ b/go.sum @@ -4,11 +4,11 @@ github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 h1:aiqS8aBlF9PsAKeMddMSfbwp3smONCn3 github.com/peterbourgon/ff/v4 v4.0.0-alpha.4/go.mod h1:H/13DK46DKXy7EaIxPhk2Y0EC8aubKm35nBjBe8AAGc= github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1 h1:NVK+OqnavpyFmUiKfUMHrpvbCi2VFoWTrcpI7aDaJ2I= github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= From e87a3100d2762a6857fe0a12b017c834ddf5c561 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Mon, 17 Mar 2025 22:06:52 -0400 Subject: [PATCH 49/54] v1.2.1 --- CHANGELOG.md | 7 +++++++ pkg/app/app.go | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9260307..f99a0f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # APC P15 Tool Changelog +## [v1.2.1] - 2025-03-17 + +Fix time check for UPS when it is set to GMT timezone. + +All dependencies updated. + + ## [v1.2.0] - 2025-01-27 Add a new feature to `install` that checks the time of the UPS to confirm diff --git a/pkg/app/app.go b/pkg/app/app.go index 2392056..65b21be 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -12,7 +12,7 @@ import ( ) const ( - appVersion = "1.2.0" + appVersion = "1.2.1" ) // struct for receivers to use common app pieces From 1392529a3f5203477dbd095b030cd763ac233f15 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 22 Apr 2025 18:27:54 -0400 Subject: [PATCH 50/54] dep: go 1.24.2 --- .github/workflows/build_releases.yml | 2 +- go.mod | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_releases.yml b/.github/workflows/build_releases.yml index 7ef37de..3b7460d 100644 --- a/.github/workflows/build_releases.yml +++ b/.github/workflows/build_releases.yml @@ -8,7 +8,7 @@ on: env: GITHUB_REF: ${{ github.ref }} - GO_VERSION: '1.24.1' + GO_VERSION: '1.24.2' jobs: build-common: diff --git a/go.mod b/go.mod index 3c00d28..2161c8f 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module apc-p15-tool -go 1.24.1 +go 1.24.2 require ( github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 From 3bb6b2a3c19452fb348ede80c137b4e82dfc2857 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 22 Apr 2025 18:27:54 -0400 Subject: [PATCH 51/54] dep: update all --- go.mod | 4 ++-- go.sum | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 2161c8f..76eb3ca 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,10 @@ go 1.24.2 require ( github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1 - golang.org/x/crypto v0.36.0 + golang.org/x/crypto v0.37.0 ) -require golang.org/x/sys v0.31.0 // indirect +require golang.org/x/sys v0.32.0 // indirect replace apc-p15-tool/cmd/install_only => /cmd/install_only diff --git a/go.sum b/go.sum index 950e8c5..0b94b0b 100644 --- a/go.sum +++ b/go.sum @@ -4,11 +4,11 @@ github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 h1:aiqS8aBlF9PsAKeMddMSfbwp3smONCn3 github.com/peterbourgon/ff/v4 v4.0.0-alpha.4/go.mod h1:H/13DK46DKXy7EaIxPhk2Y0EC8aubKm35nBjBe8AAGc= github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1 h1:NVK+OqnavpyFmUiKfUMHrpvbCi2VFoWTrcpI7aDaJ2I= github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= From 72f3f42baa8b34a7bd4523725569d4919d53112d Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 22 Apr 2025 18:27:54 -0400 Subject: [PATCH 52/54] build: add darwin arm64 & amd64 --- .github/workflows/build_releases.yml | 174 +++++++++++++++++++++++++++ build.ps1 | 22 ++++ 2 files changed, 196 insertions(+) diff --git a/.github/workflows/build_releases.yml b/.github/workflows/build_releases.yml index 3b7460d..eb1e2ff 100644 --- a/.github/workflows/build_releases.yml +++ b/.github/workflows/build_releases.yml @@ -40,6 +40,8 @@ jobs: name: CHANGELOG.md path: ./CHANGELOG.md +### + build-linux-arm64: runs-on: ubuntu-24.04 steps: @@ -171,6 +173,90 @@ jobs: name: apc-p15-install-windows-amd64 path: ./apc-p15-install.exe + build-darwin-arm64: + runs-on: macos-15 + steps: + - name: Checkout Backend Repo + uses: actions/checkout@v4 + with: + repository: gregtwallace/apc-p15-tool + ref: ${{ env.GITHUB_REF }} + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '${{ env.GO_VERSION }}' + + - name: Build Tool + run: go build -o ./apc-p15-tool -v ./cmd/tool + env: + GOOS: darwin + GOARCH: arm64 + CGO_ENABLED: 0 + + - name: Save Compiled Binary + uses: actions/upload-artifact@v4 + with: + name: apc-p15-tool-darwin-arm64 + path: ./apc-p15-tool + + - name: Build Install Only + run: go build -o ./apc-p15-install -v ./cmd/install_only + env: + GOOS: darwin + GOARCH: arm64 + CGO_ENABLED: 0 + + - name: Save Compiled Binary + uses: actions/upload-artifact@v4 + with: + name: apc-p15-install-darwin-arm64 + path: ./apc-p15-install + + build-darwin-amd64: + runs-on: macos-13 + steps: + - name: Checkout Backend Repo + uses: actions/checkout@v4 + with: + repository: gregtwallace/apc-p15-tool + ref: ${{ env.GITHUB_REF }} + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '${{ env.GO_VERSION }}' + + - name: Build Tool + run: go build -o ./apc-p15-tool -v ./cmd/tool + env: + GOOS: darwin + GOARCH: amd64 + CGO_ENABLED: 0 + + - name: Save Compiled Binary + uses: actions/upload-artifact@v4 + with: + name: apc-p15-tool-darwin-amd64 + path: ./apc-p15-tool + + - name: Build Install Only + run: go build -o ./apc-p15-install -v ./cmd/install_only + env: + GOOS: darwin + GOARCH: amd64 + CGO_ENABLED: 0 + + - name: Save Compiled Binary + uses: actions/upload-artifact@v4 + with: + name: apc-p15-install-darwin-amd64 + path: ./apc-p15-install + +### + release-file-linux-arm64: needs: [build-common, build-linux-arm64] runs-on: ubuntu-24.04 @@ -302,3 +388,91 @@ jobs: with: name: apc-p15-tool_windows_amd64 path: ./release + + release-file-darwin-arm64: + needs: [build-common, build-darwin-arm64] + runs-on: ubuntu-24.04 + + steps: + - name: Make release directory + run: mkdir ./release + + - name: Download Tool Binary + uses: actions/download-artifact@v4 + with: + name: apc-p15-tool-darwin-arm64 + path: ./release + + - name: Download Install Binary + uses: actions/download-artifact@v4 + with: + name: apc-p15-install-darwin-arm64 + path: ./release + + - name: Download README + uses: actions/download-artifact@v4 + with: + name: README.md + path: ./release + + - name: Download LICENSE + uses: actions/download-artifact@v4 + with: + name: LICENSE.md + path: ./release + + - name: Download CHANGELOG + uses: actions/download-artifact@v4 + with: + name: CHANGELOG.md + path: ./release + + - name: Save Release + uses: actions/upload-artifact@v4 + with: + name: apc-p15-tool_darwin_arm64 + path: ./release + + release-file-darwin-amd64: + needs: [build-common, build-darwin-amd64] + runs-on: ubuntu-24.04 + + steps: + - name: Make release directory + run: mkdir ./release + + - name: Download Tool Binary + uses: actions/download-artifact@v4 + with: + name: apc-p15-tool-darwin-amd64 + path: ./release + + - name: Download Install Binary + uses: actions/download-artifact@v4 + with: + name: apc-p15-install-darwin-amd64 + path: ./release + + - name: Download README + uses: actions/download-artifact@v4 + with: + name: README.md + path: ./release + + - name: Download LICENSE + uses: actions/download-artifact@v4 + with: + name: LICENSE.md + path: ./release + + - name: Download CHANGELOG + uses: actions/download-artifact@v4 + with: + name: CHANGELOG.md + path: ./release + + - name: Save Release + uses: actions/upload-artifact@v4 + with: + name: apc-p15-tool_darwin_amd64 + path: ./release diff --git a/build.ps1 b/build.ps1 index 7147a33..0c8774b 100644 --- a/build.ps1 +++ b/build.ps1 @@ -34,3 +34,25 @@ $env:GOARCH = "arm64" $env:GOOS = "linux" $env:CGO_ENABLED = 0 go build -o $outDir/apc-p15-install-arm64 ./cmd/install_only + +# Darwin (MacOS) amd64 +$env:GOARCH = "amd64" +$env:GOOS = "darwin" +$env:CGO_ENABLED = 0 +go build -o $outDir/apc-p15-tool-darwin-amd64 ./cmd/tool + +$env:GOARCH = "amd64" +$env:GOOS = "darwin" +$env:CGO_ENABLED = 0 +go build -o $outDir/apc-p15-install-darwin-amd64 ./cmd/install_only + +# Darwin (MacOS) arm64 +$env:GOARCH = "arm64" +$env:GOOS = "darwin" +$env:CGO_ENABLED = 0 +go build -o $outDir/apc-p15-tool-darwin-arm64 ./cmd/tool + +$env:GOARCH = "arm64" +$env:GOOS = "darwin" +$env:CGO_ENABLED = 0 +go build -o $outDir/apc-p15-install-darwin-arm64 ./cmd/install_only From 124c06d8be1e21786c5dfcf3d6d3f70cb5430677 Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 22 Apr 2025 18:27:55 -0400 Subject: [PATCH 53/54] build: compile linux/arm64 in native runner --- .github/workflows/build_releases.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/build_releases.yml b/.github/workflows/build_releases.yml index eb1e2ff..5b41dbe 100644 --- a/.github/workflows/build_releases.yml +++ b/.github/workflows/build_releases.yml @@ -43,7 +43,7 @@ jobs: ### build-linux-arm64: - runs-on: ubuntu-24.04 + runs-on: ubuntu-24.04-arm steps: - name: Checkout Repo uses: actions/checkout@v4 @@ -52,12 +52,6 @@ jobs: ref: ${{ env.GITHUB_REF }} fetch-depth: 0 - - name: Update apt - run: sudo apt update - - - name: Install cross-compiler for linux/arm64 - run: sudo apt-get -y install gcc-aarch64-linux-gnu - - name: Set up Go uses: actions/setup-go@v5 with: @@ -68,7 +62,6 @@ jobs: env: GOOS: linux GOARCH: arm64 - CC: aarch64-linux-gnu-gcc CGO_ENABLED: 0 - name: Save Compiled Binary From 86feabd9390c26705ec9878be8d55fb67ff7a84a Mon Sep 17 00:00:00 2001 From: "Greg T. Wallace" <greg@gregtwallace.com> Date: Tue, 22 Apr 2025 18:27:55 -0400 Subject: [PATCH 54/54] v1.2.2 --- CHANGELOG.md | 7 +++++++ pkg/app/app.go | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f99a0f0..cc534a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # APC P15 Tool Changelog +## [v1.2.2] - 2025-04-22 + +All dependencies updated. + +Add darwin arm64 and amd64 builds. + + ## [v1.2.1] - 2025-03-17 Fix time check for UPS when it is set to GMT timezone. diff --git a/pkg/app/app.go b/pkg/app/app.go index 65b21be..052a00e 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -12,7 +12,7 @@ import ( ) const ( - appVersion = "1.2.1" + appVersion = "1.2.2" ) // struct for receivers to use common app pieces